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);
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);
1064 * CLAWS: just don't allow return receipt request, even if the user
1065 * may want to send an email. simple but foolproof.
1067 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1069 compose_add_field_list( compose, listAddress );
1071 if (item && item->prefs && item->prefs->compose_with_format) {
1072 subject_format = item->prefs->compose_subject_format;
1073 body_format = item->prefs->compose_body_format;
1074 } else if (account->compose_with_format) {
1075 subject_format = account->compose_subject_format;
1076 body_format = account->compose_body_format;
1077 } else if (prefs_common.compose_with_format) {
1078 subject_format = prefs_common.compose_subject_format;
1079 body_format = prefs_common.compose_body_format;
1082 if (subject_format || body_format) {
1085 && *subject_format != '\0' )
1087 gchar *subject = NULL;
1092 dummyinfo = compose_msginfo_new_from_compose(compose);
1094 /* decode \-escape sequences in the internal representation of the quote format */
1095 tmp = malloc(strlen(subject_format)+1);
1096 pref_get_unescaped_pref(tmp, subject_format);
1098 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1100 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1101 compose->gtkaspell);
1103 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1105 quote_fmt_scan_string(tmp);
1108 buf = quote_fmt_get_buffer();
1110 alertpanel_error(_("New message subject format error."));
1112 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1113 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1114 quote_fmt_reset_vartable();
1121 && *body_format != '\0' )
1124 GtkTextBuffer *buffer;
1125 GtkTextIter start, end;
1129 dummyinfo = compose_msginfo_new_from_compose(compose);
1131 text = GTK_TEXT_VIEW(compose->text);
1132 buffer = gtk_text_view_get_buffer(text);
1133 gtk_text_buffer_get_start_iter(buffer, &start);
1134 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1135 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1137 compose_quote_fmt(compose, dummyinfo,
1139 NULL, tmp, FALSE, TRUE,
1140 _("The body of the \"New message\" template has an error at line %d."));
1141 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1142 quote_fmt_reset_vartable();
1148 procmsg_msginfo_free( dummyinfo );
1154 for (i = 0; i < attach_files->len; i++) {
1155 file = g_ptr_array_index(attach_files, i);
1156 compose_attach_append(compose, file, file, NULL);
1160 compose_show_first_last_header(compose, TRUE);
1162 /* Set save folder */
1163 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1164 gchar *folderidentifier;
1166 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1167 folderidentifier = folder_item_get_identifier(item);
1168 compose_set_save_to(compose, folderidentifier);
1169 g_free(folderidentifier);
1172 /* Place cursor according to provided input (mfield) */
1174 case NO_FIELD_PRESENT:
1175 gtk_widget_grab_focus(compose->header_last->entry);
1177 case TO_FIELD_PRESENT:
1179 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1180 gtk_widget_grab_focus(compose->subject_entry);
1182 case SUBJECT_FIELD_PRESENT:
1183 textview = GTK_TEXT_VIEW(compose->text);
1184 textbuf = gtk_text_view_get_buffer(textview);
1185 mark = gtk_text_buffer_get_insert(textbuf);
1186 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1187 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1189 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1190 * only defers where it comes to the variable body
1191 * is not null. If no body is present compose->text
1192 * will be null in which case you cannot place the
1193 * cursor inside the component so. An empty component
1194 * is therefore created before placing the cursor
1196 case BODY_FIELD_PRESENT:
1197 gtk_widget_grab_focus(compose->text);
1201 undo_unblock(compose->undostruct);
1203 if (prefs_common.auto_exteditor)
1204 compose_exec_ext_editor(compose);
1206 compose->draft_timeout_tag = -1;
1207 SCROLL_TO_CURSOR(compose);
1209 compose->modified = FALSE;
1210 compose_set_title(compose);
1214 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1215 gboolean override_pref, const gchar *system)
1217 const gchar *privacy = NULL;
1219 cm_return_if_fail(compose != NULL);
1220 cm_return_if_fail(account != NULL);
1222 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1227 else if (account->default_privacy_system
1228 && strlen(account->default_privacy_system)) {
1229 privacy = account->default_privacy_system;
1231 GSList *privacy_avail = privacy_get_system_ids();
1232 if (privacy_avail && g_slist_length(privacy_avail)) {
1233 privacy = (gchar *)(privacy_avail->data);
1236 if (privacy != NULL) {
1238 g_free(compose->privacy_system);
1239 compose->privacy_system = NULL;
1241 if (compose->privacy_system == NULL)
1242 compose->privacy_system = g_strdup(privacy);
1243 else if (*(compose->privacy_system) == '\0') {
1244 g_free(compose->privacy_system);
1245 compose->privacy_system = g_strdup(privacy);
1247 compose_update_privacy_system_menu_item(compose, FALSE);
1248 compose_use_encryption(compose, TRUE);
1252 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1254 const gchar *privacy = NULL;
1258 else if (account->default_privacy_system
1259 && strlen(account->default_privacy_system)) {
1260 privacy = account->default_privacy_system;
1262 GSList *privacy_avail = privacy_get_system_ids();
1263 if (privacy_avail && g_slist_length(privacy_avail)) {
1264 privacy = (gchar *)(privacy_avail->data);
1268 if (privacy != NULL) {
1270 g_free(compose->privacy_system);
1271 compose->privacy_system = NULL;
1273 if (compose->privacy_system == NULL)
1274 compose->privacy_system = g_strdup(privacy);
1275 compose_update_privacy_system_menu_item(compose, FALSE);
1276 compose_use_signing(compose, TRUE);
1280 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1284 Compose *compose = NULL;
1286 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1288 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1289 cm_return_val_if_fail(msginfo != NULL, NULL);
1291 list_len = g_slist_length(msginfo_list);
1295 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1296 FALSE, prefs_common.default_reply_list, FALSE, body);
1298 case COMPOSE_REPLY_WITH_QUOTE:
1299 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1300 FALSE, prefs_common.default_reply_list, FALSE, body);
1302 case COMPOSE_REPLY_WITHOUT_QUOTE:
1303 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1304 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1306 case COMPOSE_REPLY_TO_SENDER:
1307 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1308 FALSE, FALSE, TRUE, body);
1310 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1311 compose = compose_followup_and_reply_to(msginfo,
1312 COMPOSE_QUOTE_CHECK,
1313 FALSE, FALSE, body);
1315 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1316 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1317 FALSE, FALSE, TRUE, body);
1319 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1320 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1321 FALSE, FALSE, TRUE, NULL);
1323 case COMPOSE_REPLY_TO_ALL:
1324 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1325 TRUE, FALSE, FALSE, body);
1327 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1328 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1329 TRUE, FALSE, FALSE, body);
1331 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1333 TRUE, FALSE, FALSE, NULL);
1335 case COMPOSE_REPLY_TO_LIST:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1337 FALSE, TRUE, FALSE, body);
1339 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1341 FALSE, TRUE, FALSE, body);
1343 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1345 FALSE, TRUE, FALSE, NULL);
1347 case COMPOSE_FORWARD:
1348 if (prefs_common.forward_as_attachment) {
1349 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1352 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1356 case COMPOSE_FORWARD_INLINE:
1357 /* check if we reply to more than one Message */
1358 if (list_len == 1) {
1359 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1362 /* more messages FALL THROUGH */
1363 case COMPOSE_FORWARD_AS_ATTACH:
1364 compose = compose_forward_multiple(NULL, msginfo_list);
1366 case COMPOSE_REDIRECT:
1367 compose = compose_redirect(NULL, msginfo, FALSE);
1370 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1373 if (compose == NULL) {
1374 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1378 compose->rmode = mode;
1379 switch (compose->rmode) {
1381 case COMPOSE_REPLY_WITH_QUOTE:
1382 case COMPOSE_REPLY_WITHOUT_QUOTE:
1383 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1384 debug_print("reply mode Normal\n");
1385 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1386 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1388 case COMPOSE_REPLY_TO_SENDER:
1389 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1390 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1391 debug_print("reply mode Sender\n");
1392 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1394 case COMPOSE_REPLY_TO_ALL:
1395 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1396 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1397 debug_print("reply mode All\n");
1398 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1400 case COMPOSE_REPLY_TO_LIST:
1401 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1402 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1403 debug_print("reply mode List\n");
1404 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1412 static Compose *compose_reply(MsgInfo *msginfo,
1413 ComposeQuoteMode quote_mode,
1419 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1420 to_sender, FALSE, body);
1423 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1424 ComposeQuoteMode quote_mode,
1429 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1430 to_sender, TRUE, body);
1433 static void compose_extract_original_charset(Compose *compose)
1435 MsgInfo *info = NULL;
1436 if (compose->replyinfo) {
1437 info = compose->replyinfo;
1438 } else if (compose->fwdinfo) {
1439 info = compose->fwdinfo;
1440 } else if (compose->targetinfo) {
1441 info = compose->targetinfo;
1444 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1445 MimeInfo *partinfo = mimeinfo;
1446 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1447 partinfo = procmime_mimeinfo_next(partinfo);
1449 compose->orig_charset =
1450 g_strdup(procmime_mimeinfo_get_parameter(
1451 partinfo, "charset"));
1453 procmime_mimeinfo_free_all(mimeinfo);
1457 #define SIGNAL_BLOCK(buffer) { \
1458 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1459 G_CALLBACK(compose_changed_cb), \
1461 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1462 G_CALLBACK(text_inserted), \
1466 #define SIGNAL_UNBLOCK(buffer) { \
1467 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1468 G_CALLBACK(compose_changed_cb), \
1470 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1471 G_CALLBACK(text_inserted), \
1475 static Compose *compose_generic_reply(MsgInfo *msginfo,
1476 ComposeQuoteMode quote_mode,
1477 gboolean to_all, gboolean to_ml,
1479 gboolean followup_and_reply_to,
1483 PrefsAccount *account = NULL;
1484 GtkTextView *textview;
1485 GtkTextBuffer *textbuf;
1486 gboolean quote = FALSE;
1487 const gchar *qmark = NULL;
1488 const gchar *body_fmt = NULL;
1489 gchar *s_system = NULL;
1491 cm_return_val_if_fail(msginfo != NULL, NULL);
1492 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1494 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1496 cm_return_val_if_fail(account != NULL, NULL);
1498 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1500 compose->updating = TRUE;
1502 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1503 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1505 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1506 if (!compose->replyinfo)
1507 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1509 compose_extract_original_charset(compose);
1511 if (msginfo->folder && msginfo->folder->ret_rcpt)
1512 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1514 /* Set save folder */
1515 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1516 gchar *folderidentifier;
1518 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1519 folderidentifier = folder_item_get_identifier(msginfo->folder);
1520 compose_set_save_to(compose, folderidentifier);
1521 g_free(folderidentifier);
1524 if (compose_parse_header(compose, msginfo) < 0) {
1525 compose->updating = FALSE;
1526 compose_destroy(compose);
1530 /* override from name according to folder properties */
1531 if (msginfo->folder && msginfo->folder->prefs &&
1532 msginfo->folder->prefs->reply_with_format &&
1533 msginfo->folder->prefs->reply_override_from_format &&
1534 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1539 /* decode \-escape sequences in the internal representation of the quote format */
1540 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1541 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1544 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1545 compose->gtkaspell);
1547 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1549 quote_fmt_scan_string(tmp);
1552 buf = quote_fmt_get_buffer();
1554 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1556 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1557 quote_fmt_reset_vartable();
1562 textview = (GTK_TEXT_VIEW(compose->text));
1563 textbuf = gtk_text_view_get_buffer(textview);
1564 compose_create_tags(textview, compose);
1566 undo_block(compose->undostruct);
1568 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1571 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1572 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1573 /* use the reply format of folder (if enabled), or the account's one
1574 (if enabled) or fallback to the global reply format, which is always
1575 enabled (even if empty), and use the relevant quotemark */
1577 if (msginfo->folder && msginfo->folder->prefs &&
1578 msginfo->folder->prefs->reply_with_format) {
1579 qmark = msginfo->folder->prefs->reply_quotemark;
1580 body_fmt = msginfo->folder->prefs->reply_body_format;
1582 } else if (account->reply_with_format) {
1583 qmark = account->reply_quotemark;
1584 body_fmt = account->reply_body_format;
1587 qmark = prefs_common.quotemark;
1588 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1589 body_fmt = gettext(prefs_common.quotefmt);
1596 /* empty quotemark is not allowed */
1597 if (qmark == NULL || *qmark == '\0')
1599 compose_quote_fmt(compose, compose->replyinfo,
1600 body_fmt, qmark, body, FALSE, TRUE,
1601 _("The body of the \"Reply\" template has an error at line %d."));
1602 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1603 quote_fmt_reset_vartable();
1606 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1607 compose_force_encryption(compose, account, FALSE, s_system);
1610 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1611 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1612 compose_force_signing(compose, account, s_system);
1616 SIGNAL_BLOCK(textbuf);
1618 if (account->auto_sig)
1619 compose_insert_sig(compose, FALSE);
1621 compose_wrap_all(compose);
1623 SIGNAL_UNBLOCK(textbuf);
1625 gtk_widget_grab_focus(compose->text);
1627 undo_unblock(compose->undostruct);
1629 if (prefs_common.auto_exteditor)
1630 compose_exec_ext_editor(compose);
1632 compose->modified = FALSE;
1633 compose_set_title(compose);
1635 compose->updating = FALSE;
1636 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1637 SCROLL_TO_CURSOR(compose);
1639 if (compose->deferred_destroy) {
1640 compose_destroy(compose);
1647 #define INSERT_FW_HEADER(var, hdr) \
1648 if (msginfo->var && *msginfo->var) { \
1649 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1650 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1651 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1654 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1655 gboolean as_attach, const gchar *body,
1656 gboolean no_extedit,
1660 GtkTextView *textview;
1661 GtkTextBuffer *textbuf;
1664 cm_return_val_if_fail(msginfo != NULL, NULL);
1665 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1668 !(account = compose_guess_forward_account_from_msginfo
1670 account = cur_account;
1672 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1674 compose->updating = TRUE;
1675 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1676 if (!compose->fwdinfo)
1677 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1679 compose_extract_original_charset(compose);
1681 if (msginfo->subject && *msginfo->subject) {
1682 gchar *buf, *buf2, *p;
1684 buf = p = g_strdup(msginfo->subject);
1685 p += subject_get_prefix_length(p);
1686 memmove(buf, p, strlen(p) + 1);
1688 buf2 = g_strdup_printf("Fw: %s", buf);
1689 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1695 /* override from name according to folder properties */
1696 if (msginfo->folder && msginfo->folder->prefs &&
1697 msginfo->folder->prefs->forward_with_format &&
1698 msginfo->folder->prefs->forward_override_from_format &&
1699 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1703 MsgInfo *full_msginfo = NULL;
1706 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1708 full_msginfo = procmsg_msginfo_copy(msginfo);
1710 /* decode \-escape sequences in the internal representation of the quote format */
1711 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1712 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1715 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1716 compose->gtkaspell);
1718 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1720 quote_fmt_scan_string(tmp);
1723 buf = quote_fmt_get_buffer();
1725 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1727 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1728 quote_fmt_reset_vartable();
1731 procmsg_msginfo_free(full_msginfo);
1734 textview = GTK_TEXT_VIEW(compose->text);
1735 textbuf = gtk_text_view_get_buffer(textview);
1736 compose_create_tags(textview, compose);
1738 undo_block(compose->undostruct);
1742 msgfile = procmsg_get_message_file(msginfo);
1743 if (!is_file_exist(msgfile))
1744 g_warning("%s: file not exist\n", msgfile);
1746 compose_attach_append(compose, msgfile, msgfile,
1751 const gchar *qmark = NULL;
1752 const gchar *body_fmt = NULL;
1753 MsgInfo *full_msginfo;
1755 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1756 body_fmt = gettext(prefs_common.fw_quotefmt);
1760 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1762 full_msginfo = procmsg_msginfo_copy(msginfo);
1764 /* use the forward format of folder (if enabled), or the account's one
1765 (if enabled) or fallback to the global forward format, which is always
1766 enabled (even if empty), and use the relevant quotemark */
1767 if (msginfo->folder && msginfo->folder->prefs &&
1768 msginfo->folder->prefs->forward_with_format) {
1769 qmark = msginfo->folder->prefs->forward_quotemark;
1770 body_fmt = msginfo->folder->prefs->forward_body_format;
1772 } else if (account->forward_with_format) {
1773 qmark = account->forward_quotemark;
1774 body_fmt = account->forward_body_format;
1777 qmark = prefs_common.fw_quotemark;
1778 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1779 body_fmt = gettext(prefs_common.fw_quotefmt);
1784 /* empty quotemark is not allowed */
1785 if (qmark == NULL || *qmark == '\0')
1788 compose_quote_fmt(compose, full_msginfo,
1789 body_fmt, qmark, body, FALSE, TRUE,
1790 _("The body of the \"Forward\" template has an error at line %d."));
1791 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1792 quote_fmt_reset_vartable();
1793 compose_attach_parts(compose, msginfo);
1795 procmsg_msginfo_free(full_msginfo);
1798 SIGNAL_BLOCK(textbuf);
1800 if (account->auto_sig)
1801 compose_insert_sig(compose, FALSE);
1803 compose_wrap_all(compose);
1805 SIGNAL_UNBLOCK(textbuf);
1807 gtk_text_buffer_get_start_iter(textbuf, &iter);
1808 gtk_text_buffer_place_cursor(textbuf, &iter);
1810 gtk_widget_grab_focus(compose->header_last->entry);
1812 if (!no_extedit && prefs_common.auto_exteditor)
1813 compose_exec_ext_editor(compose);
1816 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1817 gchar *folderidentifier;
1819 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1820 folderidentifier = folder_item_get_identifier(msginfo->folder);
1821 compose_set_save_to(compose, folderidentifier);
1822 g_free(folderidentifier);
1825 undo_unblock(compose->undostruct);
1827 compose->modified = FALSE;
1828 compose_set_title(compose);
1830 compose->updating = FALSE;
1831 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1832 SCROLL_TO_CURSOR(compose);
1834 if (compose->deferred_destroy) {
1835 compose_destroy(compose);
1842 #undef INSERT_FW_HEADER
1844 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1847 GtkTextView *textview;
1848 GtkTextBuffer *textbuf;
1852 gboolean single_mail = TRUE;
1854 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1856 if (g_slist_length(msginfo_list) > 1)
1857 single_mail = FALSE;
1859 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1860 if (((MsgInfo *)msginfo->data)->folder == NULL)
1863 /* guess account from first selected message */
1865 !(account = compose_guess_forward_account_from_msginfo
1866 (msginfo_list->data)))
1867 account = cur_account;
1869 cm_return_val_if_fail(account != NULL, NULL);
1871 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1872 if (msginfo->data) {
1873 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1874 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1878 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1879 g_warning("no msginfo_list");
1883 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1885 compose->updating = TRUE;
1887 /* override from name according to folder properties */
1888 if (msginfo_list->data) {
1889 MsgInfo *msginfo = msginfo_list->data;
1891 if (msginfo->folder && msginfo->folder->prefs &&
1892 msginfo->folder->prefs->forward_with_format &&
1893 msginfo->folder->prefs->forward_override_from_format &&
1894 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1899 /* decode \-escape sequences in the internal representation of the quote format */
1900 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1901 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1904 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1905 compose->gtkaspell);
1907 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1909 quote_fmt_scan_string(tmp);
1912 buf = quote_fmt_get_buffer();
1914 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1916 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1917 quote_fmt_reset_vartable();
1923 textview = GTK_TEXT_VIEW(compose->text);
1924 textbuf = gtk_text_view_get_buffer(textview);
1925 compose_create_tags(textview, compose);
1927 undo_block(compose->undostruct);
1928 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1929 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1931 if (!is_file_exist(msgfile))
1932 g_warning("%s: file not exist\n", msgfile);
1934 compose_attach_append(compose, msgfile, msgfile,
1940 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1941 if (info->subject && *info->subject) {
1942 gchar *buf, *buf2, *p;
1944 buf = p = g_strdup(info->subject);
1945 p += subject_get_prefix_length(p);
1946 memmove(buf, p, strlen(p) + 1);
1948 buf2 = g_strdup_printf("Fw: %s", buf);
1949 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1955 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1956 _("Fw: multiple emails"));
1959 SIGNAL_BLOCK(textbuf);
1961 if (account->auto_sig)
1962 compose_insert_sig(compose, FALSE);
1964 compose_wrap_all(compose);
1966 SIGNAL_UNBLOCK(textbuf);
1968 gtk_text_buffer_get_start_iter(textbuf, &iter);
1969 gtk_text_buffer_place_cursor(textbuf, &iter);
1971 gtk_widget_grab_focus(compose->header_last->entry);
1972 undo_unblock(compose->undostruct);
1973 compose->modified = FALSE;
1974 compose_set_title(compose);
1976 compose->updating = FALSE;
1977 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1978 SCROLL_TO_CURSOR(compose);
1980 if (compose->deferred_destroy) {
1981 compose_destroy(compose);
1988 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1990 GtkTextIter start = *iter;
1991 GtkTextIter end_iter;
1992 int start_pos = gtk_text_iter_get_offset(&start);
1994 if (!compose->account->sig_sep)
1997 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1998 start_pos+strlen(compose->account->sig_sep));
2000 /* check sig separator */
2001 str = gtk_text_iter_get_text(&start, &end_iter);
2002 if (!strcmp(str, compose->account->sig_sep)) {
2004 /* check end of line (\n) */
2005 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2006 start_pos+strlen(compose->account->sig_sep));
2007 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2008 start_pos+strlen(compose->account->sig_sep)+1);
2009 tmp = gtk_text_iter_get_text(&start, &end_iter);
2010 if (!strcmp(tmp,"\n")) {
2022 static void compose_colorize_signature(Compose *compose)
2024 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2026 GtkTextIter end_iter;
2027 gtk_text_buffer_get_start_iter(buffer, &iter);
2028 while (gtk_text_iter_forward_line(&iter))
2029 if (compose_is_sig_separator(compose, buffer, &iter)) {
2030 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2031 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2035 #define BLOCK_WRAP() { \
2036 prev_autowrap = compose->autowrap; \
2037 buffer = gtk_text_view_get_buffer( \
2038 GTK_TEXT_VIEW(compose->text)); \
2039 compose->autowrap = FALSE; \
2041 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2042 G_CALLBACK(compose_changed_cb), \
2044 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2045 G_CALLBACK(text_inserted), \
2048 #define UNBLOCK_WRAP() { \
2049 compose->autowrap = prev_autowrap; \
2050 if (compose->autowrap) { \
2051 gint old = compose->draft_timeout_tag; \
2052 compose->draft_timeout_tag = -2; \
2053 compose_wrap_all(compose); \
2054 compose->draft_timeout_tag = old; \
2057 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2058 G_CALLBACK(compose_changed_cb), \
2060 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2061 G_CALLBACK(text_inserted), \
2065 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2067 Compose *compose = NULL;
2068 PrefsAccount *account = NULL;
2069 GtkTextView *textview;
2070 GtkTextBuffer *textbuf;
2074 gchar buf[BUFFSIZE];
2075 gboolean use_signing = FALSE;
2076 gboolean use_encryption = FALSE;
2077 gchar *privacy_system = NULL;
2078 int priority = PRIORITY_NORMAL;
2079 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2080 gboolean autowrap = prefs_common.autowrap;
2081 gboolean autoindent = prefs_common.auto_indent;
2083 cm_return_val_if_fail(msginfo != NULL, NULL);
2084 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2086 if (compose_put_existing_to_front(msginfo)) {
2090 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2091 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2092 gchar queueheader_buf[BUFFSIZE];
2095 /* Select Account from queue headers */
2096 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2097 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2098 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2099 account = account_find_from_id(id);
2101 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2102 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2103 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2104 account = account_find_from_id(id);
2106 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2107 sizeof(queueheader_buf), "NAID:")) {
2108 id = atoi(&queueheader_buf[strlen("NAID:")]);
2109 account = account_find_from_id(id);
2111 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2112 sizeof(queueheader_buf), "MAID:")) {
2113 id = atoi(&queueheader_buf[strlen("MAID:")]);
2114 account = account_find_from_id(id);
2116 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2117 sizeof(queueheader_buf), "S:")) {
2118 account = account_find_from_address(queueheader_buf, FALSE);
2120 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2121 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2122 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2123 use_signing = param;
2126 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2127 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2128 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2129 use_signing = param;
2132 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2133 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2134 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2135 use_encryption = param;
2137 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2138 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2139 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2140 use_encryption = param;
2142 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2143 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2144 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2147 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2148 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2149 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2152 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2153 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2154 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2156 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2157 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2158 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2160 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2161 sizeof(queueheader_buf), "X-Priority: ")) {
2162 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2165 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2166 sizeof(queueheader_buf), "RMID:")) {
2167 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2168 if (tokens[0] && tokens[1] && tokens[2]) {
2169 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2170 if (orig_item != NULL) {
2171 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2176 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2177 sizeof(queueheader_buf), "FMID:")) {
2178 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2179 if (tokens[0] && tokens[1] && tokens[2]) {
2180 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2181 if (orig_item != NULL) {
2182 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2188 account = msginfo->folder->folder->account;
2191 if (!account && prefs_common.reedit_account_autosel) {
2192 gchar from[BUFFSIZE];
2193 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2194 extract_address(from);
2195 account = account_find_from_address(from, FALSE);
2199 account = cur_account;
2201 cm_return_val_if_fail(account != NULL, NULL);
2203 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2205 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2206 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2207 compose->autowrap = autowrap;
2208 compose->replyinfo = replyinfo;
2209 compose->fwdinfo = fwdinfo;
2211 compose->updating = TRUE;
2212 compose->priority = priority;
2214 if (privacy_system != NULL) {
2215 compose->privacy_system = privacy_system;
2216 compose_use_signing(compose, use_signing);
2217 compose_use_encryption(compose, use_encryption);
2218 compose_update_privacy_system_menu_item(compose, FALSE);
2220 activate_privacy_system(compose, account, FALSE);
2223 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2225 compose_extract_original_charset(compose);
2227 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2228 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2229 gchar queueheader_buf[BUFFSIZE];
2231 /* Set message save folder */
2232 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2233 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2234 compose_set_save_to(compose, &queueheader_buf[4]);
2236 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2237 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2239 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2244 if (compose_parse_header(compose, msginfo) < 0) {
2245 compose->updating = FALSE;
2246 compose_destroy(compose);
2249 compose_reedit_set_entry(compose, msginfo);
2251 textview = GTK_TEXT_VIEW(compose->text);
2252 textbuf = gtk_text_view_get_buffer(textview);
2253 compose_create_tags(textview, compose);
2255 mark = gtk_text_buffer_get_insert(textbuf);
2256 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2258 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2259 G_CALLBACK(compose_changed_cb),
2262 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2263 fp = procmime_get_first_encrypted_text_content(msginfo);
2265 compose_force_encryption(compose, account, TRUE, NULL);
2268 fp = procmime_get_first_text_content(msginfo);
2271 g_warning("Can't get text part\n");
2275 gboolean prev_autowrap = compose->autowrap;
2276 GtkTextBuffer *buffer = textbuf;
2278 while (fgets(buf, sizeof(buf), fp) != NULL) {
2280 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2286 compose_attach_parts(compose, msginfo);
2288 compose_colorize_signature(compose);
2290 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2291 G_CALLBACK(compose_changed_cb),
2294 gtk_widget_grab_focus(compose->text);
2296 if (prefs_common.auto_exteditor) {
2297 compose_exec_ext_editor(compose);
2299 compose->modified = FALSE;
2300 compose_set_title(compose);
2302 compose->updating = FALSE;
2303 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2304 SCROLL_TO_CURSOR(compose);
2306 if (compose->deferred_destroy) {
2307 compose_destroy(compose);
2311 compose->sig_str = account_get_signature_str(compose->account);
2316 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2323 cm_return_val_if_fail(msginfo != NULL, NULL);
2326 account = account_get_reply_account(msginfo,
2327 prefs_common.reply_account_autosel);
2328 cm_return_val_if_fail(account != NULL, NULL);
2330 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2332 compose->updating = TRUE;
2334 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2335 compose->replyinfo = NULL;
2336 compose->fwdinfo = NULL;
2338 compose_show_first_last_header(compose, TRUE);
2340 gtk_widget_grab_focus(compose->header_last->entry);
2342 filename = procmsg_get_message_file(msginfo);
2344 if (filename == NULL) {
2345 compose->updating = FALSE;
2346 compose_destroy(compose);
2351 compose->redirect_filename = filename;
2353 /* Set save folder */
2354 item = msginfo->folder;
2355 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2356 gchar *folderidentifier;
2358 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2359 folderidentifier = folder_item_get_identifier(item);
2360 compose_set_save_to(compose, folderidentifier);
2361 g_free(folderidentifier);
2364 compose_attach_parts(compose, msginfo);
2366 if (msginfo->subject)
2367 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2369 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2371 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2372 _("The body of the \"Redirect\" template has an error at line %d."));
2373 quote_fmt_reset_vartable();
2374 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2376 compose_colorize_signature(compose);
2379 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2380 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2381 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2383 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2384 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2385 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2386 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2387 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2388 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2389 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2390 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2392 if (compose->toolbar->draft_btn)
2393 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2394 if (compose->toolbar->insert_btn)
2395 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2396 if (compose->toolbar->attach_btn)
2397 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2398 if (compose->toolbar->sig_btn)
2399 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2400 if (compose->toolbar->exteditor_btn)
2401 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2402 if (compose->toolbar->linewrap_current_btn)
2403 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2404 if (compose->toolbar->linewrap_all_btn)
2405 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2407 compose->modified = FALSE;
2408 compose_set_title(compose);
2409 compose->updating = FALSE;
2410 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2411 SCROLL_TO_CURSOR(compose);
2413 if (compose->deferred_destroy) {
2414 compose_destroy(compose);
2421 GList *compose_get_compose_list(void)
2423 return compose_list;
2426 void compose_entry_append(Compose *compose, const gchar *address,
2427 ComposeEntryType type, ComposePrefType pref_type)
2429 const gchar *header;
2431 gboolean in_quote = FALSE;
2432 if (!address || *address == '\0') return;
2439 header = N_("Bcc:");
2441 case COMPOSE_REPLYTO:
2442 header = N_("Reply-To:");
2444 case COMPOSE_NEWSGROUPS:
2445 header = N_("Newsgroups:");
2447 case COMPOSE_FOLLOWUPTO:
2448 header = N_( "Followup-To:");
2455 header = prefs_common_translated_header_name(header);
2457 cur = begin = (gchar *)address;
2459 /* we separate the line by commas, but not if we're inside a quoted
2461 while (*cur != '\0') {
2463 in_quote = !in_quote;
2464 if (*cur == ',' && !in_quote) {
2465 gchar *tmp = g_strdup(begin);
2467 tmp[cur-begin]='\0';
2470 while (*tmp == ' ' || *tmp == '\t')
2472 compose_add_header_entry(compose, header, tmp, pref_type);
2479 gchar *tmp = g_strdup(begin);
2481 tmp[cur-begin]='\0';
2484 while (*tmp == ' ' || *tmp == '\t')
2486 compose_add_header_entry(compose, header, tmp, pref_type);
2491 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2493 static GdkColor yellow;
2494 static GdkColor black;
2495 static gboolean yellow_initialised = FALSE;
2499 if (!yellow_initialised) {
2500 gdk_color_parse("#f5f6be", &yellow);
2501 gdk_color_parse("#000000", &black);
2502 yellow_initialised = gdk_colormap_alloc_color(
2503 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2504 yellow_initialised &= gdk_colormap_alloc_color(
2505 gdk_colormap_get_system(), &black, FALSE, TRUE);
2508 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2509 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2510 if (gtk_entry_get_text(entry) &&
2511 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2512 if (yellow_initialised) {
2513 gtk_widget_modify_base(
2514 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2515 GTK_STATE_NORMAL, &yellow);
2516 gtk_widget_modify_text(
2517 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2518 GTK_STATE_NORMAL, &black);
2524 void compose_toolbar_cb(gint action, gpointer data)
2526 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2527 Compose *compose = (Compose*)toolbar_item->parent;
2529 cm_return_if_fail(compose != NULL);
2533 compose_send_cb(NULL, compose);
2536 compose_send_later_cb(NULL, compose);
2539 compose_draft(compose, COMPOSE_QUIT_EDITING);
2542 compose_insert_file_cb(NULL, compose);
2545 compose_attach_cb(NULL, compose);
2548 compose_insert_sig(compose, FALSE);
2551 compose_ext_editor_cb(NULL, compose);
2553 case A_LINEWRAP_CURRENT:
2554 compose_beautify_paragraph(compose, NULL, TRUE);
2556 case A_LINEWRAP_ALL:
2557 compose_wrap_all_full(compose, TRUE);
2560 compose_address_cb(NULL, compose);
2563 case A_CHECK_SPELLING:
2564 compose_check_all(NULL, compose);
2572 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2577 gchar *subject = NULL;
2581 gchar **attach = NULL;
2582 MailField mfield = NO_FIELD_PRESENT;
2584 /* get mailto parts but skip from */
2585 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2588 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2589 mfield = TO_FIELD_PRESENT;
2592 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2594 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2596 if (!g_utf8_validate (subject, -1, NULL)) {
2597 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2598 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2601 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2603 mfield = SUBJECT_FIELD_PRESENT;
2606 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2607 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2610 gboolean prev_autowrap = compose->autowrap;
2612 compose->autowrap = FALSE;
2614 mark = gtk_text_buffer_get_insert(buffer);
2615 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2617 if (!g_utf8_validate (body, -1, NULL)) {
2618 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2619 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2622 gtk_text_buffer_insert(buffer, &iter, body, -1);
2624 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2626 compose->autowrap = prev_autowrap;
2627 if (compose->autowrap)
2628 compose_wrap_all(compose);
2629 mfield = BODY_FIELD_PRESENT;
2633 gint i = 0, att = 0;
2634 gchar *warn_files = NULL;
2635 while (attach[i] != NULL) {
2636 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2637 if (utf8_filename) {
2638 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2639 gchar *tmp = g_strdup_printf("%s%s\n",
2640 warn_files?warn_files:"",
2646 g_free(utf8_filename);
2648 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2653 alertpanel_notice(ngettext(
2654 "The following file has been attached: \n%s",
2655 "The following files have been attached: \n%s", att), warn_files);
2669 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2671 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2672 {"Cc:", NULL, TRUE},
2673 {"References:", NULL, FALSE},
2674 {"Bcc:", NULL, TRUE},
2675 {"Newsgroups:", NULL, TRUE},
2676 {"Followup-To:", NULL, TRUE},
2677 {"List-Post:", NULL, FALSE},
2678 {"X-Priority:", NULL, FALSE},
2679 {NULL, NULL, FALSE}};
2695 cm_return_val_if_fail(msginfo != NULL, -1);
2697 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2698 procheader_get_header_fields(fp, hentry);
2701 if (hentry[H_REPLY_TO].body != NULL) {
2702 if (hentry[H_REPLY_TO].body[0] != '\0') {
2704 conv_unmime_header(hentry[H_REPLY_TO].body,
2707 g_free(hentry[H_REPLY_TO].body);
2708 hentry[H_REPLY_TO].body = NULL;
2710 if (hentry[H_CC].body != NULL) {
2711 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2712 g_free(hentry[H_CC].body);
2713 hentry[H_CC].body = NULL;
2715 if (hentry[H_REFERENCES].body != NULL) {
2716 if (compose->mode == COMPOSE_REEDIT)
2717 compose->references = hentry[H_REFERENCES].body;
2719 compose->references = compose_parse_references
2720 (hentry[H_REFERENCES].body, msginfo->msgid);
2721 g_free(hentry[H_REFERENCES].body);
2723 hentry[H_REFERENCES].body = NULL;
2725 if (hentry[H_BCC].body != NULL) {
2726 if (compose->mode == COMPOSE_REEDIT)
2728 conv_unmime_header(hentry[H_BCC].body, NULL);
2729 g_free(hentry[H_BCC].body);
2730 hentry[H_BCC].body = NULL;
2732 if (hentry[H_NEWSGROUPS].body != NULL) {
2733 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2734 hentry[H_NEWSGROUPS].body = NULL;
2736 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2737 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2738 compose->followup_to =
2739 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2742 g_free(hentry[H_FOLLOWUP_TO].body);
2743 hentry[H_FOLLOWUP_TO].body = NULL;
2745 if (hentry[H_LIST_POST].body != NULL) {
2748 extract_address(hentry[H_LIST_POST].body);
2749 if (hentry[H_LIST_POST].body[0] != '\0') {
2750 scan_mailto_url(hentry[H_LIST_POST].body,
2751 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2753 g_free(compose->ml_post);
2754 compose->ml_post = to;
2757 g_free(hentry[H_LIST_POST].body);
2758 hentry[H_LIST_POST].body = NULL;
2761 /* CLAWS - X-Priority */
2762 if (compose->mode == COMPOSE_REEDIT)
2763 if (hentry[H_X_PRIORITY].body != NULL) {
2766 priority = atoi(hentry[H_X_PRIORITY].body);
2767 g_free(hentry[H_X_PRIORITY].body);
2769 hentry[H_X_PRIORITY].body = NULL;
2771 if (priority < PRIORITY_HIGHEST ||
2772 priority > PRIORITY_LOWEST)
2773 priority = PRIORITY_NORMAL;
2775 compose->priority = priority;
2778 if (compose->mode == COMPOSE_REEDIT) {
2779 if (msginfo->inreplyto && *msginfo->inreplyto)
2780 compose->inreplyto = g_strdup(msginfo->inreplyto);
2784 if (msginfo->msgid && *msginfo->msgid)
2785 compose->inreplyto = g_strdup(msginfo->msgid);
2787 if (!compose->references) {
2788 if (msginfo->msgid && *msginfo->msgid) {
2789 if (msginfo->inreplyto && *msginfo->inreplyto)
2790 compose->references =
2791 g_strdup_printf("<%s>\n\t<%s>",
2795 compose->references =
2796 g_strconcat("<", msginfo->msgid, ">",
2798 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2799 compose->references =
2800 g_strconcat("<", msginfo->inreplyto, ">",
2808 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2810 GSList *ref_id_list, *cur;
2814 ref_id_list = references_list_append(NULL, ref);
2815 if (!ref_id_list) return NULL;
2816 if (msgid && *msgid)
2817 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2822 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2823 /* "<" + Message-ID + ">" + CR+LF+TAB */
2824 len += strlen((gchar *)cur->data) + 5;
2826 if (len > MAX_REFERENCES_LEN) {
2827 /* remove second message-ID */
2828 if (ref_id_list && ref_id_list->next &&
2829 ref_id_list->next->next) {
2830 g_free(ref_id_list->next->data);
2831 ref_id_list = g_slist_remove
2832 (ref_id_list, ref_id_list->next->data);
2834 slist_free_strings(ref_id_list);
2835 g_slist_free(ref_id_list);
2842 new_ref = g_string_new("");
2843 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2844 if (new_ref->len > 0)
2845 g_string_append(new_ref, "\n\t");
2846 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2849 slist_free_strings(ref_id_list);
2850 g_slist_free(ref_id_list);
2852 new_ref_str = new_ref->str;
2853 g_string_free(new_ref, FALSE);
2858 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2859 const gchar *fmt, const gchar *qmark,
2860 const gchar *body, gboolean rewrap,
2861 gboolean need_unescape,
2862 const gchar *err_msg)
2864 MsgInfo* dummyinfo = NULL;
2865 gchar *quote_str = NULL;
2867 gboolean prev_autowrap;
2868 const gchar *trimmed_body = body;
2869 gint cursor_pos = -1;
2870 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2871 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2876 SIGNAL_BLOCK(buffer);
2879 dummyinfo = compose_msginfo_new_from_compose(compose);
2880 msginfo = dummyinfo;
2883 if (qmark != NULL) {
2885 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2886 compose->gtkaspell);
2888 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2890 quote_fmt_scan_string(qmark);
2893 buf = quote_fmt_get_buffer();
2895 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2897 Xstrdup_a(quote_str, buf, goto error)
2900 if (fmt && *fmt != '\0') {
2903 while (*trimmed_body == '\n')
2907 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2908 compose->gtkaspell);
2910 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2912 if (need_unescape) {
2915 /* decode \-escape sequences in the internal representation of the quote format */
2916 tmp = malloc(strlen(fmt)+1);
2917 pref_get_unescaped_pref(tmp, fmt);
2918 quote_fmt_scan_string(tmp);
2922 quote_fmt_scan_string(fmt);
2926 buf = quote_fmt_get_buffer();
2928 gint line = quote_fmt_get_line();
2929 alertpanel_error(err_msg, line);
2935 prev_autowrap = compose->autowrap;
2936 compose->autowrap = FALSE;
2938 mark = gtk_text_buffer_get_insert(buffer);
2939 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2940 if (g_utf8_validate(buf, -1, NULL)) {
2941 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2943 gchar *tmpout = NULL;
2944 tmpout = conv_codeset_strdup
2945 (buf, conv_get_locale_charset_str_no_utf8(),
2947 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2949 tmpout = g_malloc(strlen(buf)*2+1);
2950 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2952 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2956 cursor_pos = quote_fmt_get_cursor_pos();
2957 if (cursor_pos == -1)
2958 cursor_pos = gtk_text_iter_get_offset(&iter);
2959 compose->set_cursor_pos = cursor_pos;
2961 gtk_text_buffer_get_start_iter(buffer, &iter);
2962 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2963 gtk_text_buffer_place_cursor(buffer, &iter);
2965 compose->autowrap = prev_autowrap;
2966 if (compose->autowrap && rewrap)
2967 compose_wrap_all(compose);
2974 SIGNAL_UNBLOCK(buffer);
2976 procmsg_msginfo_free( dummyinfo );
2981 /* if ml_post is of type addr@host and from is of type
2982 * addr-anything@host, return TRUE
2984 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2986 gchar *left_ml = NULL;
2987 gchar *right_ml = NULL;
2988 gchar *left_from = NULL;
2989 gchar *right_from = NULL;
2990 gboolean result = FALSE;
2992 if (!ml_post || !from)
2995 left_ml = g_strdup(ml_post);
2996 if (strstr(left_ml, "@")) {
2997 right_ml = strstr(left_ml, "@")+1;
2998 *(strstr(left_ml, "@")) = '\0';
3001 left_from = g_strdup(from);
3002 if (strstr(left_from, "@")) {
3003 right_from = strstr(left_from, "@")+1;
3004 *(strstr(left_from, "@")) = '\0';
3007 if (left_ml && left_from && right_ml && right_from
3008 && !strncmp(left_from, left_ml, strlen(left_ml))
3009 && !strcmp(right_from, right_ml)) {
3018 static gboolean same_address(const gchar *addr1, const gchar *addr2)
3020 gchar *my_addr1, *my_addr2;
3022 if (!addr1 || !addr2)
3025 Xstrdup_a(my_addr1, addr1, return FALSE);
3026 Xstrdup_a(my_addr2, addr2, return FALSE);
3028 extract_address(my_addr1);
3029 extract_address(my_addr2);
3031 return !strcasecmp(my_addr1, my_addr2);
3034 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3035 gboolean to_all, gboolean to_ml,
3037 gboolean followup_and_reply_to)
3039 GSList *cc_list = NULL;
3042 gchar *replyto = NULL;
3043 GHashTable *to_table;
3045 gboolean reply_to_ml = FALSE;
3046 gboolean default_reply_to = FALSE;
3048 cm_return_if_fail(compose->account != NULL);
3049 cm_return_if_fail(msginfo != NULL);
3051 reply_to_ml = to_ml && compose->ml_post;
3053 default_reply_to = msginfo->folder &&
3054 msginfo->folder->prefs->enable_default_reply_to;
3056 if (compose->account->protocol != A_NNTP) {
3057 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3058 if (msginfo->folder->prefs->enable_default_replyto) {
3059 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3060 COMPOSE_REPLYTO, PREF_FOLDER);
3062 if (msginfo->folder->prefs->enable_default_bcc) {
3063 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3064 COMPOSE_BCC, PREF_FOLDER);
3066 if (msginfo->folder->prefs->enable_default_cc) {
3067 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3068 COMPOSE_CC, PREF_FOLDER);
3071 if (reply_to_ml && !default_reply_to) {
3073 gboolean is_subscr = is_subscription(compose->ml_post,
3076 /* normal answer to ml post with a reply-to */
3077 compose_entry_append(compose,
3079 COMPOSE_TO, PREF_ML);
3080 if (compose->replyto
3081 && !same_address(compose->ml_post, compose->replyto))
3082 compose_entry_append(compose,
3084 COMPOSE_CC, PREF_ML);
3086 /* answer to subscription confirmation */
3087 if (compose->replyto)
3088 compose_entry_append(compose,
3090 COMPOSE_TO, PREF_ML);
3091 else if (msginfo->from)
3092 compose_entry_append(compose,
3094 COMPOSE_TO, PREF_ML);
3097 else if (!(to_all || to_sender) && default_reply_to) {
3098 compose_entry_append(compose,
3099 msginfo->folder->prefs->default_reply_to,
3100 COMPOSE_TO, PREF_FOLDER);
3101 compose_entry_mark_default_to(compose,
3102 msginfo->folder->prefs->default_reply_to);
3107 Xstrdup_a(tmp1, msginfo->from, return);
3108 extract_address(tmp1);
3109 if (to_all || to_sender ||
3110 !account_find_from_address(tmp1, FALSE))
3111 compose_entry_append(compose,
3112 (compose->replyto && !to_sender)
3113 ? compose->replyto :
3114 msginfo->from ? msginfo->from : "",
3115 COMPOSE_TO, PREF_NONE);
3116 else if (!to_all && !to_sender) {
3117 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3118 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3119 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3120 if (compose->replyto) {
3121 compose_entry_append(compose,
3123 COMPOSE_TO, PREF_NONE);
3125 compose_entry_append(compose,
3126 msginfo->from ? msginfo->from : "",
3127 COMPOSE_TO, PREF_NONE);
3130 /* replying to own mail, use original recp */
3131 compose_entry_append(compose,
3132 msginfo->to ? msginfo->to : "",
3133 COMPOSE_TO, PREF_NONE);
3134 compose_entry_append(compose,
3135 msginfo->cc ? msginfo->cc : "",
3136 COMPOSE_CC, PREF_NONE);
3141 if (to_sender || (compose->followup_to &&
3142 !strncmp(compose->followup_to, "poster", 6)))
3143 compose_entry_append
3145 (compose->replyto ? compose->replyto :
3146 msginfo->from ? msginfo->from : ""),
3147 COMPOSE_TO, PREF_NONE);
3149 else if (followup_and_reply_to || to_all) {
3150 compose_entry_append
3152 (compose->replyto ? compose->replyto :
3153 msginfo->from ? msginfo->from : ""),
3154 COMPOSE_TO, PREF_NONE);
3156 compose_entry_append
3158 compose->followup_to ? compose->followup_to :
3159 compose->newsgroups ? compose->newsgroups : "",
3160 COMPOSE_NEWSGROUPS, PREF_NONE);
3163 compose_entry_append
3165 compose->followup_to ? compose->followup_to :
3166 compose->newsgroups ? compose->newsgroups : "",
3167 COMPOSE_NEWSGROUPS, PREF_NONE);
3170 if (msginfo->subject && *msginfo->subject) {
3174 buf = p = g_strdup(msginfo->subject);
3175 p += subject_get_prefix_length(p);
3176 memmove(buf, p, strlen(p) + 1);
3178 buf2 = g_strdup_printf("Re: %s", buf);
3179 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3184 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3186 if (to_ml && compose->ml_post) return;
3187 if (!to_all || compose->account->protocol == A_NNTP) return;
3189 if (compose->replyto) {
3190 Xstrdup_a(replyto, compose->replyto, return);
3191 extract_address(replyto);
3193 if (msginfo->from) {
3194 Xstrdup_a(from, msginfo->from, return);
3195 extract_address(from);
3198 if (replyto && from)
3199 cc_list = address_list_append_with_comments(cc_list, from);
3200 if (to_all && msginfo->folder &&
3201 msginfo->folder->prefs->enable_default_reply_to)
3202 cc_list = address_list_append_with_comments(cc_list,
3203 msginfo->folder->prefs->default_reply_to);
3204 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3205 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3207 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3209 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3210 if (compose->account) {
3211 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3212 GINT_TO_POINTER(1));
3214 /* remove address on To: and that of current account */
3215 for (cur = cc_list; cur != NULL; ) {
3216 GSList *next = cur->next;
3219 addr = g_utf8_strdown(cur->data, -1);
3220 extract_address(addr);
3222 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3223 cc_list = g_slist_remove(cc_list, cur->data);
3225 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3229 hash_free_strings(to_table);
3230 g_hash_table_destroy(to_table);
3233 for (cur = cc_list; cur != NULL; cur = cur->next)
3234 compose_entry_append(compose, (gchar *)cur->data,
3235 COMPOSE_CC, PREF_NONE);
3236 slist_free_strings(cc_list);
3237 g_slist_free(cc_list);
3242 #define SET_ENTRY(entry, str) \
3245 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3248 #define SET_ADDRESS(type, str) \
3251 compose_entry_append(compose, str, type, PREF_NONE); \
3254 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3256 cm_return_if_fail(msginfo != NULL);
3258 SET_ENTRY(subject_entry, msginfo->subject);
3259 SET_ENTRY(from_name, msginfo->from);
3260 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3261 SET_ADDRESS(COMPOSE_CC, compose->cc);
3262 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3263 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3264 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3265 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3267 compose_update_priority_menu_item(compose);
3268 compose_update_privacy_system_menu_item(compose, FALSE);
3269 compose_show_first_last_header(compose, TRUE);
3275 static void compose_insert_sig(Compose *compose, gboolean replace)
3277 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3278 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3280 GtkTextIter iter, iter_end;
3281 gint cur_pos, ins_pos;
3282 gboolean prev_autowrap;
3283 gboolean found = FALSE;
3284 gboolean exists = FALSE;
3286 cm_return_if_fail(compose->account != NULL);
3290 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3291 G_CALLBACK(compose_changed_cb),
3294 mark = gtk_text_buffer_get_insert(buffer);
3295 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3296 cur_pos = gtk_text_iter_get_offset (&iter);
3299 gtk_text_buffer_get_end_iter(buffer, &iter);
3301 exists = (compose->sig_str != NULL);
3304 GtkTextIter first_iter, start_iter, end_iter;
3306 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3308 if (!exists || compose->sig_str[0] == '\0')
3311 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3312 compose->signature_tag);
3315 /* include previous \n\n */
3316 gtk_text_iter_backward_chars(&first_iter, 1);
3317 start_iter = first_iter;
3318 end_iter = first_iter;
3320 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3321 compose->signature_tag);
3322 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3323 compose->signature_tag);
3325 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3331 g_free(compose->sig_str);
3332 compose->sig_str = account_get_signature_str(compose->account);
3334 cur_pos = gtk_text_iter_get_offset(&iter);
3336 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3337 g_free(compose->sig_str);
3338 compose->sig_str = NULL;
3340 if (compose->sig_inserted == FALSE)
3341 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3342 compose->sig_inserted = TRUE;
3344 cur_pos = gtk_text_iter_get_offset(&iter);
3345 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3347 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3348 gtk_text_iter_forward_chars(&iter, 1);
3349 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3350 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3352 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3353 cur_pos = gtk_text_buffer_get_char_count (buffer);
3356 /* put the cursor where it should be
3357 * either where the quote_fmt says, either where it was */
3358 if (compose->set_cursor_pos < 0)
3359 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3361 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3362 compose->set_cursor_pos);
3364 compose->set_cursor_pos = -1;
3365 gtk_text_buffer_place_cursor(buffer, &iter);
3366 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3367 G_CALLBACK(compose_changed_cb),
3373 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3376 GtkTextBuffer *buffer;
3379 const gchar *cur_encoding;
3380 gchar buf[BUFFSIZE];
3383 gboolean prev_autowrap;
3384 gboolean badtxt = FALSE;
3385 struct stat file_stat;
3388 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3390 /* get the size of the file we are about to insert */
3391 ret = g_stat(file, &file_stat);
3393 gchar *shortfile = g_path_get_basename(file);
3394 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3396 return COMPOSE_INSERT_NO_FILE;
3397 } else if (prefs_common.warn_large_insert == TRUE) {
3399 /* ask user for confirmation if the file is large */
3400 if (prefs_common.warn_large_insert_size < 0 ||
3401 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3405 msg = g_strdup_printf(_("You are about to insert a file of %s "
3406 "in the message body. Are you sure you want to do that?"),
3407 to_human_readable(file_stat.st_size));
3408 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3409 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3412 /* do we ask for confirmation next time? */
3413 if (aval & G_ALERTDISABLE) {
3414 /* no confirmation next time, disable feature in preferences */
3415 aval &= ~G_ALERTDISABLE;
3416 prefs_common.warn_large_insert = FALSE;
3419 /* abort file insertion if user canceled action */
3420 if (aval != G_ALERTALTERNATE) {
3421 return COMPOSE_INSERT_NO_FILE;
3427 if ((fp = g_fopen(file, "rb")) == NULL) {
3428 FILE_OP_ERROR(file, "fopen");
3429 return COMPOSE_INSERT_READ_ERROR;
3432 prev_autowrap = compose->autowrap;
3433 compose->autowrap = FALSE;
3435 text = GTK_TEXT_VIEW(compose->text);
3436 buffer = gtk_text_view_get_buffer(text);
3437 mark = gtk_text_buffer_get_insert(buffer);
3438 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3440 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3441 G_CALLBACK(text_inserted),
3444 cur_encoding = conv_get_locale_charset_str_no_utf8();
3446 while (fgets(buf, sizeof(buf), fp) != NULL) {
3449 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3450 str = g_strdup(buf);
3452 str = conv_codeset_strdup
3453 (buf, cur_encoding, CS_INTERNAL);
3456 /* strip <CR> if DOS/Windows file,
3457 replace <CR> with <LF> if Macintosh file. */
3460 if (len > 0 && str[len - 1] != '\n') {
3462 if (str[len] == '\r') str[len] = '\n';
3465 gtk_text_buffer_insert(buffer, &iter, str, -1);
3469 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3470 G_CALLBACK(text_inserted),
3472 compose->autowrap = prev_autowrap;
3473 if (compose->autowrap)
3474 compose_wrap_all(compose);
3479 return COMPOSE_INSERT_INVALID_CHARACTER;
3481 return COMPOSE_INSERT_SUCCESS;
3484 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3485 const gchar *filename,
3486 const gchar *content_type)
3494 GtkListStore *store;
3496 gboolean has_binary = FALSE;
3498 if (!is_file_exist(file)) {
3499 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3500 gboolean result = FALSE;
3501 if (file_from_uri && is_file_exist(file_from_uri)) {
3502 result = compose_attach_append(
3503 compose, file_from_uri,
3507 g_free(file_from_uri);
3510 alertpanel_error("File %s doesn't exist\n", filename);
3513 if ((size = get_file_size(file)) < 0) {
3514 alertpanel_error("Can't get file size of %s\n", filename);
3518 alertpanel_error(_("File %s is empty."), filename);
3521 if ((fp = g_fopen(file, "rb")) == NULL) {
3522 alertpanel_error(_("Can't read %s."), filename);
3527 ainfo = g_new0(AttachInfo, 1);
3528 auto_ainfo = g_auto_pointer_new_with_free
3529 (ainfo, (GFreeFunc) compose_attach_info_free);
3530 ainfo->file = g_strdup(file);
3533 ainfo->content_type = g_strdup(content_type);
3534 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3536 MsgFlags flags = {0, 0};
3538 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3539 ainfo->encoding = ENC_7BIT;
3541 ainfo->encoding = ENC_8BIT;
3543 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3544 if (msginfo && msginfo->subject)
3545 name = g_strdup(msginfo->subject);
3547 name = g_path_get_basename(filename ? filename : file);
3549 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3551 procmsg_msginfo_free(msginfo);
3553 if (!g_ascii_strncasecmp(content_type, "text", 4))
3554 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3556 ainfo->encoding = ENC_BASE64;
3557 name = g_path_get_basename(filename ? filename : file);
3558 ainfo->name = g_strdup(name);
3562 ainfo->content_type = procmime_get_mime_type(file);
3563 if (!ainfo->content_type) {
3564 ainfo->content_type =
3565 g_strdup("application/octet-stream");
3566 ainfo->encoding = ENC_BASE64;
3567 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3569 procmime_get_encoding_for_text_file(file, &has_binary);
3571 ainfo->encoding = ENC_BASE64;
3572 name = g_path_get_basename(filename ? filename : file);
3573 ainfo->name = g_strdup(name);
3577 if (ainfo->name != NULL
3578 && !strcmp(ainfo->name, ".")) {
3579 g_free(ainfo->name);
3583 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3584 g_free(ainfo->content_type);
3585 ainfo->content_type = g_strdup("application/octet-stream");
3588 ainfo->size = (goffset)size;
3589 size_text = to_human_readable((goffset)size);
3591 store = GTK_LIST_STORE(gtk_tree_view_get_model
3592 (GTK_TREE_VIEW(compose->attach_clist)));
3594 gtk_list_store_append(store, &iter);
3595 gtk_list_store_set(store, &iter,
3596 COL_MIMETYPE, ainfo->content_type,
3597 COL_SIZE, size_text,
3598 COL_NAME, ainfo->name,
3600 COL_AUTODATA, auto_ainfo,
3603 g_auto_pointer_free(auto_ainfo);
3604 compose_attach_update_label(compose);
3608 static void compose_use_signing(Compose *compose, gboolean use_signing)
3610 compose->use_signing = use_signing;
3611 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3614 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3616 compose->use_encryption = use_encryption;
3617 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3620 #define NEXT_PART_NOT_CHILD(info) \
3622 node = info->node; \
3623 while (node->children) \
3624 node = g_node_last_child(node); \
3625 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3628 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3632 MimeInfo *firsttext = NULL;
3633 MimeInfo *encrypted = NULL;
3636 const gchar *partname = NULL;
3638 mimeinfo = procmime_scan_message(msginfo);
3639 if (!mimeinfo) return;
3641 if (mimeinfo->node->children == NULL) {
3642 procmime_mimeinfo_free_all(mimeinfo);
3646 /* find first content part */
3647 child = (MimeInfo *) mimeinfo->node->children->data;
3648 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3649 child = (MimeInfo *)child->node->children->data;
3652 if (child->type == MIMETYPE_TEXT) {
3654 debug_print("First text part found\n");
3655 } else if (compose->mode == COMPOSE_REEDIT &&
3656 child->type == MIMETYPE_APPLICATION &&
3657 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3658 encrypted = (MimeInfo *)child->node->parent->data;
3661 child = (MimeInfo *) mimeinfo->node->children->data;
3662 while (child != NULL) {
3665 if (child == encrypted) {
3666 /* skip this part of tree */
3667 NEXT_PART_NOT_CHILD(child);
3671 if (child->type == MIMETYPE_MULTIPART) {
3672 /* get the actual content */
3673 child = procmime_mimeinfo_next(child);
3677 if (child == firsttext) {
3678 child = procmime_mimeinfo_next(child);
3682 outfile = procmime_get_tmp_file_name(child);
3683 if ((err = procmime_get_part(outfile, child)) < 0)
3684 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3686 gchar *content_type;
3688 content_type = procmime_get_content_type_str(child->type, child->subtype);
3690 /* if we meet a pgp signature, we don't attach it, but
3691 * we force signing. */
3692 if ((strcmp(content_type, "application/pgp-signature") &&
3693 strcmp(content_type, "application/pkcs7-signature") &&
3694 strcmp(content_type, "application/x-pkcs7-signature"))
3695 || compose->mode == COMPOSE_REDIRECT) {
3696 partname = procmime_mimeinfo_get_parameter(child, "filename");
3697 if (partname == NULL)
3698 partname = procmime_mimeinfo_get_parameter(child, "name");
3699 if (partname == NULL)
3701 compose_attach_append(compose, outfile,
3702 partname, content_type);
3704 compose_force_signing(compose, compose->account, NULL);
3706 g_free(content_type);
3709 NEXT_PART_NOT_CHILD(child);
3711 procmime_mimeinfo_free_all(mimeinfo);
3714 #undef NEXT_PART_NOT_CHILD
3719 WAIT_FOR_INDENT_CHAR,
3720 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3723 /* return indent length, we allow:
3724 indent characters followed by indent characters or spaces/tabs,
3725 alphabets and numbers immediately followed by indent characters,
3726 and the repeating sequences of the above
3727 If quote ends with multiple spaces, only the first one is included. */
3728 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3729 const GtkTextIter *start, gint *len)
3731 GtkTextIter iter = *start;
3735 IndentState state = WAIT_FOR_INDENT_CHAR;
3738 gint alnum_count = 0;
3739 gint space_count = 0;
3742 if (prefs_common.quote_chars == NULL) {
3746 while (!gtk_text_iter_ends_line(&iter)) {
3747 wc = gtk_text_iter_get_char(&iter);
3748 if (g_unichar_iswide(wc))
3750 clen = g_unichar_to_utf8(wc, ch);
3754 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3755 is_space = g_unichar_isspace(wc);
3757 if (state == WAIT_FOR_INDENT_CHAR) {
3758 if (!is_indent && !g_unichar_isalnum(wc))
3761 quote_len += alnum_count + space_count + 1;
3762 alnum_count = space_count = 0;
3763 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3766 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3767 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3771 else if (is_indent) {
3772 quote_len += alnum_count + space_count + 1;
3773 alnum_count = space_count = 0;
3776 state = WAIT_FOR_INDENT_CHAR;
3780 gtk_text_iter_forward_char(&iter);
3783 if (quote_len > 0 && space_count > 0)
3789 if (quote_len > 0) {
3791 gtk_text_iter_forward_chars(&iter, quote_len);
3792 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3798 /* return >0 if the line is itemized */
3799 static int compose_itemized_length(GtkTextBuffer *buffer,
3800 const GtkTextIter *start)
3802 GtkTextIter iter = *start;
3807 if (gtk_text_iter_ends_line(&iter))
3812 wc = gtk_text_iter_get_char(&iter);
3813 if (!g_unichar_isspace(wc))
3815 gtk_text_iter_forward_char(&iter);
3816 if (gtk_text_iter_ends_line(&iter))
3820 clen = g_unichar_to_utf8(wc, ch);
3824 if (!strchr("*-+", ch[0]))
3827 gtk_text_iter_forward_char(&iter);
3828 if (gtk_text_iter_ends_line(&iter))
3830 wc = gtk_text_iter_get_char(&iter);
3831 if (g_unichar_isspace(wc)) {
3837 /* return the string at the start of the itemization */
3838 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3839 const GtkTextIter *start)
3841 GtkTextIter iter = *start;
3844 GString *item_chars = g_string_new("");
3847 if (gtk_text_iter_ends_line(&iter))
3852 wc = gtk_text_iter_get_char(&iter);
3853 if (!g_unichar_isspace(wc))
3855 gtk_text_iter_forward_char(&iter);
3856 if (gtk_text_iter_ends_line(&iter))
3858 g_string_append_unichar(item_chars, wc);
3861 str = item_chars->str;
3862 g_string_free(item_chars, FALSE);
3866 /* return the number of spaces at a line's start */
3867 static int compose_left_offset_length(GtkTextBuffer *buffer,
3868 const GtkTextIter *start)
3870 GtkTextIter iter = *start;
3873 if (gtk_text_iter_ends_line(&iter))
3877 wc = gtk_text_iter_get_char(&iter);
3878 if (!g_unichar_isspace(wc))
3881 gtk_text_iter_forward_char(&iter);
3882 if (gtk_text_iter_ends_line(&iter))
3886 gtk_text_iter_forward_char(&iter);
3887 if (gtk_text_iter_ends_line(&iter))
3892 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3893 const GtkTextIter *start,
3894 GtkTextIter *break_pos,
3898 GtkTextIter iter = *start, line_end = *start;
3899 PangoLogAttr *attrs;
3906 gboolean can_break = FALSE;
3907 gboolean do_break = FALSE;
3908 gboolean was_white = FALSE;
3909 gboolean prev_dont_break = FALSE;
3911 gtk_text_iter_forward_to_line_end(&line_end);
3912 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3913 len = g_utf8_strlen(str, -1);
3917 g_warning("compose_get_line_break_pos: len = 0!\n");
3921 /* g_print("breaking line: %d: %s (len = %d)\n",
3922 gtk_text_iter_get_line(&iter), str, len); */
3924 attrs = g_new(PangoLogAttr, len + 1);
3926 pango_default_break(str, -1, NULL, attrs, len + 1);
3930 /* skip quote and leading spaces */
3931 for (i = 0; *p != '\0' && i < len; i++) {
3934 wc = g_utf8_get_char(p);
3935 if (i >= quote_len && !g_unichar_isspace(wc))
3937 if (g_unichar_iswide(wc))
3939 else if (*p == '\t')
3943 p = g_utf8_next_char(p);
3946 for (; *p != '\0' && i < len; i++) {
3947 PangoLogAttr *attr = attrs + i;
3951 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3954 was_white = attr->is_white;
3956 /* don't wrap URI */
3957 if ((uri_len = get_uri_len(p)) > 0) {
3959 if (pos > 0 && col > max_col) {
3969 wc = g_utf8_get_char(p);
3970 if (g_unichar_iswide(wc)) {
3972 if (prev_dont_break && can_break && attr->is_line_break)
3974 } else if (*p == '\t')
3978 if (pos > 0 && col > max_col) {
3983 if (*p == '-' || *p == '/')
3984 prev_dont_break = TRUE;
3986 prev_dont_break = FALSE;
3988 p = g_utf8_next_char(p);
3992 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3997 *break_pos = *start;
3998 gtk_text_iter_set_line_offset(break_pos, pos);
4003 static gboolean compose_join_next_line(Compose *compose,
4004 GtkTextBuffer *buffer,
4006 const gchar *quote_str)
4008 GtkTextIter iter_ = *iter, cur, prev, next, end;
4009 PangoLogAttr attrs[3];
4011 gchar *next_quote_str;
4014 gboolean keep_cursor = FALSE;
4016 if (!gtk_text_iter_forward_line(&iter_) ||
4017 gtk_text_iter_ends_line(&iter_)) {
4020 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4022 if ((quote_str || next_quote_str) &&
4023 strcmp2(quote_str, next_quote_str) != 0) {
4024 g_free(next_quote_str);
4027 g_free(next_quote_str);
4030 if (quote_len > 0) {
4031 gtk_text_iter_forward_chars(&end, quote_len);
4032 if (gtk_text_iter_ends_line(&end)) {
4037 /* don't join itemized lines */
4038 if (compose_itemized_length(buffer, &end) > 0) {
4042 /* don't join signature separator */
4043 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4046 /* delete quote str */
4048 gtk_text_buffer_delete(buffer, &iter_, &end);
4050 /* don't join line breaks put by the user */
4052 gtk_text_iter_backward_char(&cur);
4053 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4054 gtk_text_iter_forward_char(&cur);
4058 gtk_text_iter_forward_char(&cur);
4059 /* delete linebreak and extra spaces */
4060 while (gtk_text_iter_backward_char(&cur)) {
4061 wc1 = gtk_text_iter_get_char(&cur);
4062 if (!g_unichar_isspace(wc1))
4067 while (!gtk_text_iter_ends_line(&cur)) {
4068 wc1 = gtk_text_iter_get_char(&cur);
4069 if (!g_unichar_isspace(wc1))
4071 gtk_text_iter_forward_char(&cur);
4074 if (!gtk_text_iter_equal(&prev, &next)) {
4077 mark = gtk_text_buffer_get_insert(buffer);
4078 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4079 if (gtk_text_iter_equal(&prev, &cur))
4081 gtk_text_buffer_delete(buffer, &prev, &next);
4085 /* insert space if required */
4086 gtk_text_iter_backward_char(&prev);
4087 wc1 = gtk_text_iter_get_char(&prev);
4088 wc2 = gtk_text_iter_get_char(&next);
4089 gtk_text_iter_forward_char(&next);
4090 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4091 pango_default_break(str, -1, NULL, attrs, 3);
4092 if (!attrs[1].is_line_break ||
4093 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4094 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4096 gtk_text_iter_backward_char(&iter_);
4097 gtk_text_buffer_place_cursor(buffer, &iter_);
4106 #define ADD_TXT_POS(bp_, ep_, pti_) \
4107 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4108 last = last->next; \
4109 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4110 last->next = NULL; \
4112 g_warning("alloc error scanning URIs\n"); \
4115 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4117 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4118 GtkTextBuffer *buffer;
4119 GtkTextIter iter, break_pos, end_of_line;
4120 gchar *quote_str = NULL;
4122 gboolean wrap_quote = prefs_common.linewrap_quote;
4123 gboolean prev_autowrap = compose->autowrap;
4124 gint startq_offset = -1, noq_offset = -1;
4125 gint uri_start = -1, uri_stop = -1;
4126 gint nouri_start = -1, nouri_stop = -1;
4127 gint num_blocks = 0;
4128 gint quotelevel = -1;
4129 gboolean modified = force;
4130 gboolean removed = FALSE;
4131 gboolean modified_before_remove = FALSE;
4133 gboolean start = TRUE;
4134 gint itemized_len = 0, rem_item_len = 0;
4135 gchar *itemized_chars = NULL;
4136 gboolean item_continuation = FALSE;
4141 if (compose->draft_timeout_tag == -2) {
4145 compose->autowrap = FALSE;
4147 buffer = gtk_text_view_get_buffer(text);
4148 undo_wrapping(compose->undostruct, TRUE);
4153 mark = gtk_text_buffer_get_insert(buffer);
4154 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4158 if (compose->draft_timeout_tag == -2) {
4159 if (gtk_text_iter_ends_line(&iter)) {
4160 while (gtk_text_iter_ends_line(&iter) &&
4161 gtk_text_iter_forward_line(&iter))
4164 while (gtk_text_iter_backward_line(&iter)) {
4165 if (gtk_text_iter_ends_line(&iter)) {
4166 gtk_text_iter_forward_line(&iter);
4172 /* move to line start */
4173 gtk_text_iter_set_line_offset(&iter, 0);
4176 itemized_len = compose_itemized_length(buffer, &iter);
4178 if (!itemized_len) {
4179 itemized_len = compose_left_offset_length(buffer, &iter);
4180 item_continuation = TRUE;
4184 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4186 /* go until paragraph end (empty line) */
4187 while (start || !gtk_text_iter_ends_line(&iter)) {
4188 gchar *scanpos = NULL;
4189 /* parse table - in order of priority */
4191 const gchar *needle; /* token */
4193 /* token search function */
4194 gchar *(*search) (const gchar *haystack,
4195 const gchar *needle);
4196 /* part parsing function */
4197 gboolean (*parse) (const gchar *start,
4198 const gchar *scanpos,
4202 /* part to URI function */
4203 gchar *(*build_uri) (const gchar *bp,
4207 static struct table parser[] = {
4208 {"http://", strcasestr, get_uri_part, make_uri_string},
4209 {"https://", strcasestr, get_uri_part, make_uri_string},
4210 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4211 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4212 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4213 {"www.", strcasestr, get_uri_part, make_http_string},
4214 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4215 {"@", strcasestr, get_email_part, make_email_string}
4217 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4218 gint last_index = PARSE_ELEMS;
4220 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4224 if (!prev_autowrap && num_blocks == 0) {
4226 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4227 G_CALLBACK(text_inserted),
4230 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4233 uri_start = uri_stop = -1;
4235 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4238 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4239 if (startq_offset == -1)
4240 startq_offset = gtk_text_iter_get_offset(&iter);
4241 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4242 if (quotelevel > 2) {
4243 /* recycle colors */
4244 if (prefs_common.recycle_quote_colors)
4253 if (startq_offset == -1)
4254 noq_offset = gtk_text_iter_get_offset(&iter);
4258 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4261 if (gtk_text_iter_ends_line(&iter)) {
4263 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4264 prefs_common.linewrap_len,
4266 GtkTextIter prev, next, cur;
4267 if (prev_autowrap != FALSE || force) {
4268 compose->automatic_break = TRUE;
4270 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4271 compose->automatic_break = FALSE;
4272 if (itemized_len && compose->autoindent) {
4273 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4274 if (!item_continuation)
4275 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4277 } else if (quote_str && wrap_quote) {
4278 compose->automatic_break = TRUE;
4280 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4281 compose->automatic_break = FALSE;
4282 if (itemized_len && compose->autoindent) {
4283 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4284 if (!item_continuation)
4285 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4289 /* remove trailing spaces */
4291 rem_item_len = itemized_len;
4292 while (compose->autoindent && rem_item_len-- > 0)
4293 gtk_text_iter_backward_char(&cur);
4294 gtk_text_iter_backward_char(&cur);
4297 while (!gtk_text_iter_starts_line(&cur)) {
4300 gtk_text_iter_backward_char(&cur);
4301 wc = gtk_text_iter_get_char(&cur);
4302 if (!g_unichar_isspace(wc))
4306 if (!gtk_text_iter_equal(&prev, &next)) {
4307 gtk_text_buffer_delete(buffer, &prev, &next);
4309 gtk_text_iter_forward_char(&break_pos);
4313 gtk_text_buffer_insert(buffer, &break_pos,
4317 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4319 /* move iter to current line start */
4320 gtk_text_iter_set_line_offset(&iter, 0);
4327 /* move iter to next line start */
4333 if (!prev_autowrap && num_blocks > 0) {
4335 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4336 G_CALLBACK(text_inserted),
4340 while (!gtk_text_iter_ends_line(&end_of_line)) {
4341 gtk_text_iter_forward_char(&end_of_line);
4343 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4345 nouri_start = gtk_text_iter_get_offset(&iter);
4346 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4348 walk_pos = gtk_text_iter_get_offset(&iter);
4349 /* FIXME: this looks phony. scanning for anything in the parse table */
4350 for (n = 0; n < PARSE_ELEMS; n++) {
4353 tmp = parser[n].search(walk, parser[n].needle);
4355 if (scanpos == NULL || tmp < scanpos) {
4364 /* check if URI can be parsed */
4365 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4366 (const gchar **)&ep, FALSE)
4367 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4371 strlen(parser[last_index].needle);
4374 uri_start = walk_pos + (bp - o_walk);
4375 uri_stop = walk_pos + (ep - o_walk);
4379 gtk_text_iter_forward_line(&iter);
4382 if (startq_offset != -1) {
4383 GtkTextIter startquote, endquote;
4384 gtk_text_buffer_get_iter_at_offset(
4385 buffer, &startquote, startq_offset);
4388 switch (quotelevel) {
4390 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4391 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4392 gtk_text_buffer_apply_tag_by_name(
4393 buffer, "quote0", &startquote, &endquote);
4394 gtk_text_buffer_remove_tag_by_name(
4395 buffer, "quote1", &startquote, &endquote);
4396 gtk_text_buffer_remove_tag_by_name(
4397 buffer, "quote2", &startquote, &endquote);
4402 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4403 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4404 gtk_text_buffer_apply_tag_by_name(
4405 buffer, "quote1", &startquote, &endquote);
4406 gtk_text_buffer_remove_tag_by_name(
4407 buffer, "quote0", &startquote, &endquote);
4408 gtk_text_buffer_remove_tag_by_name(
4409 buffer, "quote2", &startquote, &endquote);
4414 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4415 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4416 gtk_text_buffer_apply_tag_by_name(
4417 buffer, "quote2", &startquote, &endquote);
4418 gtk_text_buffer_remove_tag_by_name(
4419 buffer, "quote0", &startquote, &endquote);
4420 gtk_text_buffer_remove_tag_by_name(
4421 buffer, "quote1", &startquote, &endquote);
4427 } else if (noq_offset != -1) {
4428 GtkTextIter startnoquote, endnoquote;
4429 gtk_text_buffer_get_iter_at_offset(
4430 buffer, &startnoquote, noq_offset);
4433 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4434 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4435 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4436 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4437 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4438 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4439 gtk_text_buffer_remove_tag_by_name(
4440 buffer, "quote0", &startnoquote, &endnoquote);
4441 gtk_text_buffer_remove_tag_by_name(
4442 buffer, "quote1", &startnoquote, &endnoquote);
4443 gtk_text_buffer_remove_tag_by_name(
4444 buffer, "quote2", &startnoquote, &endnoquote);
4450 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4451 GtkTextIter nouri_start_iter, nouri_end_iter;
4452 gtk_text_buffer_get_iter_at_offset(
4453 buffer, &nouri_start_iter, nouri_start);
4454 gtk_text_buffer_get_iter_at_offset(
4455 buffer, &nouri_end_iter, nouri_stop);
4456 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4457 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4458 gtk_text_buffer_remove_tag_by_name(
4459 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4460 modified_before_remove = modified;
4465 if (uri_start >= 0 && uri_stop > 0) {
4466 GtkTextIter uri_start_iter, uri_end_iter, back;
4467 gtk_text_buffer_get_iter_at_offset(
4468 buffer, &uri_start_iter, uri_start);
4469 gtk_text_buffer_get_iter_at_offset(
4470 buffer, &uri_end_iter, uri_stop);
4471 back = uri_end_iter;
4472 gtk_text_iter_backward_char(&back);
4473 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4474 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4475 gtk_text_buffer_apply_tag_by_name(
4476 buffer, "link", &uri_start_iter, &uri_end_iter);
4478 if (removed && !modified_before_remove) {
4484 // debug_print("not modified, out after %d lines\n", lines);
4488 // debug_print("modified, out after %d lines\n", lines);
4490 g_free(itemized_chars);
4493 undo_wrapping(compose->undostruct, FALSE);
4494 compose->autowrap = prev_autowrap;
4499 void compose_action_cb(void *data)
4501 Compose *compose = (Compose *)data;
4502 compose_wrap_all(compose);
4505 static void compose_wrap_all(Compose *compose)
4507 compose_wrap_all_full(compose, FALSE);
4510 static void compose_wrap_all_full(Compose *compose, gboolean force)
4512 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4513 GtkTextBuffer *buffer;
4515 gboolean modified = TRUE;
4517 buffer = gtk_text_view_get_buffer(text);
4519 gtk_text_buffer_get_start_iter(buffer, &iter);
4520 while (!gtk_text_iter_is_end(&iter) && modified)
4521 modified = compose_beautify_paragraph(compose, &iter, force);
4525 static void compose_set_title(Compose *compose)
4531 edited = compose->modified ? _(" [Edited]") : "";
4533 subject = gtk_editable_get_chars(
4534 GTK_EDITABLE(compose->subject_entry), 0, -1);
4536 #ifndef GENERIC_UMPC
4537 if (subject && strlen(subject))
4538 str = g_strdup_printf(_("%s - Compose message%s"),
4541 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4543 str = g_strdup(_("Compose message"));
4546 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4552 * compose_current_mail_account:
4554 * Find a current mail account (the currently selected account, or the
4555 * default account, if a news account is currently selected). If a
4556 * mail account cannot be found, display an error message.
4558 * Return value: Mail account, or NULL if not found.
4560 static PrefsAccount *
4561 compose_current_mail_account(void)
4565 if (cur_account && cur_account->protocol != A_NNTP)
4568 ac = account_get_default();
4569 if (!ac || ac->protocol == A_NNTP) {
4570 alertpanel_error(_("Account for sending mail is not specified.\n"
4571 "Please select a mail account before sending."));
4578 #define QUOTE_IF_REQUIRED(out, str) \
4580 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4584 len = strlen(str) + 3; \
4585 if ((__tmp = alloca(len)) == NULL) { \
4586 g_warning("can't allocate memory\n"); \
4587 g_string_free(header, TRUE); \
4590 g_snprintf(__tmp, len, "\"%s\"", str); \
4595 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4596 g_warning("can't allocate memory\n"); \
4597 g_string_free(header, TRUE); \
4600 strcpy(__tmp, str); \
4606 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4608 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4612 len = strlen(str) + 3; \
4613 if ((__tmp = alloca(len)) == NULL) { \
4614 g_warning("can't allocate memory\n"); \
4617 g_snprintf(__tmp, len, "\"%s\"", str); \
4622 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4623 g_warning("can't allocate memory\n"); \
4626 strcpy(__tmp, str); \
4632 static void compose_select_account(Compose *compose, PrefsAccount *account,
4635 gchar *from = NULL, *header;
4636 ComposeHeaderEntry *header_entry;
4638 cm_return_if_fail(account != NULL);
4640 compose->account = account;
4642 if (account->name && *account->name) {
4644 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4645 from = g_strdup_printf("%s <%s>",
4646 buf, account->address);
4647 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4649 from = g_strdup_printf("<%s>",
4651 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4656 compose_set_title(compose);
4658 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4659 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4661 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4662 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4663 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4665 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4667 activate_privacy_system(compose, account, FALSE);
4669 if (!init && compose->mode != COMPOSE_REDIRECT) {
4670 undo_block(compose->undostruct);
4671 compose_insert_sig(compose, TRUE);
4672 undo_unblock(compose->undostruct);
4675 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4676 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4678 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4679 if (account->protocol == A_NNTP) {
4680 if (!strcmp(header, _("To:")))
4681 combobox_select_by_text(
4682 GTK_COMBO_BOX(header_entry->combo),
4685 if (!strcmp(header, _("Newsgroups:")))
4686 combobox_select_by_text(
4687 GTK_COMBO_BOX(header_entry->combo),
4695 /* use account's dict info if set */
4696 if (compose->gtkaspell) {
4697 if (account->enable_default_dictionary)
4698 gtkaspell_change_dict(compose->gtkaspell,
4699 account->default_dictionary, FALSE);
4700 if (account->enable_default_alt_dictionary)
4701 gtkaspell_change_alt_dict(compose->gtkaspell,
4702 account->default_alt_dictionary);
4703 if (account->enable_default_dictionary
4704 || account->enable_default_alt_dictionary)
4705 compose_spell_menu_changed(compose);
4710 gboolean compose_check_for_valid_recipient(Compose *compose) {
4711 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4712 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4713 gboolean recipient_found = FALSE;
4717 /* free to and newsgroup list */
4718 slist_free_strings(compose->to_list);
4719 g_slist_free(compose->to_list);
4720 compose->to_list = NULL;
4722 slist_free_strings(compose->newsgroup_list);
4723 g_slist_free(compose->newsgroup_list);
4724 compose->newsgroup_list = NULL;
4726 /* search header entries for to and newsgroup entries */
4727 for (list = compose->header_list; list; list = list->next) {
4730 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4731 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4734 if (entry[0] != '\0') {
4735 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4736 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4737 compose->to_list = address_list_append(compose->to_list, entry);
4738 recipient_found = TRUE;
4741 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4742 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4743 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4744 recipient_found = TRUE;
4751 return recipient_found;
4754 static gboolean compose_check_for_set_recipients(Compose *compose)
4756 if (compose->account->set_autocc && compose->account->auto_cc) {
4757 gboolean found_other = FALSE;
4759 /* search header entries for to and newsgroup entries */
4760 for (list = compose->header_list; list; list = list->next) {
4763 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4764 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4767 if (strcmp(entry, compose->account->auto_cc)
4768 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4778 if (compose->batch) {
4779 gtk_widget_show_all(compose->window);
4781 aval = alertpanel(_("Send"),
4782 _("The only recipient is the default CC address. Send anyway?"),
4783 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4784 if (aval != G_ALERTALTERNATE)
4788 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4789 gboolean found_other = FALSE;
4791 /* search header entries for to and newsgroup entries */
4792 for (list = compose->header_list; list; list = list->next) {
4795 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4796 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4799 if (strcmp(entry, compose->account->auto_bcc)
4800 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4810 if (compose->batch) {
4811 gtk_widget_show_all(compose->window);
4813 aval = alertpanel(_("Send"),
4814 _("The only recipient is the default BCC address. Send anyway?"),
4815 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4816 if (aval != G_ALERTALTERNATE)
4823 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4827 if (compose_check_for_valid_recipient(compose) == FALSE) {
4828 if (compose->batch) {
4829 gtk_widget_show_all(compose->window);
4831 alertpanel_error(_("Recipient is not specified."));
4835 if (compose_check_for_set_recipients(compose) == FALSE) {
4839 if (!compose->batch) {
4840 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4841 if (*str == '\0' && check_everything == TRUE &&
4842 compose->mode != COMPOSE_REDIRECT) {
4844 gchar *button_label;
4847 if (compose->sending)
4848 button_label = _("+_Send");
4850 button_label = _("+_Queue");
4851 message = g_strdup_printf(_("Subject is empty. %s"),
4852 compose->sending?_("Send it anyway?"):
4853 _("Queue it anyway?"));
4855 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4856 GTK_STOCK_CANCEL, button_label, NULL);
4858 if (aval != G_ALERTALTERNATE)
4863 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4869 gint compose_send(Compose *compose)
4872 FolderItem *folder = NULL;
4874 gchar *msgpath = NULL;
4875 gboolean discard_window = FALSE;
4876 gchar *errstr = NULL;
4877 gchar *tmsgid = NULL;
4878 MainWindow *mainwin = mainwindow_get_mainwindow();
4879 gboolean queued_removed = FALSE;
4881 if (prefs_common.send_dialog_invisible
4882 || compose->batch == TRUE)
4883 discard_window = TRUE;
4885 compose_allow_user_actions (compose, FALSE);
4886 compose->sending = TRUE;
4888 if (compose_check_entries(compose, TRUE) == FALSE) {
4889 if (compose->batch) {
4890 gtk_widget_show_all(compose->window);
4896 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4899 if (compose->batch) {
4900 gtk_widget_show_all(compose->window);
4903 alertpanel_error(_("Could not queue message for sending:\n\n"
4904 "Charset conversion failed."));
4905 } else if (val == -5) {
4906 alertpanel_error(_("Could not queue message for sending:\n\n"
4907 "Couldn't get recipient encryption key."));
4908 } else if (val == -6) {
4910 } else if (val == -3) {
4911 if (privacy_peek_error())
4912 alertpanel_error(_("Could not queue message for sending:\n\n"
4913 "Signature failed: %s"), privacy_get_error());
4914 } else if (val == -2 && errno != 0) {
4915 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4917 alertpanel_error(_("Could not queue message for sending."));
4922 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4923 if (discard_window) {
4924 compose->sending = FALSE;
4925 compose_close(compose);
4926 /* No more compose access in the normal codepath
4927 * after this point! */
4932 alertpanel_error(_("The message was queued but could not be "
4933 "sent.\nUse \"Send queued messages\" from "
4934 "the main window to retry."));
4935 if (!discard_window) {
4942 if (msgpath == NULL) {
4943 msgpath = folder_item_fetch_msg(folder, msgnum);
4944 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4947 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4948 claws_unlink(msgpath);
4951 if (!discard_window) {
4953 if (!queued_removed)
4954 folder_item_remove_msg(folder, msgnum);
4955 folder_item_scan(folder);
4957 /* make sure we delete that */
4958 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4960 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4961 folder_item_remove_msg(folder, tmp->msgnum);
4962 procmsg_msginfo_free(tmp);
4969 if (!queued_removed)
4970 folder_item_remove_msg(folder, msgnum);
4971 folder_item_scan(folder);
4973 /* make sure we delete that */
4974 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4976 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4977 folder_item_remove_msg(folder, tmp->msgnum);
4978 procmsg_msginfo_free(tmp);
4981 if (!discard_window) {
4982 compose->sending = FALSE;
4983 compose_allow_user_actions (compose, TRUE);
4984 compose_close(compose);
4988 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4989 "the main window to retry."), errstr);
4992 alertpanel_error_log(_("The message was queued but could not be "
4993 "sent.\nUse \"Send queued messages\" from "
4994 "the main window to retry."));
4996 if (!discard_window) {
5005 toolbar_main_set_sensitive(mainwin);
5006 main_window_set_menu_sensitive(mainwin);
5012 compose_allow_user_actions (compose, TRUE);
5013 compose->sending = FALSE;
5014 compose->modified = TRUE;
5015 toolbar_main_set_sensitive(mainwin);
5016 main_window_set_menu_sensitive(mainwin);
5021 static gboolean compose_use_attach(Compose *compose)
5023 GtkTreeModel *model = gtk_tree_view_get_model
5024 (GTK_TREE_VIEW(compose->attach_clist));
5025 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5028 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5031 gchar buf[BUFFSIZE];
5033 gboolean first_to_address;
5034 gboolean first_cc_address;
5036 ComposeHeaderEntry *headerentry;
5037 const gchar *headerentryname;
5038 const gchar *cc_hdr;
5039 const gchar *to_hdr;
5040 gboolean err = FALSE;
5042 debug_print("Writing redirect header\n");
5044 cc_hdr = prefs_common_translated_header_name("Cc:");
5045 to_hdr = prefs_common_translated_header_name("To:");
5047 first_to_address = TRUE;
5048 for (list = compose->header_list; list; list = list->next) {
5049 headerentry = ((ComposeHeaderEntry *)list->data);
5050 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5052 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5053 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5054 Xstrdup_a(str, entstr, return -1);
5056 if (str[0] != '\0') {
5057 compose_convert_header
5058 (compose, buf, sizeof(buf), str,
5059 strlen("Resent-To") + 2, TRUE);
5061 if (first_to_address) {
5062 err |= (fprintf(fp, "Resent-To: ") < 0);
5063 first_to_address = FALSE;
5065 err |= (fprintf(fp, ",") < 0);
5067 err |= (fprintf(fp, "%s", buf) < 0);
5071 if (!first_to_address) {
5072 err |= (fprintf(fp, "\n") < 0);
5075 first_cc_address = TRUE;
5076 for (list = compose->header_list; list; list = list->next) {
5077 headerentry = ((ComposeHeaderEntry *)list->data);
5078 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5080 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5081 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5082 Xstrdup_a(str, strg, return -1);
5084 if (str[0] != '\0') {
5085 compose_convert_header
5086 (compose, buf, sizeof(buf), str,
5087 strlen("Resent-Cc") + 2, TRUE);
5089 if (first_cc_address) {
5090 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5091 first_cc_address = FALSE;
5093 err |= (fprintf(fp, ",") < 0);
5095 err |= (fprintf(fp, "%s", buf) < 0);
5099 if (!first_cc_address) {
5100 err |= (fprintf(fp, "\n") < 0);
5103 return (err ? -1:0);
5106 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5108 gchar buf[BUFFSIZE];
5110 const gchar *entstr;
5111 /* struct utsname utsbuf; */
5112 gboolean err = FALSE;
5114 cm_return_val_if_fail(fp != NULL, -1);
5115 cm_return_val_if_fail(compose->account != NULL, -1);
5116 cm_return_val_if_fail(compose->account->address != NULL, -1);
5119 get_rfc822_date(buf, sizeof(buf));
5120 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5123 if (compose->account->name && *compose->account->name) {
5124 compose_convert_header
5125 (compose, buf, sizeof(buf), compose->account->name,
5126 strlen("From: "), TRUE);
5127 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5128 buf, compose->account->address) < 0);
5130 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5133 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5134 if (*entstr != '\0') {
5135 Xstrdup_a(str, entstr, return -1);
5138 compose_convert_header(compose, buf, sizeof(buf), str,
5139 strlen("Subject: "), FALSE);
5140 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5144 /* Resent-Message-ID */
5145 if (compose->account->set_domain && compose->account->domain) {
5146 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5147 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5148 g_snprintf(buf, sizeof(buf), "%s",
5149 strchr(compose->account->address, '@') ?
5150 strchr(compose->account->address, '@')+1 :
5151 compose->account->address);
5153 g_snprintf(buf, sizeof(buf), "%s", "");
5156 if (compose->account->gen_msgid) {
5158 if (compose->account->msgid_with_addr) {
5159 addr = compose->account->address;
5161 generate_msgid(buf, sizeof(buf), addr);
5162 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5163 compose->msgid = g_strdup(buf);
5165 compose->msgid = NULL;
5168 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5171 /* separator between header and body */
5172 err |= (fputs("\n", fp) == EOF);
5174 return (err ? -1:0);
5177 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5181 gchar buf[BUFFSIZE];
5183 gboolean skip = FALSE;
5184 gboolean err = FALSE;
5185 gchar *not_included[]={
5186 "Return-Path:", "Delivered-To:", "Received:",
5187 "Subject:", "X-UIDL:", "AF:",
5188 "NF:", "PS:", "SRH:",
5189 "SFN:", "DSR:", "MID:",
5190 "CFG:", "PT:", "S:",
5191 "RQ:", "SSV:", "NSV:",
5192 "SSH:", "R:", "MAID:",
5193 "NAID:", "RMID:", "FMID:",
5194 "SCF:", "RRCPT:", "NG:",
5195 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5196 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5197 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5198 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5199 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5202 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5203 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5207 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5209 for (i = 0; not_included[i] != NULL; i++) {
5210 if (g_ascii_strncasecmp(buf, not_included[i],
5211 strlen(not_included[i])) == 0) {
5218 if (fputs(buf, fdest) == -1)
5221 if (!prefs_common.redirect_keep_from) {
5222 if (g_ascii_strncasecmp(buf, "From:",
5223 strlen("From:")) == 0) {
5224 err |= (fputs(" (by way of ", fdest) == EOF);
5225 if (compose->account->name
5226 && *compose->account->name) {
5227 compose_convert_header
5228 (compose, buf, sizeof(buf),
5229 compose->account->name,
5232 err |= (fprintf(fdest, "%s <%s>",
5234 compose->account->address) < 0);
5236 err |= (fprintf(fdest, "%s",
5237 compose->account->address) < 0);
5238 err |= (fputs(")", fdest) == EOF);
5242 if (fputs("\n", fdest) == -1)
5249 if (compose_redirect_write_headers(compose, fdest))
5252 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5253 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5266 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5268 GtkTextBuffer *buffer;
5269 GtkTextIter start, end;
5272 const gchar *out_codeset;
5273 EncodingType encoding = ENC_UNKNOWN;
5274 MimeInfo *mimemsg, *mimetext;
5276 const gchar *src_codeset = CS_INTERNAL;
5277 gchar *from_addr = NULL;
5278 gchar *from_name = NULL;
5280 if (action == COMPOSE_WRITE_FOR_SEND)
5281 attach_parts = TRUE;
5283 /* create message MimeInfo */
5284 mimemsg = procmime_mimeinfo_new();
5285 mimemsg->type = MIMETYPE_MESSAGE;
5286 mimemsg->subtype = g_strdup("rfc822");
5287 mimemsg->content = MIMECONTENT_MEM;
5288 mimemsg->tmp = TRUE; /* must free content later */
5289 mimemsg->data.mem = compose_get_header(compose);
5291 /* Create text part MimeInfo */
5292 /* get all composed text */
5293 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5294 gtk_text_buffer_get_start_iter(buffer, &start);
5295 gtk_text_buffer_get_end_iter(buffer, &end);
5296 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5298 out_codeset = conv_get_charset_str(compose->out_encoding);
5300 if (!out_codeset && is_ascii_str(chars)) {
5301 out_codeset = CS_US_ASCII;
5302 } else if (prefs_common.outgoing_fallback_to_ascii &&
5303 is_ascii_str(chars)) {
5304 out_codeset = CS_US_ASCII;
5305 encoding = ENC_7BIT;
5309 gchar *test_conv_global_out = NULL;
5310 gchar *test_conv_reply = NULL;
5312 /* automatic mode. be automatic. */
5313 codeconv_set_strict(TRUE);
5315 out_codeset = conv_get_outgoing_charset_str();
5317 debug_print("trying to convert to %s\n", out_codeset);
5318 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5321 if (!test_conv_global_out && compose->orig_charset
5322 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5323 out_codeset = compose->orig_charset;
5324 debug_print("failure; trying to convert to %s\n", out_codeset);
5325 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5328 if (!test_conv_global_out && !test_conv_reply) {
5330 out_codeset = CS_INTERNAL;
5331 debug_print("failure; finally using %s\n", out_codeset);
5333 g_free(test_conv_global_out);
5334 g_free(test_conv_reply);
5335 codeconv_set_strict(FALSE);
5338 if (encoding == ENC_UNKNOWN) {
5339 if (prefs_common.encoding_method == CTE_BASE64)
5340 encoding = ENC_BASE64;
5341 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5342 encoding = ENC_QUOTED_PRINTABLE;
5343 else if (prefs_common.encoding_method == CTE_8BIT)
5344 encoding = ENC_8BIT;
5346 encoding = procmime_get_encoding_for_charset(out_codeset);
5349 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5350 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5352 if (action == COMPOSE_WRITE_FOR_SEND) {
5353 codeconv_set_strict(TRUE);
5354 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5355 codeconv_set_strict(FALSE);
5361 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5362 "to the specified %s charset.\n"
5363 "Send it as %s?"), out_codeset, src_codeset);
5364 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5365 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5368 if (aval != G_ALERTALTERNATE) {
5373 out_codeset = src_codeset;
5379 out_codeset = src_codeset;
5384 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5385 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5386 strstr(buf, "\nFrom ") != NULL) {
5387 encoding = ENC_QUOTED_PRINTABLE;
5391 mimetext = procmime_mimeinfo_new();
5392 mimetext->content = MIMECONTENT_MEM;
5393 mimetext->tmp = TRUE; /* must free content later */
5394 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5395 * and free the data, which we need later. */
5396 mimetext->data.mem = g_strdup(buf);
5397 mimetext->type = MIMETYPE_TEXT;
5398 mimetext->subtype = g_strdup("plain");
5399 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5400 g_strdup(out_codeset));
5402 /* protect trailing spaces when signing message */
5403 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5404 privacy_system_can_sign(compose->privacy_system)) {
5405 encoding = ENC_QUOTED_PRINTABLE;
5408 debug_print("main text: %zd bytes encoded as %s in %d\n",
5409 strlen(buf), out_codeset, encoding);
5411 /* check for line length limit */
5412 if (action == COMPOSE_WRITE_FOR_SEND &&
5413 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5414 check_line_length(buf, 1000, &line) < 0) {
5418 msg = g_strdup_printf
5419 (_("Line %d exceeds the line length limit (998 bytes).\n"
5420 "The contents of the message might be broken on the way to the delivery.\n"
5422 "Send it anyway?"), line + 1);
5423 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5425 if (aval != G_ALERTALTERNATE) {
5431 if (encoding != ENC_UNKNOWN)
5432 procmime_encode_content(mimetext, encoding);
5434 /* append attachment parts */
5435 if (compose_use_attach(compose) && attach_parts) {
5436 MimeInfo *mimempart;
5437 gchar *boundary = NULL;
5438 mimempart = procmime_mimeinfo_new();
5439 mimempart->content = MIMECONTENT_EMPTY;
5440 mimempart->type = MIMETYPE_MULTIPART;
5441 mimempart->subtype = g_strdup("mixed");
5445 boundary = generate_mime_boundary(NULL);
5446 } while (strstr(buf, boundary) != NULL);
5448 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5451 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5453 g_node_append(mimempart->node, mimetext->node);
5454 g_node_append(mimemsg->node, mimempart->node);
5456 if (compose_add_attachments(compose, mimempart) < 0)
5459 g_node_append(mimemsg->node, mimetext->node);
5463 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5464 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5465 /* extract name and address */
5466 if (strstr(spec, " <") && strstr(spec, ">")) {
5467 from_addr = g_strdup(strrchr(spec, '<')+1);
5468 *(strrchr(from_addr, '>')) = '\0';
5469 from_name = g_strdup(spec);
5470 *(strrchr(from_name, '<')) = '\0';
5477 /* sign message if sending */
5478 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5479 privacy_system_can_sign(compose->privacy_system))
5480 if (!privacy_sign(compose->privacy_system, mimemsg,
5481 compose->account, from_addr)) {
5488 procmime_write_mimeinfo(mimemsg, fp);
5490 procmime_mimeinfo_free_all(mimemsg);
5495 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5497 GtkTextBuffer *buffer;
5498 GtkTextIter start, end;
5503 if ((fp = g_fopen(file, "wb")) == NULL) {
5504 FILE_OP_ERROR(file, "fopen");
5508 /* chmod for security */
5509 if (change_file_mode_rw(fp, file) < 0) {
5510 FILE_OP_ERROR(file, "chmod");
5511 g_warning("can't change file mode\n");
5514 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5515 gtk_text_buffer_get_start_iter(buffer, &start);
5516 gtk_text_buffer_get_end_iter(buffer, &end);
5517 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5519 chars = conv_codeset_strdup
5520 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5523 if (!chars) return -1;
5526 len = strlen(chars);
5527 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5528 FILE_OP_ERROR(file, "fwrite");
5537 if (fclose(fp) == EOF) {
5538 FILE_OP_ERROR(file, "fclose");
5545 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5548 MsgInfo *msginfo = compose->targetinfo;
5550 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5551 if (!msginfo) return -1;
5553 if (!force && MSG_IS_LOCKED(msginfo->flags))
5556 item = msginfo->folder;
5557 cm_return_val_if_fail(item != NULL, -1);
5559 if (procmsg_msg_exist(msginfo) &&
5560 (folder_has_parent_of_type(item, F_QUEUE) ||
5561 folder_has_parent_of_type(item, F_DRAFT)
5562 || msginfo == compose->autosaved_draft)) {
5563 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5564 g_warning("can't remove the old message\n");
5567 debug_print("removed reedit target %d\n", msginfo->msgnum);
5574 static void compose_remove_draft(Compose *compose)
5577 MsgInfo *msginfo = compose->targetinfo;
5578 drafts = account_get_special_folder(compose->account, F_DRAFT);
5580 if (procmsg_msg_exist(msginfo)) {
5581 folder_item_remove_msg(drafts, msginfo->msgnum);
5586 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5587 gboolean remove_reedit_target)
5589 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5592 static gboolean compose_warn_encryption(Compose *compose)
5594 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5595 AlertValue val = G_ALERTALTERNATE;
5597 if (warning == NULL)
5600 val = alertpanel_full(_("Encryption warning"), warning,
5601 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5602 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5603 if (val & G_ALERTDISABLE) {
5604 val &= ~G_ALERTDISABLE;
5605 if (val == G_ALERTALTERNATE)
5606 privacy_inhibit_encrypt_warning(compose->privacy_system,
5610 if (val == G_ALERTALTERNATE) {
5617 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5618 gchar **msgpath, gboolean check_subject,
5619 gboolean remove_reedit_target)
5626 static gboolean lock = FALSE;
5627 PrefsAccount *mailac = NULL, *newsac = NULL;
5628 gboolean err = FALSE;
5630 debug_print("queueing message...\n");
5631 cm_return_val_if_fail(compose->account != NULL, -1);
5635 if (compose_check_entries(compose, check_subject) == FALSE) {
5637 if (compose->batch) {
5638 gtk_widget_show_all(compose->window);
5643 if (!compose->to_list && !compose->newsgroup_list) {
5644 g_warning("can't get recipient list.");
5649 if (compose->to_list) {
5650 if (compose->account->protocol != A_NNTP)
5651 mailac = compose->account;
5652 else if (cur_account && cur_account->protocol != A_NNTP)
5653 mailac = cur_account;
5654 else if (!(mailac = compose_current_mail_account())) {
5656 alertpanel_error(_("No account for sending mails available!"));
5661 if (compose->newsgroup_list) {
5662 if (compose->account->protocol == A_NNTP)
5663 newsac = compose->account;
5666 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5671 /* write queue header */
5672 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5673 G_DIR_SEPARATOR, compose, (guint) rand());
5674 debug_print("queuing to %s\n", tmp);
5675 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5676 FILE_OP_ERROR(tmp, "fopen");
5682 if (change_file_mode_rw(fp, tmp) < 0) {
5683 FILE_OP_ERROR(tmp, "chmod");
5684 g_warning("can't change file mode\n");
5687 /* queueing variables */
5688 err |= (fprintf(fp, "AF:\n") < 0);
5689 err |= (fprintf(fp, "NF:0\n") < 0);
5690 err |= (fprintf(fp, "PS:10\n") < 0);
5691 err |= (fprintf(fp, "SRH:1\n") < 0);
5692 err |= (fprintf(fp, "SFN:\n") < 0);
5693 err |= (fprintf(fp, "DSR:\n") < 0);
5695 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5697 err |= (fprintf(fp, "MID:\n") < 0);
5698 err |= (fprintf(fp, "CFG:\n") < 0);
5699 err |= (fprintf(fp, "PT:0\n") < 0);
5700 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5701 err |= (fprintf(fp, "RQ:\n") < 0);
5703 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5705 err |= (fprintf(fp, "SSV:\n") < 0);
5707 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5709 err |= (fprintf(fp, "NSV:\n") < 0);
5710 err |= (fprintf(fp, "SSH:\n") < 0);
5711 /* write recepient list */
5712 if (compose->to_list) {
5713 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5714 for (cur = compose->to_list->next; cur != NULL;
5716 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5717 err |= (fprintf(fp, "\n") < 0);
5719 /* write newsgroup list */
5720 if (compose->newsgroup_list) {
5721 err |= (fprintf(fp, "NG:") < 0);
5722 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5723 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5724 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5725 err |= (fprintf(fp, "\n") < 0);
5727 /* Sylpheed account IDs */
5729 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5731 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5734 if (compose->privacy_system != NULL) {
5735 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5736 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5737 if (compose->use_encryption) {
5739 if (!compose_warn_encryption(compose)) {
5746 if (mailac && mailac->encrypt_to_self) {
5747 GSList *tmp_list = g_slist_copy(compose->to_list);
5748 tmp_list = g_slist_append(tmp_list, compose->account->address);
5749 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5750 g_slist_free(tmp_list);
5752 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5754 if (encdata != NULL) {
5755 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5756 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5757 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5759 } /* else we finally dont want to encrypt */
5761 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5762 /* and if encdata was null, it means there's been a problem in
5774 /* Save copy folder */
5775 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5776 gchar *savefolderid;
5778 savefolderid = compose_get_save_to(compose);
5779 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5780 g_free(savefolderid);
5782 /* Save copy folder */
5783 if (compose->return_receipt) {
5784 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5786 /* Message-ID of message replying to */
5787 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5790 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5791 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5794 /* Message-ID of message forwarding to */
5795 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5798 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5799 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5803 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5804 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5806 /* end of headers */
5807 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5809 if (compose->redirect_filename != NULL) {
5810 if (compose_redirect_write_to_file(compose, fp) < 0) {
5819 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5824 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5828 g_warning("failed to write queue message\n");
5835 if (fclose(fp) == EOF) {
5836 FILE_OP_ERROR(tmp, "fclose");
5843 if (item && *item) {
5846 queue = account_get_special_folder(compose->account, F_QUEUE);
5849 g_warning("can't find queue folder\n");
5855 folder_item_scan(queue);
5856 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5857 g_warning("can't queue the message\n");
5864 if (msgpath == NULL) {
5870 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5871 compose_remove_reedit_target(compose, FALSE);
5874 if ((msgnum != NULL) && (item != NULL)) {
5882 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5885 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5887 struct stat statbuf;
5888 gchar *type, *subtype;
5889 GtkTreeModel *model;
5892 model = gtk_tree_view_get_model(tree_view);
5894 if (!gtk_tree_model_get_iter_first(model, &iter))
5897 gtk_tree_model_get(model, &iter,
5901 if (!is_file_exist(ainfo->file)) {
5902 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5903 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5904 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5906 if (val == G_ALERTDEFAULT) {
5911 mimepart = procmime_mimeinfo_new();
5912 mimepart->content = MIMECONTENT_FILE;
5913 mimepart->data.filename = g_strdup(ainfo->file);
5914 mimepart->tmp = FALSE; /* or we destroy our attachment */
5915 mimepart->offset = 0;
5917 g_stat(ainfo->file, &statbuf);
5918 mimepart->length = statbuf.st_size;
5920 type = g_strdup(ainfo->content_type);
5922 if (!strchr(type, '/')) {
5924 type = g_strdup("application/octet-stream");
5927 subtype = strchr(type, '/') + 1;
5928 *(subtype - 1) = '\0';
5929 mimepart->type = procmime_get_media_type(type);
5930 mimepart->subtype = g_strdup(subtype);
5933 if (mimepart->type == MIMETYPE_MESSAGE &&
5934 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5935 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5938 if (mimepart->type == MIMETYPE_APPLICATION &&
5939 !strcmp2(mimepart->subtype, "octet-stream"))
5940 g_hash_table_insert(mimepart->typeparameters,
5941 g_strdup("name"), g_strdup(ainfo->name));
5942 g_hash_table_insert(mimepart->dispositionparameters,
5943 g_strdup("filename"), g_strdup(ainfo->name));
5944 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5948 if (compose->use_signing) {
5949 if (ainfo->encoding == ENC_7BIT)
5950 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5951 else if (ainfo->encoding == ENC_8BIT)
5952 ainfo->encoding = ENC_BASE64;
5955 procmime_encode_content(mimepart, ainfo->encoding);
5957 g_node_append(parent->node, mimepart->node);
5958 } while (gtk_tree_model_iter_next(model, &iter));
5963 #define IS_IN_CUSTOM_HEADER(header) \
5964 (compose->account->add_customhdr && \
5965 custom_header_find(compose->account->customhdr_list, header) != NULL)
5967 static void compose_add_headerfield_from_headerlist(Compose *compose,
5969 const gchar *fieldname,
5970 const gchar *seperator)
5972 gchar *str, *fieldname_w_colon;
5973 gboolean add_field = FALSE;
5975 ComposeHeaderEntry *headerentry;
5976 const gchar *headerentryname;
5977 const gchar *trans_fieldname;
5980 if (IS_IN_CUSTOM_HEADER(fieldname))
5983 debug_print("Adding %s-fields\n", fieldname);
5985 fieldstr = g_string_sized_new(64);
5987 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5988 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5990 for (list = compose->header_list; list; list = list->next) {
5991 headerentry = ((ComposeHeaderEntry *)list->data);
5992 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5994 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5995 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5997 if (str[0] != '\0') {
5999 g_string_append(fieldstr, seperator);
6000 g_string_append(fieldstr, str);
6009 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6010 compose_convert_header
6011 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6012 strlen(fieldname) + 2, TRUE);
6013 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6017 g_free(fieldname_w_colon);
6018 g_string_free(fieldstr, TRUE);
6023 static gchar *compose_get_header(Compose *compose)
6025 gchar buf[BUFFSIZE];
6026 const gchar *entry_str;
6030 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6032 gchar *from_name = NULL, *from_address = NULL;
6035 cm_return_val_if_fail(compose->account != NULL, NULL);
6036 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6038 header = g_string_sized_new(64);
6041 get_rfc822_date(buf, sizeof(buf));
6042 g_string_append_printf(header, "Date: %s\n", buf);
6046 if (compose->account->name && *compose->account->name) {
6048 QUOTE_IF_REQUIRED(buf, compose->account->name);
6049 tmp = g_strdup_printf("%s <%s>",
6050 buf, compose->account->address);
6052 tmp = g_strdup_printf("%s",
6053 compose->account->address);
6055 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6056 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6058 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6059 from_address = g_strdup(compose->account->address);
6061 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6062 /* extract name and address */
6063 if (strstr(spec, " <") && strstr(spec, ">")) {
6064 from_address = g_strdup(strrchr(spec, '<')+1);
6065 *(strrchr(from_address, '>')) = '\0';
6066 from_name = g_strdup(spec);
6067 *(strrchr(from_name, '<')) = '\0';
6070 from_address = g_strdup(spec);
6077 if (from_name && *from_name) {
6078 compose_convert_header
6079 (compose, buf, sizeof(buf), from_name,
6080 strlen("From: "), TRUE);
6081 QUOTE_IF_REQUIRED(name, buf);
6083 g_string_append_printf(header, "From: %s <%s>\n",
6084 name, from_address);
6086 g_string_append_printf(header, "From: %s\n", from_address);
6089 g_free(from_address);
6092 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6095 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6098 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6101 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6104 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6106 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6109 compose_convert_header(compose, buf, sizeof(buf), str,
6110 strlen("Subject: "), FALSE);
6111 g_string_append_printf(header, "Subject: %s\n", buf);
6117 if (compose->account->set_domain && compose->account->domain) {
6118 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6119 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6120 g_snprintf(buf, sizeof(buf), "%s",
6121 strchr(compose->account->address, '@') ?
6122 strchr(compose->account->address, '@')+1 :
6123 compose->account->address);
6125 g_snprintf(buf, sizeof(buf), "%s", "");
6128 if (compose->account->gen_msgid) {
6130 if (compose->account->msgid_with_addr) {
6131 addr = compose->account->address;
6133 generate_msgid(buf, sizeof(buf), addr);
6134 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6135 compose->msgid = g_strdup(buf);
6137 compose->msgid = NULL;
6140 if (compose->remove_references == FALSE) {
6142 if (compose->inreplyto && compose->to_list)
6143 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6146 if (compose->references)
6147 g_string_append_printf(header, "References: %s\n", compose->references);
6151 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6154 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6157 if (compose->account->organization &&
6158 strlen(compose->account->organization) &&
6159 !IS_IN_CUSTOM_HEADER("Organization")) {
6160 compose_convert_header(compose, buf, sizeof(buf),
6161 compose->account->organization,
6162 strlen("Organization: "), FALSE);
6163 g_string_append_printf(header, "Organization: %s\n", buf);
6166 /* Program version and system info */
6167 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6168 !compose->newsgroup_list) {
6169 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6171 gtk_major_version, gtk_minor_version, gtk_micro_version,
6174 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6175 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6177 gtk_major_version, gtk_minor_version, gtk_micro_version,
6181 /* custom headers */
6182 if (compose->account->add_customhdr) {
6185 for (cur = compose->account->customhdr_list; cur != NULL;
6187 CustomHeader *chdr = (CustomHeader *)cur->data;
6189 if (custom_header_is_allowed(chdr->name)) {
6190 compose_convert_header
6191 (compose, buf, sizeof(buf),
6192 chdr->value ? chdr->value : "",
6193 strlen(chdr->name) + 2, FALSE);
6194 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6199 /* Automatic Faces and X-Faces */
6200 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6201 g_string_append_printf(header, "X-Face: %s\n", buf);
6203 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6204 g_string_append_printf(header, "X-Face: %s\n", buf);
6206 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6207 g_string_append_printf(header, "Face: %s\n", buf);
6209 else if (get_default_face (buf, sizeof(buf)) == 0) {
6210 g_string_append_printf(header, "Face: %s\n", buf);
6214 switch (compose->priority) {
6215 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6216 "X-Priority: 1 (Highest)\n");
6218 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6219 "X-Priority: 2 (High)\n");
6221 case PRIORITY_NORMAL: break;
6222 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6223 "X-Priority: 4 (Low)\n");
6225 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6226 "X-Priority: 5 (Lowest)\n");
6228 default: debug_print("compose: priority unknown : %d\n",
6232 /* Request Return Receipt */
6233 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6234 if (compose->return_receipt) {
6235 if (compose->account->name
6236 && *compose->account->name) {
6237 compose_convert_header(compose, buf, sizeof(buf),
6238 compose->account->name,
6239 strlen("Disposition-Notification-To: "),
6241 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6243 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6247 /* get special headers */
6248 for (list = compose->header_list; list; list = list->next) {
6249 ComposeHeaderEntry *headerentry;
6252 gchar *headername_wcolon;
6253 const gchar *headername_trans;
6256 gboolean standard_header = FALSE;
6258 headerentry = ((ComposeHeaderEntry *)list->data);
6260 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6262 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6267 if (!strstr(tmp, ":")) {
6268 headername_wcolon = g_strconcat(tmp, ":", NULL);
6269 headername = g_strdup(tmp);
6271 headername_wcolon = g_strdup(tmp);
6272 headername = g_strdup(strtok(tmp, ":"));
6276 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6277 Xstrdup_a(headervalue, entry_str, return NULL);
6278 subst_char(headervalue, '\r', ' ');
6279 subst_char(headervalue, '\n', ' ');
6280 string = std_headers;
6281 while (*string != NULL) {
6282 headername_trans = prefs_common_translated_header_name(*string);
6283 if (!strcmp(headername_trans, headername_wcolon))
6284 standard_header = TRUE;
6287 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6288 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6291 g_free(headername_wcolon);
6295 g_string_free(header, FALSE);
6300 #undef IS_IN_CUSTOM_HEADER
6302 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6303 gint header_len, gboolean addr_field)
6305 gchar *tmpstr = NULL;
6306 const gchar *out_codeset = NULL;
6308 cm_return_if_fail(src != NULL);
6309 cm_return_if_fail(dest != NULL);
6311 if (len < 1) return;
6313 tmpstr = g_strdup(src);
6315 subst_char(tmpstr, '\n', ' ');
6316 subst_char(tmpstr, '\r', ' ');
6319 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6320 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6321 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6326 codeconv_set_strict(TRUE);
6327 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6328 conv_get_charset_str(compose->out_encoding));
6329 codeconv_set_strict(FALSE);
6331 if (!dest || *dest == '\0') {
6332 gchar *test_conv_global_out = NULL;
6333 gchar *test_conv_reply = NULL;
6335 /* automatic mode. be automatic. */
6336 codeconv_set_strict(TRUE);
6338 out_codeset = conv_get_outgoing_charset_str();
6340 debug_print("trying to convert to %s\n", out_codeset);
6341 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6344 if (!test_conv_global_out && compose->orig_charset
6345 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6346 out_codeset = compose->orig_charset;
6347 debug_print("failure; trying to convert to %s\n", out_codeset);
6348 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6351 if (!test_conv_global_out && !test_conv_reply) {
6353 out_codeset = CS_INTERNAL;
6354 debug_print("finally using %s\n", out_codeset);
6356 g_free(test_conv_global_out);
6357 g_free(test_conv_reply);
6358 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6360 codeconv_set_strict(FALSE);
6365 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6369 cm_return_if_fail(user_data != NULL);
6371 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6372 g_strstrip(address);
6373 if (*address != '\0') {
6374 gchar *name = procheader_get_fromname(address);
6375 extract_address(address);
6376 addressbook_add_contact(name, address, NULL, NULL);
6381 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6383 GtkWidget *menuitem;
6386 cm_return_if_fail(menu != NULL);
6387 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6389 menuitem = gtk_separator_menu_item_new();
6390 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6391 gtk_widget_show(menuitem);
6393 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6394 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6396 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6397 g_strstrip(address);
6398 if (*address == '\0') {
6399 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6402 g_signal_connect(G_OBJECT(menuitem), "activate",
6403 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6404 gtk_widget_show(menuitem);
6407 static void compose_create_header_entry(Compose *compose)
6409 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6416 const gchar *header = NULL;
6417 ComposeHeaderEntry *headerentry;
6418 gboolean standard_header = FALSE;
6419 GtkListStore *model;
6421 #if !(GTK_CHECK_VERSION(2,12,0))
6422 GtkTooltips *tips = compose->tooltips;
6425 headerentry = g_new0(ComposeHeaderEntry, 1);
6428 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6429 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6430 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6432 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6434 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6436 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6437 COMPOSE_NEWSGROUPS);
6438 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6440 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6441 COMPOSE_FOLLOWUPTO);
6443 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6444 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6445 G_CALLBACK(compose_grab_focus_cb), compose);
6446 gtk_widget_show(combo);
6447 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6448 compose->header_nextrow, compose->header_nextrow+1,
6449 GTK_SHRINK, GTK_FILL, 0, 0);
6450 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6451 const gchar *last_header_entry = gtk_entry_get_text(
6452 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6454 while (*string != NULL) {
6455 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6456 standard_header = TRUE;
6459 if (standard_header)
6460 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6462 if (!compose->header_last || !standard_header) {
6463 switch(compose->account->protocol) {
6465 header = prefs_common_translated_header_name("Newsgroups:");
6468 header = prefs_common_translated_header_name("To:");
6473 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6475 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6476 G_CALLBACK(compose_grab_focus_cb), compose);
6478 /* Entry field with cleanup button */
6479 #if GTK_CHECK_VERSION(2, 8, 0)
6480 button = gtk_button_new();
6481 gtk_button_set_image(GTK_BUTTON(button),
6482 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6484 button = gtk_button_new_with_label(_("Clear"));
6486 gtk_widget_show(button);
6487 CLAWS_SET_TIP(button,
6488 _("Delete entry contents"));
6489 entry = gtk_entry_new();
6490 gtk_widget_show(entry);
6491 CLAWS_SET_TIP(entry,
6492 _("Use <tab> to autocomplete from addressbook"));
6493 hbox = gtk_hbox_new (FALSE, 0);
6494 gtk_widget_show(hbox);
6495 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6496 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6497 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6498 compose->header_nextrow, compose->header_nextrow+1,
6499 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6501 g_signal_connect(G_OBJECT(entry), "key-press-event",
6502 G_CALLBACK(compose_headerentry_key_press_event_cb),
6504 g_signal_connect(G_OBJECT(entry), "changed",
6505 G_CALLBACK(compose_headerentry_changed_cb),
6507 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6508 G_CALLBACK(compose_grab_focus_cb), compose);
6510 g_signal_connect(G_OBJECT(button), "clicked",
6511 G_CALLBACK(compose_headerentry_button_clicked_cb),
6515 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6516 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6517 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6518 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6519 G_CALLBACK(compose_header_drag_received_cb),
6521 g_signal_connect(G_OBJECT(entry), "drag-drop",
6522 G_CALLBACK(compose_drag_drop),
6524 g_signal_connect(G_OBJECT(entry), "populate-popup",
6525 G_CALLBACK(compose_entry_popup_extend),
6528 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6530 headerentry->compose = compose;
6531 headerentry->combo = combo;
6532 headerentry->entry = entry;
6533 headerentry->button = button;
6534 headerentry->hbox = hbox;
6535 headerentry->headernum = compose->header_nextrow;
6536 headerentry->type = PREF_NONE;
6538 compose->header_nextrow++;
6539 compose->header_last = headerentry;
6540 compose->header_list =
6541 g_slist_append(compose->header_list,
6545 static void compose_add_header_entry(Compose *compose, const gchar *header,
6546 gchar *text, ComposePrefType pref_type)
6548 ComposeHeaderEntry *last_header = compose->header_last;
6549 gchar *tmp = g_strdup(text), *email;
6551 extract_address(tmp);
6552 email = g_utf8_strdown(tmp, -1);
6554 if (g_hash_table_lookup(compose->email_hashtable, email) != NULL) {
6555 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6556 header, text, (gint) pref_type);
6562 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6563 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6564 last_header->type = pref_type;
6566 g_hash_table_insert(compose->email_hashtable, email,
6567 GUINT_TO_POINTER(1));
6571 static void compose_destroy_headerentry(Compose *compose,
6572 ComposeHeaderEntry *headerentry)
6574 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6577 extract_address(text);
6578 email = g_utf8_strdown(text, -1);
6579 g_hash_table_remove(compose->email_hashtable, email);
6583 gtk_widget_destroy(headerentry->combo);
6584 gtk_widget_destroy(headerentry->entry);
6585 gtk_widget_destroy(headerentry->button);
6586 gtk_widget_destroy(headerentry->hbox);
6587 g_free(headerentry);
6590 static void compose_remove_header_entries(Compose *compose)
6593 for (list = compose->header_list; list; list = list->next)
6594 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6596 compose->header_last = NULL;
6597 g_slist_free(compose->header_list);
6598 compose->header_list = NULL;
6599 compose->header_nextrow = 1;
6600 compose_create_header_entry(compose);
6603 static GtkWidget *compose_create_header(Compose *compose)
6605 GtkWidget *from_optmenu_hbox;
6606 GtkWidget *header_scrolledwin;
6607 GtkWidget *header_table;
6611 /* header labels and entries */
6612 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6613 gtk_widget_show(header_scrolledwin);
6614 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6616 header_table = gtk_table_new(2, 2, FALSE);
6617 gtk_widget_show(header_table);
6618 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6619 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6620 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6623 /* option menu for selecting accounts */
6624 from_optmenu_hbox = compose_account_option_menu_create(compose);
6625 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6626 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6629 compose->header_table = header_table;
6630 compose->header_list = NULL;
6631 compose->header_nextrow = count;
6633 compose_create_header_entry(compose);
6635 compose->table = NULL;
6637 return header_scrolledwin ;
6640 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6642 Compose *compose = (Compose *)data;
6643 GdkEventButton event;
6646 event.time = gtk_get_current_event_time();
6648 return attach_button_pressed(compose->attach_clist, &event, compose);
6651 static GtkWidget *compose_create_attach(Compose *compose)
6653 GtkWidget *attach_scrwin;
6654 GtkWidget *attach_clist;
6656 GtkListStore *store;
6657 GtkCellRenderer *renderer;
6658 GtkTreeViewColumn *column;
6659 GtkTreeSelection *selection;
6661 /* attachment list */
6662 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6663 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6664 GTK_POLICY_AUTOMATIC,
6665 GTK_POLICY_AUTOMATIC);
6666 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6668 store = gtk_list_store_new(N_ATTACH_COLS,
6673 G_TYPE_AUTO_POINTER,
6675 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6676 (GTK_TREE_MODEL(store)));
6677 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6678 g_object_unref(store);
6680 renderer = gtk_cell_renderer_text_new();
6681 column = gtk_tree_view_column_new_with_attributes
6682 (_("Mime type"), renderer, "text",
6683 COL_MIMETYPE, NULL);
6684 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6686 renderer = gtk_cell_renderer_text_new();
6687 column = gtk_tree_view_column_new_with_attributes
6688 (_("Size"), renderer, "text",
6690 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6692 renderer = gtk_cell_renderer_text_new();
6693 column = gtk_tree_view_column_new_with_attributes
6694 (_("Name"), renderer, "text",
6696 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6698 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6699 prefs_common.use_stripes_everywhere);
6700 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6701 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6703 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6704 G_CALLBACK(attach_selected), compose);
6705 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6706 G_CALLBACK(attach_button_pressed), compose);
6708 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6709 G_CALLBACK(popup_attach_button_pressed), compose);
6711 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6712 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6713 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6714 G_CALLBACK(popup_attach_button_pressed), compose);
6716 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6717 G_CALLBACK(attach_key_pressed), compose);
6720 gtk_drag_dest_set(attach_clist,
6721 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6722 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6723 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6724 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6725 G_CALLBACK(compose_attach_drag_received_cb),
6727 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6728 G_CALLBACK(compose_drag_drop),
6731 compose->attach_scrwin = attach_scrwin;
6732 compose->attach_clist = attach_clist;
6734 return attach_scrwin;
6737 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6738 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6740 static GtkWidget *compose_create_others(Compose *compose)
6743 GtkWidget *savemsg_checkbtn;
6744 GtkWidget *savemsg_combo;
6745 GtkWidget *savemsg_select;
6748 gchar *folderidentifier;
6750 /* Table for settings */
6751 table = gtk_table_new(3, 1, FALSE);
6752 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6753 gtk_widget_show(table);
6754 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6757 /* Save Message to folder */
6758 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6759 gtk_widget_show(savemsg_checkbtn);
6760 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6761 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6762 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6764 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6765 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6767 savemsg_combo = gtk_combo_box_entry_new_text();
6768 compose->savemsg_checkbtn = savemsg_checkbtn;
6769 compose->savemsg_combo = savemsg_combo;
6770 gtk_widget_show(savemsg_combo);
6772 if (prefs_common.compose_save_to_history)
6773 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6774 prefs_common.compose_save_to_history);
6776 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6777 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6778 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6779 G_CALLBACK(compose_grab_focus_cb), compose);
6780 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6781 folderidentifier = folder_item_get_identifier(account_get_special_folder
6782 (compose->account, F_OUTBOX));
6783 compose_set_save_to(compose, folderidentifier);
6784 g_free(folderidentifier);
6787 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6788 gtk_widget_show(savemsg_select);
6789 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6790 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6791 G_CALLBACK(compose_savemsg_select_cb),
6799 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6801 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6802 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6805 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6810 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6813 path = folder_item_get_identifier(dest);
6815 compose_set_save_to(compose, path);
6819 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6820 GdkAtom clip, GtkTextIter *insert_place);
6823 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6827 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6829 if (event->button == 3) {
6831 GtkTextIter sel_start, sel_end;
6832 gboolean stuff_selected;
6834 /* move the cursor to allow GtkAspell to check the word
6835 * under the mouse */
6836 if (event->x && event->y) {
6837 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6838 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6840 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6843 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6844 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6847 stuff_selected = gtk_text_buffer_get_selection_bounds(
6849 &sel_start, &sel_end);
6851 gtk_text_buffer_place_cursor (buffer, &iter);
6852 /* reselect stuff */
6854 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6855 gtk_text_buffer_select_range(buffer,
6856 &sel_start, &sel_end);
6858 return FALSE; /* pass the event so that the right-click goes through */
6861 if (event->button == 2) {
6866 /* get the middle-click position to paste at the correct place */
6867 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6868 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6870 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6873 entry_paste_clipboard(compose, text,
6874 prefs_common.linewrap_pastes,
6875 GDK_SELECTION_PRIMARY, &iter);
6883 static void compose_spell_menu_changed(void *data)
6885 Compose *compose = (Compose *)data;
6887 GtkWidget *menuitem;
6888 GtkWidget *parent_item;
6889 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6892 if (compose->gtkaspell == NULL)
6895 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6896 "/Menu/Spelling/Options");
6898 /* setting the submenu removes /Spelling/Options from the factory
6899 * so we need to save it */
6901 if (parent_item == NULL) {
6902 parent_item = compose->aspell_options_menu;
6903 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6905 compose->aspell_options_menu = parent_item;
6907 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6909 spell_menu = g_slist_reverse(spell_menu);
6910 for (items = spell_menu;
6911 items; items = items->next) {
6912 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6913 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6914 gtk_widget_show(GTK_WIDGET(menuitem));
6916 g_slist_free(spell_menu);
6918 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6919 gtk_widget_show(parent_item);
6922 static void compose_dict_changed(void *data)
6924 Compose *compose = (Compose *) data;
6926 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6929 gtkaspell_highlight_all(compose->gtkaspell);
6930 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6934 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6936 Compose *compose = (Compose *)data;
6937 GdkEventButton event;
6940 event.time = gtk_get_current_event_time();
6944 return text_clicked(compose->text, &event, compose);
6947 static gboolean compose_force_window_origin = TRUE;
6948 static Compose *compose_create(PrefsAccount *account,
6957 GtkWidget *handlebox;
6959 GtkWidget *notebook;
6961 GtkWidget *attach_hbox;
6962 GtkWidget *attach_lab1;
6963 GtkWidget *attach_lab2;
6968 GtkWidget *subject_hbox;
6969 GtkWidget *subject_frame;
6970 GtkWidget *subject_entry;
6974 GtkWidget *edit_vbox;
6975 GtkWidget *ruler_hbox;
6977 GtkWidget *scrolledwin;
6979 GtkTextBuffer *buffer;
6980 GtkClipboard *clipboard;
6983 UndoMain *undostruct;
6985 gchar *titles[N_ATTACH_COLS];
6986 GtkWidget *popupmenu;
6987 GtkWidget *tmpl_menu;
6988 GtkActionGroup *action_group = NULL;
6991 GtkAspell * gtkaspell = NULL;
6994 static GdkGeometry geometry;
6996 cm_return_val_if_fail(account != NULL, NULL);
6998 debug_print("Creating compose window...\n");
6999 compose = g_new0(Compose, 1);
7001 titles[COL_MIMETYPE] = _("MIME type");
7002 titles[COL_SIZE] = _("Size");
7003 titles[COL_NAME] = _("Name");
7005 compose->batch = batch;
7006 compose->account = account;
7007 compose->folder = folder;
7009 compose->mutex = g_mutex_new();
7010 compose->set_cursor_pos = -1;
7012 #if !(GTK_CHECK_VERSION(2,12,0))
7013 compose->tooltips = tips;
7016 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7018 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7019 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7021 if (!geometry.max_width) {
7022 geometry.max_width = gdk_screen_width();
7023 geometry.max_height = gdk_screen_height();
7026 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7027 &geometry, GDK_HINT_MAX_SIZE);
7028 if (!geometry.min_width) {
7029 geometry.min_width = 600;
7030 geometry.min_height = 440;
7032 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7033 &geometry, GDK_HINT_MIN_SIZE);
7035 #ifndef GENERIC_UMPC
7036 if (compose_force_window_origin)
7037 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7038 prefs_common.compose_y);
7040 g_signal_connect(G_OBJECT(window), "delete_event",
7041 G_CALLBACK(compose_delete_cb), compose);
7042 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7043 gtk_widget_realize(window);
7045 gtkut_widget_set_composer_icon(window);
7047 vbox = gtk_vbox_new(FALSE, 0);
7048 gtk_container_add(GTK_CONTAINER(window), vbox);
7050 compose->ui_manager = gtk_ui_manager_new();
7051 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7052 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7053 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7054 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7055 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7056 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7057 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7058 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7059 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7060 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7063 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7065 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7068 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7069 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7073 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7078 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7079 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7080 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7081 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7082 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7083 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7084 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7085 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7086 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7087 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7091 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7092 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7094 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7096 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7098 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7099 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7100 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7101 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7103 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7105 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7107 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7108 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7109 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7110 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7111 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7112 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7113 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7114 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7115 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7116 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7117 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7118 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7119 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7121 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7123 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7124 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7125 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7127 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7129 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7131 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7135 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7136 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7137 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7139 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7140 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7144 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7145 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7146 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7157 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7162 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7169 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7173 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7180 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)
7181 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)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7187 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)
7188 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)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7193 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)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7197 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)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7203 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)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7209 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)
7210 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)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7215 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7217 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7221 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7222 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)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7231 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7234 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7235 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7240 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7241 gtk_widget_show_all(menubar);
7243 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7245 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7247 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7250 if (prefs_common.toolbar_detachable) {
7251 handlebox = gtk_handle_box_new();
7253 handlebox = gtk_hbox_new(FALSE, 0);
7255 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7257 gtk_widget_realize(handlebox);
7259 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7262 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7266 vbox2 = gtk_vbox_new(FALSE, 2);
7267 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7268 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7271 notebook = gtk_notebook_new();
7272 gtk_widget_set_size_request(notebook, -1, 130);
7273 gtk_widget_show(notebook);
7275 /* header labels and entries */
7276 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7277 compose_create_header(compose),
7278 gtk_label_new_with_mnemonic(_("Hea_der")));
7279 /* attachment list */
7280 attach_hbox = gtk_hbox_new(FALSE, 0);
7281 gtk_widget_show(attach_hbox);
7283 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7284 gtk_widget_show(attach_lab1);
7285 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7287 attach_lab2 = gtk_label_new("");
7288 gtk_widget_show(attach_lab2);
7289 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7291 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7292 compose_create_attach(compose),
7295 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7296 compose_create_others(compose),
7297 gtk_label_new_with_mnemonic(_("Othe_rs")));
7300 subject_hbox = gtk_hbox_new(FALSE, 0);
7301 gtk_widget_show(subject_hbox);
7303 subject_frame = gtk_frame_new(NULL);
7304 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7305 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7306 gtk_widget_show(subject_frame);
7308 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7309 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7310 gtk_widget_show(subject);
7312 label = gtk_label_new(_("Subject:"));
7313 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7314 gtk_widget_show(label);
7317 subject_entry = claws_spell_entry_new();
7319 subject_entry = gtk_entry_new();
7321 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7322 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7323 G_CALLBACK(compose_grab_focus_cb), compose);
7324 gtk_widget_show(subject_entry);
7325 compose->subject_entry = subject_entry;
7326 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7328 edit_vbox = gtk_vbox_new(FALSE, 0);
7330 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7333 ruler_hbox = gtk_hbox_new(FALSE, 0);
7334 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7336 ruler = gtk_shruler_new();
7337 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7338 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7342 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7343 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7344 GTK_POLICY_AUTOMATIC,
7345 GTK_POLICY_AUTOMATIC);
7346 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7348 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7349 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7351 text = gtk_text_view_new();
7352 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7353 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7354 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7355 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7356 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7358 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7360 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7361 G_CALLBACK(compose_edit_size_alloc),
7363 g_signal_connect(G_OBJECT(buffer), "changed",
7364 G_CALLBACK(compose_changed_cb), compose);
7365 g_signal_connect(G_OBJECT(text), "grab_focus",
7366 G_CALLBACK(compose_grab_focus_cb), compose);
7367 g_signal_connect(G_OBJECT(buffer), "insert_text",
7368 G_CALLBACK(text_inserted), compose);
7369 g_signal_connect(G_OBJECT(text), "button_press_event",
7370 G_CALLBACK(text_clicked), compose);
7372 g_signal_connect(G_OBJECT(text), "popup-menu",
7373 G_CALLBACK(compose_popup_menu), compose);
7375 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7376 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7377 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7378 G_CALLBACK(compose_popup_menu), compose);
7380 g_signal_connect(G_OBJECT(subject_entry), "changed",
7381 G_CALLBACK(compose_changed_cb), compose);
7384 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7385 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7386 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7387 g_signal_connect(G_OBJECT(text), "drag_data_received",
7388 G_CALLBACK(compose_insert_drag_received_cb),
7390 g_signal_connect(G_OBJECT(text), "drag-drop",
7391 G_CALLBACK(compose_drag_drop),
7393 gtk_widget_show_all(vbox);
7395 /* pane between attach clist and text */
7396 paned = gtk_vpaned_new();
7397 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7399 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7400 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7402 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7404 gtk_paned_add1(GTK_PANED(paned), notebook);
7405 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7406 gtk_widget_show_all(paned);
7409 if (prefs_common.textfont) {
7410 PangoFontDescription *font_desc;
7412 font_desc = pango_font_description_from_string
7413 (prefs_common.textfont);
7415 gtk_widget_modify_font(text, font_desc);
7416 pango_font_description_free(font_desc);
7420 gtk_action_group_add_actions(action_group, compose_popup_entries,
7421 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7422 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7423 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7424 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7425 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7426 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7427 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7429 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7431 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7432 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7433 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7435 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7437 undostruct = undo_init(text);
7438 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7441 address_completion_start(window);
7443 compose->window = window;
7444 compose->vbox = vbox;
7445 compose->menubar = menubar;
7446 compose->handlebox = handlebox;
7448 compose->vbox2 = vbox2;
7450 compose->paned = paned;
7452 compose->attach_label = attach_lab2;
7454 compose->notebook = notebook;
7455 compose->edit_vbox = edit_vbox;
7456 compose->ruler_hbox = ruler_hbox;
7457 compose->ruler = ruler;
7458 compose->scrolledwin = scrolledwin;
7459 compose->text = text;
7461 compose->focused_editable = NULL;
7463 compose->popupmenu = popupmenu;
7465 compose->tmpl_menu = tmpl_menu;
7467 compose->mode = mode;
7468 compose->rmode = mode;
7470 compose->targetinfo = NULL;
7471 compose->replyinfo = NULL;
7472 compose->fwdinfo = NULL;
7474 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7475 g_str_equal, (GDestroyNotify) g_free, NULL);
7477 compose->replyto = NULL;
7479 compose->bcc = NULL;
7480 compose->followup_to = NULL;
7482 compose->ml_post = NULL;
7484 compose->inreplyto = NULL;
7485 compose->references = NULL;
7486 compose->msgid = NULL;
7487 compose->boundary = NULL;
7489 compose->autowrap = prefs_common.autowrap;
7490 compose->autoindent = prefs_common.auto_indent;
7491 compose->use_signing = FALSE;
7492 compose->use_encryption = FALSE;
7493 compose->privacy_system = NULL;
7495 compose->modified = FALSE;
7497 compose->return_receipt = FALSE;
7499 compose->to_list = NULL;
7500 compose->newsgroup_list = NULL;
7502 compose->undostruct = undostruct;
7504 compose->sig_str = NULL;
7506 compose->exteditor_file = NULL;
7507 compose->exteditor_pid = -1;
7508 compose->exteditor_tag = -1;
7509 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7512 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7513 if (mode != COMPOSE_REDIRECT) {
7514 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7515 strcmp(prefs_common.dictionary, "")) {
7516 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7517 prefs_common.alt_dictionary,
7518 conv_get_locale_charset_str(),
7519 prefs_common.misspelled_col,
7520 prefs_common.check_while_typing,
7521 prefs_common.recheck_when_changing_dict,
7522 prefs_common.use_alternate,
7523 prefs_common.use_both_dicts,
7524 GTK_TEXT_VIEW(text),
7525 GTK_WINDOW(compose->window),
7526 compose_dict_changed,
7527 compose_spell_menu_changed,
7530 alertpanel_error(_("Spell checker could not "
7532 gtkaspell_checkers_strerror());
7533 gtkaspell_checkers_reset_error();
7535 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7539 compose->gtkaspell = gtkaspell;
7540 compose_spell_menu_changed(compose);
7541 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7544 compose_select_account(compose, account, TRUE);
7546 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7547 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7549 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7550 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7552 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7553 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7555 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7556 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7558 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7559 if (account->protocol != A_NNTP)
7560 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7561 prefs_common_translated_header_name("To:"));
7563 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7564 prefs_common_translated_header_name("Newsgroups:"));
7566 addressbook_set_target_compose(compose);
7568 if (mode != COMPOSE_REDIRECT)
7569 compose_set_template_menu(compose);
7571 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7574 compose_list = g_list_append(compose_list, compose);
7576 if (!prefs_common.show_ruler)
7577 gtk_widget_hide(ruler_hbox);
7579 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7582 compose->priority = PRIORITY_NORMAL;
7583 compose_update_priority_menu_item(compose);
7585 compose_set_out_encoding(compose);
7588 compose_update_actions_menu(compose);
7590 /* Privacy Systems menu */
7591 compose_update_privacy_systems_menu(compose);
7593 activate_privacy_system(compose, account, TRUE);
7594 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7596 gtk_widget_realize(window);
7598 gtk_widget_show(window);
7600 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7601 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7608 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7613 GtkWidget *optmenubox;
7616 GtkWidget *from_name = NULL;
7617 #if !(GTK_CHECK_VERSION(2,12,0))
7618 GtkTooltips *tips = compose->tooltips;
7621 gint num = 0, def_menu = 0;
7623 accounts = account_get_list();
7624 cm_return_val_if_fail(accounts != NULL, NULL);
7626 optmenubox = gtk_event_box_new();
7627 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7628 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7630 hbox = gtk_hbox_new(FALSE, 6);
7631 from_name = gtk_entry_new();
7633 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7634 G_CALLBACK(compose_grab_focus_cb), compose);
7636 for (; accounts != NULL; accounts = accounts->next, num++) {
7637 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7638 gchar *name, *from = NULL;
7640 if (ac == compose->account) def_menu = num;
7642 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7645 if (ac == compose->account) {
7646 if (ac->name && *ac->name) {
7648 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7649 from = g_strdup_printf("%s <%s>",
7651 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7653 from = g_strdup_printf("%s",
7655 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7658 COMBOBOX_ADD(menu, name, ac->account_id);
7663 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7665 g_signal_connect(G_OBJECT(optmenu), "changed",
7666 G_CALLBACK(account_activated),
7668 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7669 G_CALLBACK(compose_entry_popup_extend),
7672 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7673 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7675 CLAWS_SET_TIP(optmenubox,
7676 _("Account to use for this email"));
7677 CLAWS_SET_TIP(from_name,
7678 _("Sender address to be used"));
7680 compose->from_name = from_name;
7685 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7687 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7688 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7689 Compose *compose = (Compose *) data;
7691 compose->priority = value;
7695 static void compose_reply_change_mode(Compose *compose,
7698 gboolean was_modified = compose->modified;
7700 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7702 cm_return_if_fail(compose->replyinfo != NULL);
7704 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7706 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7708 if (action == COMPOSE_REPLY_TO_ALL)
7710 if (action == COMPOSE_REPLY_TO_SENDER)
7712 if (action == COMPOSE_REPLY_TO_LIST)
7715 compose_remove_header_entries(compose);
7716 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7717 if (compose->account->set_autocc && compose->account->auto_cc)
7718 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7720 if (compose->account->set_autobcc && compose->account->auto_bcc)
7721 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7723 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7724 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7725 compose_show_first_last_header(compose, TRUE);
7726 compose->modified = was_modified;
7727 compose_set_title(compose);
7730 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7732 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7733 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7734 Compose *compose = (Compose *) data;
7737 compose_reply_change_mode(compose, value);
7740 static void compose_update_priority_menu_item(Compose * compose)
7742 GtkWidget *menuitem = NULL;
7743 switch (compose->priority) {
7744 case PRIORITY_HIGHEST:
7745 menuitem = gtk_ui_manager_get_widget
7746 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7749 menuitem = gtk_ui_manager_get_widget
7750 (compose->ui_manager, "/Menu/Options/Priority/High");
7752 case PRIORITY_NORMAL:
7753 menuitem = gtk_ui_manager_get_widget
7754 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7757 menuitem = gtk_ui_manager_get_widget
7758 (compose->ui_manager, "/Menu/Options/Priority/Low");
7760 case PRIORITY_LOWEST:
7761 menuitem = gtk_ui_manager_get_widget
7762 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7765 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7768 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7770 Compose *compose = (Compose *) data;
7772 gboolean can_sign = FALSE, can_encrypt = FALSE;
7774 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7776 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7779 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7780 g_free(compose->privacy_system);
7781 compose->privacy_system = NULL;
7782 if (systemid != NULL) {
7783 compose->privacy_system = g_strdup(systemid);
7785 can_sign = privacy_system_can_sign(systemid);
7786 can_encrypt = privacy_system_can_encrypt(systemid);
7789 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7791 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7792 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7795 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7797 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7798 GtkWidget *menuitem = NULL;
7800 gboolean can_sign = FALSE, can_encrypt = FALSE;
7801 gboolean found = FALSE;
7803 if (compose->privacy_system != NULL) {
7805 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7806 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7807 cm_return_if_fail(menuitem != NULL);
7809 amenu = GTK_MENU_SHELL(menuitem)->children;
7811 while (amenu != NULL) {
7812 GList *alist = amenu->next;
7814 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7815 if (systemid != NULL) {
7816 if (strcmp(systemid, compose->privacy_system) == 0 &&
7817 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7818 menuitem = GTK_WIDGET(amenu->data);
7820 can_sign = privacy_system_can_sign(systemid);
7821 can_encrypt = privacy_system_can_encrypt(systemid);
7825 } else if (strlen(compose->privacy_system) == 0 &&
7826 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7827 menuitem = GTK_WIDGET(amenu->data);
7830 can_encrypt = FALSE;
7837 if (menuitem != NULL)
7838 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7840 if (warn && !found && strlen(compose->privacy_system)) {
7841 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7842 "will not be able to sign or encrypt this message."),
7843 compose->privacy_system);
7847 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7848 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7851 static void compose_set_out_encoding(Compose *compose)
7853 CharSet out_encoding;
7854 const gchar *branch = NULL;
7855 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7857 switch(out_encoding) {
7858 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7859 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7860 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7861 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7862 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7863 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7864 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7865 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7866 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7867 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7868 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7869 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7870 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7871 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7872 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7873 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7874 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7875 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7876 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7877 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7878 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7879 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7880 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7881 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7882 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7883 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7884 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7885 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7886 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7887 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7888 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7889 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7891 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7894 static void compose_set_template_menu(Compose *compose)
7896 GSList *tmpl_list, *cur;
7900 tmpl_list = template_get_config();
7902 menu = gtk_menu_new();
7904 gtk_menu_set_accel_group (GTK_MENU (menu),
7905 gtk_ui_manager_get_accel_group(compose->ui_manager));
7906 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7907 Template *tmpl = (Template *)cur->data;
7908 gchar *accel_path = NULL;
7909 item = gtk_menu_item_new_with_label(tmpl->name);
7910 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7911 g_signal_connect(G_OBJECT(item), "activate",
7912 G_CALLBACK(compose_template_activate_cb),
7914 g_object_set_data(G_OBJECT(item), "template", tmpl);
7915 gtk_widget_show(item);
7916 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7917 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7921 gtk_widget_show(menu);
7922 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7925 void compose_update_actions_menu(Compose *compose)
7927 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7930 static void compose_update_privacy_systems_menu(Compose *compose)
7932 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7933 GSList *systems, *cur;
7935 GtkWidget *system_none;
7937 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7938 GtkWidget *privacy_menu = gtk_menu_new();
7940 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7941 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7943 g_signal_connect(G_OBJECT(system_none), "activate",
7944 G_CALLBACK(compose_set_privacy_system_cb), compose);
7946 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7947 gtk_widget_show(system_none);
7949 systems = privacy_get_system_ids();
7950 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7951 gchar *systemid = cur->data;
7953 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7954 widget = gtk_radio_menu_item_new_with_label(group,
7955 privacy_system_get_name(systemid));
7956 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7957 g_strdup(systemid), g_free);
7958 g_signal_connect(G_OBJECT(widget), "activate",
7959 G_CALLBACK(compose_set_privacy_system_cb), compose);
7961 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7962 gtk_widget_show(widget);
7965 g_slist_free(systems);
7966 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7967 gtk_widget_show_all(privacy_menu);
7968 gtk_widget_show_all(privacy_menuitem);
7971 void compose_reflect_prefs_all(void)
7976 for (cur = compose_list; cur != NULL; cur = cur->next) {
7977 compose = (Compose *)cur->data;
7978 compose_set_template_menu(compose);
7982 void compose_reflect_prefs_pixmap_theme(void)
7987 for (cur = compose_list; cur != NULL; cur = cur->next) {
7988 compose = (Compose *)cur->data;
7989 toolbar_update(TOOLBAR_COMPOSE, compose);
7993 static const gchar *compose_quote_char_from_context(Compose *compose)
7995 const gchar *qmark = NULL;
7997 cm_return_val_if_fail(compose != NULL, NULL);
7999 switch (compose->mode) {
8000 /* use forward-specific quote char */
8001 case COMPOSE_FORWARD:
8002 case COMPOSE_FORWARD_AS_ATTACH:
8003 case COMPOSE_FORWARD_INLINE:
8004 if (compose->folder && compose->folder->prefs &&
8005 compose->folder->prefs->forward_with_format)
8006 qmark = compose->folder->prefs->forward_quotemark;
8007 else if (compose->account->forward_with_format)
8008 qmark = compose->account->forward_quotemark;
8010 qmark = prefs_common.fw_quotemark;
8013 /* use reply-specific quote char in all other modes */
8015 if (compose->folder && compose->folder->prefs &&
8016 compose->folder->prefs->reply_with_format)
8017 qmark = compose->folder->prefs->reply_quotemark;
8018 else if (compose->account->reply_with_format)
8019 qmark = compose->account->reply_quotemark;
8021 qmark = prefs_common.quotemark;
8025 if (qmark == NULL || *qmark == '\0')
8031 static void compose_template_apply(Compose *compose, Template *tmpl,
8035 GtkTextBuffer *buffer;
8039 gchar *parsed_str = NULL;
8040 gint cursor_pos = 0;
8041 const gchar *err_msg = _("The body of the template has an error at line %d.");
8044 /* process the body */
8046 text = GTK_TEXT_VIEW(compose->text);
8047 buffer = gtk_text_view_get_buffer(text);
8050 qmark = compose_quote_char_from_context(compose);
8052 if (compose->replyinfo != NULL) {
8055 gtk_text_buffer_set_text(buffer, "", -1);
8056 mark = gtk_text_buffer_get_insert(buffer);
8057 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8059 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8060 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8062 } else if (compose->fwdinfo != NULL) {
8065 gtk_text_buffer_set_text(buffer, "", -1);
8066 mark = gtk_text_buffer_get_insert(buffer);
8067 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8069 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8070 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8073 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8075 GtkTextIter start, end;
8078 gtk_text_buffer_get_start_iter(buffer, &start);
8079 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8080 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8082 /* clear the buffer now */
8084 gtk_text_buffer_set_text(buffer, "", -1);
8086 parsed_str = compose_quote_fmt(compose, dummyinfo,
8087 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8088 procmsg_msginfo_free( dummyinfo );
8094 gtk_text_buffer_set_text(buffer, "", -1);
8095 mark = gtk_text_buffer_get_insert(buffer);
8096 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8099 if (replace && parsed_str && compose->account->auto_sig)
8100 compose_insert_sig(compose, FALSE);
8102 if (replace && parsed_str) {
8103 gtk_text_buffer_get_start_iter(buffer, &iter);
8104 gtk_text_buffer_place_cursor(buffer, &iter);
8108 cursor_pos = quote_fmt_get_cursor_pos();
8109 compose->set_cursor_pos = cursor_pos;
8110 if (cursor_pos == -1)
8112 gtk_text_buffer_get_start_iter(buffer, &iter);
8113 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8114 gtk_text_buffer_place_cursor(buffer, &iter);
8117 /* process the other fields */
8119 compose_template_apply_fields(compose, tmpl);
8120 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8121 quote_fmt_reset_vartable();
8122 compose_changed_cb(NULL, compose);
8125 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8127 MsgInfo* dummyinfo = NULL;
8128 MsgInfo *msginfo = NULL;
8131 if (compose->replyinfo != NULL)
8132 msginfo = compose->replyinfo;
8133 else if (compose->fwdinfo != NULL)
8134 msginfo = compose->fwdinfo;
8136 dummyinfo = compose_msginfo_new_from_compose(compose);
8137 msginfo = dummyinfo;
8140 if (tmpl->from && *tmpl->from != '\0') {
8142 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8143 compose->gtkaspell);
8145 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8147 quote_fmt_scan_string(tmpl->from);
8150 buf = quote_fmt_get_buffer();
8152 alertpanel_error(_("Template From format error."));
8154 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8158 if (tmpl->to && *tmpl->to != '\0') {
8160 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8161 compose->gtkaspell);
8163 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8165 quote_fmt_scan_string(tmpl->to);
8168 buf = quote_fmt_get_buffer();
8170 alertpanel_error(_("Template To format error."));
8172 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8176 if (tmpl->cc && *tmpl->cc != '\0') {
8178 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8179 compose->gtkaspell);
8181 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8183 quote_fmt_scan_string(tmpl->cc);
8186 buf = quote_fmt_get_buffer();
8188 alertpanel_error(_("Template Cc format error."));
8190 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8194 if (tmpl->bcc && *tmpl->bcc != '\0') {
8196 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8197 compose->gtkaspell);
8199 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8201 quote_fmt_scan_string(tmpl->bcc);
8204 buf = quote_fmt_get_buffer();
8206 alertpanel_error(_("Template Bcc format error."));
8208 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8212 /* process the subject */
8213 if (tmpl->subject && *tmpl->subject != '\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->subject);
8223 buf = quote_fmt_get_buffer();
8225 alertpanel_error(_("Template subject format error."));
8227 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8231 procmsg_msginfo_free( dummyinfo );
8234 static void compose_destroy(Compose *compose)
8236 GtkTextBuffer *buffer;
8237 GtkClipboard *clipboard;
8239 compose_list = g_list_remove(compose_list, compose);
8241 if (compose->updating) {
8242 debug_print("danger, not destroying anything now\n");
8243 compose->deferred_destroy = TRUE;
8246 /* NOTE: address_completion_end() does nothing with the window
8247 * however this may change. */
8248 address_completion_end(compose->window);
8250 slist_free_strings(compose->to_list);
8251 g_slist_free(compose->to_list);
8252 slist_free_strings(compose->newsgroup_list);
8253 g_slist_free(compose->newsgroup_list);
8254 slist_free_strings(compose->header_list);
8255 g_slist_free(compose->header_list);
8257 g_hash_table_destroy(compose->email_hashtable);
8259 procmsg_msginfo_free(compose->targetinfo);
8260 procmsg_msginfo_free(compose->replyinfo);
8261 procmsg_msginfo_free(compose->fwdinfo);
8263 g_free(compose->replyto);
8264 g_free(compose->cc);
8265 g_free(compose->bcc);
8266 g_free(compose->newsgroups);
8267 g_free(compose->followup_to);
8269 g_free(compose->ml_post);
8271 g_free(compose->inreplyto);
8272 g_free(compose->references);
8273 g_free(compose->msgid);
8274 g_free(compose->boundary);
8276 g_free(compose->redirect_filename);
8277 if (compose->undostruct)
8278 undo_destroy(compose->undostruct);
8280 g_free(compose->sig_str);
8282 g_free(compose->exteditor_file);
8284 g_free(compose->orig_charset);
8286 g_free(compose->privacy_system);
8288 if (addressbook_get_target_compose() == compose)
8289 addressbook_set_target_compose(NULL);
8292 if (compose->gtkaspell) {
8293 gtkaspell_delete(compose->gtkaspell);
8294 compose->gtkaspell = NULL;
8298 if (!compose->batch) {
8299 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8300 prefs_common.compose_height = compose->window->allocation.height;
8303 if (!gtk_widget_get_parent(compose->paned))
8304 gtk_widget_destroy(compose->paned);
8305 gtk_widget_destroy(compose->popupmenu);
8307 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8308 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8309 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8311 gtk_widget_destroy(compose->window);
8312 toolbar_destroy(compose->toolbar);
8313 g_free(compose->toolbar);
8314 g_mutex_free(compose->mutex);
8318 static void compose_attach_info_free(AttachInfo *ainfo)
8320 g_free(ainfo->file);
8321 g_free(ainfo->content_type);
8322 g_free(ainfo->name);
8326 static void compose_attach_update_label(Compose *compose)
8331 GtkTreeModel *model;
8336 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8337 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8338 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8342 while(gtk_tree_model_iter_next(model, &iter))
8345 text = g_strdup_printf("(%d)", i);
8346 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8350 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8352 Compose *compose = (Compose *)data;
8353 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8354 GtkTreeSelection *selection;
8356 GtkTreeModel *model;
8358 selection = gtk_tree_view_get_selection(tree_view);
8359 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8364 for (cur = sel; cur != NULL; cur = cur->next) {
8365 GtkTreePath *path = cur->data;
8366 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8369 gtk_tree_path_free(path);
8372 for (cur = sel; cur != NULL; cur = cur->next) {
8373 GtkTreeRowReference *ref = cur->data;
8374 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8377 if (gtk_tree_model_get_iter(model, &iter, path))
8378 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8380 gtk_tree_path_free(path);
8381 gtk_tree_row_reference_free(ref);
8385 compose_attach_update_label(compose);
8388 static struct _AttachProperty
8391 GtkWidget *mimetype_entry;
8392 GtkWidget *encoding_optmenu;
8393 GtkWidget *path_entry;
8394 GtkWidget *filename_entry;
8396 GtkWidget *cancel_btn;
8399 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8401 gtk_tree_path_free((GtkTreePath *)ptr);
8404 static void compose_attach_property(GtkAction *action, gpointer data)
8406 Compose *compose = (Compose *)data;
8407 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8409 GtkComboBox *optmenu;
8410 GtkTreeSelection *selection;
8412 GtkTreeModel *model;
8415 static gboolean cancelled;
8417 /* only if one selected */
8418 selection = gtk_tree_view_get_selection(tree_view);
8419 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8422 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8426 path = (GtkTreePath *) sel->data;
8427 gtk_tree_model_get_iter(model, &iter, path);
8428 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8431 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8437 if (!attach_prop.window)
8438 compose_attach_property_create(&cancelled);
8439 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8440 gtk_widget_grab_focus(attach_prop.ok_btn);
8441 gtk_widget_show(attach_prop.window);
8442 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8444 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8445 if (ainfo->encoding == ENC_UNKNOWN)
8446 combobox_select_by_data(optmenu, ENC_BASE64);
8448 combobox_select_by_data(optmenu, ainfo->encoding);
8450 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8451 ainfo->content_type ? ainfo->content_type : "");
8452 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8453 ainfo->file ? ainfo->file : "");
8454 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8455 ainfo->name ? ainfo->name : "");
8458 const gchar *entry_text;
8460 gchar *cnttype = NULL;
8467 gtk_widget_hide(attach_prop.window);
8468 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8473 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8474 if (*entry_text != '\0') {
8477 text = g_strstrip(g_strdup(entry_text));
8478 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8479 cnttype = g_strdup(text);
8482 alertpanel_error(_("Invalid MIME type."));
8488 ainfo->encoding = combobox_get_active_data(optmenu);
8490 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8491 if (*entry_text != '\0') {
8492 if (is_file_exist(entry_text) &&
8493 (size = get_file_size(entry_text)) > 0)
8494 file = g_strdup(entry_text);
8497 (_("File doesn't exist or is empty."));
8503 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8504 if (*entry_text != '\0') {
8505 g_free(ainfo->name);
8506 ainfo->name = g_strdup(entry_text);
8510 g_free(ainfo->content_type);
8511 ainfo->content_type = cnttype;
8514 g_free(ainfo->file);
8518 ainfo->size = (goffset)size;
8520 /* update tree store */
8521 text = to_human_readable(ainfo->size);
8522 gtk_tree_model_get_iter(model, &iter, path);
8523 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8524 COL_MIMETYPE, ainfo->content_type,
8526 COL_NAME, ainfo->name,
8532 gtk_tree_path_free(path);
8535 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8537 label = gtk_label_new(str); \
8538 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8539 GTK_FILL, 0, 0, 0); \
8540 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8542 entry = gtk_entry_new(); \
8543 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8544 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8547 static void compose_attach_property_create(gboolean *cancelled)
8553 GtkWidget *mimetype_entry;
8556 GtkListStore *optmenu_menu;
8557 GtkWidget *path_entry;
8558 GtkWidget *filename_entry;
8561 GtkWidget *cancel_btn;
8562 GList *mime_type_list, *strlist;
8565 debug_print("Creating attach_property window...\n");
8567 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8568 gtk_widget_set_size_request(window, 480, -1);
8569 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8570 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8571 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8572 g_signal_connect(G_OBJECT(window), "delete_event",
8573 G_CALLBACK(attach_property_delete_event),
8575 g_signal_connect(G_OBJECT(window), "key_press_event",
8576 G_CALLBACK(attach_property_key_pressed),
8579 vbox = gtk_vbox_new(FALSE, 8);
8580 gtk_container_add(GTK_CONTAINER(window), vbox);
8582 table = gtk_table_new(4, 2, FALSE);
8583 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8584 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8585 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8587 label = gtk_label_new(_("MIME type"));
8588 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8590 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8591 mimetype_entry = gtk_combo_box_entry_new_text();
8592 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8593 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8595 /* stuff with list */
8596 mime_type_list = procmime_get_mime_type_list();
8598 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8599 MimeType *type = (MimeType *) mime_type_list->data;
8602 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8604 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8607 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8608 (GCompareFunc)strcmp2);
8611 for (mime_type_list = strlist; mime_type_list != NULL;
8612 mime_type_list = mime_type_list->next) {
8613 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8614 g_free(mime_type_list->data);
8616 g_list_free(strlist);
8617 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8618 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8620 label = gtk_label_new(_("Encoding"));
8621 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8623 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8625 hbox = gtk_hbox_new(FALSE, 0);
8626 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8627 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8629 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8630 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8632 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8633 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8634 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8635 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8636 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8638 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8640 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8641 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8643 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8644 &ok_btn, GTK_STOCK_OK,
8646 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8647 gtk_widget_grab_default(ok_btn);
8649 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8650 G_CALLBACK(attach_property_ok),
8652 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8653 G_CALLBACK(attach_property_cancel),
8656 gtk_widget_show_all(vbox);
8658 attach_prop.window = window;
8659 attach_prop.mimetype_entry = mimetype_entry;
8660 attach_prop.encoding_optmenu = optmenu;
8661 attach_prop.path_entry = path_entry;
8662 attach_prop.filename_entry = filename_entry;
8663 attach_prop.ok_btn = ok_btn;
8664 attach_prop.cancel_btn = cancel_btn;
8667 #undef SET_LABEL_AND_ENTRY
8669 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8675 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8681 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8682 gboolean *cancelled)
8690 static gboolean attach_property_key_pressed(GtkWidget *widget,
8692 gboolean *cancelled)
8694 if (event && event->keyval == GDK_Escape) {
8698 if (event && event->keyval == GDK_Return) {
8706 static void compose_exec_ext_editor(Compose *compose)
8713 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8714 G_DIR_SEPARATOR, compose);
8716 if (pipe(pipe_fds) < 0) {
8722 if ((pid = fork()) < 0) {
8729 /* close the write side of the pipe */
8732 compose->exteditor_file = g_strdup(tmp);
8733 compose->exteditor_pid = pid;
8735 compose_set_ext_editor_sensitive(compose, FALSE);
8738 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8740 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8742 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8746 } else { /* process-monitoring process */
8752 /* close the read side of the pipe */
8755 if (compose_write_body_to_file(compose, tmp) < 0) {
8756 fd_write_all(pipe_fds[1], "2\n", 2);
8760 pid_ed = compose_exec_ext_editor_real(tmp);
8762 fd_write_all(pipe_fds[1], "1\n", 2);
8766 /* wait until editor is terminated */
8767 waitpid(pid_ed, NULL, 0);
8769 fd_write_all(pipe_fds[1], "0\n", 2);
8776 #endif /* G_OS_UNIX */
8780 static gint compose_exec_ext_editor_real(const gchar *file)
8787 cm_return_val_if_fail(file != NULL, -1);
8789 if ((pid = fork()) < 0) {
8794 if (pid != 0) return pid;
8796 /* grandchild process */
8798 if (setpgid(0, getppid()))
8801 if (prefs_common_get_ext_editor_cmd() &&
8802 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8803 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8804 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8806 if (prefs_common_get_ext_editor_cmd())
8807 g_warning("External editor command-line is invalid: '%s'\n",
8808 prefs_common_get_ext_editor_cmd());
8809 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8812 cmdline = strsplit_with_quote(buf, " ", 1024);
8813 execvp(cmdline[0], cmdline);
8816 g_strfreev(cmdline);
8821 static gboolean compose_ext_editor_kill(Compose *compose)
8823 pid_t pgid = compose->exteditor_pid * -1;
8826 ret = kill(pgid, 0);
8828 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8832 msg = g_strdup_printf
8833 (_("The external editor is still working.\n"
8834 "Force terminating the process?\n"
8835 "process group id: %d"), -pgid);
8836 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8837 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8841 if (val == G_ALERTALTERNATE) {
8842 g_source_remove(compose->exteditor_tag);
8843 g_io_channel_shutdown(compose->exteditor_ch,
8845 g_io_channel_unref(compose->exteditor_ch);
8847 if (kill(pgid, SIGTERM) < 0) perror("kill");
8848 waitpid(compose->exteditor_pid, NULL, 0);
8850 g_warning("Terminated process group id: %d", -pgid);
8851 g_warning("Temporary file: %s",
8852 compose->exteditor_file);
8854 compose_set_ext_editor_sensitive(compose, TRUE);
8856 g_free(compose->exteditor_file);
8857 compose->exteditor_file = NULL;
8858 compose->exteditor_pid = -1;
8859 compose->exteditor_ch = NULL;
8860 compose->exteditor_tag = -1;
8868 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8872 Compose *compose = (Compose *)data;
8875 debug_print(_("Compose: input from monitoring process\n"));
8877 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8879 g_io_channel_shutdown(source, FALSE, NULL);
8880 g_io_channel_unref(source);
8882 waitpid(compose->exteditor_pid, NULL, 0);
8884 if (buf[0] == '0') { /* success */
8885 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8886 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8888 gtk_text_buffer_set_text(buffer, "", -1);
8889 compose_insert_file(compose, compose->exteditor_file);
8890 compose_changed_cb(NULL, compose);
8891 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8893 if (claws_unlink(compose->exteditor_file) < 0)
8894 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8895 } else if (buf[0] == '1') { /* failed */
8896 g_warning("Couldn't exec external editor\n");
8897 if (claws_unlink(compose->exteditor_file) < 0)
8898 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8899 } else if (buf[0] == '2') {
8900 g_warning("Couldn't write to file\n");
8901 } else if (buf[0] == '3') {
8902 g_warning("Pipe read failed\n");
8905 compose_set_ext_editor_sensitive(compose, TRUE);
8907 g_free(compose->exteditor_file);
8908 compose->exteditor_file = NULL;
8909 compose->exteditor_pid = -1;
8910 compose->exteditor_ch = NULL;
8911 compose->exteditor_tag = -1;
8916 static void compose_set_ext_editor_sensitive(Compose *compose,
8919 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8920 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8921 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8922 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8923 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8924 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8925 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8927 gtk_widget_set_sensitive(compose->text, sensitive);
8928 if (compose->toolbar->send_btn)
8929 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8930 if (compose->toolbar->sendl_btn)
8931 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8932 if (compose->toolbar->draft_btn)
8933 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8934 if (compose->toolbar->insert_btn)
8935 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8936 if (compose->toolbar->sig_btn)
8937 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8938 if (compose->toolbar->exteditor_btn)
8939 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8940 if (compose->toolbar->linewrap_current_btn)
8941 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8942 if (compose->toolbar->linewrap_all_btn)
8943 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8945 #endif /* G_OS_UNIX */
8948 * compose_undo_state_changed:
8950 * Change the sensivity of the menuentries undo and redo
8952 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8953 gint redo_state, gpointer data)
8955 Compose *compose = (Compose *)data;
8957 switch (undo_state) {
8958 case UNDO_STATE_TRUE:
8959 if (!undostruct->undo_state) {
8960 undostruct->undo_state = TRUE;
8961 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8964 case UNDO_STATE_FALSE:
8965 if (undostruct->undo_state) {
8966 undostruct->undo_state = FALSE;
8967 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8970 case UNDO_STATE_UNCHANGED:
8972 case UNDO_STATE_REFRESH:
8973 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8976 g_warning("Undo state not recognized");
8980 switch (redo_state) {
8981 case UNDO_STATE_TRUE:
8982 if (!undostruct->redo_state) {
8983 undostruct->redo_state = TRUE;
8984 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8987 case UNDO_STATE_FALSE:
8988 if (undostruct->redo_state) {
8989 undostruct->redo_state = FALSE;
8990 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8993 case UNDO_STATE_UNCHANGED:
8995 case UNDO_STATE_REFRESH:
8996 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8999 g_warning("Redo state not recognized");
9004 /* callback functions */
9006 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9007 * includes "non-client" (windows-izm) in calculation, so this calculation
9008 * may not be accurate.
9010 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9011 GtkAllocation *allocation,
9012 GtkSHRuler *shruler)
9014 if (prefs_common.show_ruler) {
9015 gint char_width = 0, char_height = 0;
9016 gint line_width_in_chars;
9018 gtkut_get_font_size(GTK_WIDGET(widget),
9019 &char_width, &char_height);
9020 line_width_in_chars =
9021 (allocation->width - allocation->x) / char_width;
9023 /* got the maximum */
9024 gtk_ruler_set_range(GTK_RULER(shruler),
9025 0.0, line_width_in_chars, 0,
9026 /*line_width_in_chars*/ char_width);
9033 ComposeEntryType header;
9035 ComposePrefType type;
9036 gboolean entry_marked;
9039 static void account_activated(GtkComboBox *optmenu, gpointer data)
9041 Compose *compose = (Compose *)data;
9044 gchar *folderidentifier;
9045 gint account_id = 0;
9048 GSList *list, *saved_list = NULL;
9049 HeaderEntryState *state;
9050 GtkRcStyle *style = NULL;
9051 static GdkColor yellow;
9052 static gboolean color_set = FALSE;
9054 /* Get ID of active account in the combo box */
9055 menu = gtk_combo_box_get_model(optmenu);
9056 gtk_combo_box_get_active_iter(optmenu, &iter);
9057 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9059 ac = account_find_from_id(account_id);
9060 cm_return_if_fail(ac != NULL);
9062 if (ac != compose->account) {
9063 compose_select_account(compose, ac, FALSE);
9065 for (list = compose->header_list; list; list = list->next) {
9066 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9068 if (hentry->type == PREF_ACCOUNT || !list->next) {
9069 compose_destroy_headerentry(compose, hentry);
9073 state = g_malloc0(sizeof(HeaderEntryState));
9074 state->header = combobox_get_active_data(
9075 GTK_COMBO_BOX(hentry->combo));
9076 state->entry = gtk_editable_get_chars(
9077 GTK_EDITABLE(hentry->entry), 0, -1);
9078 state->type = hentry->type;
9081 gdk_color_parse("#f5f6be", &yellow);
9082 color_set = gdk_colormap_alloc_color(
9083 gdk_colormap_get_system(),
9084 &yellow, FALSE, TRUE);
9087 style = gtk_widget_get_modifier_style(hentry->entry);
9088 state->entry_marked = gdk_color_equal(&yellow,
9089 &style->base[GTK_STATE_NORMAL]);
9091 saved_list = g_slist_append(saved_list, state);
9092 compose_destroy_headerentry(compose, hentry);
9095 compose->header_last = NULL;
9096 g_slist_free(compose->header_list);
9097 compose->header_list = NULL;
9098 compose->header_nextrow = 1;
9099 compose_create_header_entry(compose);
9101 if (ac->set_autocc && ac->auto_cc)
9102 compose_entry_append(compose, ac->auto_cc,
9103 COMPOSE_CC, PREF_ACCOUNT);
9105 if (ac->set_autobcc && ac->auto_bcc)
9106 compose_entry_append(compose, ac->auto_bcc,
9107 COMPOSE_BCC, PREF_ACCOUNT);
9109 if (ac->set_autoreplyto && ac->auto_replyto)
9110 compose_entry_append(compose, ac->auto_replyto,
9111 COMPOSE_REPLYTO, PREF_ACCOUNT);
9113 for (list = saved_list; list; list = list->next) {
9114 state = (HeaderEntryState *) list->data;
9116 compose_entry_append(compose, state->entry,
9117 state->header, state->type);
9118 if (state->entry_marked)
9119 compose_entry_mark_default_to(compose, state->entry);
9121 g_free(state->entry);
9123 g_slist_free(saved_list);
9125 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9126 (ac->protocol == A_NNTP) ?
9127 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9130 /* Set message save folder */
9131 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9132 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9134 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9135 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9137 compose_set_save_to(compose, NULL);
9138 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9139 folderidentifier = folder_item_get_identifier(account_get_special_folder
9140 (compose->account, F_OUTBOX));
9141 compose_set_save_to(compose, folderidentifier);
9142 g_free(folderidentifier);
9146 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9147 GtkTreeViewColumn *column, Compose *compose)
9149 compose_attach_property(NULL, compose);
9152 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9155 Compose *compose = (Compose *)data;
9156 GtkTreeSelection *attach_selection;
9157 gint attach_nr_selected;
9159 if (!event) return FALSE;
9161 if (event->button == 3) {
9162 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9163 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9165 if (attach_nr_selected > 0)
9167 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9168 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9170 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9171 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9174 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9175 NULL, NULL, event->button, event->time);
9182 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9185 Compose *compose = (Compose *)data;
9187 if (!event) return FALSE;
9189 switch (event->keyval) {
9191 compose_attach_remove_selected(NULL, compose);
9197 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9199 toolbar_comp_set_sensitive(compose, allow);
9200 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9201 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9203 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9205 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9206 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9207 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9209 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9213 static void compose_send_cb(GtkAction *action, gpointer data)
9215 Compose *compose = (Compose *)data;
9217 if (prefs_common.work_offline &&
9218 !inc_offline_should_override(TRUE,
9219 _("Claws Mail needs network access in order "
9220 "to send this email.")))
9223 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9224 g_source_remove(compose->draft_timeout_tag);
9225 compose->draft_timeout_tag = -1;
9228 compose_send(compose);
9231 static void compose_send_later_cb(GtkAction *action, gpointer data)
9233 Compose *compose = (Compose *)data;
9237 compose_allow_user_actions(compose, FALSE);
9238 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9239 compose_allow_user_actions(compose, TRUE);
9243 compose_close(compose);
9244 } else if (val == -1) {
9245 alertpanel_error(_("Could not queue message."));
9246 } else if (val == -2) {
9247 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9248 } else if (val == -3) {
9249 if (privacy_peek_error())
9250 alertpanel_error(_("Could not queue message for sending:\n\n"
9251 "Signature failed: %s"), privacy_get_error());
9252 } else if (val == -4) {
9253 alertpanel_error(_("Could not queue message for sending:\n\n"
9254 "Charset conversion failed."));
9255 } else if (val == -5) {
9256 alertpanel_error(_("Could not queue message for sending:\n\n"
9257 "Couldn't get recipient encryption key."));
9258 } else if (val == -6) {
9261 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9264 #define DRAFTED_AT_EXIT "drafted_at_exit"
9265 static void compose_register_draft(MsgInfo *info)
9267 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9268 DRAFTED_AT_EXIT, NULL);
9269 FILE *fp = g_fopen(filepath, "ab");
9272 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9280 gboolean compose_draft (gpointer data, guint action)
9282 Compose *compose = (Compose *)data;
9286 MsgFlags flag = {0, 0};
9287 static gboolean lock = FALSE;
9288 MsgInfo *newmsginfo;
9290 gboolean target_locked = FALSE;
9291 gboolean err = FALSE;
9293 if (lock) return FALSE;
9295 if (compose->sending)
9298 draft = account_get_special_folder(compose->account, F_DRAFT);
9299 cm_return_val_if_fail(draft != NULL, FALSE);
9301 if (!g_mutex_trylock(compose->mutex)) {
9302 /* we don't want to lock the mutex once it's available,
9303 * because as the only other part of compose.c locking
9304 * it is compose_close - which means once unlocked,
9305 * the compose struct will be freed */
9306 debug_print("couldn't lock mutex, probably sending\n");
9312 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9313 G_DIR_SEPARATOR, compose);
9314 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9315 FILE_OP_ERROR(tmp, "fopen");
9319 /* chmod for security */
9320 if (change_file_mode_rw(fp, tmp) < 0) {
9321 FILE_OP_ERROR(tmp, "chmod");
9322 g_warning("can't change file mode\n");
9325 /* Save draft infos */
9326 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9327 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9329 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9330 gchar *savefolderid;
9332 savefolderid = compose_get_save_to(compose);
9333 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9334 g_free(savefolderid);
9336 if (compose->return_receipt) {
9337 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9339 if (compose->privacy_system) {
9340 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9341 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9342 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9345 /* Message-ID of message replying to */
9346 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9349 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9350 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9353 /* Message-ID of message forwarding to */
9354 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9357 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9358 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9362 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9363 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9365 /* end of headers */
9366 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9373 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9377 if (fclose(fp) == EOF) {
9381 if (compose->targetinfo) {
9382 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9383 flag.perm_flags = target_locked?MSG_LOCKED:0;
9385 flag.tmp_flags = MSG_DRAFT;
9387 folder_item_scan(draft);
9388 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9389 MsgInfo *tmpinfo = NULL;
9390 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9391 if (compose->msgid) {
9392 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9395 msgnum = tmpinfo->msgnum;
9396 procmsg_msginfo_free(tmpinfo);
9397 debug_print("got draft msgnum %d from scanning\n", msgnum);
9399 debug_print("didn't get draft msgnum after scanning\n");
9402 debug_print("got draft msgnum %d from adding\n", msgnum);
9408 if (action != COMPOSE_AUTO_SAVE) {
9409 if (action != COMPOSE_DRAFT_FOR_EXIT)
9410 alertpanel_error(_("Could not save draft."));
9413 gtkut_window_popup(compose->window);
9414 val = alertpanel_full(_("Could not save draft"),
9415 _("Could not save draft.\n"
9416 "Do you want to cancel exit or discard this email?"),
9417 _("_Cancel exit"), _("_Discard email"), NULL,
9418 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9419 if (val == G_ALERTALTERNATE) {
9421 g_mutex_unlock(compose->mutex); /* must be done before closing */
9422 compose_close(compose);
9426 g_mutex_unlock(compose->mutex); /* must be done before closing */
9435 if (compose->mode == COMPOSE_REEDIT) {
9436 compose_remove_reedit_target(compose, TRUE);
9439 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9442 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9444 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9446 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9447 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9448 procmsg_msginfo_set_flags(newmsginfo, 0,
9449 MSG_HAS_ATTACHMENT);
9451 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9452 compose_register_draft(newmsginfo);
9454 procmsg_msginfo_free(newmsginfo);
9457 folder_item_scan(draft);
9459 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9461 g_mutex_unlock(compose->mutex); /* must be done before closing */
9462 compose_close(compose);
9468 path = folder_item_fetch_msg(draft, msgnum);
9470 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9473 if (g_stat(path, &s) < 0) {
9474 FILE_OP_ERROR(path, "stat");
9480 procmsg_msginfo_free(compose->targetinfo);
9481 compose->targetinfo = procmsg_msginfo_new();
9482 compose->targetinfo->msgnum = msgnum;
9483 compose->targetinfo->size = (goffset)s.st_size;
9484 compose->targetinfo->mtime = s.st_mtime;
9485 compose->targetinfo->folder = draft;
9487 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9488 compose->mode = COMPOSE_REEDIT;
9490 if (action == COMPOSE_AUTO_SAVE) {
9491 compose->autosaved_draft = compose->targetinfo;
9493 compose->modified = FALSE;
9494 compose_set_title(compose);
9498 g_mutex_unlock(compose->mutex);
9502 void compose_clear_exit_drafts(void)
9504 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9505 DRAFTED_AT_EXIT, NULL);
9506 if (is_file_exist(filepath))
9507 claws_unlink(filepath);
9512 void compose_reopen_exit_drafts(void)
9514 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9515 DRAFTED_AT_EXIT, NULL);
9516 FILE *fp = g_fopen(filepath, "rb");
9520 while (fgets(buf, sizeof(buf), fp)) {
9521 gchar **parts = g_strsplit(buf, "\t", 2);
9522 const gchar *folder = parts[0];
9523 int msgnum = parts[1] ? atoi(parts[1]):-1;
9525 if (folder && *folder && msgnum > -1) {
9526 FolderItem *item = folder_find_item_from_identifier(folder);
9527 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9529 compose_reedit(info, FALSE);
9536 compose_clear_exit_drafts();
9539 static void compose_save_cb(GtkAction *action, gpointer data)
9541 Compose *compose = (Compose *)data;
9542 compose_draft(compose, COMPOSE_KEEP_EDITING);
9543 compose->rmode = COMPOSE_REEDIT;
9546 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9548 if (compose && file_list) {
9551 for ( tmp = file_list; tmp; tmp = tmp->next) {
9552 gchar *file = (gchar *) tmp->data;
9553 gchar *utf8_filename = conv_filename_to_utf8(file);
9554 compose_attach_append(compose, file, utf8_filename, NULL);
9555 compose_changed_cb(NULL, compose);
9560 g_free(utf8_filename);
9565 static void compose_attach_cb(GtkAction *action, gpointer data)
9567 Compose *compose = (Compose *)data;
9570 if (compose->redirect_filename != NULL)
9573 file_list = filesel_select_multiple_files_open(_("Select file"));
9576 compose_attach_from_list(compose, file_list, TRUE);
9577 g_list_free(file_list);
9581 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9583 Compose *compose = (Compose *)data;
9586 file_list = filesel_select_multiple_files_open(_("Select file"));
9591 for ( tmp = file_list; tmp; tmp = tmp->next) {
9592 gchar *file = (gchar *) tmp->data;
9593 gchar *filedup = g_strdup(file);
9594 gchar *shortfile = g_path_get_basename(filedup);
9595 ComposeInsertResult res;
9596 /* insert the file if the file is short or if the user confirmed that
9597 he/she wants to insert the large file */
9598 res = compose_insert_file(compose, file);
9599 if (res == COMPOSE_INSERT_READ_ERROR) {
9600 alertpanel_error(_("File '%s' could not be read."), shortfile);
9601 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9602 alertpanel_error(_("File '%s' contained invalid characters\n"
9603 "for the current encoding, insertion may be incorrect."),
9611 g_list_free(file_list);
9615 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9617 Compose *compose = (Compose *)data;
9619 compose_insert_sig(compose, FALSE);
9622 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9626 Compose *compose = (Compose *)data;
9628 gtkut_widget_get_uposition(widget, &x, &y);
9629 if (!compose->batch) {
9630 prefs_common.compose_x = x;
9631 prefs_common.compose_y = y;
9633 if (compose->sending || compose->updating)
9635 compose_close_cb(NULL, compose);
9639 void compose_close_toolbar(Compose *compose)
9641 compose_close_cb(NULL, compose);
9644 static void compose_close_cb(GtkAction *action, gpointer data)
9646 Compose *compose = (Compose *)data;
9650 if (compose->exteditor_tag != -1) {
9651 if (!compose_ext_editor_kill(compose))
9656 if (compose->modified) {
9657 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9658 if (!g_mutex_trylock(compose->mutex)) {
9659 /* we don't want to lock the mutex once it's available,
9660 * because as the only other part of compose.c locking
9661 * it is compose_close - which means once unlocked,
9662 * the compose struct will be freed */
9663 debug_print("couldn't lock mutex, probably sending\n");
9667 val = alertpanel(_("Discard message"),
9668 _("This message has been modified. Discard it?"),
9669 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9671 val = alertpanel(_("Save changes"),
9672 _("This message has been modified. Save the latest changes?"),
9673 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9675 g_mutex_unlock(compose->mutex);
9677 case G_ALERTDEFAULT:
9678 if (prefs_common.autosave && !reedit)
9679 compose_remove_draft(compose);
9681 case G_ALERTALTERNATE:
9682 compose_draft(data, COMPOSE_QUIT_EDITING);
9689 compose_close(compose);
9692 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9694 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9695 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9696 Compose *compose = (Compose *) data;
9699 compose->out_encoding = (CharSet)value;
9702 static void compose_address_cb(GtkAction *action, gpointer data)
9704 Compose *compose = (Compose *)data;
9706 addressbook_open(compose);
9709 static void about_show_cb(GtkAction *action, gpointer data)
9714 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9716 Compose *compose = (Compose *)data;
9721 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9722 cm_return_if_fail(tmpl != NULL);
9724 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9726 val = alertpanel(_("Apply template"), msg,
9727 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9730 if (val == G_ALERTDEFAULT)
9731 compose_template_apply(compose, tmpl, TRUE);
9732 else if (val == G_ALERTALTERNATE)
9733 compose_template_apply(compose, tmpl, FALSE);
9736 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9738 Compose *compose = (Compose *)data;
9740 compose_exec_ext_editor(compose);
9743 static void compose_undo_cb(GtkAction *action, gpointer data)
9745 Compose *compose = (Compose *)data;
9746 gboolean prev_autowrap = compose->autowrap;
9748 compose->autowrap = FALSE;
9749 undo_undo(compose->undostruct);
9750 compose->autowrap = prev_autowrap;
9753 static void compose_redo_cb(GtkAction *action, gpointer data)
9755 Compose *compose = (Compose *)data;
9756 gboolean prev_autowrap = compose->autowrap;
9758 compose->autowrap = FALSE;
9759 undo_redo(compose->undostruct);
9760 compose->autowrap = prev_autowrap;
9763 static void entry_cut_clipboard(GtkWidget *entry)
9765 if (GTK_IS_EDITABLE(entry))
9766 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9767 else if (GTK_IS_TEXT_VIEW(entry))
9768 gtk_text_buffer_cut_clipboard(
9769 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9770 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9774 static void entry_copy_clipboard(GtkWidget *entry)
9776 if (GTK_IS_EDITABLE(entry))
9777 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9778 else if (GTK_IS_TEXT_VIEW(entry))
9779 gtk_text_buffer_copy_clipboard(
9780 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9781 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9784 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9785 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9787 if (GTK_IS_TEXT_VIEW(entry)) {
9788 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9789 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9790 GtkTextIter start_iter, end_iter;
9792 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9794 if (contents == NULL)
9797 /* we shouldn't delete the selection when middle-click-pasting, or we
9798 * can't mid-click-paste our own selection */
9799 if (clip != GDK_SELECTION_PRIMARY) {
9800 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9801 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9804 if (insert_place == NULL) {
9805 /* if insert_place isn't specified, insert at the cursor.
9806 * used for Ctrl-V pasting */
9807 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9808 start = gtk_text_iter_get_offset(&start_iter);
9809 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9811 /* if insert_place is specified, paste here.
9812 * used for mid-click-pasting */
9813 start = gtk_text_iter_get_offset(insert_place);
9814 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9815 if (prefs_common.primary_paste_unselects)
9816 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9820 /* paste unwrapped: mark the paste so it's not wrapped later */
9821 end = start + strlen(contents);
9822 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9823 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9824 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9825 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9826 /* rewrap paragraph now (after a mid-click-paste) */
9827 mark_start = gtk_text_buffer_get_insert(buffer);
9828 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9829 gtk_text_iter_backward_char(&start_iter);
9830 compose_beautify_paragraph(compose, &start_iter, TRUE);
9832 } else if (GTK_IS_EDITABLE(entry))
9833 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9835 compose->modified = TRUE;
9838 static void entry_allsel(GtkWidget *entry)
9840 if (GTK_IS_EDITABLE(entry))
9841 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9842 else if (GTK_IS_TEXT_VIEW(entry)) {
9843 GtkTextIter startiter, enditer;
9844 GtkTextBuffer *textbuf;
9846 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9847 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9848 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9850 gtk_text_buffer_move_mark_by_name(textbuf,
9851 "selection_bound", &startiter);
9852 gtk_text_buffer_move_mark_by_name(textbuf,
9853 "insert", &enditer);
9857 static void compose_cut_cb(GtkAction *action, gpointer data)
9859 Compose *compose = (Compose *)data;
9860 if (compose->focused_editable
9861 #ifndef GENERIC_UMPC
9862 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9865 entry_cut_clipboard(compose->focused_editable);
9868 static void compose_copy_cb(GtkAction *action, gpointer data)
9870 Compose *compose = (Compose *)data;
9871 if (compose->focused_editable
9872 #ifndef GENERIC_UMPC
9873 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9876 entry_copy_clipboard(compose->focused_editable);
9879 static void compose_paste_cb(GtkAction *action, gpointer data)
9881 Compose *compose = (Compose *)data;
9883 GtkTextBuffer *buffer;
9885 if (compose->focused_editable &&
9886 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9887 entry_paste_clipboard(compose, compose->focused_editable,
9888 prefs_common.linewrap_pastes,
9889 GDK_SELECTION_CLIPBOARD, NULL);
9893 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9895 Compose *compose = (Compose *)data;
9896 gint wrap_quote = prefs_common.linewrap_quote;
9897 if (compose->focused_editable
9898 #ifndef GENERIC_UMPC
9899 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9902 /* let text_insert() (called directly or at a later time
9903 * after the gtk_editable_paste_clipboard) know that
9904 * text is to be inserted as a quotation. implemented
9905 * by using a simple refcount... */
9906 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9907 G_OBJECT(compose->focused_editable),
9908 "paste_as_quotation"));
9909 g_object_set_data(G_OBJECT(compose->focused_editable),
9910 "paste_as_quotation",
9911 GINT_TO_POINTER(paste_as_quotation + 1));
9912 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9913 entry_paste_clipboard(compose, compose->focused_editable,
9914 prefs_common.linewrap_pastes,
9915 GDK_SELECTION_CLIPBOARD, NULL);
9916 prefs_common.linewrap_quote = wrap_quote;
9920 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9922 Compose *compose = (Compose *)data;
9924 GtkTextBuffer *buffer;
9926 if (compose->focused_editable
9927 #ifndef GENERIC_UMPC
9928 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9931 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9932 GDK_SELECTION_CLIPBOARD, NULL);
9936 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9938 Compose *compose = (Compose *)data;
9940 GtkTextBuffer *buffer;
9942 if (compose->focused_editable
9943 #ifndef GENERIC_UMPC
9944 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9947 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9948 GDK_SELECTION_CLIPBOARD, NULL);
9952 static void compose_allsel_cb(GtkAction *action, gpointer data)
9954 Compose *compose = (Compose *)data;
9955 if (compose->focused_editable
9956 #ifndef GENERIC_UMPC
9957 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9960 entry_allsel(compose->focused_editable);
9963 static void textview_move_beginning_of_line (GtkTextView *text)
9965 GtkTextBuffer *buffer;
9969 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9971 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9972 mark = gtk_text_buffer_get_insert(buffer);
9973 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9974 gtk_text_iter_set_line_offset(&ins, 0);
9975 gtk_text_buffer_place_cursor(buffer, &ins);
9978 static void textview_move_forward_character (GtkTextView *text)
9980 GtkTextBuffer *buffer;
9984 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9986 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9987 mark = gtk_text_buffer_get_insert(buffer);
9988 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9989 if (gtk_text_iter_forward_cursor_position(&ins))
9990 gtk_text_buffer_place_cursor(buffer, &ins);
9993 static void textview_move_backward_character (GtkTextView *text)
9995 GtkTextBuffer *buffer;
9999 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10001 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10002 mark = gtk_text_buffer_get_insert(buffer);
10003 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10004 if (gtk_text_iter_backward_cursor_position(&ins))
10005 gtk_text_buffer_place_cursor(buffer, &ins);
10008 static void textview_move_forward_word (GtkTextView *text)
10010 GtkTextBuffer *buffer;
10015 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10017 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10018 mark = gtk_text_buffer_get_insert(buffer);
10019 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10020 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10021 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10022 gtk_text_iter_backward_word_start(&ins);
10023 gtk_text_buffer_place_cursor(buffer, &ins);
10027 static void textview_move_backward_word (GtkTextView *text)
10029 GtkTextBuffer *buffer;
10034 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10036 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10037 mark = gtk_text_buffer_get_insert(buffer);
10038 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10039 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10040 if (gtk_text_iter_backward_word_starts(&ins, 1))
10041 gtk_text_buffer_place_cursor(buffer, &ins);
10044 static void textview_move_end_of_line (GtkTextView *text)
10046 GtkTextBuffer *buffer;
10050 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10052 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10053 mark = gtk_text_buffer_get_insert(buffer);
10054 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10055 if (gtk_text_iter_forward_to_line_end(&ins))
10056 gtk_text_buffer_place_cursor(buffer, &ins);
10059 static void textview_move_next_line (GtkTextView *text)
10061 GtkTextBuffer *buffer;
10066 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10068 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10069 mark = gtk_text_buffer_get_insert(buffer);
10070 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10071 offset = gtk_text_iter_get_line_offset(&ins);
10072 if (gtk_text_iter_forward_line(&ins)) {
10073 gtk_text_iter_set_line_offset(&ins, offset);
10074 gtk_text_buffer_place_cursor(buffer, &ins);
10078 static void textview_move_previous_line (GtkTextView *text)
10080 GtkTextBuffer *buffer;
10085 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10087 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10088 mark = gtk_text_buffer_get_insert(buffer);
10089 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10090 offset = gtk_text_iter_get_line_offset(&ins);
10091 if (gtk_text_iter_backward_line(&ins)) {
10092 gtk_text_iter_set_line_offset(&ins, offset);
10093 gtk_text_buffer_place_cursor(buffer, &ins);
10097 static void textview_delete_forward_character (GtkTextView *text)
10099 GtkTextBuffer *buffer;
10101 GtkTextIter ins, end_iter;
10103 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10105 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10106 mark = gtk_text_buffer_get_insert(buffer);
10107 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10109 if (gtk_text_iter_forward_char(&end_iter)) {
10110 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10114 static void textview_delete_backward_character (GtkTextView *text)
10116 GtkTextBuffer *buffer;
10118 GtkTextIter ins, end_iter;
10120 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10122 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10123 mark = gtk_text_buffer_get_insert(buffer);
10124 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10126 if (gtk_text_iter_backward_char(&end_iter)) {
10127 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10131 static void textview_delete_forward_word (GtkTextView *text)
10133 GtkTextBuffer *buffer;
10135 GtkTextIter ins, end_iter;
10137 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10139 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10140 mark = gtk_text_buffer_get_insert(buffer);
10141 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10143 if (gtk_text_iter_forward_word_end(&end_iter)) {
10144 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10148 static void textview_delete_backward_word (GtkTextView *text)
10150 GtkTextBuffer *buffer;
10152 GtkTextIter ins, end_iter;
10154 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10156 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10157 mark = gtk_text_buffer_get_insert(buffer);
10158 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10160 if (gtk_text_iter_backward_word_start(&end_iter)) {
10161 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10165 static void textview_delete_line (GtkTextView *text)
10167 GtkTextBuffer *buffer;
10169 GtkTextIter ins, start_iter, 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);
10178 gtk_text_iter_set_line_offset(&start_iter, 0);
10181 if (gtk_text_iter_ends_line(&end_iter)){
10182 if (!gtk_text_iter_forward_char(&end_iter))
10183 gtk_text_iter_backward_char(&start_iter);
10186 gtk_text_iter_forward_to_line_end(&end_iter);
10187 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10190 static void textview_delete_to_line_end (GtkTextView *text)
10192 GtkTextBuffer *buffer;
10194 GtkTextIter ins, end_iter;
10196 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10198 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10199 mark = gtk_text_buffer_get_insert(buffer);
10200 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10202 if (gtk_text_iter_ends_line(&end_iter))
10203 gtk_text_iter_forward_char(&end_iter);
10205 gtk_text_iter_forward_to_line_end(&end_iter);
10206 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10209 #define DO_ACTION(name, act) { \
10210 if(!strcmp(name, a_name)) { \
10214 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10216 const gchar *a_name = gtk_action_get_name(action);
10217 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10218 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10219 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10220 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10221 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10222 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10223 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10224 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10225 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10226 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10227 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10228 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10229 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10230 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10234 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10236 Compose *compose = (Compose *)data;
10237 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10238 ComposeCallAdvancedAction action = -1;
10240 action = compose_call_advanced_action_from_path(gaction);
10243 void (*do_action) (GtkTextView *text);
10244 } action_table[] = {
10245 {textview_move_beginning_of_line},
10246 {textview_move_forward_character},
10247 {textview_move_backward_character},
10248 {textview_move_forward_word},
10249 {textview_move_backward_word},
10250 {textview_move_end_of_line},
10251 {textview_move_next_line},
10252 {textview_move_previous_line},
10253 {textview_delete_forward_character},
10254 {textview_delete_backward_character},
10255 {textview_delete_forward_word},
10256 {textview_delete_backward_word},
10257 {textview_delete_line},
10258 {textview_delete_to_line_end}
10261 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10263 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10264 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10265 if (action_table[action].do_action)
10266 action_table[action].do_action(text);
10268 g_warning("Not implemented yet.");
10272 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10276 if (GTK_IS_EDITABLE(widget)) {
10277 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10278 gtk_editable_set_position(GTK_EDITABLE(widget),
10281 if (widget->parent && widget->parent->parent
10282 && widget->parent->parent->parent) {
10283 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10284 gint y = widget->allocation.y;
10285 gint height = widget->allocation.height;
10286 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10287 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10289 if (y < (int)shown->value) {
10290 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10292 if (y + height > (int)shown->value + (int)shown->page_size) {
10293 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10294 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10295 y + height - (int)shown->page_size - 1);
10297 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10298 (int)shown->upper - (int)shown->page_size - 1);
10305 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10306 compose->focused_editable = widget;
10308 #ifdef GENERIC_UMPC
10309 if (GTK_IS_TEXT_VIEW(widget)
10310 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10311 g_object_ref(compose->notebook);
10312 g_object_ref(compose->edit_vbox);
10313 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10314 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10315 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10316 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10317 g_object_unref(compose->notebook);
10318 g_object_unref(compose->edit_vbox);
10319 g_signal_handlers_block_by_func(G_OBJECT(widget),
10320 G_CALLBACK(compose_grab_focus_cb),
10322 gtk_widget_grab_focus(widget);
10323 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10324 G_CALLBACK(compose_grab_focus_cb),
10326 } else if (!GTK_IS_TEXT_VIEW(widget)
10327 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10328 g_object_ref(compose->notebook);
10329 g_object_ref(compose->edit_vbox);
10330 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10331 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10332 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10333 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10334 g_object_unref(compose->notebook);
10335 g_object_unref(compose->edit_vbox);
10336 g_signal_handlers_block_by_func(G_OBJECT(widget),
10337 G_CALLBACK(compose_grab_focus_cb),
10339 gtk_widget_grab_focus(widget);
10340 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10341 G_CALLBACK(compose_grab_focus_cb),
10347 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10349 compose->modified = TRUE;
10350 // compose_beautify_paragraph(compose, NULL, TRUE);
10351 #ifndef GENERIC_UMPC
10352 compose_set_title(compose);
10356 static void compose_wrap_cb(GtkAction *action, gpointer data)
10358 Compose *compose = (Compose *)data;
10359 compose_beautify_paragraph(compose, NULL, TRUE);
10362 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10364 Compose *compose = (Compose *)data;
10365 compose_wrap_all_full(compose, TRUE);
10368 static void compose_find_cb(GtkAction *action, gpointer data)
10370 Compose *compose = (Compose *)data;
10372 message_search_compose(compose);
10375 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10378 Compose *compose = (Compose *)data;
10379 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10380 if (compose->autowrap)
10381 compose_wrap_all_full(compose, TRUE);
10382 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10385 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10388 Compose *compose = (Compose *)data;
10389 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10392 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10394 Compose *compose = (Compose *)data;
10396 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10399 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10401 Compose *compose = (Compose *)data;
10403 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10406 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10408 g_free(compose->privacy_system);
10410 compose->privacy_system = g_strdup(account->default_privacy_system);
10411 compose_update_privacy_system_menu_item(compose, warn);
10414 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10416 Compose *compose = (Compose *)data;
10418 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10419 gtk_widget_show(compose->ruler_hbox);
10420 prefs_common.show_ruler = TRUE;
10422 gtk_widget_hide(compose->ruler_hbox);
10423 gtk_widget_queue_resize(compose->edit_vbox);
10424 prefs_common.show_ruler = FALSE;
10428 static void compose_attach_drag_received_cb (GtkWidget *widget,
10429 GdkDragContext *context,
10432 GtkSelectionData *data,
10435 gpointer user_data)
10437 Compose *compose = (Compose *)user_data;
10440 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10442 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10444 ) && gtk_drag_get_source_widget(context) !=
10445 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10446 list = uri_list_extract_filenames((const gchar *)data->data);
10447 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10448 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10449 compose_attach_append
10450 (compose, (const gchar *)tmp->data,
10451 utf8_filename, NULL);
10452 g_free(utf8_filename);
10454 if (list) compose_changed_cb(NULL, compose);
10455 list_free_strings(list);
10457 } else if (gtk_drag_get_source_widget(context)
10458 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10459 /* comes from our summaryview */
10460 SummaryView * summaryview = NULL;
10461 GSList * list = NULL, *cur = NULL;
10463 if (mainwindow_get_mainwindow())
10464 summaryview = mainwindow_get_mainwindow()->summaryview;
10467 list = summary_get_selected_msg_list(summaryview);
10469 for (cur = list; cur; cur = cur->next) {
10470 MsgInfo *msginfo = (MsgInfo *)cur->data;
10471 gchar *file = NULL;
10473 file = procmsg_get_message_file_full(msginfo,
10476 compose_attach_append(compose, (const gchar *)file,
10477 (const gchar *)file, "message/rfc822");
10481 g_slist_free(list);
10485 static gboolean compose_drag_drop(GtkWidget *widget,
10486 GdkDragContext *drag_context,
10488 guint time, gpointer user_data)
10490 /* not handling this signal makes compose_insert_drag_received_cb
10495 static void compose_insert_drag_received_cb (GtkWidget *widget,
10496 GdkDragContext *drag_context,
10499 GtkSelectionData *data,
10502 gpointer user_data)
10504 Compose *compose = (Compose *)user_data;
10507 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10510 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10512 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10514 AlertValue val = G_ALERTDEFAULT;
10516 list = uri_list_extract_filenames((const gchar *)data->data);
10517 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10518 /* Assume a list of no files, and data has ://, is a remote link */
10519 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10520 gchar *tmpfile = get_tmp_file();
10521 str_write_to_file(tmpdata, tmpfile);
10523 compose_insert_file(compose, tmpfile);
10524 claws_unlink(tmpfile);
10526 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10527 compose_beautify_paragraph(compose, NULL, TRUE);
10530 switch (prefs_common.compose_dnd_mode) {
10531 case COMPOSE_DND_ASK:
10532 val = alertpanel_full(_("Insert or attach?"),
10533 _("Do you want to insert the contents of the file(s) "
10534 "into the message body, or attach it to the email?"),
10535 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10536 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10538 case COMPOSE_DND_INSERT:
10539 val = G_ALERTALTERNATE;
10541 case COMPOSE_DND_ATTACH:
10542 val = G_ALERTOTHER;
10545 /* unexpected case */
10546 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10549 if (val & G_ALERTDISABLE) {
10550 val &= ~G_ALERTDISABLE;
10551 /* remember what action to perform by default, only if we don't click Cancel */
10552 if (val == G_ALERTALTERNATE)
10553 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10554 else if (val == G_ALERTOTHER)
10555 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10558 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10559 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10560 list_free_strings(list);
10563 } else if (val == G_ALERTOTHER) {
10564 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10565 list_free_strings(list);
10570 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10571 compose_insert_file(compose, (const gchar *)tmp->data);
10573 list_free_strings(list);
10575 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10578 #if GTK_CHECK_VERSION(2, 8, 0)
10579 /* do nothing, handled by GTK */
10581 gchar *tmpfile = get_tmp_file();
10582 str_write_to_file((const gchar *)data->data, tmpfile);
10583 compose_insert_file(compose, tmpfile);
10584 claws_unlink(tmpfile);
10586 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10590 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10593 static void compose_header_drag_received_cb (GtkWidget *widget,
10594 GdkDragContext *drag_context,
10597 GtkSelectionData *data,
10600 gpointer user_data)
10602 GtkEditable *entry = (GtkEditable *)user_data;
10603 gchar *email = (gchar *)data->data;
10605 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10608 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10609 gchar *decoded=g_new(gchar, strlen(email));
10612 email += strlen("mailto:");
10613 decode_uri(decoded, email); /* will fit */
10614 gtk_editable_delete_text(entry, 0, -1);
10615 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10616 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10620 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10623 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10625 Compose *compose = (Compose *)data;
10627 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10628 compose->return_receipt = TRUE;
10630 compose->return_receipt = FALSE;
10633 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10635 Compose *compose = (Compose *)data;
10637 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10638 compose->remove_references = TRUE;
10640 compose->remove_references = FALSE;
10643 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10644 ComposeHeaderEntry *headerentry)
10646 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10650 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10651 GdkEventKey *event,
10652 ComposeHeaderEntry *headerentry)
10654 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10655 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10656 !(event->state & GDK_MODIFIER_MASK) &&
10657 (event->keyval == GDK_BackSpace) &&
10658 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10659 gtk_container_remove
10660 (GTK_CONTAINER(headerentry->compose->header_table),
10661 headerentry->combo);
10662 gtk_container_remove
10663 (GTK_CONTAINER(headerentry->compose->header_table),
10664 headerentry->entry);
10665 headerentry->compose->header_list =
10666 g_slist_remove(headerentry->compose->header_list,
10668 g_free(headerentry);
10669 } else if (event->keyval == GDK_Tab) {
10670 if (headerentry->compose->header_last == headerentry) {
10671 /* Override default next focus, and give it to subject_entry
10672 * instead of notebook tabs
10674 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10675 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10682 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10683 ComposeHeaderEntry *headerentry)
10685 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10686 compose_create_header_entry(headerentry->compose);
10687 g_signal_handlers_disconnect_matched
10688 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10689 0, 0, NULL, NULL, headerentry);
10691 /* Automatically scroll down */
10692 GTK_EVENTS_FLUSH();
10693 compose_show_first_last_header(headerentry->compose, FALSE);
10699 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10701 GtkAdjustment *vadj;
10703 cm_return_if_fail(compose);
10704 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10705 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10706 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10707 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10708 gtk_adjustment_changed(vadj);
10711 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10712 const gchar *text, gint len, Compose *compose)
10714 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10715 (G_OBJECT(compose->text), "paste_as_quotation"));
10718 cm_return_if_fail(text != NULL);
10720 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10721 G_CALLBACK(text_inserted),
10723 if (paste_as_quotation) {
10725 const gchar *qmark;
10727 GtkTextIter start_iter;
10730 len = strlen(text);
10732 new_text = g_strndup(text, len);
10734 qmark = compose_quote_char_from_context(compose);
10736 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10737 gtk_text_buffer_place_cursor(buffer, iter);
10739 pos = gtk_text_iter_get_offset(iter);
10741 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10742 _("Quote format error at line %d."));
10743 quote_fmt_reset_vartable();
10745 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10746 GINT_TO_POINTER(paste_as_quotation - 1));
10748 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10749 gtk_text_buffer_place_cursor(buffer, iter);
10750 gtk_text_buffer_delete_mark(buffer, mark);
10752 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10753 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10754 compose_beautify_paragraph(compose, &start_iter, FALSE);
10755 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10756 gtk_text_buffer_delete_mark(buffer, mark);
10758 if (strcmp(text, "\n") || compose->automatic_break
10759 || gtk_text_iter_starts_line(iter)) {
10760 GtkTextIter before_ins;
10761 gtk_text_buffer_insert(buffer, iter, text, len);
10762 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10763 before_ins = *iter;
10764 gtk_text_iter_backward_chars(&before_ins, len);
10765 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10768 /* check if the preceding is just whitespace or quote */
10769 GtkTextIter start_line;
10770 gchar *tmp = NULL, *quote = NULL;
10771 gint quote_len = 0, is_normal = 0;
10772 start_line = *iter;
10773 gtk_text_iter_set_line_offset(&start_line, 0);
10774 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10777 if (*tmp == '\0') {
10780 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10788 gtk_text_buffer_insert(buffer, iter, text, len);
10790 gtk_text_buffer_insert_with_tags_by_name(buffer,
10791 iter, text, len, "no_join", NULL);
10796 if (!paste_as_quotation) {
10797 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10798 compose_beautify_paragraph(compose, iter, FALSE);
10799 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10800 gtk_text_buffer_delete_mark(buffer, mark);
10803 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10804 G_CALLBACK(text_inserted),
10806 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10808 if (prefs_common.autosave &&
10809 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10810 compose->draft_timeout_tag != -2 /* disabled while loading */)
10811 compose->draft_timeout_tag = g_timeout_add
10812 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10814 static gint compose_defer_auto_save_draft(Compose *compose)
10816 compose->draft_timeout_tag = -1;
10817 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10822 static void compose_check_all(GtkAction *action, gpointer data)
10824 Compose *compose = (Compose *)data;
10825 if (!compose->gtkaspell)
10828 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10829 claws_spell_entry_check_all(
10830 CLAWS_SPELL_ENTRY(compose->subject_entry));
10832 gtkaspell_check_all(compose->gtkaspell);
10835 static void compose_highlight_all(GtkAction *action, gpointer data)
10837 Compose *compose = (Compose *)data;
10838 if (compose->gtkaspell) {
10839 claws_spell_entry_recheck_all(
10840 CLAWS_SPELL_ENTRY(compose->subject_entry));
10841 gtkaspell_highlight_all(compose->gtkaspell);
10845 static void compose_check_backwards(GtkAction *action, gpointer data)
10847 Compose *compose = (Compose *)data;
10848 if (!compose->gtkaspell) {
10849 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10853 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10854 claws_spell_entry_check_backwards(
10855 CLAWS_SPELL_ENTRY(compose->subject_entry));
10857 gtkaspell_check_backwards(compose->gtkaspell);
10860 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10862 Compose *compose = (Compose *)data;
10863 if (!compose->gtkaspell) {
10864 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10868 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10869 claws_spell_entry_check_forwards_go(
10870 CLAWS_SPELL_ENTRY(compose->subject_entry));
10872 gtkaspell_check_forwards_go(compose->gtkaspell);
10877 *\brief Guess originating forward account from MsgInfo and several
10878 * "common preference" settings. Return NULL if no guess.
10880 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10882 PrefsAccount *account = NULL;
10884 cm_return_val_if_fail(msginfo, NULL);
10885 cm_return_val_if_fail(msginfo->folder, NULL);
10886 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10888 if (msginfo->folder->prefs->enable_default_account)
10889 account = account_find_from_id(msginfo->folder->prefs->default_account);
10892 account = msginfo->folder->folder->account;
10894 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10896 Xstrdup_a(to, msginfo->to, return NULL);
10897 extract_address(to);
10898 account = account_find_from_address(to, FALSE);
10901 if (!account && prefs_common.forward_account_autosel) {
10902 gchar cc[BUFFSIZE];
10903 if (!procheader_get_header_from_msginfo
10904 (msginfo, cc,sizeof cc , "Cc:")) {
10905 gchar *buf = cc + strlen("Cc:");
10906 extract_address(buf);
10907 account = account_find_from_address(buf, FALSE);
10911 if (!account && prefs_common.forward_account_autosel) {
10912 gchar deliveredto[BUFFSIZE];
10913 if (!procheader_get_header_from_msginfo
10914 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10915 gchar *buf = deliveredto + strlen("Delivered-To:");
10916 extract_address(buf);
10917 account = account_find_from_address(buf, FALSE);
10924 gboolean compose_close(Compose *compose)
10928 if (!g_mutex_trylock(compose->mutex)) {
10929 /* we have to wait for the (possibly deferred by auto-save)
10930 * drafting to be done, before destroying the compose under
10932 debug_print("waiting for drafting to finish...\n");
10933 compose_allow_user_actions(compose, FALSE);
10934 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10937 cm_return_val_if_fail(compose, FALSE);
10938 gtkut_widget_get_uposition(compose->window, &x, &y);
10939 if (!compose->batch) {
10940 prefs_common.compose_x = x;
10941 prefs_common.compose_y = y;
10943 g_mutex_unlock(compose->mutex);
10944 compose_destroy(compose);
10949 * Add entry field for each address in list.
10950 * \param compose E-Mail composition object.
10951 * \param listAddress List of (formatted) E-Mail addresses.
10953 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10956 node = listAddress;
10958 addr = ( gchar * ) node->data;
10959 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
10960 node = g_list_next( node );
10964 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10965 guint action, gboolean opening_multiple)
10967 gchar *body = NULL;
10968 GSList *new_msglist = NULL;
10969 MsgInfo *tmp_msginfo = NULL;
10970 gboolean originally_enc = FALSE;
10971 gboolean originally_sig = FALSE;
10972 Compose *compose = NULL;
10973 gchar *s_system = NULL;
10975 cm_return_if_fail(msgview != NULL);
10977 cm_return_if_fail(msginfo_list != NULL);
10979 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10980 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10981 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10983 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10984 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10985 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10986 orig_msginfo, mimeinfo);
10987 if (tmp_msginfo != NULL) {
10988 new_msglist = g_slist_append(NULL, tmp_msginfo);
10990 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10991 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10992 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10994 tmp_msginfo->folder = orig_msginfo->folder;
10995 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10996 if (orig_msginfo->tags) {
10997 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10998 tmp_msginfo->folder->tags_dirty = TRUE;
11004 if (!opening_multiple)
11005 body = messageview_get_selection(msgview);
11008 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11009 procmsg_msginfo_free(tmp_msginfo);
11010 g_slist_free(new_msglist);
11012 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11014 if (compose && originally_enc) {
11015 compose_force_encryption(compose, compose->account, FALSE, s_system);
11018 if (compose && originally_sig && compose->account->default_sign_reply) {
11019 compose_force_signing(compose, compose->account, s_system);
11025 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11028 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11029 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11030 GSList *cur = msginfo_list;
11031 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11032 "messages. Opening the windows "
11033 "could take some time. Do you "
11034 "want to continue?"),
11035 g_slist_length(msginfo_list));
11036 if (g_slist_length(msginfo_list) > 9
11037 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11038 != G_ALERTALTERNATE) {
11043 /* We'll open multiple compose windows */
11044 /* let the WM place the next windows */
11045 compose_force_window_origin = FALSE;
11046 for (; cur; cur = cur->next) {
11048 tmplist.data = cur->data;
11049 tmplist.next = NULL;
11050 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11052 compose_force_window_origin = TRUE;
11054 /* forwarding multiple mails as attachments is done via a
11055 * single compose window */
11056 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11060 void compose_set_position(Compose *compose, gint pos)
11062 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11064 gtkut_text_view_set_position(text, pos);
11067 gboolean compose_search_string(Compose *compose,
11068 const gchar *str, gboolean case_sens)
11070 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11072 return gtkut_text_view_search_string(text, str, case_sens);
11075 gboolean compose_search_string_backward(Compose *compose,
11076 const gchar *str, gboolean case_sens)
11078 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11080 return gtkut_text_view_search_string_backward(text, str, case_sens);
11083 /* allocate a msginfo structure and populate its data from a compose data structure */
11084 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11086 MsgInfo *newmsginfo;
11088 gchar buf[BUFFSIZE];
11090 cm_return_val_if_fail( compose != NULL, NULL );
11092 newmsginfo = procmsg_msginfo_new();
11095 get_rfc822_date(buf, sizeof(buf));
11096 newmsginfo->date = g_strdup(buf);
11099 if (compose->from_name) {
11100 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11101 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11105 if (compose->subject_entry)
11106 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11108 /* to, cc, reply-to, newsgroups */
11109 for (list = compose->header_list; list; list = list->next) {
11110 gchar *header = gtk_editable_get_chars(
11112 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11113 gchar *entry = gtk_editable_get_chars(
11114 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11116 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11117 if ( newmsginfo->to == NULL ) {
11118 newmsginfo->to = g_strdup(entry);
11119 } else if (entry && *entry) {
11120 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11121 g_free(newmsginfo->to);
11122 newmsginfo->to = tmp;
11125 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11126 if ( newmsginfo->cc == NULL ) {
11127 newmsginfo->cc = g_strdup(entry);
11128 } else if (entry && *entry) {
11129 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11130 g_free(newmsginfo->cc);
11131 newmsginfo->cc = tmp;
11134 if ( strcasecmp(header,
11135 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11136 if ( newmsginfo->newsgroups == NULL ) {
11137 newmsginfo->newsgroups = g_strdup(entry);
11138 } else if (entry && *entry) {
11139 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11140 g_free(newmsginfo->newsgroups);
11141 newmsginfo->newsgroups = tmp;
11149 /* other data is unset */
11155 /* update compose's dictionaries from folder dict settings */
11156 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11157 FolderItem *folder_item)
11159 cm_return_if_fail(compose != NULL);
11161 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11162 FolderItemPrefs *prefs = folder_item->prefs;
11164 if (prefs->enable_default_dictionary)
11165 gtkaspell_change_dict(compose->gtkaspell,
11166 prefs->default_dictionary, FALSE);
11167 if (folder_item->prefs->enable_default_alt_dictionary)
11168 gtkaspell_change_alt_dict(compose->gtkaspell,
11169 prefs->default_alt_dictionary);
11170 if (prefs->enable_default_dictionary
11171 || prefs->enable_default_alt_dictionary)
11172 compose_spell_menu_changed(compose);