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);
537 static GtkActionEntry compose_popup_entries[] =
539 {"Compose", NULL, "Compose" },
540 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
541 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
542 {"Compose/---", NULL, "---", NULL, NULL, NULL },
543 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
546 static GtkActionEntry compose_entries[] =
548 {"Menu", NULL, "Menu" },
550 {"Message", NULL, N_("_Message") },
551 {"Edit", NULL, N_("_Edit") },
553 {"Spelling", NULL, N_("_Spelling") },
555 {"Options", NULL, N_("_Options") },
556 {"Tools", NULL, N_("_Tools") },
557 {"Help", NULL, N_("_Help") },
559 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
560 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
561 {"Message/---", NULL, "---" },
563 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
564 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
565 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
566 /* {"Message/---", NULL, "---" }, */
567 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
568 /* {"Message/---", NULL, "---" }, */
569 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
572 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
573 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
574 {"Edit/---", NULL, "---" },
576 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
577 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
578 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
580 {"Edit/SpecialPaste", NULL, N_("Special paste") },
581 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
582 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
583 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
585 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
587 {"Edit/Advanced", NULL, N_("A_dvanced") },
588 {"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*/
589 {"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*/
590 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
591 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
592 {"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*/
593 {"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*/
594 {"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*/
595 {"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*/
596 {"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*/
597 {"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*/
598 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
599 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
600 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
601 {"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*/
603 /* {"Edit/---", NULL, "---" }, */
604 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
606 /* {"Edit/---", NULL, "---" }, */
607 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
608 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
609 /* {"Edit/---", NULL, "---" }, */
610 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
613 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
614 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
615 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
616 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
618 {"Spelling/---", NULL, "---" },
619 {"Spelling/Options", NULL, N_("_Options") },
624 {"Options/ReplyMode", NULL, N_("Reply _mode") },
625 {"Options/---", NULL, "---" },
626 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
627 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
629 /* {"Options/---", NULL, "---" }, */
631 {"Options/Priority", NULL, N_("_Priority") },
633 {"Options/Encoding", NULL, N_("Character _encoding") },
634 {"Options/Encoding/---", NULL, "---" },
635 #define ENC_ACTION(cs_char,c_char,string) \
636 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
638 {"Options/Encoding/Western", NULL, N_("Western European") },
639 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
640 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
641 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
642 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
643 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
644 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
645 {"Options/Encoding/Korean", NULL, N_("Korean") },
646 {"Options/Encoding/Thai", NULL, N_("Thai") },
649 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
651 {"Tools/Template", NULL, N_("_Template") },
652 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
653 {"Tools/Actions", NULL, N_("Actio_ns") },
654 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
657 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
660 static GtkToggleActionEntry compose_toggle_entries[] =
662 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
663 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
664 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
665 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
666 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
667 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
668 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
671 static GtkRadioActionEntry compose_radio_rm_entries[] =
673 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
674 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
675 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
676 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
679 static GtkRadioActionEntry compose_radio_prio_entries[] =
681 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
682 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
683 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
684 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
685 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
688 static GtkRadioActionEntry compose_radio_enc_entries[] =
690 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
723 static GtkTargetEntry compose_mime_types[] =
725 {"text/uri-list", 0, 0},
726 {"UTF8_STRING", 0, 0},
730 static gboolean compose_put_existing_to_front(MsgInfo *info)
732 GList *compose_list = compose_get_compose_list();
736 for (elem = compose_list; elem != NULL && elem->data != NULL;
738 Compose *c = (Compose*)elem->data;
740 if (!c->targetinfo || !c->targetinfo->msgid ||
744 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
745 gtkut_window_popup(c->window);
753 static GdkColor quote_color1 =
754 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
755 static GdkColor quote_color2 =
756 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
757 static GdkColor quote_color3 =
758 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
760 static GdkColor quote_bgcolor1 =
761 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
762 static GdkColor quote_bgcolor2 =
763 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
764 static GdkColor quote_bgcolor3 =
765 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor signature_color = {
774 static GdkColor uri_color = {
781 static void compose_create_tags(GtkTextView *text, Compose *compose)
783 GtkTextBuffer *buffer;
784 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 buffer = gtk_text_view_get_buffer(text);
792 if (prefs_common.enable_color) {
793 /* grab the quote colors, converting from an int to a GdkColor */
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
796 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
798 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
800 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
802 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
804 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
806 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
808 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
811 signature_color = quote_color1 = quote_color2 = quote_color3 =
812 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
815 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
816 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
817 "foreground-gdk", "e_color1,
818 "paragraph-background-gdk", "e_bgcolor1,
820 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
821 "foreground-gdk", "e_color2,
822 "paragraph-background-gdk", "e_bgcolor2,
824 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
825 "foreground-gdk", "e_color3,
826 "paragraph-background-gdk", "e_bgcolor3,
829 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
830 "foreground-gdk", "e_color1,
832 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
833 "foreground-gdk", "e_color2,
835 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
836 "foreground-gdk", "e_color3,
840 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
841 "foreground-gdk", &signature_color,
844 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
845 "foreground-gdk", &uri_color,
847 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
848 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
850 color[0] = quote_color1;
851 color[1] = quote_color2;
852 color[2] = quote_color3;
853 color[3] = quote_bgcolor1;
854 color[4] = quote_bgcolor2;
855 color[5] = quote_bgcolor3;
856 color[6] = signature_color;
857 color[7] = uri_color;
858 cmap = gdk_drawable_get_colormap(compose->window->window);
859 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
861 for (i = 0; i < 8; i++) {
862 if (success[i] == FALSE) {
865 g_warning("Compose: color allocation failed.\n");
866 style = gtk_widget_get_style(GTK_WIDGET(text));
867 quote_color1 = quote_color2 = quote_color3 =
868 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
869 signature_color = uri_color = black;
874 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
875 GPtrArray *attach_files)
877 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
880 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
882 return compose_generic_new(account, mailto, item, NULL, NULL);
885 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
887 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
890 #define SCROLL_TO_CURSOR(compose) { \
891 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
892 gtk_text_view_get_buffer( \
893 GTK_TEXT_VIEW(compose->text))); \
894 gtk_text_view_scroll_mark_onscreen( \
895 GTK_TEXT_VIEW(compose->text), \
899 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
902 if (folderidentifier) {
903 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
904 prefs_common.compose_save_to_history = add_history(
905 prefs_common.compose_save_to_history, folderidentifier);
906 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
907 prefs_common.compose_save_to_history);
910 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
911 if (folderidentifier)
912 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
914 gtk_entry_set_text(GTK_ENTRY(entry), "");
917 static gchar *compose_get_save_to(Compose *compose)
920 gchar *result = NULL;
921 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
922 result = gtk_editable_get_chars(entry, 0, -1);
925 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
926 prefs_common.compose_save_to_history = add_history(
927 prefs_common.compose_save_to_history, result);
928 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
929 prefs_common.compose_save_to_history);
934 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
935 GPtrArray *attach_files, GList *listAddress )
938 GtkTextView *textview;
939 GtkTextBuffer *textbuf;
941 const gchar *subject_format = NULL;
942 const gchar *body_format = NULL;
943 gchar *mailto_from = NULL;
944 PrefsAccount *mailto_account = NULL;
945 MsgInfo* dummyinfo = NULL;
946 MailField mfield = NO_FIELD_PRESENT;
950 /* check if mailto defines a from */
951 if (mailto && *mailto != '\0') {
952 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
953 /* mailto defines a from, check if we can get account prefs from it,
954 if not, the account prefs will be guessed using other ways, but we'll keep
957 mailto_account = account_find_from_address(mailto_from, TRUE);
959 account = mailto_account;
962 /* if no account prefs set from mailto, set if from folder prefs (if any) */
963 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
964 account = account_find_from_id(item->prefs->default_account);
966 /* if no account prefs set, fallback to the current one */
967 if (!account) account = cur_account;
968 cm_return_val_if_fail(account != NULL, NULL);
970 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
972 /* override from name if mailto asked for it */
974 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
977 /* override from name according to folder properties */
978 if (item && item->prefs &&
979 item->prefs->compose_with_format &&
980 item->prefs->compose_override_from_format &&
981 *item->prefs->compose_override_from_format != '\0') {
986 dummyinfo = compose_msginfo_new_from_compose(compose);
988 /* decode \-escape sequences in the internal representation of the quote format */
989 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
990 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
993 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
996 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
998 quote_fmt_scan_string(tmp);
1001 buf = quote_fmt_get_buffer();
1003 alertpanel_error(_("New message From format error."));
1005 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1006 quote_fmt_reset_vartable();
1011 compose->replyinfo = NULL;
1012 compose->fwdinfo = NULL;
1014 textview = GTK_TEXT_VIEW(compose->text);
1015 textbuf = gtk_text_view_get_buffer(textview);
1016 compose_create_tags(textview, compose);
1018 undo_block(compose->undostruct);
1020 compose_set_dictionaries_from_folder_prefs(compose, item);
1023 if (account->auto_sig)
1024 compose_insert_sig(compose, FALSE);
1025 gtk_text_buffer_get_start_iter(textbuf, &iter);
1026 gtk_text_buffer_place_cursor(textbuf, &iter);
1028 if (account->protocol != A_NNTP) {
1029 if (mailto && *mailto != '\0') {
1030 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1032 } else if (item && item->prefs) {
1033 if (item->prefs->enable_default_bcc) {
1034 compose_entry_append(compose, item->prefs->default_bcc,
1035 COMPOSE_BCC, PREF_FOLDER);
1037 if (item->prefs->enable_default_cc) {
1038 compose_entry_append(compose, item->prefs->default_cc,
1039 COMPOSE_CC, PREF_FOLDER);
1041 if (item->prefs->enable_default_replyto) {
1042 compose_entry_append(compose, item->prefs->default_replyto,
1043 COMPOSE_REPLYTO, PREF_FOLDER);
1045 if (item->prefs->enable_default_to) {
1046 compose_entry_append(compose, item->prefs->default_to,
1047 COMPOSE_TO, PREF_FOLDER);
1048 compose_entry_mark_default_to(compose, item->prefs->default_to);
1051 if (item && item->ret_rcpt) {
1052 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1055 if (mailto && *mailto != '\0') {
1056 if (!strchr(mailto, '@'))
1057 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1059 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1060 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1061 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1062 mfield = TO_FIELD_PRESENT;
1065 * CLAWS: just don't allow return receipt request, even if the user
1066 * may want to send an email. simple but foolproof.
1068 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1070 compose_add_field_list( compose, listAddress );
1072 if (item && item->prefs && item->prefs->compose_with_format) {
1073 subject_format = item->prefs->compose_subject_format;
1074 body_format = item->prefs->compose_body_format;
1075 } else if (account->compose_with_format) {
1076 subject_format = account->compose_subject_format;
1077 body_format = account->compose_body_format;
1078 } else if (prefs_common.compose_with_format) {
1079 subject_format = prefs_common.compose_subject_format;
1080 body_format = prefs_common.compose_body_format;
1083 if (subject_format || body_format) {
1086 && *subject_format != '\0' )
1088 gchar *subject = NULL;
1093 dummyinfo = compose_msginfo_new_from_compose(compose);
1095 /* decode \-escape sequences in the internal representation of the quote format */
1096 tmp = malloc(strlen(subject_format)+1);
1097 pref_get_unescaped_pref(tmp, subject_format);
1099 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1101 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1102 compose->gtkaspell);
1104 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1106 quote_fmt_scan_string(tmp);
1109 buf = quote_fmt_get_buffer();
1111 alertpanel_error(_("New message subject format error."));
1113 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1114 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1115 quote_fmt_reset_vartable();
1119 mfield = SUBJECT_FIELD_PRESENT;
1123 && *body_format != '\0' )
1126 GtkTextBuffer *buffer;
1127 GtkTextIter start, end;
1131 dummyinfo = compose_msginfo_new_from_compose(compose);
1133 text = GTK_TEXT_VIEW(compose->text);
1134 buffer = gtk_text_view_get_buffer(text);
1135 gtk_text_buffer_get_start_iter(buffer, &start);
1136 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1137 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1139 compose_quote_fmt(compose, dummyinfo,
1141 NULL, tmp, FALSE, TRUE,
1142 _("The body of the \"New message\" template has an error at line %d."));
1143 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1144 quote_fmt_reset_vartable();
1148 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1149 gtkaspell_highlight_all(compose->gtkaspell);
1151 mfield = BODY_FIELD_PRESENT;
1155 procmsg_msginfo_free( dummyinfo );
1161 for (i = 0; i < attach_files->len; i++) {
1162 file = g_ptr_array_index(attach_files, i);
1163 compose_attach_append(compose, file, file, NULL);
1167 compose_show_first_last_header(compose, TRUE);
1169 /* Set save folder */
1170 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1171 gchar *folderidentifier;
1173 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1174 folderidentifier = folder_item_get_identifier(item);
1175 compose_set_save_to(compose, folderidentifier);
1176 g_free(folderidentifier);
1179 /* Place cursor according to provided input (mfield) */
1181 case NO_FIELD_PRESENT:
1182 if (compose->header_last)
1183 gtk_widget_grab_focus(compose->header_last->entry);
1185 case TO_FIELD_PRESENT:
1186 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1188 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1191 gtk_widget_grab_focus(compose->subject_entry);
1193 case SUBJECT_FIELD_PRESENT:
1194 textview = GTK_TEXT_VIEW(compose->text);
1197 textbuf = gtk_text_view_get_buffer(textview);
1200 mark = gtk_text_buffer_get_insert(textbuf);
1201 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1202 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1204 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1205 * only defers where it comes to the variable body
1206 * is not null. If no body is present compose->text
1207 * will be null in which case you cannot place the
1208 * cursor inside the component so. An empty component
1209 * is therefore created before placing the cursor
1211 case BODY_FIELD_PRESENT:
1212 gtk_widget_grab_focus(compose->text);
1216 undo_unblock(compose->undostruct);
1218 if (prefs_common.auto_exteditor)
1219 compose_exec_ext_editor(compose);
1221 compose->draft_timeout_tag = -1;
1222 SCROLL_TO_CURSOR(compose);
1224 compose->modified = FALSE;
1225 compose_set_title(compose);
1227 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1232 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1233 gboolean override_pref, const gchar *system)
1235 const gchar *privacy = NULL;
1237 cm_return_if_fail(compose != NULL);
1238 cm_return_if_fail(account != NULL);
1240 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1245 else if (account->default_privacy_system
1246 && strlen(account->default_privacy_system)) {
1247 privacy = account->default_privacy_system;
1249 GSList *privacy_avail = privacy_get_system_ids();
1250 if (privacy_avail && g_slist_length(privacy_avail)) {
1251 privacy = (gchar *)(privacy_avail->data);
1254 if (privacy != NULL) {
1256 g_free(compose->privacy_system);
1257 compose->privacy_system = NULL;
1259 if (compose->privacy_system == NULL)
1260 compose->privacy_system = g_strdup(privacy);
1261 else if (*(compose->privacy_system) == '\0') {
1262 g_free(compose->privacy_system);
1263 compose->privacy_system = g_strdup(privacy);
1265 compose_update_privacy_system_menu_item(compose, FALSE);
1266 compose_use_encryption(compose, TRUE);
1270 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1272 const gchar *privacy = NULL;
1276 else if (account->default_privacy_system
1277 && strlen(account->default_privacy_system)) {
1278 privacy = account->default_privacy_system;
1280 GSList *privacy_avail = privacy_get_system_ids();
1281 if (privacy_avail && g_slist_length(privacy_avail)) {
1282 privacy = (gchar *)(privacy_avail->data);
1286 if (privacy != NULL) {
1288 g_free(compose->privacy_system);
1289 compose->privacy_system = NULL;
1291 if (compose->privacy_system == NULL)
1292 compose->privacy_system = g_strdup(privacy);
1293 compose_update_privacy_system_menu_item(compose, FALSE);
1294 compose_use_signing(compose, TRUE);
1298 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1302 Compose *compose = NULL;
1304 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1306 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1307 cm_return_val_if_fail(msginfo != NULL, NULL);
1309 list_len = g_slist_length(msginfo_list);
1313 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1314 FALSE, prefs_common.default_reply_list, FALSE, body);
1316 case COMPOSE_REPLY_WITH_QUOTE:
1317 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1318 FALSE, prefs_common.default_reply_list, FALSE, body);
1320 case COMPOSE_REPLY_WITHOUT_QUOTE:
1321 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1322 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1324 case COMPOSE_REPLY_TO_SENDER:
1325 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1326 FALSE, FALSE, TRUE, body);
1328 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1329 compose = compose_followup_and_reply_to(msginfo,
1330 COMPOSE_QUOTE_CHECK,
1331 FALSE, FALSE, body);
1333 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1334 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1335 FALSE, FALSE, TRUE, body);
1337 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1338 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1339 FALSE, FALSE, TRUE, NULL);
1341 case COMPOSE_REPLY_TO_ALL:
1342 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1343 TRUE, FALSE, FALSE, body);
1345 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1346 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1347 TRUE, FALSE, FALSE, body);
1349 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1350 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1351 TRUE, FALSE, FALSE, NULL);
1353 case COMPOSE_REPLY_TO_LIST:
1354 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1355 FALSE, TRUE, FALSE, body);
1357 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1358 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1359 FALSE, TRUE, FALSE, body);
1361 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1362 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1363 FALSE, TRUE, FALSE, NULL);
1365 case COMPOSE_FORWARD:
1366 if (prefs_common.forward_as_attachment) {
1367 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1370 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1374 case COMPOSE_FORWARD_INLINE:
1375 /* check if we reply to more than one Message */
1376 if (list_len == 1) {
1377 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1380 /* more messages FALL THROUGH */
1381 case COMPOSE_FORWARD_AS_ATTACH:
1382 compose = compose_forward_multiple(NULL, msginfo_list);
1384 case COMPOSE_REDIRECT:
1385 compose = compose_redirect(NULL, msginfo, FALSE);
1388 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1391 if (compose == NULL) {
1392 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1396 compose->rmode = mode;
1397 switch (compose->rmode) {
1399 case COMPOSE_REPLY_WITH_QUOTE:
1400 case COMPOSE_REPLY_WITHOUT_QUOTE:
1401 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1402 debug_print("reply mode Normal\n");
1403 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1404 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1406 case COMPOSE_REPLY_TO_SENDER:
1407 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1408 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1409 debug_print("reply mode Sender\n");
1410 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1412 case COMPOSE_REPLY_TO_ALL:
1413 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1414 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1415 debug_print("reply mode All\n");
1416 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1418 case COMPOSE_REPLY_TO_LIST:
1419 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1420 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1421 debug_print("reply mode List\n");
1422 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1430 static Compose *compose_reply(MsgInfo *msginfo,
1431 ComposeQuoteMode quote_mode,
1437 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1438 to_sender, FALSE, body);
1441 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1442 ComposeQuoteMode quote_mode,
1447 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1448 to_sender, TRUE, body);
1451 static void compose_extract_original_charset(Compose *compose)
1453 MsgInfo *info = NULL;
1454 if (compose->replyinfo) {
1455 info = compose->replyinfo;
1456 } else if (compose->fwdinfo) {
1457 info = compose->fwdinfo;
1458 } else if (compose->targetinfo) {
1459 info = compose->targetinfo;
1462 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1463 MimeInfo *partinfo = mimeinfo;
1464 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1465 partinfo = procmime_mimeinfo_next(partinfo);
1467 compose->orig_charset =
1468 g_strdup(procmime_mimeinfo_get_parameter(
1469 partinfo, "charset"));
1471 procmime_mimeinfo_free_all(mimeinfo);
1475 #define SIGNAL_BLOCK(buffer) { \
1476 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1477 G_CALLBACK(compose_changed_cb), \
1479 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1480 G_CALLBACK(text_inserted), \
1484 #define SIGNAL_UNBLOCK(buffer) { \
1485 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1486 G_CALLBACK(compose_changed_cb), \
1488 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1489 G_CALLBACK(text_inserted), \
1493 static Compose *compose_generic_reply(MsgInfo *msginfo,
1494 ComposeQuoteMode quote_mode,
1495 gboolean to_all, gboolean to_ml,
1497 gboolean followup_and_reply_to,
1501 PrefsAccount *account = NULL;
1502 GtkTextView *textview;
1503 GtkTextBuffer *textbuf;
1504 gboolean quote = FALSE;
1505 const gchar *qmark = NULL;
1506 const gchar *body_fmt = NULL;
1507 gchar *s_system = NULL;
1509 cm_return_val_if_fail(msginfo != NULL, NULL);
1510 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1512 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1514 cm_return_val_if_fail(account != NULL, NULL);
1516 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1518 compose->updating = TRUE;
1520 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1521 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1523 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1524 if (!compose->replyinfo)
1525 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1527 compose_extract_original_charset(compose);
1529 if (msginfo->folder && msginfo->folder->ret_rcpt)
1530 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1532 /* Set save folder */
1533 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1534 gchar *folderidentifier;
1536 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1537 folderidentifier = folder_item_get_identifier(msginfo->folder);
1538 compose_set_save_to(compose, folderidentifier);
1539 g_free(folderidentifier);
1542 if (compose_parse_header(compose, msginfo) < 0) {
1543 compose->updating = FALSE;
1544 compose_destroy(compose);
1548 /* override from name according to folder properties */
1549 if (msginfo->folder && msginfo->folder->prefs &&
1550 msginfo->folder->prefs->reply_with_format &&
1551 msginfo->folder->prefs->reply_override_from_format &&
1552 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1557 /* decode \-escape sequences in the internal representation of the quote format */
1558 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1559 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1562 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1563 compose->gtkaspell);
1565 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1567 quote_fmt_scan_string(tmp);
1570 buf = quote_fmt_get_buffer();
1572 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1574 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1575 quote_fmt_reset_vartable();
1580 textview = (GTK_TEXT_VIEW(compose->text));
1581 textbuf = gtk_text_view_get_buffer(textview);
1582 compose_create_tags(textview, compose);
1584 undo_block(compose->undostruct);
1586 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1589 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1590 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1591 /* use the reply format of folder (if enabled), or the account's one
1592 (if enabled) or fallback to the global reply format, which is always
1593 enabled (even if empty), and use the relevant quotemark */
1595 if (msginfo->folder && msginfo->folder->prefs &&
1596 msginfo->folder->prefs->reply_with_format) {
1597 qmark = msginfo->folder->prefs->reply_quotemark;
1598 body_fmt = msginfo->folder->prefs->reply_body_format;
1600 } else if (account->reply_with_format) {
1601 qmark = account->reply_quotemark;
1602 body_fmt = account->reply_body_format;
1605 qmark = prefs_common.quotemark;
1606 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1607 body_fmt = gettext(prefs_common.quotefmt);
1614 /* empty quotemark is not allowed */
1615 if (qmark == NULL || *qmark == '\0')
1617 compose_quote_fmt(compose, compose->replyinfo,
1618 body_fmt, qmark, body, FALSE, TRUE,
1619 _("The body of the \"Reply\" template has an error at line %d."));
1620 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1621 quote_fmt_reset_vartable();
1623 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1624 gtkaspell_highlight_all(compose->gtkaspell);
1628 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1629 compose_force_encryption(compose, account, FALSE, s_system);
1632 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1633 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1634 compose_force_signing(compose, account, s_system);
1638 SIGNAL_BLOCK(textbuf);
1640 if (account->auto_sig)
1641 compose_insert_sig(compose, FALSE);
1643 compose_wrap_all(compose);
1645 SIGNAL_UNBLOCK(textbuf);
1647 gtk_widget_grab_focus(compose->text);
1649 undo_unblock(compose->undostruct);
1651 if (prefs_common.auto_exteditor)
1652 compose_exec_ext_editor(compose);
1654 compose->modified = FALSE;
1655 compose_set_title(compose);
1657 compose->updating = FALSE;
1658 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1659 SCROLL_TO_CURSOR(compose);
1661 if (compose->deferred_destroy) {
1662 compose_destroy(compose);
1670 #define INSERT_FW_HEADER(var, hdr) \
1671 if (msginfo->var && *msginfo->var) { \
1672 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1673 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1674 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1677 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1678 gboolean as_attach, const gchar *body,
1679 gboolean no_extedit,
1683 GtkTextView *textview;
1684 GtkTextBuffer *textbuf;
1687 cm_return_val_if_fail(msginfo != NULL, NULL);
1688 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1691 !(account = compose_guess_forward_account_from_msginfo
1693 account = cur_account;
1695 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1697 compose->updating = TRUE;
1698 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1699 if (!compose->fwdinfo)
1700 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1702 compose_extract_original_charset(compose);
1704 if (msginfo->subject && *msginfo->subject) {
1705 gchar *buf, *buf2, *p;
1707 buf = p = g_strdup(msginfo->subject);
1708 p += subject_get_prefix_length(p);
1709 memmove(buf, p, strlen(p) + 1);
1711 buf2 = g_strdup_printf("Fw: %s", buf);
1712 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1718 /* override from name according to folder properties */
1719 if (msginfo->folder && msginfo->folder->prefs &&
1720 msginfo->folder->prefs->forward_with_format &&
1721 msginfo->folder->prefs->forward_override_from_format &&
1722 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1726 MsgInfo *full_msginfo = NULL;
1729 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1731 full_msginfo = procmsg_msginfo_copy(msginfo);
1733 /* decode \-escape sequences in the internal representation of the quote format */
1734 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1735 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1738 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1739 compose->gtkaspell);
1741 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1743 quote_fmt_scan_string(tmp);
1746 buf = quote_fmt_get_buffer();
1748 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1750 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1751 quote_fmt_reset_vartable();
1754 procmsg_msginfo_free(full_msginfo);
1757 textview = GTK_TEXT_VIEW(compose->text);
1758 textbuf = gtk_text_view_get_buffer(textview);
1759 compose_create_tags(textview, compose);
1761 undo_block(compose->undostruct);
1765 msgfile = procmsg_get_message_file(msginfo);
1766 if (!is_file_exist(msgfile))
1767 g_warning("%s: file not exist\n", msgfile);
1769 compose_attach_append(compose, msgfile, msgfile,
1774 const gchar *qmark = NULL;
1775 const gchar *body_fmt = NULL;
1776 MsgInfo *full_msginfo;
1778 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1779 body_fmt = gettext(prefs_common.fw_quotefmt);
1783 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1785 full_msginfo = procmsg_msginfo_copy(msginfo);
1787 /* use the forward format of folder (if enabled), or the account's one
1788 (if enabled) or fallback to the global forward format, which is always
1789 enabled (even if empty), and use the relevant quotemark */
1790 if (msginfo->folder && msginfo->folder->prefs &&
1791 msginfo->folder->prefs->forward_with_format) {
1792 qmark = msginfo->folder->prefs->forward_quotemark;
1793 body_fmt = msginfo->folder->prefs->forward_body_format;
1795 } else if (account->forward_with_format) {
1796 qmark = account->forward_quotemark;
1797 body_fmt = account->forward_body_format;
1800 qmark = prefs_common.fw_quotemark;
1801 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1802 body_fmt = gettext(prefs_common.fw_quotefmt);
1807 /* empty quotemark is not allowed */
1808 if (qmark == NULL || *qmark == '\0')
1811 compose_quote_fmt(compose, full_msginfo,
1812 body_fmt, qmark, body, FALSE, TRUE,
1813 _("The body of the \"Forward\" template has an error at line %d."));
1814 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1815 quote_fmt_reset_vartable();
1816 compose_attach_parts(compose, msginfo);
1818 procmsg_msginfo_free(full_msginfo);
1820 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1821 gtkaspell_highlight_all(compose->gtkaspell);
1825 SIGNAL_BLOCK(textbuf);
1827 if (account->auto_sig)
1828 compose_insert_sig(compose, FALSE);
1830 compose_wrap_all(compose);
1832 SIGNAL_UNBLOCK(textbuf);
1834 gtk_text_buffer_get_start_iter(textbuf, &iter);
1835 gtk_text_buffer_place_cursor(textbuf, &iter);
1837 gtk_widget_grab_focus(compose->header_last->entry);
1839 if (!no_extedit && prefs_common.auto_exteditor)
1840 compose_exec_ext_editor(compose);
1843 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1844 gchar *folderidentifier;
1846 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1847 folderidentifier = folder_item_get_identifier(msginfo->folder);
1848 compose_set_save_to(compose, folderidentifier);
1849 g_free(folderidentifier);
1852 undo_unblock(compose->undostruct);
1854 compose->modified = FALSE;
1855 compose_set_title(compose);
1857 compose->updating = FALSE;
1858 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1859 SCROLL_TO_CURSOR(compose);
1861 if (compose->deferred_destroy) {
1862 compose_destroy(compose);
1866 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1871 #undef INSERT_FW_HEADER
1873 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1876 GtkTextView *textview;
1877 GtkTextBuffer *textbuf;
1881 gboolean single_mail = TRUE;
1883 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1885 if (g_slist_length(msginfo_list) > 1)
1886 single_mail = FALSE;
1888 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1889 if (((MsgInfo *)msginfo->data)->folder == NULL)
1892 /* guess account from first selected message */
1894 !(account = compose_guess_forward_account_from_msginfo
1895 (msginfo_list->data)))
1896 account = cur_account;
1898 cm_return_val_if_fail(account != NULL, NULL);
1900 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1901 if (msginfo->data) {
1902 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1903 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1907 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1908 g_warning("no msginfo_list");
1912 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1914 compose->updating = TRUE;
1916 /* override from name according to folder properties */
1917 if (msginfo_list->data) {
1918 MsgInfo *msginfo = msginfo_list->data;
1920 if (msginfo->folder && msginfo->folder->prefs &&
1921 msginfo->folder->prefs->forward_with_format &&
1922 msginfo->folder->prefs->forward_override_from_format &&
1923 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1928 /* decode \-escape sequences in the internal representation of the quote format */
1929 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1930 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1933 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1934 compose->gtkaspell);
1936 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1938 quote_fmt_scan_string(tmp);
1941 buf = quote_fmt_get_buffer();
1943 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1945 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1946 quote_fmt_reset_vartable();
1952 textview = GTK_TEXT_VIEW(compose->text);
1953 textbuf = gtk_text_view_get_buffer(textview);
1954 compose_create_tags(textview, compose);
1956 undo_block(compose->undostruct);
1957 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1958 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1960 if (!is_file_exist(msgfile))
1961 g_warning("%s: file not exist\n", msgfile);
1963 compose_attach_append(compose, msgfile, msgfile,
1969 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1970 if (info->subject && *info->subject) {
1971 gchar *buf, *buf2, *p;
1973 buf = p = g_strdup(info->subject);
1974 p += subject_get_prefix_length(p);
1975 memmove(buf, p, strlen(p) + 1);
1977 buf2 = g_strdup_printf("Fw: %s", buf);
1978 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1984 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1985 _("Fw: multiple emails"));
1988 SIGNAL_BLOCK(textbuf);
1990 if (account->auto_sig)
1991 compose_insert_sig(compose, FALSE);
1993 compose_wrap_all(compose);
1995 SIGNAL_UNBLOCK(textbuf);
1997 gtk_text_buffer_get_start_iter(textbuf, &iter);
1998 gtk_text_buffer_place_cursor(textbuf, &iter);
2000 gtk_widget_grab_focus(compose->header_last->entry);
2001 undo_unblock(compose->undostruct);
2002 compose->modified = FALSE;
2003 compose_set_title(compose);
2005 compose->updating = FALSE;
2006 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2007 SCROLL_TO_CURSOR(compose);
2009 if (compose->deferred_destroy) {
2010 compose_destroy(compose);
2014 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2019 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2021 GtkTextIter start = *iter;
2022 GtkTextIter end_iter;
2023 int start_pos = gtk_text_iter_get_offset(&start);
2025 if (!compose->account->sig_sep)
2028 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2029 start_pos+strlen(compose->account->sig_sep));
2031 /* check sig separator */
2032 str = gtk_text_iter_get_text(&start, &end_iter);
2033 if (!strcmp(str, compose->account->sig_sep)) {
2035 /* check end of line (\n) */
2036 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2037 start_pos+strlen(compose->account->sig_sep));
2038 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2039 start_pos+strlen(compose->account->sig_sep)+1);
2040 tmp = gtk_text_iter_get_text(&start, &end_iter);
2041 if (!strcmp(tmp,"\n")) {
2053 static void compose_colorize_signature(Compose *compose)
2055 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2057 GtkTextIter end_iter;
2058 gtk_text_buffer_get_start_iter(buffer, &iter);
2059 while (gtk_text_iter_forward_line(&iter))
2060 if (compose_is_sig_separator(compose, buffer, &iter)) {
2061 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2062 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2066 #define BLOCK_WRAP() { \
2067 prev_autowrap = compose->autowrap; \
2068 buffer = gtk_text_view_get_buffer( \
2069 GTK_TEXT_VIEW(compose->text)); \
2070 compose->autowrap = FALSE; \
2072 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2073 G_CALLBACK(compose_changed_cb), \
2075 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2076 G_CALLBACK(text_inserted), \
2079 #define UNBLOCK_WRAP() { \
2080 compose->autowrap = prev_autowrap; \
2081 if (compose->autowrap) { \
2082 gint old = compose->draft_timeout_tag; \
2083 compose->draft_timeout_tag = -2; \
2084 compose_wrap_all(compose); \
2085 compose->draft_timeout_tag = old; \
2088 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2089 G_CALLBACK(compose_changed_cb), \
2091 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2092 G_CALLBACK(text_inserted), \
2096 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2098 Compose *compose = NULL;
2099 PrefsAccount *account = NULL;
2100 GtkTextView *textview;
2101 GtkTextBuffer *textbuf;
2105 gchar buf[BUFFSIZE];
2106 gboolean use_signing = FALSE;
2107 gboolean use_encryption = FALSE;
2108 gchar *privacy_system = NULL;
2109 int priority = PRIORITY_NORMAL;
2110 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2111 gboolean autowrap = prefs_common.autowrap;
2112 gboolean autoindent = prefs_common.auto_indent;
2114 cm_return_val_if_fail(msginfo != NULL, NULL);
2115 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2117 if (compose_put_existing_to_front(msginfo)) {
2121 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2122 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2123 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2124 gchar queueheader_buf[BUFFSIZE];
2127 /* Select Account from queue headers */
2128 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2129 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2130 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2131 account = account_find_from_id(id);
2133 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2134 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2135 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2136 account = account_find_from_id(id);
2138 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2139 sizeof(queueheader_buf), "NAID:")) {
2140 id = atoi(&queueheader_buf[strlen("NAID:")]);
2141 account = account_find_from_id(id);
2143 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2144 sizeof(queueheader_buf), "MAID:")) {
2145 id = atoi(&queueheader_buf[strlen("MAID:")]);
2146 account = account_find_from_id(id);
2148 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2149 sizeof(queueheader_buf), "S:")) {
2150 account = account_find_from_address(queueheader_buf, FALSE);
2152 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2153 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2154 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2155 use_signing = param;
2158 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2159 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2160 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2161 use_signing = param;
2164 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2165 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2166 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2167 use_encryption = param;
2169 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2170 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2171 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2172 use_encryption = param;
2174 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2175 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2176 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2179 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2180 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2181 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2184 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2185 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2186 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2188 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2189 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2190 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2193 sizeof(queueheader_buf), "X-Priority: ")) {
2194 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2197 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2198 sizeof(queueheader_buf), "RMID:")) {
2199 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2200 if (tokens[0] && tokens[1] && tokens[2]) {
2201 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2202 if (orig_item != NULL) {
2203 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "FMID:")) {
2210 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2211 if (tokens[0] && tokens[1] && tokens[2]) {
2212 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2213 if (orig_item != NULL) {
2214 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2220 account = msginfo->folder->folder->account;
2223 if (!account && prefs_common.reedit_account_autosel) {
2224 gchar from[BUFFSIZE];
2225 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2226 extract_address(from);
2227 account = account_find_from_address(from, FALSE);
2231 account = cur_account;
2233 cm_return_val_if_fail(account != NULL, NULL);
2235 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2237 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2238 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2239 compose->autowrap = autowrap;
2240 compose->replyinfo = replyinfo;
2241 compose->fwdinfo = fwdinfo;
2243 compose->updating = TRUE;
2244 compose->priority = priority;
2246 if (privacy_system != NULL) {
2247 compose->privacy_system = privacy_system;
2248 compose_use_signing(compose, use_signing);
2249 compose_use_encryption(compose, use_encryption);
2250 compose_update_privacy_system_menu_item(compose, FALSE);
2252 activate_privacy_system(compose, account, FALSE);
2255 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2257 compose_extract_original_charset(compose);
2259 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2260 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2261 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2262 gchar queueheader_buf[BUFFSIZE];
2264 /* Set message save folder */
2265 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2266 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2267 compose_set_save_to(compose, &queueheader_buf[4]);
2269 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2270 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2272 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2277 if (compose_parse_header(compose, msginfo) < 0) {
2278 compose->updating = FALSE;
2279 compose_destroy(compose);
2282 compose_reedit_set_entry(compose, msginfo);
2284 textview = GTK_TEXT_VIEW(compose->text);
2285 textbuf = gtk_text_view_get_buffer(textview);
2286 compose_create_tags(textview, compose);
2288 mark = gtk_text_buffer_get_insert(textbuf);
2289 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2291 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2292 G_CALLBACK(compose_changed_cb),
2295 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2296 fp = procmime_get_first_encrypted_text_content(msginfo);
2298 compose_force_encryption(compose, account, TRUE, NULL);
2301 fp = procmime_get_first_text_content(msginfo);
2304 g_warning("Can't get text part\n");
2308 gboolean prev_autowrap = compose->autowrap;
2309 GtkTextBuffer *buffer = textbuf;
2311 while (fgets(buf, sizeof(buf), fp) != NULL) {
2313 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2319 compose_attach_parts(compose, msginfo);
2321 compose_colorize_signature(compose);
2323 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2324 G_CALLBACK(compose_changed_cb),
2327 gtk_widget_grab_focus(compose->text);
2329 if (prefs_common.auto_exteditor) {
2330 compose_exec_ext_editor(compose);
2332 compose->modified = FALSE;
2333 compose_set_title(compose);
2335 compose->updating = FALSE;
2336 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2337 SCROLL_TO_CURSOR(compose);
2339 if (compose->deferred_destroy) {
2340 compose_destroy(compose);
2344 compose->sig_str = account_get_signature_str(compose->account);
2346 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2351 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2358 cm_return_val_if_fail(msginfo != NULL, NULL);
2361 account = account_get_reply_account(msginfo,
2362 prefs_common.reply_account_autosel);
2363 cm_return_val_if_fail(account != NULL, NULL);
2365 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2367 compose->updating = TRUE;
2369 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2370 compose->replyinfo = NULL;
2371 compose->fwdinfo = NULL;
2373 compose_show_first_last_header(compose, TRUE);
2375 gtk_widget_grab_focus(compose->header_last->entry);
2377 filename = procmsg_get_message_file(msginfo);
2379 if (filename == NULL) {
2380 compose->updating = FALSE;
2381 compose_destroy(compose);
2386 compose->redirect_filename = filename;
2388 /* Set save folder */
2389 item = msginfo->folder;
2390 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2391 gchar *folderidentifier;
2393 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2394 folderidentifier = folder_item_get_identifier(item);
2395 compose_set_save_to(compose, folderidentifier);
2396 g_free(folderidentifier);
2399 compose_attach_parts(compose, msginfo);
2401 if (msginfo->subject)
2402 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2404 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2406 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2407 _("The body of the \"Redirect\" template has an error at line %d."));
2408 quote_fmt_reset_vartable();
2409 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2411 compose_colorize_signature(compose);
2414 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2415 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2416 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2418 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2419 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2420 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2421 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2422 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2423 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2424 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2425 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2427 if (compose->toolbar->draft_btn)
2428 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2429 if (compose->toolbar->insert_btn)
2430 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2431 if (compose->toolbar->attach_btn)
2432 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2433 if (compose->toolbar->sig_btn)
2434 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2435 if (compose->toolbar->exteditor_btn)
2436 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2437 if (compose->toolbar->linewrap_current_btn)
2438 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2439 if (compose->toolbar->linewrap_all_btn)
2440 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2442 compose->modified = FALSE;
2443 compose_set_title(compose);
2444 compose->updating = FALSE;
2445 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2446 SCROLL_TO_CURSOR(compose);
2448 if (compose->deferred_destroy) {
2449 compose_destroy(compose);
2453 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2458 GList *compose_get_compose_list(void)
2460 return compose_list;
2463 void compose_entry_append(Compose *compose, const gchar *address,
2464 ComposeEntryType type, ComposePrefType pref_type)
2466 const gchar *header;
2468 gboolean in_quote = FALSE;
2469 if (!address || *address == '\0') return;
2476 header = N_("Bcc:");
2478 case COMPOSE_REPLYTO:
2479 header = N_("Reply-To:");
2481 case COMPOSE_NEWSGROUPS:
2482 header = N_("Newsgroups:");
2484 case COMPOSE_FOLLOWUPTO:
2485 header = N_( "Followup-To:");
2487 case COMPOSE_INREPLYTO:
2488 header = N_( "In-Reply-To:");
2495 header = prefs_common_translated_header_name(header);
2497 cur = begin = (gchar *)address;
2499 /* we separate the line by commas, but not if we're inside a quoted
2501 while (*cur != '\0') {
2503 in_quote = !in_quote;
2504 if (*cur == ',' && !in_quote) {
2505 gchar *tmp = g_strdup(begin);
2507 tmp[cur-begin]='\0';
2510 while (*tmp == ' ' || *tmp == '\t')
2512 compose_add_header_entry(compose, header, tmp, pref_type);
2519 gchar *tmp = g_strdup(begin);
2521 tmp[cur-begin]='\0';
2524 while (*tmp == ' ' || *tmp == '\t')
2526 compose_add_header_entry(compose, header, tmp, pref_type);
2531 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2533 static GdkColor yellow;
2534 static GdkColor black;
2535 static gboolean yellow_initialised = FALSE;
2539 if (!yellow_initialised) {
2540 gdk_color_parse("#f5f6be", &yellow);
2541 gdk_color_parse("#000000", &black);
2542 yellow_initialised = gdk_colormap_alloc_color(
2543 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2544 yellow_initialised &= gdk_colormap_alloc_color(
2545 gdk_colormap_get_system(), &black, FALSE, TRUE);
2548 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2549 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2550 if (gtk_entry_get_text(entry) &&
2551 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2552 if (yellow_initialised) {
2553 gtk_widget_modify_base(
2554 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2555 GTK_STATE_NORMAL, &yellow);
2556 gtk_widget_modify_text(
2557 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2558 GTK_STATE_NORMAL, &black);
2564 void compose_toolbar_cb(gint action, gpointer data)
2566 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2567 Compose *compose = (Compose*)toolbar_item->parent;
2569 cm_return_if_fail(compose != NULL);
2573 compose_send_cb(NULL, compose);
2576 compose_send_later_cb(NULL, compose);
2579 compose_draft(compose, COMPOSE_QUIT_EDITING);
2582 compose_insert_file_cb(NULL, compose);
2585 compose_attach_cb(NULL, compose);
2588 compose_insert_sig(compose, FALSE);
2591 compose_ext_editor_cb(NULL, compose);
2593 case A_LINEWRAP_CURRENT:
2594 compose_beautify_paragraph(compose, NULL, TRUE);
2596 case A_LINEWRAP_ALL:
2597 compose_wrap_all_full(compose, TRUE);
2600 compose_address_cb(NULL, compose);
2603 case A_CHECK_SPELLING:
2604 compose_check_all(NULL, compose);
2612 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2617 gchar *subject = NULL;
2621 gchar **attach = NULL;
2622 gchar *inreplyto = NULL;
2623 MailField mfield = NO_FIELD_PRESENT;
2625 /* get mailto parts but skip from */
2626 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2629 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2630 mfield = TO_FIELD_PRESENT;
2633 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2635 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2637 if (!g_utf8_validate (subject, -1, NULL)) {
2638 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2639 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2642 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2644 mfield = SUBJECT_FIELD_PRESENT;
2647 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2648 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2651 gboolean prev_autowrap = compose->autowrap;
2653 compose->autowrap = FALSE;
2655 mark = gtk_text_buffer_get_insert(buffer);
2656 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2658 if (!g_utf8_validate (body, -1, NULL)) {
2659 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2660 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2663 gtk_text_buffer_insert(buffer, &iter, body, -1);
2665 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2667 compose->autowrap = prev_autowrap;
2668 if (compose->autowrap)
2669 compose_wrap_all(compose);
2670 mfield = BODY_FIELD_PRESENT;
2674 gint i = 0, att = 0;
2675 gchar *warn_files = NULL;
2676 while (attach[i] != NULL) {
2677 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2678 if (utf8_filename) {
2679 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2680 gchar *tmp = g_strdup_printf("%s%s\n",
2681 warn_files?warn_files:"",
2687 g_free(utf8_filename);
2689 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2694 alertpanel_notice(ngettext(
2695 "The following file has been attached: \n%s",
2696 "The following files have been attached: \n%s", att), warn_files);
2701 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2714 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2716 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2717 {"Cc:", NULL, TRUE},
2718 {"References:", NULL, FALSE},
2719 {"Bcc:", NULL, TRUE},
2720 {"Newsgroups:", NULL, TRUE},
2721 {"Followup-To:", NULL, TRUE},
2722 {"List-Post:", NULL, FALSE},
2723 {"X-Priority:", NULL, FALSE},
2724 {NULL, NULL, FALSE}};
2740 cm_return_val_if_fail(msginfo != NULL, -1);
2742 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2743 procheader_get_header_fields(fp, hentry);
2746 if (hentry[H_REPLY_TO].body != NULL) {
2747 if (hentry[H_REPLY_TO].body[0] != '\0') {
2749 conv_unmime_header(hentry[H_REPLY_TO].body,
2752 g_free(hentry[H_REPLY_TO].body);
2753 hentry[H_REPLY_TO].body = NULL;
2755 if (hentry[H_CC].body != NULL) {
2756 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2757 g_free(hentry[H_CC].body);
2758 hentry[H_CC].body = NULL;
2760 if (hentry[H_REFERENCES].body != NULL) {
2761 if (compose->mode == COMPOSE_REEDIT)
2762 compose->references = hentry[H_REFERENCES].body;
2764 compose->references = compose_parse_references
2765 (hentry[H_REFERENCES].body, msginfo->msgid);
2766 g_free(hentry[H_REFERENCES].body);
2768 hentry[H_REFERENCES].body = NULL;
2770 if (hentry[H_BCC].body != NULL) {
2771 if (compose->mode == COMPOSE_REEDIT)
2773 conv_unmime_header(hentry[H_BCC].body, NULL);
2774 g_free(hentry[H_BCC].body);
2775 hentry[H_BCC].body = NULL;
2777 if (hentry[H_NEWSGROUPS].body != NULL) {
2778 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2779 hentry[H_NEWSGROUPS].body = NULL;
2781 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2782 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2783 compose->followup_to =
2784 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2787 g_free(hentry[H_FOLLOWUP_TO].body);
2788 hentry[H_FOLLOWUP_TO].body = NULL;
2790 if (hentry[H_LIST_POST].body != NULL) {
2791 gchar *to = NULL, *start = NULL;
2793 extract_address(hentry[H_LIST_POST].body);
2794 if (hentry[H_LIST_POST].body[0] != '\0') {
2795 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2797 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2798 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2801 g_free(compose->ml_post);
2802 compose->ml_post = to;
2805 g_free(hentry[H_LIST_POST].body);
2806 hentry[H_LIST_POST].body = NULL;
2809 /* CLAWS - X-Priority */
2810 if (compose->mode == COMPOSE_REEDIT)
2811 if (hentry[H_X_PRIORITY].body != NULL) {
2814 priority = atoi(hentry[H_X_PRIORITY].body);
2815 g_free(hentry[H_X_PRIORITY].body);
2817 hentry[H_X_PRIORITY].body = NULL;
2819 if (priority < PRIORITY_HIGHEST ||
2820 priority > PRIORITY_LOWEST)
2821 priority = PRIORITY_NORMAL;
2823 compose->priority = priority;
2826 if (compose->mode == COMPOSE_REEDIT) {
2827 if (msginfo->inreplyto && *msginfo->inreplyto)
2828 compose->inreplyto = g_strdup(msginfo->inreplyto);
2832 if (msginfo->msgid && *msginfo->msgid)
2833 compose->inreplyto = g_strdup(msginfo->msgid);
2835 if (!compose->references) {
2836 if (msginfo->msgid && *msginfo->msgid) {
2837 if (msginfo->inreplyto && *msginfo->inreplyto)
2838 compose->references =
2839 g_strdup_printf("<%s>\n\t<%s>",
2843 compose->references =
2844 g_strconcat("<", msginfo->msgid, ">",
2846 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2847 compose->references =
2848 g_strconcat("<", msginfo->inreplyto, ">",
2856 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2858 GSList *ref_id_list, *cur;
2862 ref_id_list = references_list_append(NULL, ref);
2863 if (!ref_id_list) return NULL;
2864 if (msgid && *msgid)
2865 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2870 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2871 /* "<" + Message-ID + ">" + CR+LF+TAB */
2872 len += strlen((gchar *)cur->data) + 5;
2874 if (len > MAX_REFERENCES_LEN) {
2875 /* remove second message-ID */
2876 if (ref_id_list && ref_id_list->next &&
2877 ref_id_list->next->next) {
2878 g_free(ref_id_list->next->data);
2879 ref_id_list = g_slist_remove
2880 (ref_id_list, ref_id_list->next->data);
2882 slist_free_strings(ref_id_list);
2883 g_slist_free(ref_id_list);
2890 new_ref = g_string_new("");
2891 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2892 if (new_ref->len > 0)
2893 g_string_append(new_ref, "\n\t");
2894 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2897 slist_free_strings(ref_id_list);
2898 g_slist_free(ref_id_list);
2900 new_ref_str = new_ref->str;
2901 g_string_free(new_ref, FALSE);
2906 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2907 const gchar *fmt, const gchar *qmark,
2908 const gchar *body, gboolean rewrap,
2909 gboolean need_unescape,
2910 const gchar *err_msg)
2912 MsgInfo* dummyinfo = NULL;
2913 gchar *quote_str = NULL;
2915 gboolean prev_autowrap;
2916 const gchar *trimmed_body = body;
2917 gint cursor_pos = -1;
2918 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2919 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2924 SIGNAL_BLOCK(buffer);
2927 dummyinfo = compose_msginfo_new_from_compose(compose);
2928 msginfo = dummyinfo;
2931 if (qmark != NULL) {
2933 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2934 compose->gtkaspell);
2936 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2938 quote_fmt_scan_string(qmark);
2941 buf = quote_fmt_get_buffer();
2943 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2945 Xstrdup_a(quote_str, buf, goto error)
2948 if (fmt && *fmt != '\0') {
2951 while (*trimmed_body == '\n')
2955 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2956 compose->gtkaspell);
2958 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2960 if (need_unescape) {
2963 /* decode \-escape sequences in the internal representation of the quote format */
2964 tmp = malloc(strlen(fmt)+1);
2965 pref_get_unescaped_pref(tmp, fmt);
2966 quote_fmt_scan_string(tmp);
2970 quote_fmt_scan_string(fmt);
2974 buf = quote_fmt_get_buffer();
2976 gint line = quote_fmt_get_line();
2977 alertpanel_error(err_msg, line);
2983 prev_autowrap = compose->autowrap;
2984 compose->autowrap = FALSE;
2986 mark = gtk_text_buffer_get_insert(buffer);
2987 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2988 if (g_utf8_validate(buf, -1, NULL)) {
2989 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2991 gchar *tmpout = NULL;
2992 tmpout = conv_codeset_strdup
2993 (buf, conv_get_locale_charset_str_no_utf8(),
2995 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2997 tmpout = g_malloc(strlen(buf)*2+1);
2998 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3000 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3004 cursor_pos = quote_fmt_get_cursor_pos();
3005 if (cursor_pos == -1)
3006 cursor_pos = gtk_text_iter_get_offset(&iter);
3007 compose->set_cursor_pos = cursor_pos;
3009 gtk_text_buffer_get_start_iter(buffer, &iter);
3010 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3011 gtk_text_buffer_place_cursor(buffer, &iter);
3013 compose->autowrap = prev_autowrap;
3014 if (compose->autowrap && rewrap)
3015 compose_wrap_all(compose);
3022 SIGNAL_UNBLOCK(buffer);
3024 procmsg_msginfo_free( dummyinfo );
3029 /* if ml_post is of type addr@host and from is of type
3030 * addr-anything@host, return TRUE
3032 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3034 gchar *left_ml = NULL;
3035 gchar *right_ml = NULL;
3036 gchar *left_from = NULL;
3037 gchar *right_from = NULL;
3038 gboolean result = FALSE;
3040 if (!ml_post || !from)
3043 left_ml = g_strdup(ml_post);
3044 if (strstr(left_ml, "@")) {
3045 right_ml = strstr(left_ml, "@")+1;
3046 *(strstr(left_ml, "@")) = '\0';
3049 left_from = g_strdup(from);
3050 if (strstr(left_from, "@")) {
3051 right_from = strstr(left_from, "@")+1;
3052 *(strstr(left_from, "@")) = '\0';
3055 if (left_ml && left_from && right_ml && right_from
3056 && !strncmp(left_from, left_ml, strlen(left_ml))
3057 && !strcmp(right_from, right_ml)) {
3066 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3067 gboolean to_all, gboolean to_ml,
3069 gboolean followup_and_reply_to)
3071 GSList *cc_list = NULL;
3074 gchar *replyto = NULL;
3075 gchar *ac_email = NULL;
3077 gboolean reply_to_ml = FALSE;
3078 gboolean default_reply_to = FALSE;
3080 cm_return_if_fail(compose->account != NULL);
3081 cm_return_if_fail(msginfo != NULL);
3083 reply_to_ml = to_ml && compose->ml_post;
3085 default_reply_to = msginfo->folder &&
3086 msginfo->folder->prefs->enable_default_reply_to;
3088 if (compose->account->protocol != A_NNTP) {
3089 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3090 if (msginfo->folder->prefs->enable_default_replyto) {
3091 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3092 COMPOSE_REPLYTO, PREF_FOLDER);
3094 if (msginfo->folder->prefs->enable_default_bcc) {
3095 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3096 COMPOSE_BCC, PREF_FOLDER);
3098 if (msginfo->folder->prefs->enable_default_cc) {
3099 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3100 COMPOSE_CC, PREF_FOLDER);
3103 if (reply_to_ml && !default_reply_to) {
3105 gboolean is_subscr = is_subscription(compose->ml_post,
3108 /* normal answer to ml post with a reply-to */
3109 compose_entry_append(compose,
3111 COMPOSE_TO, PREF_ML);
3112 if (compose->replyto)
3113 compose_entry_append(compose,
3115 COMPOSE_CC, PREF_ML);
3117 /* answer to subscription confirmation */
3118 if (compose->replyto)
3119 compose_entry_append(compose,
3121 COMPOSE_TO, PREF_ML);
3122 else if (msginfo->from)
3123 compose_entry_append(compose,
3125 COMPOSE_TO, PREF_ML);
3128 else if (!(to_all || to_sender) && default_reply_to) {
3129 compose_entry_append(compose,
3130 msginfo->folder->prefs->default_reply_to,
3131 COMPOSE_TO, PREF_FOLDER);
3132 compose_entry_mark_default_to(compose,
3133 msginfo->folder->prefs->default_reply_to);
3138 Xstrdup_a(tmp1, msginfo->from, return);
3139 extract_address(tmp1);
3140 if (to_all || to_sender ||
3141 !account_find_from_address(tmp1, FALSE))
3142 compose_entry_append(compose,
3143 (compose->replyto && !to_sender)
3144 ? compose->replyto :
3145 msginfo->from ? msginfo->from : "",
3146 COMPOSE_TO, PREF_NONE);
3147 else if (!to_all && !to_sender) {
3148 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3149 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3150 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3151 if (compose->replyto) {
3152 compose_entry_append(compose,
3154 COMPOSE_TO, PREF_NONE);
3156 compose_entry_append(compose,
3157 msginfo->from ? msginfo->from : "",
3158 COMPOSE_TO, PREF_NONE);
3161 /* replying to own mail, use original recp */
3162 compose_entry_append(compose,
3163 msginfo->to ? msginfo->to : "",
3164 COMPOSE_TO, PREF_NONE);
3165 compose_entry_append(compose,
3166 msginfo->cc ? msginfo->cc : "",
3167 COMPOSE_CC, PREF_NONE);
3172 if (to_sender || (compose->followup_to &&
3173 !strncmp(compose->followup_to, "poster", 6)))
3174 compose_entry_append
3176 (compose->replyto ? compose->replyto :
3177 msginfo->from ? msginfo->from : ""),
3178 COMPOSE_TO, PREF_NONE);
3180 else if (followup_and_reply_to || to_all) {
3181 compose_entry_append
3183 (compose->replyto ? compose->replyto :
3184 msginfo->from ? msginfo->from : ""),
3185 COMPOSE_TO, PREF_NONE);
3187 compose_entry_append
3189 compose->followup_to ? compose->followup_to :
3190 compose->newsgroups ? compose->newsgroups : "",
3191 COMPOSE_NEWSGROUPS, PREF_NONE);
3194 compose_entry_append
3196 compose->followup_to ? compose->followup_to :
3197 compose->newsgroups ? compose->newsgroups : "",
3198 COMPOSE_NEWSGROUPS, PREF_NONE);
3201 if (msginfo->subject && *msginfo->subject) {
3205 buf = p = g_strdup(msginfo->subject);
3206 p += subject_get_prefix_length(p);
3207 memmove(buf, p, strlen(p) + 1);
3209 buf2 = g_strdup_printf("Re: %s", buf);
3210 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3215 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3217 if (to_ml && compose->ml_post) return;
3218 if (!to_all || compose->account->protocol == A_NNTP) return;
3220 if (compose->replyto) {
3221 Xstrdup_a(replyto, compose->replyto, return);
3222 extract_address(replyto);
3224 if (msginfo->from) {
3225 Xstrdup_a(from, msginfo->from, return);
3226 extract_address(from);
3229 if (replyto && from)
3230 cc_list = address_list_append_with_comments(cc_list, from);
3231 if (to_all && msginfo->folder &&
3232 msginfo->folder->prefs->enable_default_reply_to)
3233 cc_list = address_list_append_with_comments(cc_list,
3234 msginfo->folder->prefs->default_reply_to);
3235 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3236 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3238 ac_email = g_utf8_strdown(compose->account->address, -1);
3241 for (cur = cc_list; cur != NULL; cur = cur->next) {
3242 gchar *addr = g_utf8_strdown(cur->data, -1);
3243 extract_address(addr);
3245 if (strcmp(ac_email, addr))
3246 compose_entry_append(compose, (gchar *)cur->data,
3247 COMPOSE_CC, PREF_NONE);
3249 debug_print("Cc address same as compose account's, ignoring\n");
3254 slist_free_strings(cc_list);
3255 g_slist_free(cc_list);
3261 #define SET_ENTRY(entry, str) \
3264 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3267 #define SET_ADDRESS(type, str) \
3270 compose_entry_append(compose, str, type, PREF_NONE); \
3273 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3275 cm_return_if_fail(msginfo != NULL);
3277 SET_ENTRY(subject_entry, msginfo->subject);
3278 SET_ENTRY(from_name, msginfo->from);
3279 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3280 SET_ADDRESS(COMPOSE_CC, compose->cc);
3281 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3282 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3283 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3284 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3285 SET_ADDRESS(COMPOSE_INREPLYTO, compose->inreplyto);
3287 compose_update_priority_menu_item(compose);
3288 compose_update_privacy_system_menu_item(compose, FALSE);
3289 compose_show_first_last_header(compose, TRUE);
3295 static void compose_insert_sig(Compose *compose, gboolean replace)
3297 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3298 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3300 GtkTextIter iter, iter_end;
3301 gint cur_pos, ins_pos;
3302 gboolean prev_autowrap;
3303 gboolean found = FALSE;
3304 gboolean exists = FALSE;
3306 cm_return_if_fail(compose->account != NULL);
3310 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3311 G_CALLBACK(compose_changed_cb),
3314 mark = gtk_text_buffer_get_insert(buffer);
3315 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3316 cur_pos = gtk_text_iter_get_offset (&iter);
3319 gtk_text_buffer_get_end_iter(buffer, &iter);
3321 exists = (compose->sig_str != NULL);
3324 GtkTextIter first_iter, start_iter, end_iter;
3326 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3328 if (!exists || compose->sig_str[0] == '\0')
3331 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3332 compose->signature_tag);
3335 /* include previous \n\n */
3336 gtk_text_iter_backward_chars(&first_iter, 1);
3337 start_iter = first_iter;
3338 end_iter = first_iter;
3340 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3341 compose->signature_tag);
3342 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3343 compose->signature_tag);
3345 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3351 g_free(compose->sig_str);
3352 compose->sig_str = account_get_signature_str(compose->account);
3354 cur_pos = gtk_text_iter_get_offset(&iter);
3356 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3357 g_free(compose->sig_str);
3358 compose->sig_str = NULL;
3360 if (compose->sig_inserted == FALSE)
3361 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3362 compose->sig_inserted = TRUE;
3364 cur_pos = gtk_text_iter_get_offset(&iter);
3365 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3367 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3368 gtk_text_iter_forward_chars(&iter, 1);
3369 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3370 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3372 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3373 cur_pos = gtk_text_buffer_get_char_count (buffer);
3376 /* put the cursor where it should be
3377 * either where the quote_fmt says, either where it was */
3378 if (compose->set_cursor_pos < 0)
3379 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3381 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3382 compose->set_cursor_pos);
3384 compose->set_cursor_pos = -1;
3385 gtk_text_buffer_place_cursor(buffer, &iter);
3386 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3387 G_CALLBACK(compose_changed_cb),
3393 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3396 GtkTextBuffer *buffer;
3399 const gchar *cur_encoding;
3400 gchar buf[BUFFSIZE];
3403 gboolean prev_autowrap;
3404 gboolean badtxt = FALSE;
3405 struct stat file_stat;
3408 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3410 /* get the size of the file we are about to insert */
3411 ret = g_stat(file, &file_stat);
3413 gchar *shortfile = g_path_get_basename(file);
3414 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3416 return COMPOSE_INSERT_NO_FILE;
3417 } else if (prefs_common.warn_large_insert == TRUE) {
3419 /* ask user for confirmation if the file is large */
3420 if (prefs_common.warn_large_insert_size < 0 ||
3421 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3425 msg = g_strdup_printf(_("You are about to insert a file of %s "
3426 "in the message body. Are you sure you want to do that?"),
3427 to_human_readable(file_stat.st_size));
3428 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3429 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3432 /* do we ask for confirmation next time? */
3433 if (aval & G_ALERTDISABLE) {
3434 /* no confirmation next time, disable feature in preferences */
3435 aval &= ~G_ALERTDISABLE;
3436 prefs_common.warn_large_insert = FALSE;
3439 /* abort file insertion if user canceled action */
3440 if (aval != G_ALERTALTERNATE) {
3441 return COMPOSE_INSERT_NO_FILE;
3447 if ((fp = g_fopen(file, "rb")) == NULL) {
3448 FILE_OP_ERROR(file, "fopen");
3449 return COMPOSE_INSERT_READ_ERROR;
3452 prev_autowrap = compose->autowrap;
3453 compose->autowrap = FALSE;
3455 text = GTK_TEXT_VIEW(compose->text);
3456 buffer = gtk_text_view_get_buffer(text);
3457 mark = gtk_text_buffer_get_insert(buffer);
3458 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3460 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3461 G_CALLBACK(text_inserted),
3464 cur_encoding = conv_get_locale_charset_str_no_utf8();
3466 while (fgets(buf, sizeof(buf), fp) != NULL) {
3469 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3470 str = g_strdup(buf);
3472 str = conv_codeset_strdup
3473 (buf, cur_encoding, CS_INTERNAL);
3476 /* strip <CR> if DOS/Windows file,
3477 replace <CR> with <LF> if Macintosh file. */
3480 if (len > 0 && str[len - 1] != '\n') {
3482 if (str[len] == '\r') str[len] = '\n';
3485 gtk_text_buffer_insert(buffer, &iter, str, -1);
3489 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3490 G_CALLBACK(text_inserted),
3492 compose->autowrap = prev_autowrap;
3493 if (compose->autowrap)
3494 compose_wrap_all(compose);
3499 return COMPOSE_INSERT_INVALID_CHARACTER;
3501 return COMPOSE_INSERT_SUCCESS;
3504 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3505 const gchar *filename,
3506 const gchar *content_type)
3514 GtkListStore *store;
3516 gboolean has_binary = FALSE;
3518 if (!is_file_exist(file)) {
3519 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3520 gboolean result = FALSE;
3521 if (file_from_uri && is_file_exist(file_from_uri)) {
3522 result = compose_attach_append(
3523 compose, file_from_uri,
3527 g_free(file_from_uri);
3530 alertpanel_error("File %s doesn't exist\n", filename);
3533 if ((size = get_file_size(file)) < 0) {
3534 alertpanel_error("Can't get file size of %s\n", filename);
3538 alertpanel_error(_("File %s is empty."), filename);
3541 if ((fp = g_fopen(file, "rb")) == NULL) {
3542 alertpanel_error(_("Can't read %s."), filename);
3547 ainfo = g_new0(AttachInfo, 1);
3548 auto_ainfo = g_auto_pointer_new_with_free
3549 (ainfo, (GFreeFunc) compose_attach_info_free);
3550 ainfo->file = g_strdup(file);
3553 ainfo->content_type = g_strdup(content_type);
3554 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3556 MsgFlags flags = {0, 0};
3558 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3559 ainfo->encoding = ENC_7BIT;
3561 ainfo->encoding = ENC_8BIT;
3563 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3564 if (msginfo && msginfo->subject)
3565 name = g_strdup(msginfo->subject);
3567 name = g_path_get_basename(filename ? filename : file);
3569 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3571 procmsg_msginfo_free(msginfo);
3573 if (!g_ascii_strncasecmp(content_type, "text", 4))
3574 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3576 ainfo->encoding = ENC_BASE64;
3577 name = g_path_get_basename(filename ? filename : file);
3578 ainfo->name = g_strdup(name);
3582 ainfo->content_type = procmime_get_mime_type(file);
3583 if (!ainfo->content_type) {
3584 ainfo->content_type =
3585 g_strdup("application/octet-stream");
3586 ainfo->encoding = ENC_BASE64;
3587 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3589 procmime_get_encoding_for_text_file(file, &has_binary);
3591 ainfo->encoding = ENC_BASE64;
3592 name = g_path_get_basename(filename ? filename : file);
3593 ainfo->name = g_strdup(name);
3597 if (ainfo->name != NULL
3598 && !strcmp(ainfo->name, ".")) {
3599 g_free(ainfo->name);
3603 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3604 g_free(ainfo->content_type);
3605 ainfo->content_type = g_strdup("application/octet-stream");
3608 ainfo->size = (goffset)size;
3609 size_text = to_human_readable((goffset)size);
3611 store = GTK_LIST_STORE(gtk_tree_view_get_model
3612 (GTK_TREE_VIEW(compose->attach_clist)));
3614 gtk_list_store_append(store, &iter);
3615 gtk_list_store_set(store, &iter,
3616 COL_MIMETYPE, ainfo->content_type,
3617 COL_SIZE, size_text,
3618 COL_NAME, ainfo->name,
3620 COL_AUTODATA, auto_ainfo,
3623 g_auto_pointer_free(auto_ainfo);
3624 compose_attach_update_label(compose);
3628 static void compose_use_signing(Compose *compose, gboolean use_signing)
3630 compose->use_signing = use_signing;
3631 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3634 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3636 compose->use_encryption = use_encryption;
3637 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3640 #define NEXT_PART_NOT_CHILD(info) \
3642 node = info->node; \
3643 while (node->children) \
3644 node = g_node_last_child(node); \
3645 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3648 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3652 MimeInfo *firsttext = NULL;
3653 MimeInfo *encrypted = NULL;
3656 const gchar *partname = NULL;
3658 mimeinfo = procmime_scan_message(msginfo);
3659 if (!mimeinfo) return;
3661 if (mimeinfo->node->children == NULL) {
3662 procmime_mimeinfo_free_all(mimeinfo);
3666 /* find first content part */
3667 child = (MimeInfo *) mimeinfo->node->children->data;
3668 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3669 child = (MimeInfo *)child->node->children->data;
3672 if (child->type == MIMETYPE_TEXT) {
3674 debug_print("First text part found\n");
3675 } else if (compose->mode == COMPOSE_REEDIT &&
3676 child->type == MIMETYPE_APPLICATION &&
3677 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3678 encrypted = (MimeInfo *)child->node->parent->data;
3681 child = (MimeInfo *) mimeinfo->node->children->data;
3682 while (child != NULL) {
3685 if (child == encrypted) {
3686 /* skip this part of tree */
3687 NEXT_PART_NOT_CHILD(child);
3691 if (child->type == MIMETYPE_MULTIPART) {
3692 /* get the actual content */
3693 child = procmime_mimeinfo_next(child);
3697 if (child == firsttext) {
3698 child = procmime_mimeinfo_next(child);
3702 outfile = procmime_get_tmp_file_name(child);
3703 if ((err = procmime_get_part(outfile, child)) < 0)
3704 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3706 gchar *content_type;
3708 content_type = procmime_get_content_type_str(child->type, child->subtype);
3710 /* if we meet a pgp signature, we don't attach it, but
3711 * we force signing. */
3712 if ((strcmp(content_type, "application/pgp-signature") &&
3713 strcmp(content_type, "application/pkcs7-signature") &&
3714 strcmp(content_type, "application/x-pkcs7-signature"))
3715 || compose->mode == COMPOSE_REDIRECT) {
3716 partname = procmime_mimeinfo_get_parameter(child, "filename");
3717 if (partname == NULL)
3718 partname = procmime_mimeinfo_get_parameter(child, "name");
3719 if (partname == NULL)
3721 compose_attach_append(compose, outfile,
3722 partname, content_type);
3724 compose_force_signing(compose, compose->account, NULL);
3726 g_free(content_type);
3729 NEXT_PART_NOT_CHILD(child);
3731 procmime_mimeinfo_free_all(mimeinfo);
3734 #undef NEXT_PART_NOT_CHILD
3739 WAIT_FOR_INDENT_CHAR,
3740 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3743 /* return indent length, we allow:
3744 indent characters followed by indent characters or spaces/tabs,
3745 alphabets and numbers immediately followed by indent characters,
3746 and the repeating sequences of the above
3747 If quote ends with multiple spaces, only the first one is included. */
3748 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3749 const GtkTextIter *start, gint *len)
3751 GtkTextIter iter = *start;
3755 IndentState state = WAIT_FOR_INDENT_CHAR;
3758 gint alnum_count = 0;
3759 gint space_count = 0;
3762 if (prefs_common.quote_chars == NULL) {
3766 while (!gtk_text_iter_ends_line(&iter)) {
3767 wc = gtk_text_iter_get_char(&iter);
3768 if (g_unichar_iswide(wc))
3770 clen = g_unichar_to_utf8(wc, ch);
3774 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3775 is_space = g_unichar_isspace(wc);
3777 if (state == WAIT_FOR_INDENT_CHAR) {
3778 if (!is_indent && !g_unichar_isalnum(wc))
3781 quote_len += alnum_count + space_count + 1;
3782 alnum_count = space_count = 0;
3783 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3786 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3787 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3791 else if (is_indent) {
3792 quote_len += alnum_count + space_count + 1;
3793 alnum_count = space_count = 0;
3796 state = WAIT_FOR_INDENT_CHAR;
3800 gtk_text_iter_forward_char(&iter);
3803 if (quote_len > 0 && space_count > 0)
3809 if (quote_len > 0) {
3811 gtk_text_iter_forward_chars(&iter, quote_len);
3812 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3818 /* return >0 if the line is itemized */
3819 static int compose_itemized_length(GtkTextBuffer *buffer,
3820 const GtkTextIter *start)
3822 GtkTextIter iter = *start;
3827 if (gtk_text_iter_ends_line(&iter))
3832 wc = gtk_text_iter_get_char(&iter);
3833 if (!g_unichar_isspace(wc))
3835 gtk_text_iter_forward_char(&iter);
3836 if (gtk_text_iter_ends_line(&iter))
3840 clen = g_unichar_to_utf8(wc, ch);
3844 if (!strchr("*-+", ch[0]))
3847 gtk_text_iter_forward_char(&iter);
3848 if (gtk_text_iter_ends_line(&iter))
3850 wc = gtk_text_iter_get_char(&iter);
3851 if (g_unichar_isspace(wc)) {
3857 /* return the string at the start of the itemization */
3858 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3859 const GtkTextIter *start)
3861 GtkTextIter iter = *start;
3864 GString *item_chars = g_string_new("");
3867 if (gtk_text_iter_ends_line(&iter))
3872 wc = gtk_text_iter_get_char(&iter);
3873 if (!g_unichar_isspace(wc))
3875 gtk_text_iter_forward_char(&iter);
3876 if (gtk_text_iter_ends_line(&iter))
3878 g_string_append_unichar(item_chars, wc);
3881 str = item_chars->str;
3882 g_string_free(item_chars, FALSE);
3886 /* return the number of spaces at a line's start */
3887 static int compose_left_offset_length(GtkTextBuffer *buffer,
3888 const GtkTextIter *start)
3890 GtkTextIter iter = *start;
3893 if (gtk_text_iter_ends_line(&iter))
3897 wc = gtk_text_iter_get_char(&iter);
3898 if (!g_unichar_isspace(wc))
3901 gtk_text_iter_forward_char(&iter);
3902 if (gtk_text_iter_ends_line(&iter))
3906 gtk_text_iter_forward_char(&iter);
3907 if (gtk_text_iter_ends_line(&iter))
3912 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3913 const GtkTextIter *start,
3914 GtkTextIter *break_pos,
3918 GtkTextIter iter = *start, line_end = *start;
3919 PangoLogAttr *attrs;
3926 gboolean can_break = FALSE;
3927 gboolean do_break = FALSE;
3928 gboolean was_white = FALSE;
3929 gboolean prev_dont_break = FALSE;
3931 gtk_text_iter_forward_to_line_end(&line_end);
3932 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3933 len = g_utf8_strlen(str, -1);
3937 g_warning("compose_get_line_break_pos: len = 0!\n");
3941 /* g_print("breaking line: %d: %s (len = %d)\n",
3942 gtk_text_iter_get_line(&iter), str, len); */
3944 attrs = g_new(PangoLogAttr, len + 1);
3946 pango_default_break(str, -1, NULL, attrs, len + 1);
3950 /* skip quote and leading spaces */
3951 for (i = 0; *p != '\0' && i < len; i++) {
3954 wc = g_utf8_get_char(p);
3955 if (i >= quote_len && !g_unichar_isspace(wc))
3957 if (g_unichar_iswide(wc))
3959 else if (*p == '\t')
3963 p = g_utf8_next_char(p);
3966 for (; *p != '\0' && i < len; i++) {
3967 PangoLogAttr *attr = attrs + i;
3971 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3974 was_white = attr->is_white;
3976 /* don't wrap URI */
3977 if ((uri_len = get_uri_len(p)) > 0) {
3979 if (pos > 0 && col > max_col) {
3989 wc = g_utf8_get_char(p);
3990 if (g_unichar_iswide(wc)) {
3992 if (prev_dont_break && can_break && attr->is_line_break)
3994 } else if (*p == '\t')
3998 if (pos > 0 && col > max_col) {
4003 if (*p == '-' || *p == '/')
4004 prev_dont_break = TRUE;
4006 prev_dont_break = FALSE;
4008 p = g_utf8_next_char(p);
4012 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4017 *break_pos = *start;
4018 gtk_text_iter_set_line_offset(break_pos, pos);
4023 static gboolean compose_join_next_line(Compose *compose,
4024 GtkTextBuffer *buffer,
4026 const gchar *quote_str)
4028 GtkTextIter iter_ = *iter, cur, prev, next, end;
4029 PangoLogAttr attrs[3];
4031 gchar *next_quote_str;
4034 gboolean keep_cursor = FALSE;
4036 if (!gtk_text_iter_forward_line(&iter_) ||
4037 gtk_text_iter_ends_line(&iter_)) {
4040 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4042 if ((quote_str || next_quote_str) &&
4043 strcmp2(quote_str, next_quote_str) != 0) {
4044 g_free(next_quote_str);
4047 g_free(next_quote_str);
4050 if (quote_len > 0) {
4051 gtk_text_iter_forward_chars(&end, quote_len);
4052 if (gtk_text_iter_ends_line(&end)) {
4057 /* don't join itemized lines */
4058 if (compose_itemized_length(buffer, &end) > 0) {
4062 /* don't join signature separator */
4063 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4066 /* delete quote str */
4068 gtk_text_buffer_delete(buffer, &iter_, &end);
4070 /* don't join line breaks put by the user */
4072 gtk_text_iter_backward_char(&cur);
4073 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4074 gtk_text_iter_forward_char(&cur);
4078 gtk_text_iter_forward_char(&cur);
4079 /* delete linebreak and extra spaces */
4080 while (gtk_text_iter_backward_char(&cur)) {
4081 wc1 = gtk_text_iter_get_char(&cur);
4082 if (!g_unichar_isspace(wc1))
4087 while (!gtk_text_iter_ends_line(&cur)) {
4088 wc1 = gtk_text_iter_get_char(&cur);
4089 if (!g_unichar_isspace(wc1))
4091 gtk_text_iter_forward_char(&cur);
4094 if (!gtk_text_iter_equal(&prev, &next)) {
4097 mark = gtk_text_buffer_get_insert(buffer);
4098 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4099 if (gtk_text_iter_equal(&prev, &cur))
4101 gtk_text_buffer_delete(buffer, &prev, &next);
4105 /* insert space if required */
4106 gtk_text_iter_backward_char(&prev);
4107 wc1 = gtk_text_iter_get_char(&prev);
4108 wc2 = gtk_text_iter_get_char(&next);
4109 gtk_text_iter_forward_char(&next);
4110 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4111 pango_default_break(str, -1, NULL, attrs, 3);
4112 if (!attrs[1].is_line_break ||
4113 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4114 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4116 gtk_text_iter_backward_char(&iter_);
4117 gtk_text_buffer_place_cursor(buffer, &iter_);
4126 #define ADD_TXT_POS(bp_, ep_, pti_) \
4127 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4128 last = last->next; \
4129 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4130 last->next = NULL; \
4132 g_warning("alloc error scanning URIs\n"); \
4135 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4137 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4138 GtkTextBuffer *buffer;
4139 GtkTextIter iter, break_pos, end_of_line;
4140 gchar *quote_str = NULL;
4142 gboolean wrap_quote = prefs_common.linewrap_quote;
4143 gboolean prev_autowrap = compose->autowrap;
4144 gint startq_offset = -1, noq_offset = -1;
4145 gint uri_start = -1, uri_stop = -1;
4146 gint nouri_start = -1, nouri_stop = -1;
4147 gint num_blocks = 0;
4148 gint quotelevel = -1;
4149 gboolean modified = force;
4150 gboolean removed = FALSE;
4151 gboolean modified_before_remove = FALSE;
4153 gboolean start = TRUE;
4154 gint itemized_len = 0, rem_item_len = 0;
4155 gchar *itemized_chars = NULL;
4156 gboolean item_continuation = FALSE;
4161 if (compose->draft_timeout_tag == -2) {
4165 compose->autowrap = FALSE;
4167 buffer = gtk_text_view_get_buffer(text);
4168 undo_wrapping(compose->undostruct, TRUE);
4173 mark = gtk_text_buffer_get_insert(buffer);
4174 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4178 if (compose->draft_timeout_tag == -2) {
4179 if (gtk_text_iter_ends_line(&iter)) {
4180 while (gtk_text_iter_ends_line(&iter) &&
4181 gtk_text_iter_forward_line(&iter))
4184 while (gtk_text_iter_backward_line(&iter)) {
4185 if (gtk_text_iter_ends_line(&iter)) {
4186 gtk_text_iter_forward_line(&iter);
4192 /* move to line start */
4193 gtk_text_iter_set_line_offset(&iter, 0);
4196 itemized_len = compose_itemized_length(buffer, &iter);
4198 if (!itemized_len) {
4199 itemized_len = compose_left_offset_length(buffer, &iter);
4200 item_continuation = TRUE;
4204 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4206 /* go until paragraph end (empty line) */
4207 while (start || !gtk_text_iter_ends_line(&iter)) {
4208 gchar *scanpos = NULL;
4209 /* parse table - in order of priority */
4211 const gchar *needle; /* token */
4213 /* token search function */
4214 gchar *(*search) (const gchar *haystack,
4215 const gchar *needle);
4216 /* part parsing function */
4217 gboolean (*parse) (const gchar *start,
4218 const gchar *scanpos,
4222 /* part to URI function */
4223 gchar *(*build_uri) (const gchar *bp,
4227 static struct table parser[] = {
4228 {"http://", strcasestr, get_uri_part, make_uri_string},
4229 {"https://", strcasestr, get_uri_part, make_uri_string},
4230 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4231 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4232 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4233 {"www.", strcasestr, get_uri_part, make_http_string},
4234 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4235 {"@", strcasestr, get_email_part, make_email_string}
4237 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4238 gint last_index = PARSE_ELEMS;
4240 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4244 if (!prev_autowrap && num_blocks == 0) {
4246 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4247 G_CALLBACK(text_inserted),
4250 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4253 uri_start = uri_stop = -1;
4255 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4258 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4259 if (startq_offset == -1)
4260 startq_offset = gtk_text_iter_get_offset(&iter);
4261 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4262 if (quotelevel > 2) {
4263 /* recycle colors */
4264 if (prefs_common.recycle_quote_colors)
4273 if (startq_offset == -1)
4274 noq_offset = gtk_text_iter_get_offset(&iter);
4278 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4281 if (gtk_text_iter_ends_line(&iter)) {
4283 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4284 prefs_common.linewrap_len,
4286 GtkTextIter prev, next, cur;
4287 if (prev_autowrap != FALSE || force) {
4288 compose->automatic_break = TRUE;
4290 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4291 compose->automatic_break = FALSE;
4292 if (itemized_len && compose->autoindent) {
4293 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4294 if (!item_continuation)
4295 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4297 } else if (quote_str && wrap_quote) {
4298 compose->automatic_break = TRUE;
4300 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4301 compose->automatic_break = FALSE;
4302 if (itemized_len && compose->autoindent) {
4303 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4304 if (!item_continuation)
4305 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4309 /* remove trailing spaces */
4311 rem_item_len = itemized_len;
4312 while (compose->autoindent && rem_item_len-- > 0)
4313 gtk_text_iter_backward_char(&cur);
4314 gtk_text_iter_backward_char(&cur);
4317 while (!gtk_text_iter_starts_line(&cur)) {
4320 gtk_text_iter_backward_char(&cur);
4321 wc = gtk_text_iter_get_char(&cur);
4322 if (!g_unichar_isspace(wc))
4326 if (!gtk_text_iter_equal(&prev, &next)) {
4327 gtk_text_buffer_delete(buffer, &prev, &next);
4329 gtk_text_iter_forward_char(&break_pos);
4333 gtk_text_buffer_insert(buffer, &break_pos,
4337 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4339 /* move iter to current line start */
4340 gtk_text_iter_set_line_offset(&iter, 0);
4347 /* move iter to next line start */
4353 if (!prev_autowrap && num_blocks > 0) {
4355 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4356 G_CALLBACK(text_inserted),
4360 while (!gtk_text_iter_ends_line(&end_of_line)) {
4361 gtk_text_iter_forward_char(&end_of_line);
4363 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4365 nouri_start = gtk_text_iter_get_offset(&iter);
4366 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4368 walk_pos = gtk_text_iter_get_offset(&iter);
4369 /* FIXME: this looks phony. scanning for anything in the parse table */
4370 for (n = 0; n < PARSE_ELEMS; n++) {
4373 tmp = parser[n].search(walk, parser[n].needle);
4375 if (scanpos == NULL || tmp < scanpos) {
4384 /* check if URI can be parsed */
4385 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4386 (const gchar **)&ep, FALSE)
4387 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4391 strlen(parser[last_index].needle);
4394 uri_start = walk_pos + (bp - o_walk);
4395 uri_stop = walk_pos + (ep - o_walk);
4399 gtk_text_iter_forward_line(&iter);
4402 if (startq_offset != -1) {
4403 GtkTextIter startquote, endquote;
4404 gtk_text_buffer_get_iter_at_offset(
4405 buffer, &startquote, startq_offset);
4408 switch (quotelevel) {
4410 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4411 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4412 gtk_text_buffer_apply_tag_by_name(
4413 buffer, "quote0", &startquote, &endquote);
4414 gtk_text_buffer_remove_tag_by_name(
4415 buffer, "quote1", &startquote, &endquote);
4416 gtk_text_buffer_remove_tag_by_name(
4417 buffer, "quote2", &startquote, &endquote);
4422 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4423 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4424 gtk_text_buffer_apply_tag_by_name(
4425 buffer, "quote1", &startquote, &endquote);
4426 gtk_text_buffer_remove_tag_by_name(
4427 buffer, "quote0", &startquote, &endquote);
4428 gtk_text_buffer_remove_tag_by_name(
4429 buffer, "quote2", &startquote, &endquote);
4434 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4435 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4436 gtk_text_buffer_apply_tag_by_name(
4437 buffer, "quote2", &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, "quote1", &startquote, &endquote);
4447 } else if (noq_offset != -1) {
4448 GtkTextIter startnoquote, endnoquote;
4449 gtk_text_buffer_get_iter_at_offset(
4450 buffer, &startnoquote, noq_offset);
4453 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4454 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4455 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4456 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4457 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4458 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4459 gtk_text_buffer_remove_tag_by_name(
4460 buffer, "quote0", &startnoquote, &endnoquote);
4461 gtk_text_buffer_remove_tag_by_name(
4462 buffer, "quote1", &startnoquote, &endnoquote);
4463 gtk_text_buffer_remove_tag_by_name(
4464 buffer, "quote2", &startnoquote, &endnoquote);
4470 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4471 GtkTextIter nouri_start_iter, nouri_end_iter;
4472 gtk_text_buffer_get_iter_at_offset(
4473 buffer, &nouri_start_iter, nouri_start);
4474 gtk_text_buffer_get_iter_at_offset(
4475 buffer, &nouri_end_iter, nouri_stop);
4476 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4477 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4478 gtk_text_buffer_remove_tag_by_name(
4479 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4480 modified_before_remove = modified;
4485 if (uri_start >= 0 && uri_stop > 0) {
4486 GtkTextIter uri_start_iter, uri_end_iter, back;
4487 gtk_text_buffer_get_iter_at_offset(
4488 buffer, &uri_start_iter, uri_start);
4489 gtk_text_buffer_get_iter_at_offset(
4490 buffer, &uri_end_iter, uri_stop);
4491 back = uri_end_iter;
4492 gtk_text_iter_backward_char(&back);
4493 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4494 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4495 gtk_text_buffer_apply_tag_by_name(
4496 buffer, "link", &uri_start_iter, &uri_end_iter);
4498 if (removed && !modified_before_remove) {
4504 // debug_print("not modified, out after %d lines\n", lines);
4508 // debug_print("modified, out after %d lines\n", lines);
4510 g_free(itemized_chars);
4513 undo_wrapping(compose->undostruct, FALSE);
4514 compose->autowrap = prev_autowrap;
4519 void compose_action_cb(void *data)
4521 Compose *compose = (Compose *)data;
4522 compose_wrap_all(compose);
4525 static void compose_wrap_all(Compose *compose)
4527 compose_wrap_all_full(compose, FALSE);
4530 static void compose_wrap_all_full(Compose *compose, gboolean force)
4532 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4533 GtkTextBuffer *buffer;
4535 gboolean modified = TRUE;
4537 buffer = gtk_text_view_get_buffer(text);
4539 gtk_text_buffer_get_start_iter(buffer, &iter);
4540 while (!gtk_text_iter_is_end(&iter) && modified)
4541 modified = compose_beautify_paragraph(compose, &iter, force);
4545 static void compose_set_title(Compose *compose)
4551 edited = compose->modified ? _(" [Edited]") : "";
4553 subject = gtk_editable_get_chars(
4554 GTK_EDITABLE(compose->subject_entry), 0, -1);
4556 #ifndef GENERIC_UMPC
4557 if (subject && strlen(subject))
4558 str = g_strdup_printf(_("%s - Compose message%s"),
4561 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4563 str = g_strdup(_("Compose message"));
4566 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4572 * compose_current_mail_account:
4574 * Find a current mail account (the currently selected account, or the
4575 * default account, if a news account is currently selected). If a
4576 * mail account cannot be found, display an error message.
4578 * Return value: Mail account, or NULL if not found.
4580 static PrefsAccount *
4581 compose_current_mail_account(void)
4585 if (cur_account && cur_account->protocol != A_NNTP)
4588 ac = account_get_default();
4589 if (!ac || ac->protocol == A_NNTP) {
4590 alertpanel_error(_("Account for sending mail is not specified.\n"
4591 "Please select a mail account before sending."));
4598 #define QUOTE_IF_REQUIRED(out, str) \
4600 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4604 len = strlen(str) + 3; \
4605 if ((__tmp = alloca(len)) == NULL) { \
4606 g_warning("can't allocate memory\n"); \
4607 g_string_free(header, TRUE); \
4610 g_snprintf(__tmp, len, "\"%s\"", str); \
4615 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4616 g_warning("can't allocate memory\n"); \
4617 g_string_free(header, TRUE); \
4620 strcpy(__tmp, str); \
4626 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4628 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4632 len = strlen(str) + 3; \
4633 if ((__tmp = alloca(len)) == NULL) { \
4634 g_warning("can't allocate memory\n"); \
4637 g_snprintf(__tmp, len, "\"%s\"", str); \
4642 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4643 g_warning("can't allocate memory\n"); \
4646 strcpy(__tmp, str); \
4652 static void compose_select_account(Compose *compose, PrefsAccount *account,
4655 gchar *from = NULL, *header;
4656 ComposeHeaderEntry *header_entry;
4658 cm_return_if_fail(account != NULL);
4660 compose->account = account;
4661 if (account->name && *account->name) {
4663 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4664 from = g_strdup_printf("%s <%s>",
4665 buf, account->address);
4666 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4668 from = g_strdup_printf("<%s>",
4670 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4675 compose_set_title(compose);
4677 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4678 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4680 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4681 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4682 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4684 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4686 activate_privacy_system(compose, account, FALSE);
4688 if (!init && compose->mode != COMPOSE_REDIRECT) {
4689 undo_block(compose->undostruct);
4690 compose_insert_sig(compose, TRUE);
4691 undo_unblock(compose->undostruct);
4694 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4695 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4697 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4698 if (account->protocol == A_NNTP) {
4699 if (!strcmp(header, _("To:")))
4700 combobox_select_by_text(
4701 GTK_COMBO_BOX(header_entry->combo),
4704 if (!strcmp(header, _("Newsgroups:")))
4705 combobox_select_by_text(
4706 GTK_COMBO_BOX(header_entry->combo),
4714 /* use account's dict info if set */
4715 if (compose->gtkaspell) {
4716 if (account->enable_default_dictionary)
4717 gtkaspell_change_dict(compose->gtkaspell,
4718 account->default_dictionary, FALSE);
4719 if (account->enable_default_alt_dictionary)
4720 gtkaspell_change_alt_dict(compose->gtkaspell,
4721 account->default_alt_dictionary);
4722 if (account->enable_default_dictionary
4723 || account->enable_default_alt_dictionary)
4724 compose_spell_menu_changed(compose);
4729 gboolean compose_check_for_valid_recipient(Compose *compose) {
4730 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4731 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4732 gboolean recipient_found = FALSE;
4736 /* free to and newsgroup list */
4737 slist_free_strings(compose->to_list);
4738 g_slist_free(compose->to_list);
4739 compose->to_list = NULL;
4741 slist_free_strings(compose->newsgroup_list);
4742 g_slist_free(compose->newsgroup_list);
4743 compose->newsgroup_list = NULL;
4745 /* search header entries for to and newsgroup entries */
4746 for (list = compose->header_list; list; list = list->next) {
4749 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4750 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4753 if (entry[0] != '\0') {
4754 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4755 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4756 compose->to_list = address_list_append(compose->to_list, entry);
4757 recipient_found = TRUE;
4760 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4761 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4762 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4763 recipient_found = TRUE;
4770 return recipient_found;
4773 static gboolean compose_check_for_set_recipients(Compose *compose)
4775 if (compose->account->set_autocc && compose->account->auto_cc) {
4776 gboolean found_other = FALSE;
4778 /* search header entries for to and newsgroup entries */
4779 for (list = compose->header_list; list; list = list->next) {
4782 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4783 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4786 if (strcmp(entry, compose->account->auto_cc)
4787 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4797 if (compose->batch) {
4798 gtk_widget_show_all(compose->window);
4800 aval = alertpanel(_("Send"),
4801 _("The only recipient is the default CC address. Send anyway?"),
4802 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4803 if (aval != G_ALERTALTERNATE)
4807 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4808 gboolean found_other = FALSE;
4810 /* search header entries for to and newsgroup entries */
4811 for (list = compose->header_list; list; list = list->next) {
4814 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4815 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4818 if (strcmp(entry, compose->account->auto_bcc)
4819 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4829 if (compose->batch) {
4830 gtk_widget_show_all(compose->window);
4832 aval = alertpanel(_("Send"),
4833 _("The only recipient is the default BCC address. Send anyway?"),
4834 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4835 if (aval != G_ALERTALTERNATE)
4842 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4846 if (compose_check_for_valid_recipient(compose) == FALSE) {
4847 if (compose->batch) {
4848 gtk_widget_show_all(compose->window);
4850 alertpanel_error(_("Recipient is not specified."));
4854 if (compose_check_for_set_recipients(compose) == FALSE) {
4858 if (!compose->batch) {
4859 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4860 if (*str == '\0' && check_everything == TRUE &&
4861 compose->mode != COMPOSE_REDIRECT) {
4863 gchar *button_label;
4866 if (compose->sending)
4867 button_label = _("+_Send");
4869 button_label = _("+_Queue");
4870 message = g_strdup_printf(_("Subject is empty. %s"),
4871 compose->sending?_("Send it anyway?"):
4872 _("Queue it anyway?"));
4874 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4875 GTK_STOCK_CANCEL, button_label, NULL);
4877 if (aval != G_ALERTALTERNATE)
4882 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4888 gint compose_send(Compose *compose)
4891 FolderItem *folder = NULL;
4893 gchar *msgpath = NULL;
4894 gboolean discard_window = FALSE;
4895 gchar *errstr = NULL;
4896 gchar *tmsgid = NULL;
4897 MainWindow *mainwin = mainwindow_get_mainwindow();
4898 gboolean queued_removed = FALSE;
4900 if (prefs_common.send_dialog_invisible
4901 || compose->batch == TRUE)
4902 discard_window = TRUE;
4904 compose_allow_user_actions (compose, FALSE);
4905 compose->sending = TRUE;
4907 if (compose_check_entries(compose, TRUE) == FALSE) {
4908 if (compose->batch) {
4909 gtk_widget_show_all(compose->window);
4915 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4918 if (compose->batch) {
4919 gtk_widget_show_all(compose->window);
4922 alertpanel_error(_("Could not queue message for sending:\n\n"
4923 "Charset conversion failed."));
4924 } else if (val == -5) {
4925 alertpanel_error(_("Could not queue message for sending:\n\n"
4926 "Couldn't get recipient encryption key."));
4927 } else if (val == -6) {
4929 } else if (val == -3) {
4930 if (privacy_peek_error())
4931 alertpanel_error(_("Could not queue message for sending:\n\n"
4932 "Signature failed: %s"), privacy_get_error());
4933 } else if (val == -2 && errno != 0) {
4934 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4936 alertpanel_error(_("Could not queue message for sending."));
4941 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4942 if (discard_window) {
4943 compose->sending = FALSE;
4944 compose_close(compose);
4945 /* No more compose access in the normal codepath
4946 * after this point! */
4951 alertpanel_error(_("The message was queued but could not be "
4952 "sent.\nUse \"Send queued messages\" from "
4953 "the main window to retry."));
4954 if (!discard_window) {
4961 if (msgpath == NULL) {
4962 msgpath = folder_item_fetch_msg(folder, msgnum);
4963 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4966 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4967 claws_unlink(msgpath);
4970 if (!discard_window) {
4972 if (!queued_removed)
4973 folder_item_remove_msg(folder, msgnum);
4974 folder_item_scan(folder);
4976 /* make sure we delete that */
4977 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4979 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4980 folder_item_remove_msg(folder, tmp->msgnum);
4981 procmsg_msginfo_free(tmp);
4988 if (!queued_removed)
4989 folder_item_remove_msg(folder, msgnum);
4990 folder_item_scan(folder);
4992 /* make sure we delete that */
4993 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4995 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4996 folder_item_remove_msg(folder, tmp->msgnum);
4997 procmsg_msginfo_free(tmp);
5000 if (!discard_window) {
5001 compose->sending = FALSE;
5002 compose_allow_user_actions (compose, TRUE);
5003 compose_close(compose);
5007 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5008 "the main window to retry."), errstr);
5011 alertpanel_error_log(_("The message was queued but could not be "
5012 "sent.\nUse \"Send queued messages\" from "
5013 "the main window to retry."));
5015 if (!discard_window) {
5024 toolbar_main_set_sensitive(mainwin);
5025 main_window_set_menu_sensitive(mainwin);
5031 compose_allow_user_actions (compose, TRUE);
5032 compose->sending = FALSE;
5033 compose->modified = TRUE;
5034 toolbar_main_set_sensitive(mainwin);
5035 main_window_set_menu_sensitive(mainwin);
5040 static gboolean compose_use_attach(Compose *compose)
5042 GtkTreeModel *model = gtk_tree_view_get_model
5043 (GTK_TREE_VIEW(compose->attach_clist));
5044 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5047 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5050 gchar buf[BUFFSIZE];
5052 gboolean first_to_address;
5053 gboolean first_cc_address;
5055 ComposeHeaderEntry *headerentry;
5056 const gchar *headerentryname;
5057 const gchar *cc_hdr;
5058 const gchar *to_hdr;
5059 gboolean err = FALSE;
5061 debug_print("Writing redirect header\n");
5063 cc_hdr = prefs_common_translated_header_name("Cc:");
5064 to_hdr = prefs_common_translated_header_name("To:");
5066 first_to_address = TRUE;
5067 for (list = compose->header_list; list; list = list->next) {
5068 headerentry = ((ComposeHeaderEntry *)list->data);
5069 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5071 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5072 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5073 Xstrdup_a(str, entstr, return -1);
5075 if (str[0] != '\0') {
5076 compose_convert_header
5077 (compose, buf, sizeof(buf), str,
5078 strlen("Resent-To") + 2, TRUE);
5080 if (first_to_address) {
5081 err |= (fprintf(fp, "Resent-To: ") < 0);
5082 first_to_address = FALSE;
5084 err |= (fprintf(fp, ",") < 0);
5086 err |= (fprintf(fp, "%s", buf) < 0);
5090 if (!first_to_address) {
5091 err |= (fprintf(fp, "\n") < 0);
5094 first_cc_address = TRUE;
5095 for (list = compose->header_list; list; list = list->next) {
5096 headerentry = ((ComposeHeaderEntry *)list->data);
5097 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5099 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5100 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5101 Xstrdup_a(str, strg, return -1);
5103 if (str[0] != '\0') {
5104 compose_convert_header
5105 (compose, buf, sizeof(buf), str,
5106 strlen("Resent-Cc") + 2, TRUE);
5108 if (first_cc_address) {
5109 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5110 first_cc_address = FALSE;
5112 err |= (fprintf(fp, ",") < 0);
5114 err |= (fprintf(fp, "%s", buf) < 0);
5118 if (!first_cc_address) {
5119 err |= (fprintf(fp, "\n") < 0);
5122 return (err ? -1:0);
5125 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5127 gchar buf[BUFFSIZE];
5129 const gchar *entstr;
5130 /* struct utsname utsbuf; */
5131 gboolean err = FALSE;
5133 cm_return_val_if_fail(fp != NULL, -1);
5134 cm_return_val_if_fail(compose->account != NULL, -1);
5135 cm_return_val_if_fail(compose->account->address != NULL, -1);
5138 get_rfc822_date(buf, sizeof(buf));
5139 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5142 if (compose->account->name && *compose->account->name) {
5143 compose_convert_header
5144 (compose, buf, sizeof(buf), compose->account->name,
5145 strlen("From: "), TRUE);
5146 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5147 buf, compose->account->address) < 0);
5149 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5152 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5153 if (*entstr != '\0') {
5154 Xstrdup_a(str, entstr, return -1);
5157 compose_convert_header(compose, buf, sizeof(buf), str,
5158 strlen("Subject: "), FALSE);
5159 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5163 /* Resent-Message-ID */
5164 if (compose->account->set_domain && compose->account->domain) {
5165 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5166 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5167 g_snprintf(buf, sizeof(buf), "%s",
5168 strchr(compose->account->address, '@') ?
5169 strchr(compose->account->address, '@')+1 :
5170 compose->account->address);
5172 g_snprintf(buf, sizeof(buf), "%s", "");
5175 if (compose->account->gen_msgid) {
5177 if (compose->account->msgid_with_addr) {
5178 addr = compose->account->address;
5180 generate_msgid(buf, sizeof(buf), addr);
5181 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5182 compose->msgid = g_strdup(buf);
5184 compose->msgid = NULL;
5187 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5190 /* separator between header and body */
5191 err |= (fputs("\n", fp) == EOF);
5193 return (err ? -1:0);
5196 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5200 gchar buf[BUFFSIZE];
5202 gboolean skip = FALSE;
5203 gboolean err = FALSE;
5204 gchar *not_included[]={
5205 "Return-Path:", "Delivered-To:", "Received:",
5206 "Subject:", "X-UIDL:", "AF:",
5207 "NF:", "PS:", "SRH:",
5208 "SFN:", "DSR:", "MID:",
5209 "CFG:", "PT:", "S:",
5210 "RQ:", "SSV:", "NSV:",
5211 "SSH:", "R:", "MAID:",
5212 "NAID:", "RMID:", "FMID:",
5213 "SCF:", "RRCPT:", "NG:",
5214 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5215 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5216 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5217 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5218 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5221 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5222 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5226 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5228 for (i = 0; not_included[i] != NULL; i++) {
5229 if (g_ascii_strncasecmp(buf, not_included[i],
5230 strlen(not_included[i])) == 0) {
5237 if (fputs(buf, fdest) == -1)
5240 if (!prefs_common.redirect_keep_from) {
5241 if (g_ascii_strncasecmp(buf, "From:",
5242 strlen("From:")) == 0) {
5243 err |= (fputs(" (by way of ", fdest) == EOF);
5244 if (compose->account->name
5245 && *compose->account->name) {
5246 compose_convert_header
5247 (compose, buf, sizeof(buf),
5248 compose->account->name,
5251 err |= (fprintf(fdest, "%s <%s>",
5253 compose->account->address) < 0);
5255 err |= (fprintf(fdest, "%s",
5256 compose->account->address) < 0);
5257 err |= (fputs(")", fdest) == EOF);
5261 if (fputs("\n", fdest) == -1)
5268 if (compose_redirect_write_headers(compose, fdest))
5271 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5272 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5285 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5287 GtkTextBuffer *buffer;
5288 GtkTextIter start, end;
5291 const gchar *out_codeset;
5292 EncodingType encoding = ENC_UNKNOWN;
5293 MimeInfo *mimemsg, *mimetext;
5295 const gchar *src_codeset = CS_INTERNAL;
5296 gchar *from_addr = NULL;
5297 gchar *from_name = NULL;
5299 if (action == COMPOSE_WRITE_FOR_SEND)
5300 attach_parts = TRUE;
5302 /* create message MimeInfo */
5303 mimemsg = procmime_mimeinfo_new();
5304 mimemsg->type = MIMETYPE_MESSAGE;
5305 mimemsg->subtype = g_strdup("rfc822");
5306 mimemsg->content = MIMECONTENT_MEM;
5307 mimemsg->tmp = TRUE; /* must free content later */
5308 mimemsg->data.mem = compose_get_header(compose);
5310 /* Create text part MimeInfo */
5311 /* get all composed text */
5312 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5313 gtk_text_buffer_get_start_iter(buffer, &start);
5314 gtk_text_buffer_get_end_iter(buffer, &end);
5315 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5317 out_codeset = conv_get_charset_str(compose->out_encoding);
5319 if (!out_codeset && is_ascii_str(chars)) {
5320 out_codeset = CS_US_ASCII;
5321 } else if (prefs_common.outgoing_fallback_to_ascii &&
5322 is_ascii_str(chars)) {
5323 out_codeset = CS_US_ASCII;
5324 encoding = ENC_7BIT;
5328 gchar *test_conv_global_out = NULL;
5329 gchar *test_conv_reply = NULL;
5331 /* automatic mode. be automatic. */
5332 codeconv_set_strict(TRUE);
5334 out_codeset = conv_get_outgoing_charset_str();
5336 debug_print("trying to convert to %s\n", out_codeset);
5337 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5340 if (!test_conv_global_out && compose->orig_charset
5341 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5342 out_codeset = compose->orig_charset;
5343 debug_print("failure; trying to convert to %s\n", out_codeset);
5344 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5347 if (!test_conv_global_out && !test_conv_reply) {
5349 out_codeset = CS_INTERNAL;
5350 debug_print("failure; finally using %s\n", out_codeset);
5352 g_free(test_conv_global_out);
5353 g_free(test_conv_reply);
5354 codeconv_set_strict(FALSE);
5357 if (encoding == ENC_UNKNOWN) {
5358 if (prefs_common.encoding_method == CTE_BASE64)
5359 encoding = ENC_BASE64;
5360 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5361 encoding = ENC_QUOTED_PRINTABLE;
5362 else if (prefs_common.encoding_method == CTE_8BIT)
5363 encoding = ENC_8BIT;
5365 encoding = procmime_get_encoding_for_charset(out_codeset);
5368 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5369 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5371 if (action == COMPOSE_WRITE_FOR_SEND) {
5372 codeconv_set_strict(TRUE);
5373 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5374 codeconv_set_strict(FALSE);
5380 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5381 "to the specified %s charset.\n"
5382 "Send it as %s?"), out_codeset, src_codeset);
5383 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5384 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5387 if (aval != G_ALERTALTERNATE) {
5392 out_codeset = src_codeset;
5398 out_codeset = src_codeset;
5403 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5404 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5405 strstr(buf, "\nFrom ") != NULL) {
5406 encoding = ENC_QUOTED_PRINTABLE;
5410 mimetext = procmime_mimeinfo_new();
5411 mimetext->content = MIMECONTENT_MEM;
5412 mimetext->tmp = TRUE; /* must free content later */
5413 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5414 * and free the data, which we need later. */
5415 mimetext->data.mem = g_strdup(buf);
5416 mimetext->type = MIMETYPE_TEXT;
5417 mimetext->subtype = g_strdup("plain");
5418 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5419 g_strdup(out_codeset));
5421 /* protect trailing spaces when signing message */
5422 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5423 privacy_system_can_sign(compose->privacy_system)) {
5424 encoding = ENC_QUOTED_PRINTABLE;
5427 debug_print("main text: %zd bytes encoded as %s in %d\n",
5428 strlen(buf), out_codeset, encoding);
5430 /* check for line length limit */
5431 if (action == COMPOSE_WRITE_FOR_SEND &&
5432 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5433 check_line_length(buf, 1000, &line) < 0) {
5437 msg = g_strdup_printf
5438 (_("Line %d exceeds the line length limit (998 bytes).\n"
5439 "The contents of the message might be broken on the way to the delivery.\n"
5441 "Send it anyway?"), line + 1);
5442 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5444 if (aval != G_ALERTALTERNATE) {
5450 if (encoding != ENC_UNKNOWN)
5451 procmime_encode_content(mimetext, encoding);
5453 /* append attachment parts */
5454 if (compose_use_attach(compose) && attach_parts) {
5455 MimeInfo *mimempart;
5456 gchar *boundary = NULL;
5457 mimempart = procmime_mimeinfo_new();
5458 mimempart->content = MIMECONTENT_EMPTY;
5459 mimempart->type = MIMETYPE_MULTIPART;
5460 mimempart->subtype = g_strdup("mixed");
5464 boundary = generate_mime_boundary(NULL);
5465 } while (strstr(buf, boundary) != NULL);
5467 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5470 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5472 g_node_append(mimempart->node, mimetext->node);
5473 g_node_append(mimemsg->node, mimempart->node);
5475 if (compose_add_attachments(compose, mimempart) < 0)
5478 g_node_append(mimemsg->node, mimetext->node);
5482 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5483 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5484 /* extract name and address */
5485 if (strstr(spec, " <") && strstr(spec, ">")) {
5486 from_addr = g_strdup(strrchr(spec, '<')+1);
5487 *(strrchr(from_addr, '>')) = '\0';
5488 from_name = g_strdup(spec);
5489 *(strrchr(from_name, '<')) = '\0';
5496 /* sign message if sending */
5497 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5498 privacy_system_can_sign(compose->privacy_system))
5499 if (!privacy_sign(compose->privacy_system, mimemsg,
5500 compose->account, from_addr)) {
5507 procmime_write_mimeinfo(mimemsg, fp);
5509 procmime_mimeinfo_free_all(mimemsg);
5514 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5516 GtkTextBuffer *buffer;
5517 GtkTextIter start, end;
5522 if ((fp = g_fopen(file, "wb")) == NULL) {
5523 FILE_OP_ERROR(file, "fopen");
5527 /* chmod for security */
5528 if (change_file_mode_rw(fp, file) < 0) {
5529 FILE_OP_ERROR(file, "chmod");
5530 g_warning("can't change file mode\n");
5533 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5534 gtk_text_buffer_get_start_iter(buffer, &start);
5535 gtk_text_buffer_get_end_iter(buffer, &end);
5536 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5538 chars = conv_codeset_strdup
5539 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5542 if (!chars) return -1;
5545 len = strlen(chars);
5546 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5547 FILE_OP_ERROR(file, "fwrite");
5556 if (fclose(fp) == EOF) {
5557 FILE_OP_ERROR(file, "fclose");
5564 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5567 MsgInfo *msginfo = compose->targetinfo;
5569 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5570 if (!msginfo) return -1;
5572 if (!force && MSG_IS_LOCKED(msginfo->flags))
5575 item = msginfo->folder;
5576 cm_return_val_if_fail(item != NULL, -1);
5578 if (procmsg_msg_exist(msginfo) &&
5579 (folder_has_parent_of_type(item, F_QUEUE) ||
5580 folder_has_parent_of_type(item, F_DRAFT)
5581 || msginfo == compose->autosaved_draft)) {
5582 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5583 g_warning("can't remove the old message\n");
5586 debug_print("removed reedit target %d\n", msginfo->msgnum);
5593 static void compose_remove_draft(Compose *compose)
5596 MsgInfo *msginfo = compose->targetinfo;
5597 drafts = account_get_special_folder(compose->account, F_DRAFT);
5599 if (procmsg_msg_exist(msginfo)) {
5600 folder_item_remove_msg(drafts, msginfo->msgnum);
5605 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5606 gboolean remove_reedit_target)
5608 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5611 static gboolean compose_warn_encryption(Compose *compose)
5613 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5614 AlertValue val = G_ALERTALTERNATE;
5616 if (warning == NULL)
5619 val = alertpanel_full(_("Encryption warning"), warning,
5620 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5621 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5622 if (val & G_ALERTDISABLE) {
5623 val &= ~G_ALERTDISABLE;
5624 if (val == G_ALERTALTERNATE)
5625 privacy_inhibit_encrypt_warning(compose->privacy_system,
5629 if (val == G_ALERTALTERNATE) {
5636 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5637 gchar **msgpath, gboolean check_subject,
5638 gboolean remove_reedit_target)
5645 static gboolean lock = FALSE;
5646 PrefsAccount *mailac = NULL, *newsac = NULL;
5647 gboolean err = FALSE;
5649 debug_print("queueing message...\n");
5650 cm_return_val_if_fail(compose->account != NULL, -1);
5654 if (compose_check_entries(compose, check_subject) == FALSE) {
5656 if (compose->batch) {
5657 gtk_widget_show_all(compose->window);
5662 if (!compose->to_list && !compose->newsgroup_list) {
5663 g_warning("can't get recipient list.");
5668 if (compose->to_list) {
5669 if (compose->account->protocol != A_NNTP)
5670 mailac = compose->account;
5671 else if (cur_account && cur_account->protocol != A_NNTP)
5672 mailac = cur_account;
5673 else if (!(mailac = compose_current_mail_account())) {
5675 alertpanel_error(_("No account for sending mails available!"));
5680 if (compose->newsgroup_list) {
5681 if (compose->account->protocol == A_NNTP)
5682 newsac = compose->account;
5685 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5690 /* write queue header */
5691 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5692 G_DIR_SEPARATOR, compose, (guint) rand());
5693 debug_print("queuing to %s\n", tmp);
5694 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5695 FILE_OP_ERROR(tmp, "fopen");
5701 if (change_file_mode_rw(fp, tmp) < 0) {
5702 FILE_OP_ERROR(tmp, "chmod");
5703 g_warning("can't change file mode\n");
5706 /* queueing variables */
5707 err |= (fprintf(fp, "AF:\n") < 0);
5708 err |= (fprintf(fp, "NF:0\n") < 0);
5709 err |= (fprintf(fp, "PS:10\n") < 0);
5710 err |= (fprintf(fp, "SRH:1\n") < 0);
5711 err |= (fprintf(fp, "SFN:\n") < 0);
5712 err |= (fprintf(fp, "DSR:\n") < 0);
5714 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5716 err |= (fprintf(fp, "MID:\n") < 0);
5717 err |= (fprintf(fp, "CFG:\n") < 0);
5718 err |= (fprintf(fp, "PT:0\n") < 0);
5719 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5720 err |= (fprintf(fp, "RQ:\n") < 0);
5722 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5724 err |= (fprintf(fp, "SSV:\n") < 0);
5726 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5728 err |= (fprintf(fp, "NSV:\n") < 0);
5729 err |= (fprintf(fp, "SSH:\n") < 0);
5730 /* write recepient list */
5731 if (compose->to_list) {
5732 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5733 for (cur = compose->to_list->next; cur != NULL;
5735 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5736 err |= (fprintf(fp, "\n") < 0);
5738 /* write newsgroup list */
5739 if (compose->newsgroup_list) {
5740 err |= (fprintf(fp, "NG:") < 0);
5741 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5742 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5743 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5744 err |= (fprintf(fp, "\n") < 0);
5746 /* Sylpheed account IDs */
5748 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5750 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5753 if (compose->privacy_system != NULL) {
5754 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5755 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5756 if (compose->use_encryption) {
5758 if (!compose_warn_encryption(compose)) {
5765 if (mailac && mailac->encrypt_to_self) {
5766 GSList *tmp_list = g_slist_copy(compose->to_list);
5767 tmp_list = g_slist_append(tmp_list, compose->account->address);
5768 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5769 g_slist_free(tmp_list);
5771 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5773 if (encdata != NULL) {
5774 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5775 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5776 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5778 } /* else we finally dont want to encrypt */
5780 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5781 /* and if encdata was null, it means there's been a problem in
5793 /* Save copy folder */
5794 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5795 gchar *savefolderid;
5797 savefolderid = compose_get_save_to(compose);
5798 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5799 g_free(savefolderid);
5801 /* Save copy folder */
5802 if (compose->return_receipt) {
5803 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5805 /* Message-ID of message replying to */
5806 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5809 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5810 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5813 /* Message-ID of message forwarding to */
5814 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5817 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5818 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5822 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5823 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5825 /* end of headers */
5826 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5828 if (compose->redirect_filename != NULL) {
5829 if (compose_redirect_write_to_file(compose, fp) < 0) {
5838 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5843 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5847 g_warning("failed to write queue message\n");
5854 if (fclose(fp) == EOF) {
5855 FILE_OP_ERROR(tmp, "fclose");
5862 if (item && *item) {
5865 queue = account_get_special_folder(compose->account, F_QUEUE);
5868 g_warning("can't find queue folder\n");
5874 folder_item_scan(queue);
5875 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5876 g_warning("can't queue the message\n");
5883 if (msgpath == NULL) {
5889 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5890 compose_remove_reedit_target(compose, FALSE);
5893 if ((msgnum != NULL) && (item != NULL)) {
5901 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5904 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5906 struct stat statbuf;
5907 gchar *type, *subtype;
5908 GtkTreeModel *model;
5911 model = gtk_tree_view_get_model(tree_view);
5913 if (!gtk_tree_model_get_iter_first(model, &iter))
5916 gtk_tree_model_get(model, &iter,
5920 if (!is_file_exist(ainfo->file)) {
5921 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5922 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5923 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5925 if (val == G_ALERTDEFAULT) {
5930 mimepart = procmime_mimeinfo_new();
5931 mimepart->content = MIMECONTENT_FILE;
5932 mimepart->data.filename = g_strdup(ainfo->file);
5933 mimepart->tmp = FALSE; /* or we destroy our attachment */
5934 mimepart->offset = 0;
5936 g_stat(ainfo->file, &statbuf);
5937 mimepart->length = statbuf.st_size;
5939 type = g_strdup(ainfo->content_type);
5941 if (!strchr(type, '/')) {
5943 type = g_strdup("application/octet-stream");
5946 subtype = strchr(type, '/') + 1;
5947 *(subtype - 1) = '\0';
5948 mimepart->type = procmime_get_media_type(type);
5949 mimepart->subtype = g_strdup(subtype);
5952 if (mimepart->type == MIMETYPE_MESSAGE &&
5953 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5954 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5957 if (mimepart->type == MIMETYPE_APPLICATION &&
5958 !strcmp2(mimepart->subtype, "octet-stream"))
5959 g_hash_table_insert(mimepart->typeparameters,
5960 g_strdup("name"), g_strdup(ainfo->name));
5961 g_hash_table_insert(mimepart->dispositionparameters,
5962 g_strdup("filename"), g_strdup(ainfo->name));
5963 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5967 if (compose->use_signing) {
5968 if (ainfo->encoding == ENC_7BIT)
5969 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5970 else if (ainfo->encoding == ENC_8BIT)
5971 ainfo->encoding = ENC_BASE64;
5974 procmime_encode_content(mimepart, ainfo->encoding);
5976 g_node_append(parent->node, mimepart->node);
5977 } while (gtk_tree_model_iter_next(model, &iter));
5982 #define IS_IN_CUSTOM_HEADER(header) \
5983 (compose->account->add_customhdr && \
5984 custom_header_find(compose->account->customhdr_list, header) != NULL)
5986 static void compose_add_headerfield_from_headerlist(Compose *compose,
5988 const gchar *fieldname,
5989 const gchar *seperator)
5991 gchar *str, *fieldname_w_colon;
5992 gboolean add_field = FALSE;
5994 ComposeHeaderEntry *headerentry;
5995 const gchar *headerentryname;
5996 const gchar *trans_fieldname;
5999 if (IS_IN_CUSTOM_HEADER(fieldname))
6002 debug_print("Adding %s-fields\n", fieldname);
6004 fieldstr = g_string_sized_new(64);
6006 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6007 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6009 for (list = compose->header_list; list; list = list->next) {
6010 headerentry = ((ComposeHeaderEntry *)list->data);
6011 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6013 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6014 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6016 if (str[0] != '\0') {
6018 g_string_append(fieldstr, seperator);
6019 g_string_append(fieldstr, str);
6028 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6029 compose_convert_header
6030 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6031 strlen(fieldname) + 2, TRUE);
6032 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6036 g_free(fieldname_w_colon);
6037 g_string_free(fieldstr, TRUE);
6042 static gchar *compose_get_header(Compose *compose)
6044 gchar buf[BUFFSIZE];
6045 const gchar *entry_str;
6049 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6051 gchar *from_name = NULL, *from_address = NULL;
6054 cm_return_val_if_fail(compose->account != NULL, NULL);
6055 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6057 header = g_string_sized_new(64);
6060 get_rfc822_date(buf, sizeof(buf));
6061 g_string_append_printf(header, "Date: %s\n", buf);
6065 if (compose->account->name && *compose->account->name) {
6067 QUOTE_IF_REQUIRED(buf, compose->account->name);
6068 tmp = g_strdup_printf("%s <%s>",
6069 buf, compose->account->address);
6071 tmp = g_strdup_printf("%s",
6072 compose->account->address);
6074 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6075 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6077 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6078 from_address = g_strdup(compose->account->address);
6080 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6081 /* extract name and address */
6082 if (strstr(spec, " <") && strstr(spec, ">")) {
6083 from_address = g_strdup(strrchr(spec, '<')+1);
6084 *(strrchr(from_address, '>')) = '\0';
6085 from_name = g_strdup(spec);
6086 *(strrchr(from_name, '<')) = '\0';
6089 from_address = g_strdup(spec);
6096 if (from_name && *from_name) {
6097 compose_convert_header
6098 (compose, buf, sizeof(buf), from_name,
6099 strlen("From: "), TRUE);
6100 QUOTE_IF_REQUIRED(name, buf);
6102 g_string_append_printf(header, "From: %s <%s>\n",
6103 name, from_address);
6105 g_string_append_printf(header, "From: %s\n", from_address);
6108 g_free(from_address);
6111 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6114 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6117 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6121 * If this account is a NNTP account remove Bcc header from
6122 * message body since it otherwise will be publicly shown
6124 if (compose->account->protocol != A_NNTP)
6125 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6128 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6130 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6133 compose_convert_header(compose, buf, sizeof(buf), str,
6134 strlen("Subject: "), FALSE);
6135 g_string_append_printf(header, "Subject: %s\n", buf);
6141 if (compose->account->set_domain && compose->account->domain) {
6142 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6143 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6144 g_snprintf(buf, sizeof(buf), "%s",
6145 strchr(compose->account->address, '@') ?
6146 strchr(compose->account->address, '@')+1 :
6147 compose->account->address);
6149 g_snprintf(buf, sizeof(buf), "%s", "");
6152 if (compose->account->gen_msgid) {
6154 if (compose->account->msgid_with_addr) {
6155 addr = compose->account->address;
6157 generate_msgid(buf, sizeof(buf), addr);
6158 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6159 compose->msgid = g_strdup(buf);
6161 compose->msgid = NULL;
6164 if (compose->remove_references == FALSE) {
6166 if (compose->inreplyto && compose->to_list)
6167 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6170 if (compose->references)
6171 g_string_append_printf(header, "References: %s\n", compose->references);
6175 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6178 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6181 if (compose->account->organization &&
6182 strlen(compose->account->organization) &&
6183 !IS_IN_CUSTOM_HEADER("Organization")) {
6184 compose_convert_header(compose, buf, sizeof(buf),
6185 compose->account->organization,
6186 strlen("Organization: "), FALSE);
6187 g_string_append_printf(header, "Organization: %s\n", buf);
6190 /* Program version and system info */
6191 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6192 !compose->newsgroup_list) {
6193 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6195 gtk_major_version, gtk_minor_version, gtk_micro_version,
6198 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6199 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6201 gtk_major_version, gtk_minor_version, gtk_micro_version,
6205 /* custom headers */
6206 if (compose->account->add_customhdr) {
6209 for (cur = compose->account->customhdr_list; cur != NULL;
6211 CustomHeader *chdr = (CustomHeader *)cur->data;
6213 if (custom_header_is_allowed(chdr->name)) {
6214 compose_convert_header
6215 (compose, buf, sizeof(buf),
6216 chdr->value ? chdr->value : "",
6217 strlen(chdr->name) + 2, FALSE);
6218 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6223 /* Automatic Faces and X-Faces */
6224 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6225 g_string_append_printf(header, "X-Face: %s\n", buf);
6227 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6228 g_string_append_printf(header, "X-Face: %s\n", buf);
6230 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6231 g_string_append_printf(header, "Face: %s\n", buf);
6233 else if (get_default_face (buf, sizeof(buf)) == 0) {
6234 g_string_append_printf(header, "Face: %s\n", buf);
6238 switch (compose->priority) {
6239 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6240 "X-Priority: 1 (Highest)\n");
6242 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6243 "X-Priority: 2 (High)\n");
6245 case PRIORITY_NORMAL: break;
6246 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6247 "X-Priority: 4 (Low)\n");
6249 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6250 "X-Priority: 5 (Lowest)\n");
6252 default: debug_print("compose: priority unknown : %d\n",
6256 /* Request Return Receipt */
6257 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6258 if (compose->return_receipt) {
6259 if (compose->account->name
6260 && *compose->account->name) {
6261 compose_convert_header(compose, buf, sizeof(buf),
6262 compose->account->name,
6263 strlen("Disposition-Notification-To: "),
6265 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6267 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6271 /* get special headers */
6272 for (list = compose->header_list; list; list = list->next) {
6273 ComposeHeaderEntry *headerentry;
6276 gchar *headername_wcolon;
6277 const gchar *headername_trans;
6280 gboolean standard_header = FALSE;
6282 headerentry = ((ComposeHeaderEntry *)list->data);
6284 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6286 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6291 if (!strstr(tmp, ":")) {
6292 headername_wcolon = g_strconcat(tmp, ":", NULL);
6293 headername = g_strdup(tmp);
6295 headername_wcolon = g_strdup(tmp);
6296 headername = g_strdup(strtok(tmp, ":"));
6300 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6301 Xstrdup_a(headervalue, entry_str, return NULL);
6302 subst_char(headervalue, '\r', ' ');
6303 subst_char(headervalue, '\n', ' ');
6304 string = std_headers;
6305 while (*string != NULL) {
6306 headername_trans = prefs_common_translated_header_name(*string);
6307 if (!strcmp(headername_trans, headername_wcolon))
6308 standard_header = TRUE;
6311 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6312 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6315 g_free(headername_wcolon);
6319 g_string_free(header, FALSE);
6324 #undef IS_IN_CUSTOM_HEADER
6326 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6327 gint header_len, gboolean addr_field)
6329 gchar *tmpstr = NULL;
6330 const gchar *out_codeset = NULL;
6332 cm_return_if_fail(src != NULL);
6333 cm_return_if_fail(dest != NULL);
6335 if (len < 1) return;
6337 tmpstr = g_strdup(src);
6339 subst_char(tmpstr, '\n', ' ');
6340 subst_char(tmpstr, '\r', ' ');
6343 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6344 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6345 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6350 codeconv_set_strict(TRUE);
6351 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6352 conv_get_charset_str(compose->out_encoding));
6353 codeconv_set_strict(FALSE);
6355 if (!dest || *dest == '\0') {
6356 gchar *test_conv_global_out = NULL;
6357 gchar *test_conv_reply = NULL;
6359 /* automatic mode. be automatic. */
6360 codeconv_set_strict(TRUE);
6362 out_codeset = conv_get_outgoing_charset_str();
6364 debug_print("trying to convert to %s\n", out_codeset);
6365 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6368 if (!test_conv_global_out && compose->orig_charset
6369 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6370 out_codeset = compose->orig_charset;
6371 debug_print("failure; trying to convert to %s\n", out_codeset);
6372 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6375 if (!test_conv_global_out && !test_conv_reply) {
6377 out_codeset = CS_INTERNAL;
6378 debug_print("finally using %s\n", out_codeset);
6380 g_free(test_conv_global_out);
6381 g_free(test_conv_reply);
6382 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6384 codeconv_set_strict(FALSE);
6389 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6393 cm_return_if_fail(user_data != NULL);
6395 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6396 g_strstrip(address);
6397 if (*address != '\0') {
6398 gchar *name = procheader_get_fromname(address);
6399 extract_address(address);
6400 addressbook_add_contact(name, address, NULL, NULL);
6405 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6407 GtkWidget *menuitem;
6410 cm_return_if_fail(menu != NULL);
6411 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6413 menuitem = gtk_separator_menu_item_new();
6414 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6415 gtk_widget_show(menuitem);
6417 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6418 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6420 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6421 g_strstrip(address);
6422 if (*address == '\0') {
6423 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6426 g_signal_connect(G_OBJECT(menuitem), "activate",
6427 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6428 gtk_widget_show(menuitem);
6431 static void compose_create_header_entry(Compose *compose)
6433 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6440 const gchar *header = NULL;
6441 ComposeHeaderEntry *headerentry;
6442 gboolean standard_header = FALSE;
6443 GtkListStore *model;
6445 #if !(GTK_CHECK_VERSION(2,12,0))
6446 GtkTooltips *tips = compose->tooltips;
6449 headerentry = g_new0(ComposeHeaderEntry, 1);
6452 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6453 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6454 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6456 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6458 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6460 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6461 COMPOSE_NEWSGROUPS);
6462 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6464 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6465 COMPOSE_FOLLOWUPTO);
6467 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6468 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6469 G_CALLBACK(compose_grab_focus_cb), compose);
6470 gtk_widget_show(combo);
6471 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6472 compose->header_nextrow, compose->header_nextrow+1,
6473 GTK_SHRINK, GTK_FILL, 0, 0);
6474 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6475 const gchar *last_header_entry = gtk_entry_get_text(
6476 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6478 while (*string != NULL) {
6479 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6480 standard_header = TRUE;
6483 if (standard_header)
6484 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6486 if (!compose->header_last || !standard_header) {
6487 switch(compose->account->protocol) {
6489 header = prefs_common_translated_header_name("Newsgroups:");
6492 header = prefs_common_translated_header_name("To:");
6497 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6499 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6500 G_CALLBACK(compose_grab_focus_cb), compose);
6502 /* Entry field with cleanup button */
6503 button = gtk_button_new();
6504 gtk_button_set_image(GTK_BUTTON(button),
6505 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6506 gtk_widget_show(button);
6507 CLAWS_SET_TIP(button,
6508 _("Delete entry contents"));
6509 entry = gtk_entry_new();
6510 gtk_widget_show(entry);
6511 CLAWS_SET_TIP(entry,
6512 _("Use <tab> to autocomplete from addressbook"));
6513 hbox = gtk_hbox_new (FALSE, 0);
6514 gtk_widget_show(hbox);
6515 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6516 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6517 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6518 compose->header_nextrow, compose->header_nextrow+1,
6519 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6521 g_signal_connect(G_OBJECT(entry), "key-press-event",
6522 G_CALLBACK(compose_headerentry_key_press_event_cb),
6524 g_signal_connect(G_OBJECT(entry), "changed",
6525 G_CALLBACK(compose_headerentry_changed_cb),
6527 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6528 G_CALLBACK(compose_grab_focus_cb), compose);
6530 g_signal_connect(G_OBJECT(button), "clicked",
6531 G_CALLBACK(compose_headerentry_button_clicked_cb),
6535 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6536 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6537 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6538 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6539 G_CALLBACK(compose_header_drag_received_cb),
6541 g_signal_connect(G_OBJECT(entry), "drag-drop",
6542 G_CALLBACK(compose_drag_drop),
6544 g_signal_connect(G_OBJECT(entry), "populate-popup",
6545 G_CALLBACK(compose_entry_popup_extend),
6548 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6550 headerentry->compose = compose;
6551 headerentry->combo = combo;
6552 headerentry->entry = entry;
6553 headerentry->button = button;
6554 headerentry->hbox = hbox;
6555 headerentry->headernum = compose->header_nextrow;
6556 headerentry->type = PREF_NONE;
6558 compose->header_nextrow++;
6559 compose->header_last = headerentry;
6560 compose->header_list =
6561 g_slist_append(compose->header_list,
6565 static void compose_add_header_entry(Compose *compose, const gchar *header,
6566 gchar *text, ComposePrefType pref_type)
6568 ComposeHeaderEntry *last_header = compose->header_last;
6569 gchar *tmp = g_strdup(text), *email;
6570 gboolean replyto_hdr = g_str_has_suffix(header, "-To:");
6572 extract_address(tmp);
6573 email = g_utf8_strdown(tmp, -1);
6575 if (replyto_hdr == FALSE &&
6576 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6578 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6579 header, text, (gint) pref_type);
6585 if (!strcmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6586 gtk_entry_set_text(GTK_ENTRY(
6587 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6589 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6590 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6591 last_header->type = pref_type;
6593 if (replyto_hdr == FALSE)
6594 g_hash_table_insert(compose->email_hashtable, email,
6595 GUINT_TO_POINTER(1));
6602 static void compose_destroy_headerentry(Compose *compose,
6603 ComposeHeaderEntry *headerentry)
6605 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6608 extract_address(text);
6609 email = g_utf8_strdown(text, -1);
6610 g_hash_table_remove(compose->email_hashtable, email);
6614 gtk_widget_destroy(headerentry->combo);
6615 gtk_widget_destroy(headerentry->entry);
6616 gtk_widget_destroy(headerentry->button);
6617 gtk_widget_destroy(headerentry->hbox);
6618 g_free(headerentry);
6621 static void compose_remove_header_entries(Compose *compose)
6624 for (list = compose->header_list; list; list = list->next)
6625 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6627 compose->header_last = NULL;
6628 g_slist_free(compose->header_list);
6629 compose->header_list = NULL;
6630 compose->header_nextrow = 1;
6631 compose_create_header_entry(compose);
6634 static GtkWidget *compose_create_header(Compose *compose)
6636 GtkWidget *from_optmenu_hbox;
6637 GtkWidget *header_scrolledwin;
6638 GtkWidget *header_table;
6642 /* header labels and entries */
6643 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6644 gtk_widget_show(header_scrolledwin);
6645 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6647 header_table = gtk_table_new(2, 2, FALSE);
6648 gtk_widget_show(header_table);
6649 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6650 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6651 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6654 /* option menu for selecting accounts */
6655 from_optmenu_hbox = compose_account_option_menu_create(compose);
6656 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6657 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6660 compose->header_table = header_table;
6661 compose->header_list = NULL;
6662 compose->header_nextrow = count;
6664 compose_create_header_entry(compose);
6666 compose->table = NULL;
6668 return header_scrolledwin ;
6671 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6673 Compose *compose = (Compose *)data;
6674 GdkEventButton event;
6677 event.time = gtk_get_current_event_time();
6679 return attach_button_pressed(compose->attach_clist, &event, compose);
6682 static GtkWidget *compose_create_attach(Compose *compose)
6684 GtkWidget *attach_scrwin;
6685 GtkWidget *attach_clist;
6687 GtkListStore *store;
6688 GtkCellRenderer *renderer;
6689 GtkTreeViewColumn *column;
6690 GtkTreeSelection *selection;
6692 /* attachment list */
6693 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6694 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6695 GTK_POLICY_AUTOMATIC,
6696 GTK_POLICY_AUTOMATIC);
6697 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6699 store = gtk_list_store_new(N_ATTACH_COLS,
6704 G_TYPE_AUTO_POINTER,
6706 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6707 (GTK_TREE_MODEL(store)));
6708 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6709 g_object_unref(store);
6711 renderer = gtk_cell_renderer_text_new();
6712 column = gtk_tree_view_column_new_with_attributes
6713 (_("Mime type"), renderer, "text",
6714 COL_MIMETYPE, NULL);
6715 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6717 renderer = gtk_cell_renderer_text_new();
6718 column = gtk_tree_view_column_new_with_attributes
6719 (_("Size"), renderer, "text",
6721 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6723 renderer = gtk_cell_renderer_text_new();
6724 column = gtk_tree_view_column_new_with_attributes
6725 (_("Name"), renderer, "text",
6727 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6729 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6730 prefs_common.use_stripes_everywhere);
6731 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6732 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6734 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6735 G_CALLBACK(attach_selected), compose);
6736 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6737 G_CALLBACK(attach_button_pressed), compose);
6739 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6740 G_CALLBACK(popup_attach_button_pressed), compose);
6742 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6743 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6744 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6745 G_CALLBACK(popup_attach_button_pressed), compose);
6747 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6748 G_CALLBACK(attach_key_pressed), compose);
6751 gtk_drag_dest_set(attach_clist,
6752 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6753 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6754 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6755 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6756 G_CALLBACK(compose_attach_drag_received_cb),
6758 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6759 G_CALLBACK(compose_drag_drop),
6762 compose->attach_scrwin = attach_scrwin;
6763 compose->attach_clist = attach_clist;
6765 return attach_scrwin;
6768 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6769 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6771 static GtkWidget *compose_create_others(Compose *compose)
6774 GtkWidget *savemsg_checkbtn;
6775 GtkWidget *savemsg_combo;
6776 GtkWidget *savemsg_select;
6779 gchar *folderidentifier;
6781 /* Table for settings */
6782 table = gtk_table_new(3, 1, FALSE);
6783 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6784 gtk_widget_show(table);
6785 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6788 /* Save Message to folder */
6789 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6790 gtk_widget_show(savemsg_checkbtn);
6791 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6792 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6793 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6795 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6796 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6798 savemsg_combo = gtk_combo_box_entry_new_text();
6799 compose->savemsg_checkbtn = savemsg_checkbtn;
6800 compose->savemsg_combo = savemsg_combo;
6801 gtk_widget_show(savemsg_combo);
6803 if (prefs_common.compose_save_to_history)
6804 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6805 prefs_common.compose_save_to_history);
6807 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6808 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6809 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6810 G_CALLBACK(compose_grab_focus_cb), compose);
6811 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6812 folderidentifier = folder_item_get_identifier(account_get_special_folder
6813 (compose->account, F_OUTBOX));
6814 compose_set_save_to(compose, folderidentifier);
6815 g_free(folderidentifier);
6818 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6819 gtk_widget_show(savemsg_select);
6820 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6821 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6822 G_CALLBACK(compose_savemsg_select_cb),
6830 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6832 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6833 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6836 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6841 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6844 path = folder_item_get_identifier(dest);
6846 compose_set_save_to(compose, path);
6850 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6851 GdkAtom clip, GtkTextIter *insert_place);
6854 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6858 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6860 if (event->button == 3) {
6862 GtkTextIter sel_start, sel_end;
6863 gboolean stuff_selected;
6865 /* move the cursor to allow GtkAspell to check the word
6866 * under the mouse */
6867 if (event->x && event->y) {
6868 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6869 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6871 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6874 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6875 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6878 stuff_selected = gtk_text_buffer_get_selection_bounds(
6880 &sel_start, &sel_end);
6882 gtk_text_buffer_place_cursor (buffer, &iter);
6883 /* reselect stuff */
6885 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6886 gtk_text_buffer_select_range(buffer,
6887 &sel_start, &sel_end);
6889 return FALSE; /* pass the event so that the right-click goes through */
6892 if (event->button == 2) {
6897 /* get the middle-click position to paste at the correct place */
6898 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6899 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6901 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6904 entry_paste_clipboard(compose, text,
6905 prefs_common.linewrap_pastes,
6906 GDK_SELECTION_PRIMARY, &iter);
6914 static void compose_spell_menu_changed(void *data)
6916 Compose *compose = (Compose *)data;
6918 GtkWidget *menuitem;
6919 GtkWidget *parent_item;
6920 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6923 if (compose->gtkaspell == NULL)
6926 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6927 "/Menu/Spelling/Options");
6929 /* setting the submenu removes /Spelling/Options from the factory
6930 * so we need to save it */
6932 if (parent_item == NULL) {
6933 parent_item = compose->aspell_options_menu;
6934 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6936 compose->aspell_options_menu = parent_item;
6938 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6940 spell_menu = g_slist_reverse(spell_menu);
6941 for (items = spell_menu;
6942 items; items = items->next) {
6943 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6944 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6945 gtk_widget_show(GTK_WIDGET(menuitem));
6947 g_slist_free(spell_menu);
6949 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6950 gtk_widget_show(parent_item);
6953 static void compose_dict_changed(void *data)
6955 Compose *compose = (Compose *) data;
6957 if(compose->gtkaspell &&
6958 compose->gtkaspell->recheck_when_changing_dict == FALSE)
6961 gtkaspell_highlight_all(compose->gtkaspell);
6962 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6966 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6968 Compose *compose = (Compose *)data;
6969 GdkEventButton event;
6972 event.time = gtk_get_current_event_time();
6976 return text_clicked(compose->text, &event, compose);
6979 static gboolean compose_force_window_origin = TRUE;
6980 static Compose *compose_create(PrefsAccount *account,
6989 GtkWidget *handlebox;
6991 GtkWidget *notebook;
6993 GtkWidget *attach_hbox;
6994 GtkWidget *attach_lab1;
6995 GtkWidget *attach_lab2;
7000 GtkWidget *subject_hbox;
7001 GtkWidget *subject_frame;
7002 GtkWidget *subject_entry;
7006 GtkWidget *edit_vbox;
7007 GtkWidget *ruler_hbox;
7009 GtkWidget *scrolledwin;
7011 GtkTextBuffer *buffer;
7012 GtkClipboard *clipboard;
7015 UndoMain *undostruct;
7017 gchar *titles[N_ATTACH_COLS];
7018 GtkWidget *popupmenu;
7019 GtkWidget *tmpl_menu;
7020 GtkActionGroup *action_group = NULL;
7023 GtkAspell * gtkaspell = NULL;
7026 static GdkGeometry geometry;
7028 cm_return_val_if_fail(account != NULL, NULL);
7030 debug_print("Creating compose window...\n");
7031 compose = g_new0(Compose, 1);
7033 titles[COL_MIMETYPE] = _("MIME type");
7034 titles[COL_SIZE] = _("Size");
7035 titles[COL_NAME] = _("Name");
7037 compose->batch = batch;
7038 compose->account = account;
7039 compose->folder = folder;
7041 compose->mutex = g_mutex_new();
7042 compose->set_cursor_pos = -1;
7044 #if !(GTK_CHECK_VERSION(2,12,0))
7045 compose->tooltips = tips;
7048 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7050 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7051 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7053 if (!geometry.max_width) {
7054 geometry.max_width = gdk_screen_width();
7055 geometry.max_height = gdk_screen_height();
7058 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7059 &geometry, GDK_HINT_MAX_SIZE);
7060 if (!geometry.min_width) {
7061 geometry.min_width = 600;
7062 geometry.min_height = 440;
7064 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7065 &geometry, GDK_HINT_MIN_SIZE);
7067 #ifndef GENERIC_UMPC
7068 if (compose_force_window_origin)
7069 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7070 prefs_common.compose_y);
7072 g_signal_connect(G_OBJECT(window), "delete_event",
7073 G_CALLBACK(compose_delete_cb), compose);
7074 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7075 gtk_widget_realize(window);
7077 gtkut_widget_set_composer_icon(window);
7079 vbox = gtk_vbox_new(FALSE, 0);
7080 gtk_container_add(GTK_CONTAINER(window), vbox);
7082 compose->ui_manager = gtk_ui_manager_new();
7083 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7084 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7085 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7086 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7087 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7088 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7089 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7090 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7091 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7092 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7097 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7100 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7101 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7103 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7105 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7107 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7110 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7111 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7112 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7113 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7114 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7115 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7116 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7117 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7118 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7119 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7122 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7123 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7124 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7127 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7128 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7130 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7131 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7132 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7133 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7135 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7137 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7139 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7140 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7144 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7145 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7146 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7149 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7155 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7157 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7169 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7199 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7212 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)
7213 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)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7219 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)
7220 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)
7222 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7225 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)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7229 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)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7234 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7235 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)
7236 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7237 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7240 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7241 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)
7242 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)
7243 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7244 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7248 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7250 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7252 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7254 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)
7256 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7257 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7258 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7262 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7263 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7264 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7265 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7266 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7267 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7270 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7272 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7273 gtk_widget_show_all(menubar);
7275 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7277 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7279 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7282 if (prefs_common.toolbar_detachable) {
7283 handlebox = gtk_handle_box_new();
7285 handlebox = gtk_hbox_new(FALSE, 0);
7287 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7289 gtk_widget_realize(handlebox);
7291 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7294 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7298 vbox2 = gtk_vbox_new(FALSE, 2);
7299 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7300 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7303 notebook = gtk_notebook_new();
7304 gtk_widget_set_size_request(notebook, -1, 130);
7305 gtk_widget_show(notebook);
7307 /* header labels and entries */
7308 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7309 compose_create_header(compose),
7310 gtk_label_new_with_mnemonic(_("Hea_der")));
7311 /* attachment list */
7312 attach_hbox = gtk_hbox_new(FALSE, 0);
7313 gtk_widget_show(attach_hbox);
7315 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7316 gtk_widget_show(attach_lab1);
7317 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7319 attach_lab2 = gtk_label_new("");
7320 gtk_widget_show(attach_lab2);
7321 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7323 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7324 compose_create_attach(compose),
7327 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7328 compose_create_others(compose),
7329 gtk_label_new_with_mnemonic(_("Othe_rs")));
7332 subject_hbox = gtk_hbox_new(FALSE, 0);
7333 gtk_widget_show(subject_hbox);
7335 subject_frame = gtk_frame_new(NULL);
7336 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7337 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7338 gtk_widget_show(subject_frame);
7340 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7341 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7342 gtk_widget_show(subject);
7344 label = gtk_label_new(_("Subject:"));
7345 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7346 gtk_widget_show(label);
7349 subject_entry = claws_spell_entry_new();
7351 subject_entry = gtk_entry_new();
7353 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7354 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7355 G_CALLBACK(compose_grab_focus_cb), compose);
7356 gtk_widget_show(subject_entry);
7357 compose->subject_entry = subject_entry;
7358 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7360 edit_vbox = gtk_vbox_new(FALSE, 0);
7362 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7365 ruler_hbox = gtk_hbox_new(FALSE, 0);
7366 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7368 ruler = gtk_shruler_new();
7369 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7370 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7374 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7375 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7376 GTK_POLICY_AUTOMATIC,
7377 GTK_POLICY_AUTOMATIC);
7378 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7380 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7381 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7383 text = gtk_text_view_new();
7384 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7385 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7386 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7387 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7388 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7390 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7392 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7393 G_CALLBACK(compose_edit_size_alloc),
7395 g_signal_connect(G_OBJECT(buffer), "changed",
7396 G_CALLBACK(compose_changed_cb), compose);
7397 g_signal_connect(G_OBJECT(text), "grab_focus",
7398 G_CALLBACK(compose_grab_focus_cb), compose);
7399 g_signal_connect(G_OBJECT(buffer), "insert_text",
7400 G_CALLBACK(text_inserted), compose);
7401 g_signal_connect(G_OBJECT(text), "button_press_event",
7402 G_CALLBACK(text_clicked), compose);
7404 g_signal_connect(G_OBJECT(text), "popup-menu",
7405 G_CALLBACK(compose_popup_menu), compose);
7407 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7408 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7409 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7410 G_CALLBACK(compose_popup_menu), compose);
7412 g_signal_connect(G_OBJECT(subject_entry), "changed",
7413 G_CALLBACK(compose_changed_cb), compose);
7416 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7417 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7418 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7419 g_signal_connect(G_OBJECT(text), "drag_data_received",
7420 G_CALLBACK(compose_insert_drag_received_cb),
7422 g_signal_connect(G_OBJECT(text), "drag-drop",
7423 G_CALLBACK(compose_drag_drop),
7425 gtk_widget_show_all(vbox);
7427 /* pane between attach clist and text */
7428 paned = gtk_vpaned_new();
7429 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7431 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7432 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7434 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7436 gtk_paned_add1(GTK_PANED(paned), notebook);
7437 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7438 gtk_widget_show_all(paned);
7441 if (prefs_common.textfont) {
7442 PangoFontDescription *font_desc;
7444 font_desc = pango_font_description_from_string
7445 (prefs_common.textfont);
7447 gtk_widget_modify_font(text, font_desc);
7448 pango_font_description_free(font_desc);
7452 gtk_action_group_add_actions(action_group, compose_popup_entries,
7453 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7454 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7455 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7456 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7457 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7458 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7459 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7461 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7464 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7465 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7467 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7469 undostruct = undo_init(text);
7470 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7473 address_completion_start(window);
7475 compose->window = window;
7476 compose->vbox = vbox;
7477 compose->menubar = menubar;
7478 compose->handlebox = handlebox;
7480 compose->vbox2 = vbox2;
7482 compose->paned = paned;
7484 compose->attach_label = attach_lab2;
7486 compose->notebook = notebook;
7487 compose->edit_vbox = edit_vbox;
7488 compose->ruler_hbox = ruler_hbox;
7489 compose->ruler = ruler;
7490 compose->scrolledwin = scrolledwin;
7491 compose->text = text;
7493 compose->focused_editable = NULL;
7495 compose->popupmenu = popupmenu;
7497 compose->tmpl_menu = tmpl_menu;
7499 compose->mode = mode;
7500 compose->rmode = mode;
7502 compose->targetinfo = NULL;
7503 compose->replyinfo = NULL;
7504 compose->fwdinfo = NULL;
7506 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7507 g_str_equal, (GDestroyNotify) g_free, NULL);
7509 compose->replyto = NULL;
7511 compose->bcc = NULL;
7512 compose->followup_to = NULL;
7514 compose->ml_post = NULL;
7516 compose->inreplyto = NULL;
7517 compose->references = NULL;
7518 compose->msgid = NULL;
7519 compose->boundary = NULL;
7521 compose->autowrap = prefs_common.autowrap;
7522 compose->autoindent = prefs_common.auto_indent;
7523 compose->use_signing = FALSE;
7524 compose->use_encryption = FALSE;
7525 compose->privacy_system = NULL;
7527 compose->modified = FALSE;
7529 compose->return_receipt = FALSE;
7531 compose->to_list = NULL;
7532 compose->newsgroup_list = NULL;
7534 compose->undostruct = undostruct;
7536 compose->sig_str = NULL;
7538 compose->exteditor_file = NULL;
7539 compose->exteditor_pid = -1;
7540 compose->exteditor_tag = -1;
7541 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7544 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7545 if (mode != COMPOSE_REDIRECT) {
7546 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7547 strcmp(prefs_common.dictionary, "")) {
7548 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7549 prefs_common.alt_dictionary,
7550 conv_get_locale_charset_str(),
7551 prefs_common.misspelled_col,
7552 prefs_common.check_while_typing,
7553 prefs_common.recheck_when_changing_dict,
7554 prefs_common.use_alternate,
7555 prefs_common.use_both_dicts,
7556 GTK_TEXT_VIEW(text),
7557 GTK_WINDOW(compose->window),
7558 compose_dict_changed,
7559 compose_spell_menu_changed,
7562 alertpanel_error(_("Spell checker could not "
7564 gtkaspell_checkers_strerror());
7565 gtkaspell_checkers_reset_error();
7567 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7571 compose->gtkaspell = gtkaspell;
7572 compose_spell_menu_changed(compose);
7573 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7576 compose_select_account(compose, account, TRUE);
7578 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7579 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7581 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7582 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7584 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7585 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7587 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7588 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7590 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7591 if (account->protocol != A_NNTP)
7592 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7593 prefs_common_translated_header_name("To:"));
7595 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7596 prefs_common_translated_header_name("Newsgroups:"));
7598 addressbook_set_target_compose(compose);
7600 if (mode != COMPOSE_REDIRECT)
7601 compose_set_template_menu(compose);
7603 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7606 compose_list = g_list_append(compose_list, compose);
7608 if (!prefs_common.show_ruler)
7609 gtk_widget_hide(ruler_hbox);
7611 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7614 compose->priority = PRIORITY_NORMAL;
7615 compose_update_priority_menu_item(compose);
7617 compose_set_out_encoding(compose);
7620 compose_update_actions_menu(compose);
7622 /* Privacy Systems menu */
7623 compose_update_privacy_systems_menu(compose);
7625 activate_privacy_system(compose, account, TRUE);
7626 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7628 gtk_widget_realize(window);
7630 gtk_widget_show(window);
7632 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7633 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7640 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7645 GtkWidget *optmenubox;
7648 GtkWidget *from_name = NULL;
7649 #if !(GTK_CHECK_VERSION(2,12,0))
7650 GtkTooltips *tips = compose->tooltips;
7653 gint num = 0, def_menu = 0;
7655 accounts = account_get_list();
7656 cm_return_val_if_fail(accounts != NULL, NULL);
7658 optmenubox = gtk_event_box_new();
7659 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7660 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7662 hbox = gtk_hbox_new(FALSE, 6);
7663 from_name = gtk_entry_new();
7665 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7666 G_CALLBACK(compose_grab_focus_cb), compose);
7668 for (; accounts != NULL; accounts = accounts->next, num++) {
7669 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7670 gchar *name, *from = NULL;
7672 if (ac == compose->account) def_menu = num;
7674 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7677 if (ac == compose->account) {
7678 if (ac->name && *ac->name) {
7680 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7681 from = g_strdup_printf("%s <%s>",
7683 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7685 from = g_strdup_printf("%s",
7687 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7690 COMBOBOX_ADD(menu, name, ac->account_id);
7695 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7697 g_signal_connect(G_OBJECT(optmenu), "changed",
7698 G_CALLBACK(account_activated),
7700 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7701 G_CALLBACK(compose_entry_popup_extend),
7704 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7705 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7707 CLAWS_SET_TIP(optmenubox,
7708 _("Account to use for this email"));
7709 CLAWS_SET_TIP(from_name,
7710 _("Sender address to be used"));
7712 compose->from_name = from_name;
7717 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7719 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7720 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7721 Compose *compose = (Compose *) data;
7723 compose->priority = value;
7727 static void compose_reply_change_mode(Compose *compose,
7730 gboolean was_modified = compose->modified;
7732 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7734 cm_return_if_fail(compose->replyinfo != NULL);
7736 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7738 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7740 if (action == COMPOSE_REPLY_TO_ALL)
7742 if (action == COMPOSE_REPLY_TO_SENDER)
7744 if (action == COMPOSE_REPLY_TO_LIST)
7747 compose_remove_header_entries(compose);
7748 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7749 if (compose->account->set_autocc && compose->account->auto_cc)
7750 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7752 if (compose->account->set_autobcc && compose->account->auto_bcc)
7753 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7755 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7756 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7757 compose_show_first_last_header(compose, TRUE);
7758 compose->modified = was_modified;
7759 compose_set_title(compose);
7762 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7764 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7765 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7766 Compose *compose = (Compose *) data;
7769 compose_reply_change_mode(compose, value);
7772 static void compose_update_priority_menu_item(Compose * compose)
7774 GtkWidget *menuitem = NULL;
7775 switch (compose->priority) {
7776 case PRIORITY_HIGHEST:
7777 menuitem = gtk_ui_manager_get_widget
7778 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7781 menuitem = gtk_ui_manager_get_widget
7782 (compose->ui_manager, "/Menu/Options/Priority/High");
7784 case PRIORITY_NORMAL:
7785 menuitem = gtk_ui_manager_get_widget
7786 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7789 menuitem = gtk_ui_manager_get_widget
7790 (compose->ui_manager, "/Menu/Options/Priority/Low");
7792 case PRIORITY_LOWEST:
7793 menuitem = gtk_ui_manager_get_widget
7794 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7797 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7800 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7802 Compose *compose = (Compose *) data;
7804 gboolean can_sign = FALSE, can_encrypt = FALSE;
7806 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7808 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7811 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7812 g_free(compose->privacy_system);
7813 compose->privacy_system = NULL;
7814 if (systemid != NULL) {
7815 compose->privacy_system = g_strdup(systemid);
7817 can_sign = privacy_system_can_sign(systemid);
7818 can_encrypt = privacy_system_can_encrypt(systemid);
7821 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7823 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7824 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7827 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7829 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7830 GtkWidget *menuitem = NULL;
7832 gboolean can_sign = FALSE, can_encrypt = FALSE;
7833 gboolean found = FALSE;
7835 if (compose->privacy_system != NULL) {
7837 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7838 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7839 cm_return_if_fail(menuitem != NULL);
7841 amenu = GTK_MENU_SHELL(menuitem)->children;
7843 while (amenu != NULL) {
7844 GList *alist = amenu->next;
7846 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7847 if (systemid != NULL) {
7848 if (strcmp(systemid, compose->privacy_system) == 0 &&
7849 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7850 menuitem = GTK_WIDGET(amenu->data);
7852 can_sign = privacy_system_can_sign(systemid);
7853 can_encrypt = privacy_system_can_encrypt(systemid);
7857 } else if (strlen(compose->privacy_system) == 0 &&
7858 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7859 menuitem = GTK_WIDGET(amenu->data);
7862 can_encrypt = FALSE;
7869 if (menuitem != NULL)
7870 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7872 if (warn && !found && strlen(compose->privacy_system)) {
7873 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7874 "will not be able to sign or encrypt this message."),
7875 compose->privacy_system);
7879 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7880 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7883 static void compose_set_out_encoding(Compose *compose)
7885 CharSet out_encoding;
7886 const gchar *branch = NULL;
7887 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7889 switch(out_encoding) {
7890 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7891 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7892 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7893 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7894 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7895 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7896 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7897 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7898 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7899 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7900 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7901 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7902 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7903 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7904 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7905 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7906 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7907 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7908 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7909 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7910 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7911 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7912 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7913 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7914 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7915 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7916 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7917 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7918 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7919 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7920 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7921 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7923 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7926 static void compose_set_template_menu(Compose *compose)
7928 GSList *tmpl_list, *cur;
7932 tmpl_list = template_get_config();
7934 menu = gtk_menu_new();
7936 gtk_menu_set_accel_group (GTK_MENU (menu),
7937 gtk_ui_manager_get_accel_group(compose->ui_manager));
7938 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7939 Template *tmpl = (Template *)cur->data;
7940 gchar *accel_path = NULL;
7941 item = gtk_menu_item_new_with_label(tmpl->name);
7942 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7943 g_signal_connect(G_OBJECT(item), "activate",
7944 G_CALLBACK(compose_template_activate_cb),
7946 g_object_set_data(G_OBJECT(item), "template", tmpl);
7947 gtk_widget_show(item);
7948 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7949 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7953 gtk_widget_show(menu);
7954 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7957 void compose_update_actions_menu(Compose *compose)
7959 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7962 static void compose_update_privacy_systems_menu(Compose *compose)
7964 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7965 GSList *systems, *cur;
7967 GtkWidget *system_none;
7969 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7970 GtkWidget *privacy_menu = gtk_menu_new();
7972 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7973 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7975 g_signal_connect(G_OBJECT(system_none), "activate",
7976 G_CALLBACK(compose_set_privacy_system_cb), compose);
7978 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7979 gtk_widget_show(system_none);
7981 systems = privacy_get_system_ids();
7982 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7983 gchar *systemid = cur->data;
7985 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7986 widget = gtk_radio_menu_item_new_with_label(group,
7987 privacy_system_get_name(systemid));
7988 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7989 g_strdup(systemid), g_free);
7990 g_signal_connect(G_OBJECT(widget), "activate",
7991 G_CALLBACK(compose_set_privacy_system_cb), compose);
7993 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7994 gtk_widget_show(widget);
7997 g_slist_free(systems);
7998 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7999 gtk_widget_show_all(privacy_menu);
8000 gtk_widget_show_all(privacy_menuitem);
8003 void compose_reflect_prefs_all(void)
8008 for (cur = compose_list; cur != NULL; cur = cur->next) {
8009 compose = (Compose *)cur->data;
8010 compose_set_template_menu(compose);
8014 void compose_reflect_prefs_pixmap_theme(void)
8019 for (cur = compose_list; cur != NULL; cur = cur->next) {
8020 compose = (Compose *)cur->data;
8021 toolbar_update(TOOLBAR_COMPOSE, compose);
8025 static const gchar *compose_quote_char_from_context(Compose *compose)
8027 const gchar *qmark = NULL;
8029 cm_return_val_if_fail(compose != NULL, NULL);
8031 switch (compose->mode) {
8032 /* use forward-specific quote char */
8033 case COMPOSE_FORWARD:
8034 case COMPOSE_FORWARD_AS_ATTACH:
8035 case COMPOSE_FORWARD_INLINE:
8036 if (compose->folder && compose->folder->prefs &&
8037 compose->folder->prefs->forward_with_format)
8038 qmark = compose->folder->prefs->forward_quotemark;
8039 else if (compose->account->forward_with_format)
8040 qmark = compose->account->forward_quotemark;
8042 qmark = prefs_common.fw_quotemark;
8045 /* use reply-specific quote char in all other modes */
8047 if (compose->folder && compose->folder->prefs &&
8048 compose->folder->prefs->reply_with_format)
8049 qmark = compose->folder->prefs->reply_quotemark;
8050 else if (compose->account->reply_with_format)
8051 qmark = compose->account->reply_quotemark;
8053 qmark = prefs_common.quotemark;
8057 if (qmark == NULL || *qmark == '\0')
8063 static void compose_template_apply(Compose *compose, Template *tmpl,
8067 GtkTextBuffer *buffer;
8071 gchar *parsed_str = NULL;
8072 gint cursor_pos = 0;
8073 const gchar *err_msg = _("The body of the template has an error at line %d.");
8076 /* process the body */
8078 text = GTK_TEXT_VIEW(compose->text);
8079 buffer = gtk_text_view_get_buffer(text);
8082 qmark = compose_quote_char_from_context(compose);
8084 if (compose->replyinfo != NULL) {
8087 gtk_text_buffer_set_text(buffer, "", -1);
8088 mark = gtk_text_buffer_get_insert(buffer);
8089 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8091 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8092 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8094 } else if (compose->fwdinfo != NULL) {
8097 gtk_text_buffer_set_text(buffer, "", -1);
8098 mark = gtk_text_buffer_get_insert(buffer);
8099 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8101 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8102 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8105 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8107 GtkTextIter start, end;
8110 gtk_text_buffer_get_start_iter(buffer, &start);
8111 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8112 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8114 /* clear the buffer now */
8116 gtk_text_buffer_set_text(buffer, "", -1);
8118 parsed_str = compose_quote_fmt(compose, dummyinfo,
8119 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8120 procmsg_msginfo_free( dummyinfo );
8126 gtk_text_buffer_set_text(buffer, "", -1);
8127 mark = gtk_text_buffer_get_insert(buffer);
8128 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8131 if (replace && parsed_str && compose->account->auto_sig)
8132 compose_insert_sig(compose, FALSE);
8134 if (replace && parsed_str) {
8135 gtk_text_buffer_get_start_iter(buffer, &iter);
8136 gtk_text_buffer_place_cursor(buffer, &iter);
8140 cursor_pos = quote_fmt_get_cursor_pos();
8141 compose->set_cursor_pos = cursor_pos;
8142 if (cursor_pos == -1)
8144 gtk_text_buffer_get_start_iter(buffer, &iter);
8145 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8146 gtk_text_buffer_place_cursor(buffer, &iter);
8149 /* process the other fields */
8151 compose_template_apply_fields(compose, tmpl);
8152 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8153 quote_fmt_reset_vartable();
8154 compose_changed_cb(NULL, compose);
8157 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8158 gtkaspell_highlight_all(compose->gtkaspell);
8162 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8164 MsgInfo* dummyinfo = NULL;
8165 MsgInfo *msginfo = NULL;
8168 if (compose->replyinfo != NULL)
8169 msginfo = compose->replyinfo;
8170 else if (compose->fwdinfo != NULL)
8171 msginfo = compose->fwdinfo;
8173 dummyinfo = compose_msginfo_new_from_compose(compose);
8174 msginfo = dummyinfo;
8177 if (tmpl->from && *tmpl->from != '\0') {
8179 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8180 compose->gtkaspell);
8182 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8184 quote_fmt_scan_string(tmpl->from);
8187 buf = quote_fmt_get_buffer();
8189 alertpanel_error(_("Template From format error."));
8191 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8195 if (tmpl->to && *tmpl->to != '\0') {
8197 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8198 compose->gtkaspell);
8200 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8202 quote_fmt_scan_string(tmpl->to);
8205 buf = quote_fmt_get_buffer();
8207 alertpanel_error(_("Template To format error."));
8209 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8213 if (tmpl->cc && *tmpl->cc != '\0') {
8215 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8216 compose->gtkaspell);
8218 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8220 quote_fmt_scan_string(tmpl->cc);
8223 buf = quote_fmt_get_buffer();
8225 alertpanel_error(_("Template Cc format error."));
8227 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8231 if (tmpl->bcc && *tmpl->bcc != '\0') {
8233 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8234 compose->gtkaspell);
8236 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8238 quote_fmt_scan_string(tmpl->bcc);
8241 buf = quote_fmt_get_buffer();
8243 alertpanel_error(_("Template Bcc format error."));
8245 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8249 /* process the subject */
8250 if (tmpl->subject && *tmpl->subject != '\0') {
8252 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8253 compose->gtkaspell);
8255 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8257 quote_fmt_scan_string(tmpl->subject);
8260 buf = quote_fmt_get_buffer();
8262 alertpanel_error(_("Template subject format error."));
8264 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8268 procmsg_msginfo_free( dummyinfo );
8271 static void compose_destroy(Compose *compose)
8273 GtkTextBuffer *buffer;
8274 GtkClipboard *clipboard;
8276 compose_list = g_list_remove(compose_list, compose);
8278 if (compose->updating) {
8279 debug_print("danger, not destroying anything now\n");
8280 compose->deferred_destroy = TRUE;
8283 /* NOTE: address_completion_end() does nothing with the window
8284 * however this may change. */
8285 address_completion_end(compose->window);
8287 slist_free_strings(compose->to_list);
8288 g_slist_free(compose->to_list);
8289 slist_free_strings(compose->newsgroup_list);
8290 g_slist_free(compose->newsgroup_list);
8291 slist_free_strings(compose->header_list);
8292 g_slist_free(compose->header_list);
8294 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8296 g_hash_table_destroy(compose->email_hashtable);
8298 procmsg_msginfo_free(compose->targetinfo);
8299 procmsg_msginfo_free(compose->replyinfo);
8300 procmsg_msginfo_free(compose->fwdinfo);
8302 g_free(compose->replyto);
8303 g_free(compose->cc);
8304 g_free(compose->bcc);
8305 g_free(compose->newsgroups);
8306 g_free(compose->followup_to);
8308 g_free(compose->ml_post);
8310 g_free(compose->inreplyto);
8311 g_free(compose->references);
8312 g_free(compose->msgid);
8313 g_free(compose->boundary);
8315 g_free(compose->redirect_filename);
8316 if (compose->undostruct)
8317 undo_destroy(compose->undostruct);
8319 g_free(compose->sig_str);
8321 g_free(compose->exteditor_file);
8323 g_free(compose->orig_charset);
8325 g_free(compose->privacy_system);
8327 if (addressbook_get_target_compose() == compose)
8328 addressbook_set_target_compose(NULL);
8331 if (compose->gtkaspell) {
8332 gtkaspell_delete(compose->gtkaspell);
8333 compose->gtkaspell = NULL;
8337 if (!compose->batch) {
8338 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8339 prefs_common.compose_height = compose->window->allocation.height;
8342 if (!gtk_widget_get_parent(compose->paned))
8343 gtk_widget_destroy(compose->paned);
8344 gtk_widget_destroy(compose->popupmenu);
8346 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8347 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8348 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8350 gtk_widget_destroy(compose->window);
8351 toolbar_destroy(compose->toolbar);
8352 g_free(compose->toolbar);
8353 g_mutex_free(compose->mutex);
8357 static void compose_attach_info_free(AttachInfo *ainfo)
8359 g_free(ainfo->file);
8360 g_free(ainfo->content_type);
8361 g_free(ainfo->name);
8365 static void compose_attach_update_label(Compose *compose)
8370 GtkTreeModel *model;
8375 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8376 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8377 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8381 while(gtk_tree_model_iter_next(model, &iter))
8384 text = g_strdup_printf("(%d)", i);
8385 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8389 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8391 Compose *compose = (Compose *)data;
8392 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8393 GtkTreeSelection *selection;
8395 GtkTreeModel *model;
8397 selection = gtk_tree_view_get_selection(tree_view);
8398 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8403 for (cur = sel; cur != NULL; cur = cur->next) {
8404 GtkTreePath *path = cur->data;
8405 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8408 gtk_tree_path_free(path);
8411 for (cur = sel; cur != NULL; cur = cur->next) {
8412 GtkTreeRowReference *ref = cur->data;
8413 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8416 if (gtk_tree_model_get_iter(model, &iter, path))
8417 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8419 gtk_tree_path_free(path);
8420 gtk_tree_row_reference_free(ref);
8424 compose_attach_update_label(compose);
8427 static struct _AttachProperty
8430 GtkWidget *mimetype_entry;
8431 GtkWidget *encoding_optmenu;
8432 GtkWidget *path_entry;
8433 GtkWidget *filename_entry;
8435 GtkWidget *cancel_btn;
8438 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8440 gtk_tree_path_free((GtkTreePath *)ptr);
8443 static void compose_attach_property(GtkAction *action, gpointer data)
8445 Compose *compose = (Compose *)data;
8446 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8448 GtkComboBox *optmenu;
8449 GtkTreeSelection *selection;
8451 GtkTreeModel *model;
8454 static gboolean cancelled;
8456 /* only if one selected */
8457 selection = gtk_tree_view_get_selection(tree_view);
8458 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8461 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8465 path = (GtkTreePath *) sel->data;
8466 gtk_tree_model_get_iter(model, &iter, path);
8467 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8470 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8476 if (!attach_prop.window)
8477 compose_attach_property_create(&cancelled);
8478 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8479 gtk_widget_grab_focus(attach_prop.ok_btn);
8480 gtk_widget_show(attach_prop.window);
8481 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8483 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8484 if (ainfo->encoding == ENC_UNKNOWN)
8485 combobox_select_by_data(optmenu, ENC_BASE64);
8487 combobox_select_by_data(optmenu, ainfo->encoding);
8489 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8490 ainfo->content_type ? ainfo->content_type : "");
8491 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8492 ainfo->file ? ainfo->file : "");
8493 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8494 ainfo->name ? ainfo->name : "");
8497 const gchar *entry_text;
8499 gchar *cnttype = NULL;
8506 gtk_widget_hide(attach_prop.window);
8507 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8512 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8513 if (*entry_text != '\0') {
8516 text = g_strstrip(g_strdup(entry_text));
8517 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8518 cnttype = g_strdup(text);
8521 alertpanel_error(_("Invalid MIME type."));
8527 ainfo->encoding = combobox_get_active_data(optmenu);
8529 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8530 if (*entry_text != '\0') {
8531 if (is_file_exist(entry_text) &&
8532 (size = get_file_size(entry_text)) > 0)
8533 file = g_strdup(entry_text);
8536 (_("File doesn't exist or is empty."));
8542 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8543 if (*entry_text != '\0') {
8544 g_free(ainfo->name);
8545 ainfo->name = g_strdup(entry_text);
8549 g_free(ainfo->content_type);
8550 ainfo->content_type = cnttype;
8553 g_free(ainfo->file);
8557 ainfo->size = (goffset)size;
8559 /* update tree store */
8560 text = to_human_readable(ainfo->size);
8561 gtk_tree_model_get_iter(model, &iter, path);
8562 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8563 COL_MIMETYPE, ainfo->content_type,
8565 COL_NAME, ainfo->name,
8571 gtk_tree_path_free(path);
8574 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8576 label = gtk_label_new(str); \
8577 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8578 GTK_FILL, 0, 0, 0); \
8579 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8581 entry = gtk_entry_new(); \
8582 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8583 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8586 static void compose_attach_property_create(gboolean *cancelled)
8592 GtkWidget *mimetype_entry;
8595 GtkListStore *optmenu_menu;
8596 GtkWidget *path_entry;
8597 GtkWidget *filename_entry;
8600 GtkWidget *cancel_btn;
8601 GList *mime_type_list, *strlist;
8604 debug_print("Creating attach_property window...\n");
8606 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8607 gtk_widget_set_size_request(window, 480, -1);
8608 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8609 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8610 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8611 g_signal_connect(G_OBJECT(window), "delete_event",
8612 G_CALLBACK(attach_property_delete_event),
8614 g_signal_connect(G_OBJECT(window), "key_press_event",
8615 G_CALLBACK(attach_property_key_pressed),
8618 vbox = gtk_vbox_new(FALSE, 8);
8619 gtk_container_add(GTK_CONTAINER(window), vbox);
8621 table = gtk_table_new(4, 2, FALSE);
8622 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8623 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8624 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8626 label = gtk_label_new(_("MIME type"));
8627 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8629 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8630 mimetype_entry = gtk_combo_box_entry_new_text();
8631 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8632 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8634 /* stuff with list */
8635 mime_type_list = procmime_get_mime_type_list();
8637 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8638 MimeType *type = (MimeType *) mime_type_list->data;
8641 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8643 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8646 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8647 (GCompareFunc)strcmp2);
8650 for (mime_type_list = strlist; mime_type_list != NULL;
8651 mime_type_list = mime_type_list->next) {
8652 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8653 g_free(mime_type_list->data);
8655 g_list_free(strlist);
8656 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8657 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8659 label = gtk_label_new(_("Encoding"));
8660 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8662 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8664 hbox = gtk_hbox_new(FALSE, 0);
8665 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8666 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8668 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8669 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8671 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8672 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8673 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8674 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8675 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8677 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8679 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8680 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8682 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8683 &ok_btn, GTK_STOCK_OK,
8685 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8686 gtk_widget_grab_default(ok_btn);
8688 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8689 G_CALLBACK(attach_property_ok),
8691 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8692 G_CALLBACK(attach_property_cancel),
8695 gtk_widget_show_all(vbox);
8697 attach_prop.window = window;
8698 attach_prop.mimetype_entry = mimetype_entry;
8699 attach_prop.encoding_optmenu = optmenu;
8700 attach_prop.path_entry = path_entry;
8701 attach_prop.filename_entry = filename_entry;
8702 attach_prop.ok_btn = ok_btn;
8703 attach_prop.cancel_btn = cancel_btn;
8706 #undef SET_LABEL_AND_ENTRY
8708 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8714 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8720 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8721 gboolean *cancelled)
8729 static gboolean attach_property_key_pressed(GtkWidget *widget,
8731 gboolean *cancelled)
8733 if (event && event->keyval == GDK_Escape) {
8737 if (event && event->keyval == GDK_Return) {
8745 static void compose_exec_ext_editor(Compose *compose)
8752 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8753 G_DIR_SEPARATOR, compose);
8755 if (pipe(pipe_fds) < 0) {
8761 if ((pid = fork()) < 0) {
8768 /* close the write side of the pipe */
8771 compose->exteditor_file = g_strdup(tmp);
8772 compose->exteditor_pid = pid;
8774 compose_set_ext_editor_sensitive(compose, FALSE);
8777 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8779 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8781 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8785 } else { /* process-monitoring process */
8791 /* close the read side of the pipe */
8794 if (compose_write_body_to_file(compose, tmp) < 0) {
8795 fd_write_all(pipe_fds[1], "2\n", 2);
8799 pid_ed = compose_exec_ext_editor_real(tmp);
8801 fd_write_all(pipe_fds[1], "1\n", 2);
8805 /* wait until editor is terminated */
8806 waitpid(pid_ed, NULL, 0);
8808 fd_write_all(pipe_fds[1], "0\n", 2);
8815 #endif /* G_OS_UNIX */
8819 static gint compose_exec_ext_editor_real(const gchar *file)
8826 cm_return_val_if_fail(file != NULL, -1);
8828 if ((pid = fork()) < 0) {
8833 if (pid != 0) return pid;
8835 /* grandchild process */
8837 if (setpgid(0, getppid()))
8840 if (prefs_common_get_ext_editor_cmd() &&
8841 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8842 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8843 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8845 if (prefs_common_get_ext_editor_cmd())
8846 g_warning("External editor command-line is invalid: '%s'\n",
8847 prefs_common_get_ext_editor_cmd());
8848 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8851 cmdline = strsplit_with_quote(buf, " ", 1024);
8852 execvp(cmdline[0], cmdline);
8855 g_strfreev(cmdline);
8860 static gboolean compose_ext_editor_kill(Compose *compose)
8862 pid_t pgid = compose->exteditor_pid * -1;
8865 ret = kill(pgid, 0);
8867 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8871 msg = g_strdup_printf
8872 (_("The external editor is still working.\n"
8873 "Force terminating the process?\n"
8874 "process group id: %d"), -pgid);
8875 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8876 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8880 if (val == G_ALERTALTERNATE) {
8881 g_source_remove(compose->exteditor_tag);
8882 g_io_channel_shutdown(compose->exteditor_ch,
8884 g_io_channel_unref(compose->exteditor_ch);
8886 if (kill(pgid, SIGTERM) < 0) perror("kill");
8887 waitpid(compose->exteditor_pid, NULL, 0);
8889 g_warning("Terminated process group id: %d", -pgid);
8890 g_warning("Temporary file: %s",
8891 compose->exteditor_file);
8893 compose_set_ext_editor_sensitive(compose, TRUE);
8895 g_free(compose->exteditor_file);
8896 compose->exteditor_file = NULL;
8897 compose->exteditor_pid = -1;
8898 compose->exteditor_ch = NULL;
8899 compose->exteditor_tag = -1;
8907 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8911 Compose *compose = (Compose *)data;
8914 debug_print(_("Compose: input from monitoring process\n"));
8916 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8918 g_io_channel_shutdown(source, FALSE, NULL);
8919 g_io_channel_unref(source);
8921 waitpid(compose->exteditor_pid, NULL, 0);
8923 if (buf[0] == '0') { /* success */
8924 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8925 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8927 gtk_text_buffer_set_text(buffer, "", -1);
8928 compose_insert_file(compose, compose->exteditor_file);
8929 compose_changed_cb(NULL, compose);
8930 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8932 if (claws_unlink(compose->exteditor_file) < 0)
8933 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8934 } else if (buf[0] == '1') { /* failed */
8935 g_warning("Couldn't exec external editor\n");
8936 if (claws_unlink(compose->exteditor_file) < 0)
8937 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8938 } else if (buf[0] == '2') {
8939 g_warning("Couldn't write to file\n");
8940 } else if (buf[0] == '3') {
8941 g_warning("Pipe read failed\n");
8944 compose_set_ext_editor_sensitive(compose, TRUE);
8946 g_free(compose->exteditor_file);
8947 compose->exteditor_file = NULL;
8948 compose->exteditor_pid = -1;
8949 compose->exteditor_ch = NULL;
8950 compose->exteditor_tag = -1;
8955 static void compose_set_ext_editor_sensitive(Compose *compose,
8958 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8959 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8960 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8961 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8962 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8963 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8964 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8966 gtk_widget_set_sensitive(compose->text, sensitive);
8967 if (compose->toolbar->send_btn)
8968 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8969 if (compose->toolbar->sendl_btn)
8970 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8971 if (compose->toolbar->draft_btn)
8972 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8973 if (compose->toolbar->insert_btn)
8974 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8975 if (compose->toolbar->sig_btn)
8976 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8977 if (compose->toolbar->exteditor_btn)
8978 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8979 if (compose->toolbar->linewrap_current_btn)
8980 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8981 if (compose->toolbar->linewrap_all_btn)
8982 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8984 #endif /* G_OS_UNIX */
8987 * compose_undo_state_changed:
8989 * Change the sensivity of the menuentries undo and redo
8991 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8992 gint redo_state, gpointer data)
8994 Compose *compose = (Compose *)data;
8996 switch (undo_state) {
8997 case UNDO_STATE_TRUE:
8998 if (!undostruct->undo_state) {
8999 undostruct->undo_state = TRUE;
9000 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9003 case UNDO_STATE_FALSE:
9004 if (undostruct->undo_state) {
9005 undostruct->undo_state = FALSE;
9006 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9009 case UNDO_STATE_UNCHANGED:
9011 case UNDO_STATE_REFRESH:
9012 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9015 g_warning("Undo state not recognized");
9019 switch (redo_state) {
9020 case UNDO_STATE_TRUE:
9021 if (!undostruct->redo_state) {
9022 undostruct->redo_state = TRUE;
9023 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9026 case UNDO_STATE_FALSE:
9027 if (undostruct->redo_state) {
9028 undostruct->redo_state = FALSE;
9029 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9032 case UNDO_STATE_UNCHANGED:
9034 case UNDO_STATE_REFRESH:
9035 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9038 g_warning("Redo state not recognized");
9043 /* callback functions */
9045 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9046 * includes "non-client" (windows-izm) in calculation, so this calculation
9047 * may not be accurate.
9049 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9050 GtkAllocation *allocation,
9051 GtkSHRuler *shruler)
9053 if (prefs_common.show_ruler) {
9054 gint char_width = 0, char_height = 0;
9055 gint line_width_in_chars;
9057 gtkut_get_font_size(GTK_WIDGET(widget),
9058 &char_width, &char_height);
9059 line_width_in_chars =
9060 (allocation->width - allocation->x) / char_width;
9062 /* got the maximum */
9063 gtk_ruler_set_range(GTK_RULER(shruler),
9064 0.0, line_width_in_chars, 0,
9065 /*line_width_in_chars*/ char_width);
9072 ComposeEntryType header;
9074 ComposePrefType type;
9075 gboolean entry_marked;
9078 static void account_activated(GtkComboBox *optmenu, gpointer data)
9080 Compose *compose = (Compose *)data;
9083 gchar *folderidentifier;
9084 gint account_id = 0;
9087 GSList *list, *saved_list = NULL;
9088 HeaderEntryState *state;
9089 GtkRcStyle *style = NULL;
9090 static GdkColor yellow;
9091 static gboolean color_set = FALSE;
9093 /* Get ID of active account in the combo box */
9094 menu = gtk_combo_box_get_model(optmenu);
9095 gtk_combo_box_get_active_iter(optmenu, &iter);
9096 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9098 ac = account_find_from_id(account_id);
9099 cm_return_if_fail(ac != NULL);
9101 if (ac != compose->account) {
9102 compose_select_account(compose, ac, FALSE);
9104 for (list = compose->header_list; list; list = list->next) {
9105 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9107 if (hentry->type == PREF_ACCOUNT || !list->next) {
9108 compose_destroy_headerentry(compose, hentry);
9112 state = g_malloc0(sizeof(HeaderEntryState));
9113 state->header = combobox_get_active_data(
9114 GTK_COMBO_BOX(hentry->combo));
9115 state->entry = gtk_editable_get_chars(
9116 GTK_EDITABLE(hentry->entry), 0, -1);
9117 state->type = hentry->type;
9120 gdk_color_parse("#f5f6be", &yellow);
9121 color_set = gdk_colormap_alloc_color(
9122 gdk_colormap_get_system(),
9123 &yellow, FALSE, TRUE);
9126 style = gtk_widget_get_modifier_style(hentry->entry);
9127 state->entry_marked = gdk_color_equal(&yellow,
9128 &style->base[GTK_STATE_NORMAL]);
9130 saved_list = g_slist_append(saved_list, state);
9131 compose_destroy_headerentry(compose, hentry);
9134 compose->header_last = NULL;
9135 g_slist_free(compose->header_list);
9136 compose->header_list = NULL;
9137 compose->header_nextrow = 1;
9138 compose_create_header_entry(compose);
9140 if (ac->set_autocc && ac->auto_cc)
9141 compose_entry_append(compose, ac->auto_cc,
9142 COMPOSE_CC, PREF_ACCOUNT);
9144 if (ac->set_autobcc && ac->auto_bcc)
9145 compose_entry_append(compose, ac->auto_bcc,
9146 COMPOSE_BCC, PREF_ACCOUNT);
9148 if (ac->set_autoreplyto && ac->auto_replyto)
9149 compose_entry_append(compose, ac->auto_replyto,
9150 COMPOSE_REPLYTO, PREF_ACCOUNT);
9152 for (list = saved_list; list; list = list->next) {
9153 state = (HeaderEntryState *) list->data;
9155 compose_entry_append(compose, state->entry,
9156 state->header, state->type);
9157 if (state->entry_marked)
9158 compose_entry_mark_default_to(compose, state->entry);
9160 g_free(state->entry);
9162 g_slist_free(saved_list);
9164 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9165 (ac->protocol == A_NNTP) ?
9166 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9169 /* Set message save folder */
9170 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9171 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9173 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9174 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9176 compose_set_save_to(compose, NULL);
9177 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9178 folderidentifier = folder_item_get_identifier(account_get_special_folder
9179 (compose->account, F_OUTBOX));
9180 compose_set_save_to(compose, folderidentifier);
9181 g_free(folderidentifier);
9185 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9186 GtkTreeViewColumn *column, Compose *compose)
9188 compose_attach_property(NULL, compose);
9191 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9194 Compose *compose = (Compose *)data;
9195 GtkTreeSelection *attach_selection;
9196 gint attach_nr_selected;
9198 if (!event) return FALSE;
9200 if (event->button == 3) {
9201 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9202 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9204 if (attach_nr_selected > 0)
9206 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9207 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9209 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9210 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9213 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9214 NULL, NULL, event->button, event->time);
9221 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9224 Compose *compose = (Compose *)data;
9226 if (!event) return FALSE;
9228 switch (event->keyval) {
9230 compose_attach_remove_selected(NULL, compose);
9236 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9238 toolbar_comp_set_sensitive(compose, allow);
9239 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9240 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9242 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9244 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9245 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9246 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9248 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9252 static void compose_send_cb(GtkAction *action, gpointer data)
9254 Compose *compose = (Compose *)data;
9256 if (prefs_common.work_offline &&
9257 !inc_offline_should_override(TRUE,
9258 _("Claws Mail needs network access in order "
9259 "to send this email.")))
9262 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9263 g_source_remove(compose->draft_timeout_tag);
9264 compose->draft_timeout_tag = -1;
9267 compose_send(compose);
9270 static void compose_send_later_cb(GtkAction *action, gpointer data)
9272 Compose *compose = (Compose *)data;
9276 compose_allow_user_actions(compose, FALSE);
9277 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9278 compose_allow_user_actions(compose, TRUE);
9282 compose_close(compose);
9283 } else if (val == -1) {
9284 alertpanel_error(_("Could not queue message."));
9285 } else if (val == -2) {
9286 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9287 } else if (val == -3) {
9288 if (privacy_peek_error())
9289 alertpanel_error(_("Could not queue message for sending:\n\n"
9290 "Signature failed: %s"), privacy_get_error());
9291 } else if (val == -4) {
9292 alertpanel_error(_("Could not queue message for sending:\n\n"
9293 "Charset conversion failed."));
9294 } else if (val == -5) {
9295 alertpanel_error(_("Could not queue message for sending:\n\n"
9296 "Couldn't get recipient encryption key."));
9297 } else if (val == -6) {
9300 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9303 #define DRAFTED_AT_EXIT "drafted_at_exit"
9304 static void compose_register_draft(MsgInfo *info)
9306 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9307 DRAFTED_AT_EXIT, NULL);
9308 FILE *fp = g_fopen(filepath, "ab");
9311 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9319 gboolean compose_draft (gpointer data, guint action)
9321 Compose *compose = (Compose *)data;
9325 MsgFlags flag = {0, 0};
9326 static gboolean lock = FALSE;
9327 MsgInfo *newmsginfo;
9329 gboolean target_locked = FALSE;
9330 gboolean err = FALSE;
9332 if (lock) return FALSE;
9334 if (compose->sending)
9337 draft = account_get_special_folder(compose->account, F_DRAFT);
9338 cm_return_val_if_fail(draft != NULL, FALSE);
9340 if (!g_mutex_trylock(compose->mutex)) {
9341 /* we don't want to lock the mutex once it's available,
9342 * because as the only other part of compose.c locking
9343 * it is compose_close - which means once unlocked,
9344 * the compose struct will be freed */
9345 debug_print("couldn't lock mutex, probably sending\n");
9351 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9352 G_DIR_SEPARATOR, compose);
9353 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9354 FILE_OP_ERROR(tmp, "fopen");
9358 /* chmod for security */
9359 if (change_file_mode_rw(fp, tmp) < 0) {
9360 FILE_OP_ERROR(tmp, "chmod");
9361 g_warning("can't change file mode\n");
9364 /* Save draft infos */
9365 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9366 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9368 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9369 gchar *savefolderid;
9371 savefolderid = compose_get_save_to(compose);
9372 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9373 g_free(savefolderid);
9375 if (compose->return_receipt) {
9376 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9378 if (compose->privacy_system) {
9379 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9380 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9381 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9384 /* Message-ID of message replying to */
9385 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9388 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9389 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9392 /* Message-ID of message forwarding to */
9393 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9396 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9397 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9401 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9402 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9404 /* end of headers */
9405 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9412 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9416 if (fclose(fp) == EOF) {
9420 if (compose->targetinfo) {
9421 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9422 flag.perm_flags = target_locked?MSG_LOCKED:0;
9424 flag.tmp_flags = MSG_DRAFT;
9426 folder_item_scan(draft);
9427 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9428 MsgInfo *tmpinfo = NULL;
9429 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9430 if (compose->msgid) {
9431 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9434 msgnum = tmpinfo->msgnum;
9435 procmsg_msginfo_free(tmpinfo);
9436 debug_print("got draft msgnum %d from scanning\n", msgnum);
9438 debug_print("didn't get draft msgnum after scanning\n");
9441 debug_print("got draft msgnum %d from adding\n", msgnum);
9447 if (action != COMPOSE_AUTO_SAVE) {
9448 if (action != COMPOSE_DRAFT_FOR_EXIT)
9449 alertpanel_error(_("Could not save draft."));
9452 gtkut_window_popup(compose->window);
9453 val = alertpanel_full(_("Could not save draft"),
9454 _("Could not save draft.\n"
9455 "Do you want to cancel exit or discard this email?"),
9456 _("_Cancel exit"), _("_Discard email"), NULL,
9457 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9458 if (val == G_ALERTALTERNATE) {
9460 g_mutex_unlock(compose->mutex); /* must be done before closing */
9461 compose_close(compose);
9465 g_mutex_unlock(compose->mutex); /* must be done before closing */
9474 if (compose->mode == COMPOSE_REEDIT) {
9475 compose_remove_reedit_target(compose, TRUE);
9478 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9481 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9483 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9485 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9486 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9487 procmsg_msginfo_set_flags(newmsginfo, 0,
9488 MSG_HAS_ATTACHMENT);
9490 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9491 compose_register_draft(newmsginfo);
9493 procmsg_msginfo_free(newmsginfo);
9496 folder_item_scan(draft);
9498 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9500 g_mutex_unlock(compose->mutex); /* must be done before closing */
9501 compose_close(compose);
9507 path = folder_item_fetch_msg(draft, msgnum);
9509 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9512 if (g_stat(path, &s) < 0) {
9513 FILE_OP_ERROR(path, "stat");
9519 procmsg_msginfo_free(compose->targetinfo);
9520 compose->targetinfo = procmsg_msginfo_new();
9521 compose->targetinfo->msgnum = msgnum;
9522 compose->targetinfo->size = (goffset)s.st_size;
9523 compose->targetinfo->mtime = s.st_mtime;
9524 compose->targetinfo->folder = draft;
9526 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9527 compose->mode = COMPOSE_REEDIT;
9529 if (action == COMPOSE_AUTO_SAVE) {
9530 compose->autosaved_draft = compose->targetinfo;
9532 compose->modified = FALSE;
9533 compose_set_title(compose);
9537 g_mutex_unlock(compose->mutex);
9541 void compose_clear_exit_drafts(void)
9543 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9544 DRAFTED_AT_EXIT, NULL);
9545 if (is_file_exist(filepath))
9546 claws_unlink(filepath);
9551 void compose_reopen_exit_drafts(void)
9553 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9554 DRAFTED_AT_EXIT, NULL);
9555 FILE *fp = g_fopen(filepath, "rb");
9559 while (fgets(buf, sizeof(buf), fp)) {
9560 gchar **parts = g_strsplit(buf, "\t", 2);
9561 const gchar *folder = parts[0];
9562 int msgnum = parts[1] ? atoi(parts[1]):-1;
9564 if (folder && *folder && msgnum > -1) {
9565 FolderItem *item = folder_find_item_from_identifier(folder);
9566 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9568 compose_reedit(info, FALSE);
9575 compose_clear_exit_drafts();
9578 static void compose_save_cb(GtkAction *action, gpointer data)
9580 Compose *compose = (Compose *)data;
9581 compose_draft(compose, COMPOSE_KEEP_EDITING);
9582 compose->rmode = COMPOSE_REEDIT;
9585 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9587 if (compose && file_list) {
9590 for ( tmp = file_list; tmp; tmp = tmp->next) {
9591 gchar *file = (gchar *) tmp->data;
9592 gchar *utf8_filename = conv_filename_to_utf8(file);
9593 compose_attach_append(compose, file, utf8_filename, NULL);
9594 compose_changed_cb(NULL, compose);
9599 g_free(utf8_filename);
9604 static void compose_attach_cb(GtkAction *action, gpointer data)
9606 Compose *compose = (Compose *)data;
9609 if (compose->redirect_filename != NULL)
9612 file_list = filesel_select_multiple_files_open(_("Select file"));
9615 compose_attach_from_list(compose, file_list, TRUE);
9616 g_list_free(file_list);
9620 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9622 Compose *compose = (Compose *)data;
9624 gint files_inserted = 0;
9626 file_list = filesel_select_multiple_files_open(_("Select file"));
9631 for ( tmp = file_list; tmp; tmp = tmp->next) {
9632 gchar *file = (gchar *) tmp->data;
9633 gchar *filedup = g_strdup(file);
9634 gchar *shortfile = g_path_get_basename(filedup);
9635 ComposeInsertResult res;
9636 /* insert the file if the file is short or if the user confirmed that
9637 he/she wants to insert the large file */
9638 res = compose_insert_file(compose, file);
9639 if (res == COMPOSE_INSERT_READ_ERROR) {
9640 alertpanel_error(_("File '%s' could not be read."), shortfile);
9641 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9642 alertpanel_error(_("File '%s' contained invalid characters\n"
9643 "for the current encoding, insertion may be incorrect."),
9645 } else if (res == COMPOSE_INSERT_SUCCESS)
9652 g_list_free(file_list);
9656 if (files_inserted > 0 && compose->gtkaspell &&
9657 compose->gtkaspell->check_while_typing)
9658 gtkaspell_highlight_all(compose->gtkaspell);
9662 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9664 Compose *compose = (Compose *)data;
9666 compose_insert_sig(compose, FALSE);
9669 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9673 Compose *compose = (Compose *)data;
9675 gtkut_widget_get_uposition(widget, &x, &y);
9676 if (!compose->batch) {
9677 prefs_common.compose_x = x;
9678 prefs_common.compose_y = y;
9680 if (compose->sending || compose->updating)
9682 compose_close_cb(NULL, compose);
9686 void compose_close_toolbar(Compose *compose)
9688 compose_close_cb(NULL, compose);
9691 static void compose_close_cb(GtkAction *action, gpointer data)
9693 Compose *compose = (Compose *)data;
9697 if (compose->exteditor_tag != -1) {
9698 if (!compose_ext_editor_kill(compose))
9703 if (compose->modified) {
9704 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9705 if (!g_mutex_trylock(compose->mutex)) {
9706 /* we don't want to lock the mutex once it's available,
9707 * because as the only other part of compose.c locking
9708 * it is compose_close - which means once unlocked,
9709 * the compose struct will be freed */
9710 debug_print("couldn't lock mutex, probably sending\n");
9714 val = alertpanel(_("Discard message"),
9715 _("This message has been modified. Discard it?"),
9716 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9718 val = alertpanel(_("Save changes"),
9719 _("This message has been modified. Save the latest changes?"),
9720 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9722 g_mutex_unlock(compose->mutex);
9724 case G_ALERTDEFAULT:
9725 if (prefs_common.autosave && !reedit)
9726 compose_remove_draft(compose);
9728 case G_ALERTALTERNATE:
9729 compose_draft(data, COMPOSE_QUIT_EDITING);
9736 compose_close(compose);
9739 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9741 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9742 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9743 Compose *compose = (Compose *) data;
9746 compose->out_encoding = (CharSet)value;
9749 static void compose_address_cb(GtkAction *action, gpointer data)
9751 Compose *compose = (Compose *)data;
9753 addressbook_open(compose);
9756 static void about_show_cb(GtkAction *action, gpointer data)
9761 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9763 Compose *compose = (Compose *)data;
9768 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9769 cm_return_if_fail(tmpl != NULL);
9771 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9773 val = alertpanel(_("Apply template"), msg,
9774 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9777 if (val == G_ALERTDEFAULT)
9778 compose_template_apply(compose, tmpl, TRUE);
9779 else if (val == G_ALERTALTERNATE)
9780 compose_template_apply(compose, tmpl, FALSE);
9783 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9785 Compose *compose = (Compose *)data;
9787 compose_exec_ext_editor(compose);
9790 static void compose_undo_cb(GtkAction *action, gpointer data)
9792 Compose *compose = (Compose *)data;
9793 gboolean prev_autowrap = compose->autowrap;
9795 compose->autowrap = FALSE;
9796 undo_undo(compose->undostruct);
9797 compose->autowrap = prev_autowrap;
9800 static void compose_redo_cb(GtkAction *action, gpointer data)
9802 Compose *compose = (Compose *)data;
9803 gboolean prev_autowrap = compose->autowrap;
9805 compose->autowrap = FALSE;
9806 undo_redo(compose->undostruct);
9807 compose->autowrap = prev_autowrap;
9810 static void entry_cut_clipboard(GtkWidget *entry)
9812 if (GTK_IS_EDITABLE(entry))
9813 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9814 else if (GTK_IS_TEXT_VIEW(entry))
9815 gtk_text_buffer_cut_clipboard(
9816 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9817 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9821 static void entry_copy_clipboard(GtkWidget *entry)
9823 if (GTK_IS_EDITABLE(entry))
9824 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9825 else if (GTK_IS_TEXT_VIEW(entry))
9826 gtk_text_buffer_copy_clipboard(
9827 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9828 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9831 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9832 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9834 if (GTK_IS_TEXT_VIEW(entry)) {
9835 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9836 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9837 GtkTextIter start_iter, end_iter;
9839 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9841 if (contents == NULL)
9844 /* we shouldn't delete the selection when middle-click-pasting, or we
9845 * can't mid-click-paste our own selection */
9846 if (clip != GDK_SELECTION_PRIMARY) {
9847 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9848 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9851 if (insert_place == NULL) {
9852 /* if insert_place isn't specified, insert at the cursor.
9853 * used for Ctrl-V pasting */
9854 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9855 start = gtk_text_iter_get_offset(&start_iter);
9856 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9858 /* if insert_place is specified, paste here.
9859 * used for mid-click-pasting */
9860 start = gtk_text_iter_get_offset(insert_place);
9861 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9862 if (prefs_common.primary_paste_unselects)
9863 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9867 /* paste unwrapped: mark the paste so it's not wrapped later */
9868 end = start + strlen(contents);
9869 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9870 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9871 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9872 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9873 /* rewrap paragraph now (after a mid-click-paste) */
9874 mark_start = gtk_text_buffer_get_insert(buffer);
9875 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9876 gtk_text_iter_backward_char(&start_iter);
9877 compose_beautify_paragraph(compose, &start_iter, TRUE);
9879 } else if (GTK_IS_EDITABLE(entry))
9880 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9882 compose->modified = TRUE;
9885 static void entry_allsel(GtkWidget *entry)
9887 if (GTK_IS_EDITABLE(entry))
9888 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9889 else if (GTK_IS_TEXT_VIEW(entry)) {
9890 GtkTextIter startiter, enditer;
9891 GtkTextBuffer *textbuf;
9893 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9894 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9895 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9897 gtk_text_buffer_move_mark_by_name(textbuf,
9898 "selection_bound", &startiter);
9899 gtk_text_buffer_move_mark_by_name(textbuf,
9900 "insert", &enditer);
9904 static void compose_cut_cb(GtkAction *action, gpointer data)
9906 Compose *compose = (Compose *)data;
9907 if (compose->focused_editable
9908 #ifndef GENERIC_UMPC
9909 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9912 entry_cut_clipboard(compose->focused_editable);
9915 static void compose_copy_cb(GtkAction *action, gpointer data)
9917 Compose *compose = (Compose *)data;
9918 if (compose->focused_editable
9919 #ifndef GENERIC_UMPC
9920 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9923 entry_copy_clipboard(compose->focused_editable);
9926 static void compose_paste_cb(GtkAction *action, gpointer data)
9928 Compose *compose = (Compose *)data;
9930 GtkTextBuffer *buffer;
9932 if (compose->focused_editable &&
9933 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9934 entry_paste_clipboard(compose, compose->focused_editable,
9935 prefs_common.linewrap_pastes,
9936 GDK_SELECTION_CLIPBOARD, NULL);
9940 if (GTK_WIDGET_HAS_FOCUS(compose->text) &&
9941 compose->gtkaspell &&
9942 compose->gtkaspell->check_while_typing)
9943 gtkaspell_highlight_all(compose->gtkaspell);
9947 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9949 Compose *compose = (Compose *)data;
9950 gint wrap_quote = prefs_common.linewrap_quote;
9951 if (compose->focused_editable
9952 #ifndef GENERIC_UMPC
9953 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9956 /* let text_insert() (called directly or at a later time
9957 * after the gtk_editable_paste_clipboard) know that
9958 * text is to be inserted as a quotation. implemented
9959 * by using a simple refcount... */
9960 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9961 G_OBJECT(compose->focused_editable),
9962 "paste_as_quotation"));
9963 g_object_set_data(G_OBJECT(compose->focused_editable),
9964 "paste_as_quotation",
9965 GINT_TO_POINTER(paste_as_quotation + 1));
9966 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9967 entry_paste_clipboard(compose, compose->focused_editable,
9968 prefs_common.linewrap_pastes,
9969 GDK_SELECTION_CLIPBOARD, NULL);
9970 prefs_common.linewrap_quote = wrap_quote;
9974 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9976 Compose *compose = (Compose *)data;
9978 GtkTextBuffer *buffer;
9980 if (compose->focused_editable
9981 #ifndef GENERIC_UMPC
9982 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9985 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9986 GDK_SELECTION_CLIPBOARD, NULL);
9990 if (GTK_WIDGET_HAS_FOCUS(compose->text) &&
9991 compose->gtkaspell &&
9992 compose->gtkaspell->check_while_typing)
9993 gtkaspell_highlight_all(compose->gtkaspell);
9997 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9999 Compose *compose = (Compose *)data;
10000 gint prev_autowrap;
10001 GtkTextBuffer *buffer;
10003 if (compose->focused_editable
10004 #ifndef GENERIC_UMPC
10005 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
10008 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10009 GDK_SELECTION_CLIPBOARD, NULL);
10013 if (GTK_WIDGET_HAS_FOCUS(compose->text) &&
10014 compose->gtkaspell &&
10015 compose->gtkaspell->check_while_typing)
10016 gtkaspell_highlight_all(compose->gtkaspell);
10020 static void compose_allsel_cb(GtkAction *action, gpointer data)
10022 Compose *compose = (Compose *)data;
10023 if (compose->focused_editable
10024 #ifndef GENERIC_UMPC
10025 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
10028 entry_allsel(compose->focused_editable);
10031 static void textview_move_beginning_of_line (GtkTextView *text)
10033 GtkTextBuffer *buffer;
10037 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10039 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10040 mark = gtk_text_buffer_get_insert(buffer);
10041 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10042 gtk_text_iter_set_line_offset(&ins, 0);
10043 gtk_text_buffer_place_cursor(buffer, &ins);
10046 static void textview_move_forward_character (GtkTextView *text)
10048 GtkTextBuffer *buffer;
10052 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10054 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10055 mark = gtk_text_buffer_get_insert(buffer);
10056 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10057 if (gtk_text_iter_forward_cursor_position(&ins))
10058 gtk_text_buffer_place_cursor(buffer, &ins);
10061 static void textview_move_backward_character (GtkTextView *text)
10063 GtkTextBuffer *buffer;
10067 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10069 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10070 mark = gtk_text_buffer_get_insert(buffer);
10071 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10072 if (gtk_text_iter_backward_cursor_position(&ins))
10073 gtk_text_buffer_place_cursor(buffer, &ins);
10076 static void textview_move_forward_word (GtkTextView *text)
10078 GtkTextBuffer *buffer;
10083 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10085 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10086 mark = gtk_text_buffer_get_insert(buffer);
10087 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10088 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10089 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10090 gtk_text_iter_backward_word_start(&ins);
10091 gtk_text_buffer_place_cursor(buffer, &ins);
10095 static void textview_move_backward_word (GtkTextView *text)
10097 GtkTextBuffer *buffer;
10102 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10104 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10105 mark = gtk_text_buffer_get_insert(buffer);
10106 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10107 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10108 if (gtk_text_iter_backward_word_starts(&ins, 1))
10109 gtk_text_buffer_place_cursor(buffer, &ins);
10112 static void textview_move_end_of_line (GtkTextView *text)
10114 GtkTextBuffer *buffer;
10118 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10120 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10121 mark = gtk_text_buffer_get_insert(buffer);
10122 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10123 if (gtk_text_iter_forward_to_line_end(&ins))
10124 gtk_text_buffer_place_cursor(buffer, &ins);
10127 static void textview_move_next_line (GtkTextView *text)
10129 GtkTextBuffer *buffer;
10134 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10136 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10137 mark = gtk_text_buffer_get_insert(buffer);
10138 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10139 offset = gtk_text_iter_get_line_offset(&ins);
10140 if (gtk_text_iter_forward_line(&ins)) {
10141 gtk_text_iter_set_line_offset(&ins, offset);
10142 gtk_text_buffer_place_cursor(buffer, &ins);
10146 static void textview_move_previous_line (GtkTextView *text)
10148 GtkTextBuffer *buffer;
10153 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10155 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10156 mark = gtk_text_buffer_get_insert(buffer);
10157 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10158 offset = gtk_text_iter_get_line_offset(&ins);
10159 if (gtk_text_iter_backward_line(&ins)) {
10160 gtk_text_iter_set_line_offset(&ins, offset);
10161 gtk_text_buffer_place_cursor(buffer, &ins);
10165 static void textview_delete_forward_character (GtkTextView *text)
10167 GtkTextBuffer *buffer;
10169 GtkTextIter ins, end_iter;
10171 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10173 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10174 mark = gtk_text_buffer_get_insert(buffer);
10175 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10177 if (gtk_text_iter_forward_char(&end_iter)) {
10178 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10182 static void textview_delete_backward_character (GtkTextView *text)
10184 GtkTextBuffer *buffer;
10186 GtkTextIter ins, end_iter;
10188 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10190 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10191 mark = gtk_text_buffer_get_insert(buffer);
10192 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10194 if (gtk_text_iter_backward_char(&end_iter)) {
10195 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10199 static void textview_delete_forward_word (GtkTextView *text)
10201 GtkTextBuffer *buffer;
10203 GtkTextIter ins, end_iter;
10205 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10207 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10208 mark = gtk_text_buffer_get_insert(buffer);
10209 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10211 if (gtk_text_iter_forward_word_end(&end_iter)) {
10212 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10216 static void textview_delete_backward_word (GtkTextView *text)
10218 GtkTextBuffer *buffer;
10220 GtkTextIter ins, end_iter;
10222 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10224 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10225 mark = gtk_text_buffer_get_insert(buffer);
10226 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10228 if (gtk_text_iter_backward_word_start(&end_iter)) {
10229 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10233 static void textview_delete_line (GtkTextView *text)
10235 GtkTextBuffer *buffer;
10237 GtkTextIter ins, start_iter, end_iter;
10239 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10241 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10242 mark = gtk_text_buffer_get_insert(buffer);
10243 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10246 gtk_text_iter_set_line_offset(&start_iter, 0);
10249 if (gtk_text_iter_ends_line(&end_iter)){
10250 if (!gtk_text_iter_forward_char(&end_iter))
10251 gtk_text_iter_backward_char(&start_iter);
10254 gtk_text_iter_forward_to_line_end(&end_iter);
10255 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10258 static void textview_delete_to_line_end (GtkTextView *text)
10260 GtkTextBuffer *buffer;
10262 GtkTextIter ins, end_iter;
10264 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10266 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10267 mark = gtk_text_buffer_get_insert(buffer);
10268 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10270 if (gtk_text_iter_ends_line(&end_iter))
10271 gtk_text_iter_forward_char(&end_iter);
10273 gtk_text_iter_forward_to_line_end(&end_iter);
10274 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10277 #define DO_ACTION(name, act) { \
10278 if(!strcmp(name, a_name)) { \
10282 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10284 const gchar *a_name = gtk_action_get_name(action);
10285 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10286 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10287 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10288 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10289 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10290 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10291 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10292 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10293 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10294 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10295 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10296 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10297 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10298 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10302 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10304 Compose *compose = (Compose *)data;
10305 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10306 ComposeCallAdvancedAction action = -1;
10308 action = compose_call_advanced_action_from_path(gaction);
10311 void (*do_action) (GtkTextView *text);
10312 } action_table[] = {
10313 {textview_move_beginning_of_line},
10314 {textview_move_forward_character},
10315 {textview_move_backward_character},
10316 {textview_move_forward_word},
10317 {textview_move_backward_word},
10318 {textview_move_end_of_line},
10319 {textview_move_next_line},
10320 {textview_move_previous_line},
10321 {textview_delete_forward_character},
10322 {textview_delete_backward_character},
10323 {textview_delete_forward_word},
10324 {textview_delete_backward_word},
10325 {textview_delete_line},
10326 {textview_delete_to_line_end}
10329 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10331 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10332 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10333 if (action_table[action].do_action)
10334 action_table[action].do_action(text);
10336 g_warning("Not implemented yet.");
10340 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10344 if (GTK_IS_EDITABLE(widget)) {
10345 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10346 gtk_editable_set_position(GTK_EDITABLE(widget),
10349 if (widget->parent && widget->parent->parent
10350 && widget->parent->parent->parent) {
10351 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10352 gint y = widget->allocation.y;
10353 gint height = widget->allocation.height;
10354 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10355 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10357 if (y < (int)shown->value) {
10358 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10360 if (y + height > (int)shown->value + (int)shown->page_size) {
10361 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10362 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10363 y + height - (int)shown->page_size - 1);
10365 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10366 (int)shown->upper - (int)shown->page_size - 1);
10373 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10374 compose->focused_editable = widget;
10376 #ifdef GENERIC_UMPC
10377 if (GTK_IS_TEXT_VIEW(widget)
10378 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10379 g_object_ref(compose->notebook);
10380 g_object_ref(compose->edit_vbox);
10381 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10382 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10383 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10384 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10385 g_object_unref(compose->notebook);
10386 g_object_unref(compose->edit_vbox);
10387 g_signal_handlers_block_by_func(G_OBJECT(widget),
10388 G_CALLBACK(compose_grab_focus_cb),
10390 gtk_widget_grab_focus(widget);
10391 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10392 G_CALLBACK(compose_grab_focus_cb),
10394 } else if (!GTK_IS_TEXT_VIEW(widget)
10395 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10396 g_object_ref(compose->notebook);
10397 g_object_ref(compose->edit_vbox);
10398 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10399 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10400 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10401 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10402 g_object_unref(compose->notebook);
10403 g_object_unref(compose->edit_vbox);
10404 g_signal_handlers_block_by_func(G_OBJECT(widget),
10405 G_CALLBACK(compose_grab_focus_cb),
10407 gtk_widget_grab_focus(widget);
10408 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10409 G_CALLBACK(compose_grab_focus_cb),
10415 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10417 compose->modified = TRUE;
10418 // compose_beautify_paragraph(compose, NULL, TRUE);
10419 #ifndef GENERIC_UMPC
10420 compose_set_title(compose);
10424 static void compose_wrap_cb(GtkAction *action, gpointer data)
10426 Compose *compose = (Compose *)data;
10427 compose_beautify_paragraph(compose, NULL, TRUE);
10430 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10432 Compose *compose = (Compose *)data;
10433 compose_wrap_all_full(compose, TRUE);
10436 static void compose_find_cb(GtkAction *action, gpointer data)
10438 Compose *compose = (Compose *)data;
10440 message_search_compose(compose);
10443 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10446 Compose *compose = (Compose *)data;
10447 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10448 if (compose->autowrap)
10449 compose_wrap_all_full(compose, TRUE);
10450 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10453 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10456 Compose *compose = (Compose *)data;
10457 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10460 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10462 Compose *compose = (Compose *)data;
10464 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10467 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10469 Compose *compose = (Compose *)data;
10471 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10474 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10476 g_free(compose->privacy_system);
10478 compose->privacy_system = g_strdup(account->default_privacy_system);
10479 compose_update_privacy_system_menu_item(compose, warn);
10482 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10484 Compose *compose = (Compose *)data;
10486 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10487 gtk_widget_show(compose->ruler_hbox);
10488 prefs_common.show_ruler = TRUE;
10490 gtk_widget_hide(compose->ruler_hbox);
10491 gtk_widget_queue_resize(compose->edit_vbox);
10492 prefs_common.show_ruler = FALSE;
10496 static void compose_attach_drag_received_cb (GtkWidget *widget,
10497 GdkDragContext *context,
10500 GtkSelectionData *data,
10503 gpointer user_data)
10505 Compose *compose = (Compose *)user_data;
10508 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10510 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10512 ) && gtk_drag_get_source_widget(context) !=
10513 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10514 list = uri_list_extract_filenames((const gchar *)data->data);
10515 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10516 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10517 compose_attach_append
10518 (compose, (const gchar *)tmp->data,
10519 utf8_filename, NULL);
10520 g_free(utf8_filename);
10522 if (list) compose_changed_cb(NULL, compose);
10523 list_free_strings(list);
10525 } else if (gtk_drag_get_source_widget(context)
10526 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10527 /* comes from our summaryview */
10528 SummaryView * summaryview = NULL;
10529 GSList * list = NULL, *cur = NULL;
10531 if (mainwindow_get_mainwindow())
10532 summaryview = mainwindow_get_mainwindow()->summaryview;
10535 list = summary_get_selected_msg_list(summaryview);
10537 for (cur = list; cur; cur = cur->next) {
10538 MsgInfo *msginfo = (MsgInfo *)cur->data;
10539 gchar *file = NULL;
10541 file = procmsg_get_message_file_full(msginfo,
10544 compose_attach_append(compose, (const gchar *)file,
10545 (const gchar *)file, "message/rfc822");
10549 g_slist_free(list);
10553 static gboolean compose_drag_drop(GtkWidget *widget,
10554 GdkDragContext *drag_context,
10556 guint time, gpointer user_data)
10558 /* not handling this signal makes compose_insert_drag_received_cb
10563 static void compose_insert_drag_received_cb (GtkWidget *widget,
10564 GdkDragContext *drag_context,
10567 GtkSelectionData *data,
10570 gpointer user_data)
10572 Compose *compose = (Compose *)user_data;
10575 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10578 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10580 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10582 AlertValue val = G_ALERTDEFAULT;
10584 list = uri_list_extract_filenames((const gchar *)data->data);
10585 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10586 /* Assume a list of no files, and data has ://, is a remote link */
10587 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10588 gchar *tmpfile = get_tmp_file();
10589 str_write_to_file(tmpdata, tmpfile);
10591 compose_insert_file(compose, tmpfile);
10592 claws_unlink(tmpfile);
10594 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10595 compose_beautify_paragraph(compose, NULL, TRUE);
10598 switch (prefs_common.compose_dnd_mode) {
10599 case COMPOSE_DND_ASK:
10600 val = alertpanel_full(_("Insert or attach?"),
10601 _("Do you want to insert the contents of the file(s) "
10602 "into the message body, or attach it to the email?"),
10603 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10604 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10606 case COMPOSE_DND_INSERT:
10607 val = G_ALERTALTERNATE;
10609 case COMPOSE_DND_ATTACH:
10610 val = G_ALERTOTHER;
10613 /* unexpected case */
10614 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10617 if (val & G_ALERTDISABLE) {
10618 val &= ~G_ALERTDISABLE;
10619 /* remember what action to perform by default, only if we don't click Cancel */
10620 if (val == G_ALERTALTERNATE)
10621 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10622 else if (val == G_ALERTOTHER)
10623 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10626 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10627 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10628 list_free_strings(list);
10631 } else if (val == G_ALERTOTHER) {
10632 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10633 list_free_strings(list);
10638 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10639 compose_insert_file(compose, (const gchar *)tmp->data);
10641 list_free_strings(list);
10643 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10648 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10651 static void compose_header_drag_received_cb (GtkWidget *widget,
10652 GdkDragContext *drag_context,
10655 GtkSelectionData *data,
10658 gpointer user_data)
10660 GtkEditable *entry = (GtkEditable *)user_data;
10661 gchar *email = (gchar *)data->data;
10663 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10666 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10667 gchar *decoded=g_new(gchar, strlen(email));
10670 email += strlen("mailto:");
10671 decode_uri(decoded, email); /* will fit */
10672 gtk_editable_delete_text(entry, 0, -1);
10673 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10674 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10678 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10681 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10683 Compose *compose = (Compose *)data;
10685 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10686 compose->return_receipt = TRUE;
10688 compose->return_receipt = FALSE;
10691 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10693 Compose *compose = (Compose *)data;
10695 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10696 compose->remove_references = TRUE;
10698 compose->remove_references = FALSE;
10701 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10702 ComposeHeaderEntry *headerentry)
10704 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10708 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10709 GdkEventKey *event,
10710 ComposeHeaderEntry *headerentry)
10712 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10713 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10714 !(event->state & GDK_MODIFIER_MASK) &&
10715 (event->keyval == GDK_BackSpace) &&
10716 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10717 gtk_container_remove
10718 (GTK_CONTAINER(headerentry->compose->header_table),
10719 headerentry->combo);
10720 gtk_container_remove
10721 (GTK_CONTAINER(headerentry->compose->header_table),
10722 headerentry->entry);
10723 headerentry->compose->header_list =
10724 g_slist_remove(headerentry->compose->header_list,
10726 g_free(headerentry);
10727 } else if (event->keyval == GDK_Tab) {
10728 if (headerentry->compose->header_last == headerentry) {
10729 /* Override default next focus, and give it to subject_entry
10730 * instead of notebook tabs
10732 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10733 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10740 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10741 ComposeHeaderEntry *headerentry)
10743 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10744 compose_create_header_entry(headerentry->compose);
10745 g_signal_handlers_disconnect_matched
10746 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10747 0, 0, NULL, NULL, headerentry);
10749 /* Automatically scroll down */
10750 GTK_EVENTS_FLUSH();
10751 compose_show_first_last_header(headerentry->compose, FALSE);
10757 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10759 GtkAdjustment *vadj;
10761 cm_return_if_fail(compose);
10762 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10763 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10764 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10765 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10766 gtk_adjustment_changed(vadj);
10769 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10770 const gchar *text, gint len, Compose *compose)
10772 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10773 (G_OBJECT(compose->text), "paste_as_quotation"));
10776 cm_return_if_fail(text != NULL);
10778 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10779 G_CALLBACK(text_inserted),
10781 if (paste_as_quotation) {
10783 const gchar *qmark;
10785 GtkTextIter start_iter;
10788 len = strlen(text);
10790 new_text = g_strndup(text, len);
10792 qmark = compose_quote_char_from_context(compose);
10794 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10795 gtk_text_buffer_place_cursor(buffer, iter);
10797 pos = gtk_text_iter_get_offset(iter);
10799 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10800 _("Quote format error at line %d."));
10801 quote_fmt_reset_vartable();
10803 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10804 GINT_TO_POINTER(paste_as_quotation - 1));
10806 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10807 gtk_text_buffer_place_cursor(buffer, iter);
10808 gtk_text_buffer_delete_mark(buffer, mark);
10810 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10811 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10812 compose_beautify_paragraph(compose, &start_iter, FALSE);
10813 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10814 gtk_text_buffer_delete_mark(buffer, mark);
10816 if (strcmp(text, "\n") || compose->automatic_break
10817 || gtk_text_iter_starts_line(iter)) {
10818 GtkTextIter before_ins;
10819 gtk_text_buffer_insert(buffer, iter, text, len);
10820 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10821 before_ins = *iter;
10822 gtk_text_iter_backward_chars(&before_ins, len);
10823 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10826 /* check if the preceding is just whitespace or quote */
10827 GtkTextIter start_line;
10828 gchar *tmp = NULL, *quote = NULL;
10829 gint quote_len = 0, is_normal = 0;
10830 start_line = *iter;
10831 gtk_text_iter_set_line_offset(&start_line, 0);
10832 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10835 if (*tmp == '\0') {
10838 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10846 gtk_text_buffer_insert(buffer, iter, text, len);
10848 gtk_text_buffer_insert_with_tags_by_name(buffer,
10849 iter, text, len, "no_join", NULL);
10854 if (!paste_as_quotation) {
10855 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10856 compose_beautify_paragraph(compose, iter, FALSE);
10857 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10858 gtk_text_buffer_delete_mark(buffer, mark);
10861 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10862 G_CALLBACK(text_inserted),
10864 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10866 if (prefs_common.autosave &&
10867 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10868 compose->draft_timeout_tag != -2 /* disabled while loading */)
10869 compose->draft_timeout_tag = g_timeout_add
10870 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10872 static gint compose_defer_auto_save_draft(Compose *compose)
10874 compose->draft_timeout_tag = -1;
10875 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10880 static void compose_check_all(GtkAction *action, gpointer data)
10882 Compose *compose = (Compose *)data;
10883 if (!compose->gtkaspell)
10886 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10887 claws_spell_entry_check_all(
10888 CLAWS_SPELL_ENTRY(compose->subject_entry));
10890 gtkaspell_check_all(compose->gtkaspell);
10893 static void compose_highlight_all(GtkAction *action, gpointer data)
10895 Compose *compose = (Compose *)data;
10896 if (compose->gtkaspell) {
10897 claws_spell_entry_recheck_all(
10898 CLAWS_SPELL_ENTRY(compose->subject_entry));
10899 gtkaspell_highlight_all(compose->gtkaspell);
10903 static void compose_check_backwards(GtkAction *action, gpointer data)
10905 Compose *compose = (Compose *)data;
10906 if (!compose->gtkaspell) {
10907 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10911 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10912 claws_spell_entry_check_backwards(
10913 CLAWS_SPELL_ENTRY(compose->subject_entry));
10915 gtkaspell_check_backwards(compose->gtkaspell);
10918 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10920 Compose *compose = (Compose *)data;
10921 if (!compose->gtkaspell) {
10922 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10926 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10927 claws_spell_entry_check_forwards_go(
10928 CLAWS_SPELL_ENTRY(compose->subject_entry));
10930 gtkaspell_check_forwards_go(compose->gtkaspell);
10935 *\brief Guess originating forward account from MsgInfo and several
10936 * "common preference" settings. Return NULL if no guess.
10938 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10940 PrefsAccount *account = NULL;
10942 cm_return_val_if_fail(msginfo, NULL);
10943 cm_return_val_if_fail(msginfo->folder, NULL);
10944 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10946 if (msginfo->folder->prefs->enable_default_account)
10947 account = account_find_from_id(msginfo->folder->prefs->default_account);
10950 account = msginfo->folder->folder->account;
10952 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10954 Xstrdup_a(to, msginfo->to, return NULL);
10955 extract_address(to);
10956 account = account_find_from_address(to, FALSE);
10959 if (!account && prefs_common.forward_account_autosel) {
10960 gchar cc[BUFFSIZE];
10961 if (!procheader_get_header_from_msginfo
10962 (msginfo, cc,sizeof cc , "Cc:")) {
10963 gchar *buf = cc + strlen("Cc:");
10964 extract_address(buf);
10965 account = account_find_from_address(buf, FALSE);
10969 if (!account && prefs_common.forward_account_autosel) {
10970 gchar deliveredto[BUFFSIZE];
10971 if (!procheader_get_header_from_msginfo
10972 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10973 gchar *buf = deliveredto + strlen("Delivered-To:");
10974 extract_address(buf);
10975 account = account_find_from_address(buf, FALSE);
10982 gboolean compose_close(Compose *compose)
10986 if (!g_mutex_trylock(compose->mutex)) {
10987 /* we have to wait for the (possibly deferred by auto-save)
10988 * drafting to be done, before destroying the compose under
10990 debug_print("waiting for drafting to finish...\n");
10991 compose_allow_user_actions(compose, FALSE);
10992 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10995 cm_return_val_if_fail(compose, FALSE);
10996 gtkut_widget_get_uposition(compose->window, &x, &y);
10997 if (!compose->batch) {
10998 prefs_common.compose_x = x;
10999 prefs_common.compose_y = y;
11001 g_mutex_unlock(compose->mutex);
11002 compose_destroy(compose);
11007 * Add entry field for each address in list.
11008 * \param compose E-Mail composition object.
11009 * \param listAddress List of (formatted) E-Mail addresses.
11011 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11014 node = listAddress;
11016 addr = ( gchar * ) node->data;
11017 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11018 node = g_list_next( node );
11022 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11023 guint action, gboolean opening_multiple)
11025 gchar *body = NULL;
11026 GSList *new_msglist = NULL;
11027 MsgInfo *tmp_msginfo = NULL;
11028 gboolean originally_enc = FALSE;
11029 gboolean originally_sig = FALSE;
11030 Compose *compose = NULL;
11031 gchar *s_system = NULL;
11033 cm_return_if_fail(msgview != NULL);
11035 cm_return_if_fail(msginfo_list != NULL);
11037 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11038 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11039 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11041 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11042 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11043 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11044 orig_msginfo, mimeinfo);
11045 if (tmp_msginfo != NULL) {
11046 new_msglist = g_slist_append(NULL, tmp_msginfo);
11048 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11049 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11050 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11052 tmp_msginfo->folder = orig_msginfo->folder;
11053 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11054 if (orig_msginfo->tags) {
11055 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11056 tmp_msginfo->folder->tags_dirty = TRUE;
11062 if (!opening_multiple)
11063 body = messageview_get_selection(msgview);
11066 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11067 procmsg_msginfo_free(tmp_msginfo);
11068 g_slist_free(new_msglist);
11070 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11072 if (compose && originally_enc) {
11073 compose_force_encryption(compose, compose->account, FALSE, s_system);
11076 if (compose && originally_sig && compose->account->default_sign_reply) {
11077 compose_force_signing(compose, compose->account, s_system);
11081 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11084 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11087 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11088 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11089 GSList *cur = msginfo_list;
11090 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11091 "messages. Opening the windows "
11092 "could take some time. Do you "
11093 "want to continue?"),
11094 g_slist_length(msginfo_list));
11095 if (g_slist_length(msginfo_list) > 9
11096 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11097 != G_ALERTALTERNATE) {
11102 /* We'll open multiple compose windows */
11103 /* let the WM place the next windows */
11104 compose_force_window_origin = FALSE;
11105 for (; cur; cur = cur->next) {
11107 tmplist.data = cur->data;
11108 tmplist.next = NULL;
11109 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11111 compose_force_window_origin = TRUE;
11113 /* forwarding multiple mails as attachments is done via a
11114 * single compose window */
11115 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11119 void compose_set_position(Compose *compose, gint pos)
11121 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11123 gtkut_text_view_set_position(text, pos);
11126 gboolean compose_search_string(Compose *compose,
11127 const gchar *str, gboolean case_sens)
11129 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11131 return gtkut_text_view_search_string(text, str, case_sens);
11134 gboolean compose_search_string_backward(Compose *compose,
11135 const gchar *str, gboolean case_sens)
11137 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11139 return gtkut_text_view_search_string_backward(text, str, case_sens);
11142 /* allocate a msginfo structure and populate its data from a compose data structure */
11143 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11145 MsgInfo *newmsginfo;
11147 gchar buf[BUFFSIZE];
11149 cm_return_val_if_fail( compose != NULL, NULL );
11151 newmsginfo = procmsg_msginfo_new();
11154 get_rfc822_date(buf, sizeof(buf));
11155 newmsginfo->date = g_strdup(buf);
11158 if (compose->from_name) {
11159 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11160 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11164 if (compose->subject_entry)
11165 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11167 /* to, cc, reply-to, newsgroups */
11168 for (list = compose->header_list; list; list = list->next) {
11169 gchar *header = gtk_editable_get_chars(
11171 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11172 gchar *entry = gtk_editable_get_chars(
11173 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11175 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11176 if ( newmsginfo->to == NULL ) {
11177 newmsginfo->to = g_strdup(entry);
11178 } else if (entry && *entry) {
11179 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11180 g_free(newmsginfo->to);
11181 newmsginfo->to = tmp;
11184 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11185 if ( newmsginfo->cc == NULL ) {
11186 newmsginfo->cc = g_strdup(entry);
11187 } else if (entry && *entry) {
11188 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11189 g_free(newmsginfo->cc);
11190 newmsginfo->cc = tmp;
11193 if ( strcasecmp(header,
11194 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11195 if ( newmsginfo->newsgroups == NULL ) {
11196 newmsginfo->newsgroups = g_strdup(entry);
11197 } else if (entry && *entry) {
11198 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11199 g_free(newmsginfo->newsgroups);
11200 newmsginfo->newsgroups = tmp;
11208 /* other data is unset */
11214 /* update compose's dictionaries from folder dict settings */
11215 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11216 FolderItem *folder_item)
11218 cm_return_if_fail(compose != NULL);
11220 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11221 FolderItemPrefs *prefs = folder_item->prefs;
11223 if (prefs->enable_default_dictionary)
11224 gtkaspell_change_dict(compose->gtkaspell,
11225 prefs->default_dictionary, FALSE);
11226 if (folder_item->prefs->enable_default_alt_dictionary)
11227 gtkaspell_change_alt_dict(compose->gtkaspell,
11228 prefs->default_alt_dictionary);
11229 if (prefs->enable_default_dictionary
11230 || prefs->enable_default_alt_dictionary)
11231 compose_spell_menu_changed(compose);