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();
1146 if (compose->gtkaspell->check_while_typing)
1147 gtkaspell_highlight_all(compose->gtkaspell);
1152 procmsg_msginfo_free( dummyinfo );
1158 for (i = 0; i < attach_files->len; i++) {
1159 file = g_ptr_array_index(attach_files, i);
1160 compose_attach_append(compose, file, file, NULL);
1164 compose_show_first_last_header(compose, TRUE);
1166 /* Set save folder */
1167 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1168 gchar *folderidentifier;
1170 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1171 folderidentifier = folder_item_get_identifier(item);
1172 compose_set_save_to(compose, folderidentifier);
1173 g_free(folderidentifier);
1176 /* Place cursor according to provided input (mfield) */
1178 case NO_FIELD_PRESENT:
1179 gtk_widget_grab_focus(compose->header_last->entry);
1181 case TO_FIELD_PRESENT:
1183 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1184 gtk_widget_grab_focus(compose->subject_entry);
1186 case SUBJECT_FIELD_PRESENT:
1187 textview = GTK_TEXT_VIEW(compose->text);
1188 textbuf = gtk_text_view_get_buffer(textview);
1189 mark = gtk_text_buffer_get_insert(textbuf);
1190 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1191 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1193 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1194 * only defers where it comes to the variable body
1195 * is not null. If no body is present compose->text
1196 * will be null in which case you cannot place the
1197 * cursor inside the component so. An empty component
1198 * is therefore created before placing the cursor
1200 case BODY_FIELD_PRESENT:
1201 gtk_widget_grab_focus(compose->text);
1205 undo_unblock(compose->undostruct);
1207 if (prefs_common.auto_exteditor)
1208 compose_exec_ext_editor(compose);
1210 compose->draft_timeout_tag = -1;
1211 SCROLL_TO_CURSOR(compose);
1213 compose->modified = FALSE;
1214 compose_set_title(compose);
1218 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1219 gboolean override_pref, const gchar *system)
1221 const gchar *privacy = NULL;
1223 cm_return_if_fail(compose != NULL);
1224 cm_return_if_fail(account != NULL);
1226 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1231 else if (account->default_privacy_system
1232 && strlen(account->default_privacy_system)) {
1233 privacy = account->default_privacy_system;
1235 GSList *privacy_avail = privacy_get_system_ids();
1236 if (privacy_avail && g_slist_length(privacy_avail)) {
1237 privacy = (gchar *)(privacy_avail->data);
1240 if (privacy != NULL) {
1242 g_free(compose->privacy_system);
1243 compose->privacy_system = NULL;
1245 if (compose->privacy_system == NULL)
1246 compose->privacy_system = g_strdup(privacy);
1247 else if (*(compose->privacy_system) == '\0') {
1248 g_free(compose->privacy_system);
1249 compose->privacy_system = g_strdup(privacy);
1251 compose_update_privacy_system_menu_item(compose, FALSE);
1252 compose_use_encryption(compose, TRUE);
1256 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1258 const gchar *privacy = NULL;
1262 else if (account->default_privacy_system
1263 && strlen(account->default_privacy_system)) {
1264 privacy = account->default_privacy_system;
1266 GSList *privacy_avail = privacy_get_system_ids();
1267 if (privacy_avail && g_slist_length(privacy_avail)) {
1268 privacy = (gchar *)(privacy_avail->data);
1272 if (privacy != NULL) {
1274 g_free(compose->privacy_system);
1275 compose->privacy_system = NULL;
1277 if (compose->privacy_system == NULL)
1278 compose->privacy_system = g_strdup(privacy);
1279 compose_update_privacy_system_menu_item(compose, FALSE);
1280 compose_use_signing(compose, TRUE);
1284 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1288 Compose *compose = NULL;
1290 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1292 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1293 cm_return_val_if_fail(msginfo != NULL, NULL);
1295 list_len = g_slist_length(msginfo_list);
1299 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1300 FALSE, prefs_common.default_reply_list, FALSE, body);
1302 case COMPOSE_REPLY_WITH_QUOTE:
1303 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1304 FALSE, prefs_common.default_reply_list, FALSE, body);
1306 case COMPOSE_REPLY_WITHOUT_QUOTE:
1307 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1308 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1310 case COMPOSE_REPLY_TO_SENDER:
1311 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1312 FALSE, FALSE, TRUE, body);
1314 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1315 compose = compose_followup_and_reply_to(msginfo,
1316 COMPOSE_QUOTE_CHECK,
1317 FALSE, FALSE, body);
1319 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1320 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1321 FALSE, FALSE, TRUE, body);
1323 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1324 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1325 FALSE, FALSE, TRUE, NULL);
1327 case COMPOSE_REPLY_TO_ALL:
1328 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1329 TRUE, FALSE, FALSE, body);
1331 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1333 TRUE, FALSE, FALSE, body);
1335 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1337 TRUE, FALSE, FALSE, NULL);
1339 case COMPOSE_REPLY_TO_LIST:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1341 FALSE, TRUE, FALSE, body);
1343 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1345 FALSE, TRUE, FALSE, body);
1347 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1349 FALSE, TRUE, FALSE, NULL);
1351 case COMPOSE_FORWARD:
1352 if (prefs_common.forward_as_attachment) {
1353 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1356 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1360 case COMPOSE_FORWARD_INLINE:
1361 /* check if we reply to more than one Message */
1362 if (list_len == 1) {
1363 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1366 /* more messages FALL THROUGH */
1367 case COMPOSE_FORWARD_AS_ATTACH:
1368 compose = compose_forward_multiple(NULL, msginfo_list);
1370 case COMPOSE_REDIRECT:
1371 compose = compose_redirect(NULL, msginfo, FALSE);
1374 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1377 if (compose == NULL) {
1378 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1382 compose->rmode = mode;
1383 switch (compose->rmode) {
1385 case COMPOSE_REPLY_WITH_QUOTE:
1386 case COMPOSE_REPLY_WITHOUT_QUOTE:
1387 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1388 debug_print("reply mode Normal\n");
1389 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1390 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1392 case COMPOSE_REPLY_TO_SENDER:
1393 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1394 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1395 debug_print("reply mode Sender\n");
1396 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1398 case COMPOSE_REPLY_TO_ALL:
1399 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1400 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1401 debug_print("reply mode All\n");
1402 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1404 case COMPOSE_REPLY_TO_LIST:
1405 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1406 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1407 debug_print("reply mode List\n");
1408 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1416 static Compose *compose_reply(MsgInfo *msginfo,
1417 ComposeQuoteMode quote_mode,
1423 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1424 to_sender, FALSE, body);
1427 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1428 ComposeQuoteMode quote_mode,
1433 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1434 to_sender, TRUE, body);
1437 static void compose_extract_original_charset(Compose *compose)
1439 MsgInfo *info = NULL;
1440 if (compose->replyinfo) {
1441 info = compose->replyinfo;
1442 } else if (compose->fwdinfo) {
1443 info = compose->fwdinfo;
1444 } else if (compose->targetinfo) {
1445 info = compose->targetinfo;
1448 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1449 MimeInfo *partinfo = mimeinfo;
1450 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1451 partinfo = procmime_mimeinfo_next(partinfo);
1453 compose->orig_charset =
1454 g_strdup(procmime_mimeinfo_get_parameter(
1455 partinfo, "charset"));
1457 procmime_mimeinfo_free_all(mimeinfo);
1461 #define SIGNAL_BLOCK(buffer) { \
1462 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1463 G_CALLBACK(compose_changed_cb), \
1465 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1466 G_CALLBACK(text_inserted), \
1470 #define SIGNAL_UNBLOCK(buffer) { \
1471 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1472 G_CALLBACK(compose_changed_cb), \
1474 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1475 G_CALLBACK(text_inserted), \
1479 static Compose *compose_generic_reply(MsgInfo *msginfo,
1480 ComposeQuoteMode quote_mode,
1481 gboolean to_all, gboolean to_ml,
1483 gboolean followup_and_reply_to,
1487 PrefsAccount *account = NULL;
1488 GtkTextView *textview;
1489 GtkTextBuffer *textbuf;
1490 gboolean quote = FALSE;
1491 const gchar *qmark = NULL;
1492 const gchar *body_fmt = NULL;
1493 gchar *s_system = NULL;
1495 cm_return_val_if_fail(msginfo != NULL, NULL);
1496 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1498 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1500 cm_return_val_if_fail(account != NULL, NULL);
1502 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1504 compose->updating = TRUE;
1506 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1507 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1509 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1510 if (!compose->replyinfo)
1511 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1513 compose_extract_original_charset(compose);
1515 if (msginfo->folder && msginfo->folder->ret_rcpt)
1516 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1518 /* Set save folder */
1519 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1520 gchar *folderidentifier;
1522 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1523 folderidentifier = folder_item_get_identifier(msginfo->folder);
1524 compose_set_save_to(compose, folderidentifier);
1525 g_free(folderidentifier);
1528 if (compose_parse_header(compose, msginfo) < 0) {
1529 compose->updating = FALSE;
1530 compose_destroy(compose);
1534 /* override from name according to folder properties */
1535 if (msginfo->folder && msginfo->folder->prefs &&
1536 msginfo->folder->prefs->reply_with_format &&
1537 msginfo->folder->prefs->reply_override_from_format &&
1538 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1543 /* decode \-escape sequences in the internal representation of the quote format */
1544 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1545 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1548 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1549 compose->gtkaspell);
1551 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1553 quote_fmt_scan_string(tmp);
1556 buf = quote_fmt_get_buffer();
1558 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1560 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1561 quote_fmt_reset_vartable();
1566 textview = (GTK_TEXT_VIEW(compose->text));
1567 textbuf = gtk_text_view_get_buffer(textview);
1568 compose_create_tags(textview, compose);
1570 undo_block(compose->undostruct);
1572 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1575 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1576 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1577 /* use the reply format of folder (if enabled), or the account's one
1578 (if enabled) or fallback to the global reply format, which is always
1579 enabled (even if empty), and use the relevant quotemark */
1581 if (msginfo->folder && msginfo->folder->prefs &&
1582 msginfo->folder->prefs->reply_with_format) {
1583 qmark = msginfo->folder->prefs->reply_quotemark;
1584 body_fmt = msginfo->folder->prefs->reply_body_format;
1586 } else if (account->reply_with_format) {
1587 qmark = account->reply_quotemark;
1588 body_fmt = account->reply_body_format;
1591 qmark = prefs_common.quotemark;
1592 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1593 body_fmt = gettext(prefs_common.quotefmt);
1600 /* empty quotemark is not allowed */
1601 if (qmark == NULL || *qmark == '\0')
1603 compose_quote_fmt(compose, compose->replyinfo,
1604 body_fmt, qmark, body, FALSE, TRUE,
1605 _("The body of the \"Reply\" template has an error at line %d."));
1606 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1607 quote_fmt_reset_vartable();
1609 if (compose->gtkaspell->check_while_typing)
1610 gtkaspell_highlight_all(compose->gtkaspell);
1614 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1615 compose_force_encryption(compose, account, FALSE, s_system);
1618 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1619 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1620 compose_force_signing(compose, account, s_system);
1624 SIGNAL_BLOCK(textbuf);
1626 if (account->auto_sig)
1627 compose_insert_sig(compose, FALSE);
1629 compose_wrap_all(compose);
1631 SIGNAL_UNBLOCK(textbuf);
1633 gtk_widget_grab_focus(compose->text);
1635 undo_unblock(compose->undostruct);
1637 if (prefs_common.auto_exteditor)
1638 compose_exec_ext_editor(compose);
1640 compose->modified = FALSE;
1641 compose_set_title(compose);
1643 compose->updating = FALSE;
1644 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1645 SCROLL_TO_CURSOR(compose);
1647 if (compose->deferred_destroy) {
1648 compose_destroy(compose);
1655 #define INSERT_FW_HEADER(var, hdr) \
1656 if (msginfo->var && *msginfo->var) { \
1657 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1658 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1659 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1662 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1663 gboolean as_attach, const gchar *body,
1664 gboolean no_extedit,
1668 GtkTextView *textview;
1669 GtkTextBuffer *textbuf;
1672 cm_return_val_if_fail(msginfo != NULL, NULL);
1673 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1676 !(account = compose_guess_forward_account_from_msginfo
1678 account = cur_account;
1680 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1682 compose->updating = TRUE;
1683 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1684 if (!compose->fwdinfo)
1685 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1687 compose_extract_original_charset(compose);
1689 if (msginfo->subject && *msginfo->subject) {
1690 gchar *buf, *buf2, *p;
1692 buf = p = g_strdup(msginfo->subject);
1693 p += subject_get_prefix_length(p);
1694 memmove(buf, p, strlen(p) + 1);
1696 buf2 = g_strdup_printf("Fw: %s", buf);
1697 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1703 /* override from name according to folder properties */
1704 if (msginfo->folder && msginfo->folder->prefs &&
1705 msginfo->folder->prefs->forward_with_format &&
1706 msginfo->folder->prefs->forward_override_from_format &&
1707 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1711 MsgInfo *full_msginfo = NULL;
1714 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1716 full_msginfo = procmsg_msginfo_copy(msginfo);
1718 /* decode \-escape sequences in the internal representation of the quote format */
1719 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1720 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1723 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1724 compose->gtkaspell);
1726 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1728 quote_fmt_scan_string(tmp);
1731 buf = quote_fmt_get_buffer();
1733 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1735 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1736 quote_fmt_reset_vartable();
1739 procmsg_msginfo_free(full_msginfo);
1742 textview = GTK_TEXT_VIEW(compose->text);
1743 textbuf = gtk_text_view_get_buffer(textview);
1744 compose_create_tags(textview, compose);
1746 undo_block(compose->undostruct);
1750 msgfile = procmsg_get_message_file(msginfo);
1751 if (!is_file_exist(msgfile))
1752 g_warning("%s: file not exist\n", msgfile);
1754 compose_attach_append(compose, msgfile, msgfile,
1759 const gchar *qmark = NULL;
1760 const gchar *body_fmt = NULL;
1761 MsgInfo *full_msginfo;
1763 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1764 body_fmt = gettext(prefs_common.fw_quotefmt);
1768 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1770 full_msginfo = procmsg_msginfo_copy(msginfo);
1772 /* use the forward format of folder (if enabled), or the account's one
1773 (if enabled) or fallback to the global forward format, which is always
1774 enabled (even if empty), and use the relevant quotemark */
1775 if (msginfo->folder && msginfo->folder->prefs &&
1776 msginfo->folder->prefs->forward_with_format) {
1777 qmark = msginfo->folder->prefs->forward_quotemark;
1778 body_fmt = msginfo->folder->prefs->forward_body_format;
1780 } else if (account->forward_with_format) {
1781 qmark = account->forward_quotemark;
1782 body_fmt = account->forward_body_format;
1785 qmark = prefs_common.fw_quotemark;
1786 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1787 body_fmt = gettext(prefs_common.fw_quotefmt);
1792 /* empty quotemark is not allowed */
1793 if (qmark == NULL || *qmark == '\0')
1796 compose_quote_fmt(compose, full_msginfo,
1797 body_fmt, qmark, body, FALSE, TRUE,
1798 _("The body of the \"Forward\" template has an error at line %d."));
1799 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1800 quote_fmt_reset_vartable();
1801 compose_attach_parts(compose, msginfo);
1803 procmsg_msginfo_free(full_msginfo);
1805 if (compose->gtkaspell->check_while_typing)
1806 gtkaspell_highlight_all(compose->gtkaspell);
1810 SIGNAL_BLOCK(textbuf);
1812 if (account->auto_sig)
1813 compose_insert_sig(compose, FALSE);
1815 compose_wrap_all(compose);
1817 SIGNAL_UNBLOCK(textbuf);
1819 gtk_text_buffer_get_start_iter(textbuf, &iter);
1820 gtk_text_buffer_place_cursor(textbuf, &iter);
1822 gtk_widget_grab_focus(compose->header_last->entry);
1824 if (!no_extedit && prefs_common.auto_exteditor)
1825 compose_exec_ext_editor(compose);
1828 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1829 gchar *folderidentifier;
1831 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1832 folderidentifier = folder_item_get_identifier(msginfo->folder);
1833 compose_set_save_to(compose, folderidentifier);
1834 g_free(folderidentifier);
1837 undo_unblock(compose->undostruct);
1839 compose->modified = FALSE;
1840 compose_set_title(compose);
1842 compose->updating = FALSE;
1843 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1844 SCROLL_TO_CURSOR(compose);
1846 if (compose->deferred_destroy) {
1847 compose_destroy(compose);
1854 #undef INSERT_FW_HEADER
1856 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1859 GtkTextView *textview;
1860 GtkTextBuffer *textbuf;
1864 gboolean single_mail = TRUE;
1866 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1868 if (g_slist_length(msginfo_list) > 1)
1869 single_mail = FALSE;
1871 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1872 if (((MsgInfo *)msginfo->data)->folder == NULL)
1875 /* guess account from first selected message */
1877 !(account = compose_guess_forward_account_from_msginfo
1878 (msginfo_list->data)))
1879 account = cur_account;
1881 cm_return_val_if_fail(account != NULL, NULL);
1883 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1884 if (msginfo->data) {
1885 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1886 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1890 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1891 g_warning("no msginfo_list");
1895 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1897 compose->updating = TRUE;
1899 /* override from name according to folder properties */
1900 if (msginfo_list->data) {
1901 MsgInfo *msginfo = msginfo_list->data;
1903 if (msginfo->folder && msginfo->folder->prefs &&
1904 msginfo->folder->prefs->forward_with_format &&
1905 msginfo->folder->prefs->forward_override_from_format &&
1906 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1911 /* decode \-escape sequences in the internal representation of the quote format */
1912 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1913 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1916 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1917 compose->gtkaspell);
1919 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1921 quote_fmt_scan_string(tmp);
1924 buf = quote_fmt_get_buffer();
1926 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1928 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1929 quote_fmt_reset_vartable();
1935 textview = GTK_TEXT_VIEW(compose->text);
1936 textbuf = gtk_text_view_get_buffer(textview);
1937 compose_create_tags(textview, compose);
1939 undo_block(compose->undostruct);
1940 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1941 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1943 if (!is_file_exist(msgfile))
1944 g_warning("%s: file not exist\n", msgfile);
1946 compose_attach_append(compose, msgfile, msgfile,
1952 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1953 if (info->subject && *info->subject) {
1954 gchar *buf, *buf2, *p;
1956 buf = p = g_strdup(info->subject);
1957 p += subject_get_prefix_length(p);
1958 memmove(buf, p, strlen(p) + 1);
1960 buf2 = g_strdup_printf("Fw: %s", buf);
1961 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1967 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1968 _("Fw: multiple emails"));
1971 SIGNAL_BLOCK(textbuf);
1973 if (account->auto_sig)
1974 compose_insert_sig(compose, FALSE);
1976 compose_wrap_all(compose);
1978 SIGNAL_UNBLOCK(textbuf);
1980 gtk_text_buffer_get_start_iter(textbuf, &iter);
1981 gtk_text_buffer_place_cursor(textbuf, &iter);
1983 gtk_widget_grab_focus(compose->header_last->entry);
1984 undo_unblock(compose->undostruct);
1985 compose->modified = FALSE;
1986 compose_set_title(compose);
1988 compose->updating = FALSE;
1989 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1990 SCROLL_TO_CURSOR(compose);
1992 if (compose->deferred_destroy) {
1993 compose_destroy(compose);
2000 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2002 GtkTextIter start = *iter;
2003 GtkTextIter end_iter;
2004 int start_pos = gtk_text_iter_get_offset(&start);
2006 if (!compose->account->sig_sep)
2009 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2010 start_pos+strlen(compose->account->sig_sep));
2012 /* check sig separator */
2013 str = gtk_text_iter_get_text(&start, &end_iter);
2014 if (!strcmp(str, compose->account->sig_sep)) {
2016 /* check end of line (\n) */
2017 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2018 start_pos+strlen(compose->account->sig_sep));
2019 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2020 start_pos+strlen(compose->account->sig_sep)+1);
2021 tmp = gtk_text_iter_get_text(&start, &end_iter);
2022 if (!strcmp(tmp,"\n")) {
2034 static void compose_colorize_signature(Compose *compose)
2036 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2038 GtkTextIter end_iter;
2039 gtk_text_buffer_get_start_iter(buffer, &iter);
2040 while (gtk_text_iter_forward_line(&iter))
2041 if (compose_is_sig_separator(compose, buffer, &iter)) {
2042 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2043 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2047 #define BLOCK_WRAP() { \
2048 prev_autowrap = compose->autowrap; \
2049 buffer = gtk_text_view_get_buffer( \
2050 GTK_TEXT_VIEW(compose->text)); \
2051 compose->autowrap = FALSE; \
2053 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2054 G_CALLBACK(compose_changed_cb), \
2056 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2057 G_CALLBACK(text_inserted), \
2060 #define UNBLOCK_WRAP() { \
2061 compose->autowrap = prev_autowrap; \
2062 if (compose->autowrap) { \
2063 gint old = compose->draft_timeout_tag; \
2064 compose->draft_timeout_tag = -2; \
2065 compose_wrap_all(compose); \
2066 compose->draft_timeout_tag = old; \
2069 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2070 G_CALLBACK(compose_changed_cb), \
2072 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2073 G_CALLBACK(text_inserted), \
2077 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2079 Compose *compose = NULL;
2080 PrefsAccount *account = NULL;
2081 GtkTextView *textview;
2082 GtkTextBuffer *textbuf;
2086 gchar buf[BUFFSIZE];
2087 gboolean use_signing = FALSE;
2088 gboolean use_encryption = FALSE;
2089 gchar *privacy_system = NULL;
2090 int priority = PRIORITY_NORMAL;
2091 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2092 gboolean autowrap = prefs_common.autowrap;
2093 gboolean autoindent = prefs_common.auto_indent;
2095 cm_return_val_if_fail(msginfo != NULL, NULL);
2096 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2098 if (compose_put_existing_to_front(msginfo)) {
2102 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2103 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2104 gchar queueheader_buf[BUFFSIZE];
2107 /* Select Account from queue headers */
2108 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2109 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2110 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2111 account = account_find_from_id(id);
2113 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2114 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2115 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2116 account = account_find_from_id(id);
2118 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2119 sizeof(queueheader_buf), "NAID:")) {
2120 id = atoi(&queueheader_buf[strlen("NAID:")]);
2121 account = account_find_from_id(id);
2123 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2124 sizeof(queueheader_buf), "MAID:")) {
2125 id = atoi(&queueheader_buf[strlen("MAID:")]);
2126 account = account_find_from_id(id);
2128 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2129 sizeof(queueheader_buf), "S:")) {
2130 account = account_find_from_address(queueheader_buf, FALSE);
2132 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2133 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2134 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2135 use_signing = param;
2138 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2139 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2140 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2141 use_signing = param;
2144 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2145 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2146 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2147 use_encryption = param;
2149 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2150 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2151 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2152 use_encryption = param;
2154 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2155 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2156 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2159 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2160 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2161 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2164 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2165 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2166 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2168 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2169 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2170 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2172 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "X-Priority: ")) {
2174 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2177 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "RMID:")) {
2179 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2180 if (tokens[0] && tokens[1] && tokens[2]) {
2181 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2182 if (orig_item != NULL) {
2183 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2188 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2189 sizeof(queueheader_buf), "FMID:")) {
2190 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2191 if (tokens[0] && tokens[1] && tokens[2]) {
2192 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2193 if (orig_item != NULL) {
2194 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2200 account = msginfo->folder->folder->account;
2203 if (!account && prefs_common.reedit_account_autosel) {
2204 gchar from[BUFFSIZE];
2205 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2206 extract_address(from);
2207 account = account_find_from_address(from, FALSE);
2211 account = cur_account;
2213 cm_return_val_if_fail(account != NULL, NULL);
2215 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2217 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2218 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2219 compose->autowrap = autowrap;
2220 compose->replyinfo = replyinfo;
2221 compose->fwdinfo = fwdinfo;
2223 compose->updating = TRUE;
2224 compose->priority = priority;
2226 if (privacy_system != NULL) {
2227 compose->privacy_system = privacy_system;
2228 compose_use_signing(compose, use_signing);
2229 compose_use_encryption(compose, use_encryption);
2230 compose_update_privacy_system_menu_item(compose, FALSE);
2232 activate_privacy_system(compose, account, FALSE);
2235 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2237 compose_extract_original_charset(compose);
2239 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2240 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2241 gchar queueheader_buf[BUFFSIZE];
2243 /* Set message save folder */
2244 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2245 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2246 compose_set_save_to(compose, &queueheader_buf[4]);
2248 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2249 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2251 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2256 if (compose_parse_header(compose, msginfo) < 0) {
2257 compose->updating = FALSE;
2258 compose_destroy(compose);
2261 compose_reedit_set_entry(compose, msginfo);
2263 textview = GTK_TEXT_VIEW(compose->text);
2264 textbuf = gtk_text_view_get_buffer(textview);
2265 compose_create_tags(textview, compose);
2267 mark = gtk_text_buffer_get_insert(textbuf);
2268 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2270 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2271 G_CALLBACK(compose_changed_cb),
2274 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2275 fp = procmime_get_first_encrypted_text_content(msginfo);
2277 compose_force_encryption(compose, account, TRUE, NULL);
2280 fp = procmime_get_first_text_content(msginfo);
2283 g_warning("Can't get text part\n");
2287 gboolean prev_autowrap = compose->autowrap;
2288 GtkTextBuffer *buffer = textbuf;
2290 while (fgets(buf, sizeof(buf), fp) != NULL) {
2292 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2298 compose_attach_parts(compose, msginfo);
2300 compose_colorize_signature(compose);
2302 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2303 G_CALLBACK(compose_changed_cb),
2306 gtk_widget_grab_focus(compose->text);
2308 if (prefs_common.auto_exteditor) {
2309 compose_exec_ext_editor(compose);
2311 compose->modified = FALSE;
2312 compose_set_title(compose);
2314 compose->updating = FALSE;
2315 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2316 SCROLL_TO_CURSOR(compose);
2318 if (compose->deferred_destroy) {
2319 compose_destroy(compose);
2323 compose->sig_str = account_get_signature_str(compose->account);
2328 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2335 cm_return_val_if_fail(msginfo != NULL, NULL);
2338 account = account_get_reply_account(msginfo,
2339 prefs_common.reply_account_autosel);
2340 cm_return_val_if_fail(account != NULL, NULL);
2342 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2344 compose->updating = TRUE;
2346 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2347 compose->replyinfo = NULL;
2348 compose->fwdinfo = NULL;
2350 compose_show_first_last_header(compose, TRUE);
2352 gtk_widget_grab_focus(compose->header_last->entry);
2354 filename = procmsg_get_message_file(msginfo);
2356 if (filename == NULL) {
2357 compose->updating = FALSE;
2358 compose_destroy(compose);
2363 compose->redirect_filename = filename;
2365 /* Set save folder */
2366 item = msginfo->folder;
2367 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2368 gchar *folderidentifier;
2370 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2371 folderidentifier = folder_item_get_identifier(item);
2372 compose_set_save_to(compose, folderidentifier);
2373 g_free(folderidentifier);
2376 compose_attach_parts(compose, msginfo);
2378 if (msginfo->subject)
2379 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2381 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2383 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2384 _("The body of the \"Redirect\" template has an error at line %d."));
2385 quote_fmt_reset_vartable();
2386 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2388 compose_colorize_signature(compose);
2391 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2392 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2393 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2395 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2396 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2397 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2398 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2399 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2400 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2401 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2402 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2404 if (compose->toolbar->draft_btn)
2405 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2406 if (compose->toolbar->insert_btn)
2407 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2408 if (compose->toolbar->attach_btn)
2409 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2410 if (compose->toolbar->sig_btn)
2411 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2412 if (compose->toolbar->exteditor_btn)
2413 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2414 if (compose->toolbar->linewrap_current_btn)
2415 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2416 if (compose->toolbar->linewrap_all_btn)
2417 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2419 compose->modified = FALSE;
2420 compose_set_title(compose);
2421 compose->updating = FALSE;
2422 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2423 SCROLL_TO_CURSOR(compose);
2425 if (compose->deferred_destroy) {
2426 compose_destroy(compose);
2433 GList *compose_get_compose_list(void)
2435 return compose_list;
2438 void compose_entry_append(Compose *compose, const gchar *address,
2439 ComposeEntryType type, ComposePrefType pref_type)
2441 const gchar *header;
2443 gboolean in_quote = FALSE;
2444 if (!address || *address == '\0') return;
2451 header = N_("Bcc:");
2453 case COMPOSE_REPLYTO:
2454 header = N_("Reply-To:");
2456 case COMPOSE_NEWSGROUPS:
2457 header = N_("Newsgroups:");
2459 case COMPOSE_FOLLOWUPTO:
2460 header = N_( "Followup-To:");
2467 header = prefs_common_translated_header_name(header);
2469 cur = begin = (gchar *)address;
2471 /* we separate the line by commas, but not if we're inside a quoted
2473 while (*cur != '\0') {
2475 in_quote = !in_quote;
2476 if (*cur == ',' && !in_quote) {
2477 gchar *tmp = g_strdup(begin);
2479 tmp[cur-begin]='\0';
2482 while (*tmp == ' ' || *tmp == '\t')
2484 compose_add_header_entry(compose, header, tmp, pref_type);
2491 gchar *tmp = g_strdup(begin);
2493 tmp[cur-begin]='\0';
2496 while (*tmp == ' ' || *tmp == '\t')
2498 compose_add_header_entry(compose, header, tmp, pref_type);
2503 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2505 static GdkColor yellow;
2506 static GdkColor black;
2507 static gboolean yellow_initialised = FALSE;
2511 if (!yellow_initialised) {
2512 gdk_color_parse("#f5f6be", &yellow);
2513 gdk_color_parse("#000000", &black);
2514 yellow_initialised = gdk_colormap_alloc_color(
2515 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2516 yellow_initialised &= gdk_colormap_alloc_color(
2517 gdk_colormap_get_system(), &black, FALSE, TRUE);
2520 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2521 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2522 if (gtk_entry_get_text(entry) &&
2523 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2524 if (yellow_initialised) {
2525 gtk_widget_modify_base(
2526 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2527 GTK_STATE_NORMAL, &yellow);
2528 gtk_widget_modify_text(
2529 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2530 GTK_STATE_NORMAL, &black);
2536 void compose_toolbar_cb(gint action, gpointer data)
2538 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2539 Compose *compose = (Compose*)toolbar_item->parent;
2541 cm_return_if_fail(compose != NULL);
2545 compose_send_cb(NULL, compose);
2548 compose_send_later_cb(NULL, compose);
2551 compose_draft(compose, COMPOSE_QUIT_EDITING);
2554 compose_insert_file_cb(NULL, compose);
2557 compose_attach_cb(NULL, compose);
2560 compose_insert_sig(compose, FALSE);
2563 compose_ext_editor_cb(NULL, compose);
2565 case A_LINEWRAP_CURRENT:
2566 compose_beautify_paragraph(compose, NULL, TRUE);
2568 case A_LINEWRAP_ALL:
2569 compose_wrap_all_full(compose, TRUE);
2572 compose_address_cb(NULL, compose);
2575 case A_CHECK_SPELLING:
2576 compose_check_all(NULL, compose);
2584 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2589 gchar *subject = NULL;
2593 gchar **attach = NULL;
2594 MailField mfield = NO_FIELD_PRESENT;
2596 /* get mailto parts but skip from */
2597 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2600 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2601 mfield = TO_FIELD_PRESENT;
2604 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2606 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2608 if (!g_utf8_validate (subject, -1, NULL)) {
2609 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2610 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2613 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2615 mfield = SUBJECT_FIELD_PRESENT;
2618 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2619 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2622 gboolean prev_autowrap = compose->autowrap;
2624 compose->autowrap = FALSE;
2626 mark = gtk_text_buffer_get_insert(buffer);
2627 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2629 if (!g_utf8_validate (body, -1, NULL)) {
2630 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2631 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2634 gtk_text_buffer_insert(buffer, &iter, body, -1);
2636 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2638 compose->autowrap = prev_autowrap;
2639 if (compose->autowrap)
2640 compose_wrap_all(compose);
2641 mfield = BODY_FIELD_PRESENT;
2645 gint i = 0, att = 0;
2646 gchar *warn_files = NULL;
2647 while (attach[i] != NULL) {
2648 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2649 if (utf8_filename) {
2650 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2651 gchar *tmp = g_strdup_printf("%s%s\n",
2652 warn_files?warn_files:"",
2658 g_free(utf8_filename);
2660 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2665 alertpanel_notice(ngettext(
2666 "The following file has been attached: \n%s",
2667 "The following files have been attached: \n%s", att), warn_files);
2681 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2683 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2684 {"Cc:", NULL, TRUE},
2685 {"References:", NULL, FALSE},
2686 {"Bcc:", NULL, TRUE},
2687 {"Newsgroups:", NULL, TRUE},
2688 {"Followup-To:", NULL, TRUE},
2689 {"List-Post:", NULL, FALSE},
2690 {"X-Priority:", NULL, FALSE},
2691 {NULL, NULL, FALSE}};
2707 cm_return_val_if_fail(msginfo != NULL, -1);
2709 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2710 procheader_get_header_fields(fp, hentry);
2713 if (hentry[H_REPLY_TO].body != NULL) {
2714 if (hentry[H_REPLY_TO].body[0] != '\0') {
2716 conv_unmime_header(hentry[H_REPLY_TO].body,
2719 g_free(hentry[H_REPLY_TO].body);
2720 hentry[H_REPLY_TO].body = NULL;
2722 if (hentry[H_CC].body != NULL) {
2723 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2724 g_free(hentry[H_CC].body);
2725 hentry[H_CC].body = NULL;
2727 if (hentry[H_REFERENCES].body != NULL) {
2728 if (compose->mode == COMPOSE_REEDIT)
2729 compose->references = hentry[H_REFERENCES].body;
2731 compose->references = compose_parse_references
2732 (hentry[H_REFERENCES].body, msginfo->msgid);
2733 g_free(hentry[H_REFERENCES].body);
2735 hentry[H_REFERENCES].body = NULL;
2737 if (hentry[H_BCC].body != NULL) {
2738 if (compose->mode == COMPOSE_REEDIT)
2740 conv_unmime_header(hentry[H_BCC].body, NULL);
2741 g_free(hentry[H_BCC].body);
2742 hentry[H_BCC].body = NULL;
2744 if (hentry[H_NEWSGROUPS].body != NULL) {
2745 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2746 hentry[H_NEWSGROUPS].body = NULL;
2748 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2749 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2750 compose->followup_to =
2751 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2754 g_free(hentry[H_FOLLOWUP_TO].body);
2755 hentry[H_FOLLOWUP_TO].body = NULL;
2757 if (hentry[H_LIST_POST].body != NULL) {
2758 gchar *to = NULL, *start = NULL;
2760 extract_address(hentry[H_LIST_POST].body);
2761 if (hentry[H_LIST_POST].body[0] != '\0') {
2762 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2764 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2765 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2768 g_free(compose->ml_post);
2769 compose->ml_post = to;
2772 g_free(hentry[H_LIST_POST].body);
2773 hentry[H_LIST_POST].body = NULL;
2776 /* CLAWS - X-Priority */
2777 if (compose->mode == COMPOSE_REEDIT)
2778 if (hentry[H_X_PRIORITY].body != NULL) {
2781 priority = atoi(hentry[H_X_PRIORITY].body);
2782 g_free(hentry[H_X_PRIORITY].body);
2784 hentry[H_X_PRIORITY].body = NULL;
2786 if (priority < PRIORITY_HIGHEST ||
2787 priority > PRIORITY_LOWEST)
2788 priority = PRIORITY_NORMAL;
2790 compose->priority = priority;
2793 if (compose->mode == COMPOSE_REEDIT) {
2794 if (msginfo->inreplyto && *msginfo->inreplyto)
2795 compose->inreplyto = g_strdup(msginfo->inreplyto);
2799 if (msginfo->msgid && *msginfo->msgid)
2800 compose->inreplyto = g_strdup(msginfo->msgid);
2802 if (!compose->references) {
2803 if (msginfo->msgid && *msginfo->msgid) {
2804 if (msginfo->inreplyto && *msginfo->inreplyto)
2805 compose->references =
2806 g_strdup_printf("<%s>\n\t<%s>",
2810 compose->references =
2811 g_strconcat("<", msginfo->msgid, ">",
2813 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2814 compose->references =
2815 g_strconcat("<", msginfo->inreplyto, ">",
2823 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2825 GSList *ref_id_list, *cur;
2829 ref_id_list = references_list_append(NULL, ref);
2830 if (!ref_id_list) return NULL;
2831 if (msgid && *msgid)
2832 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2837 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2838 /* "<" + Message-ID + ">" + CR+LF+TAB */
2839 len += strlen((gchar *)cur->data) + 5;
2841 if (len > MAX_REFERENCES_LEN) {
2842 /* remove second message-ID */
2843 if (ref_id_list && ref_id_list->next &&
2844 ref_id_list->next->next) {
2845 g_free(ref_id_list->next->data);
2846 ref_id_list = g_slist_remove
2847 (ref_id_list, ref_id_list->next->data);
2849 slist_free_strings(ref_id_list);
2850 g_slist_free(ref_id_list);
2857 new_ref = g_string_new("");
2858 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2859 if (new_ref->len > 0)
2860 g_string_append(new_ref, "\n\t");
2861 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2864 slist_free_strings(ref_id_list);
2865 g_slist_free(ref_id_list);
2867 new_ref_str = new_ref->str;
2868 g_string_free(new_ref, FALSE);
2873 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2874 const gchar *fmt, const gchar *qmark,
2875 const gchar *body, gboolean rewrap,
2876 gboolean need_unescape,
2877 const gchar *err_msg)
2879 MsgInfo* dummyinfo = NULL;
2880 gchar *quote_str = NULL;
2882 gboolean prev_autowrap;
2883 const gchar *trimmed_body = body;
2884 gint cursor_pos = -1;
2885 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2886 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2891 SIGNAL_BLOCK(buffer);
2894 dummyinfo = compose_msginfo_new_from_compose(compose);
2895 msginfo = dummyinfo;
2898 if (qmark != NULL) {
2900 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2901 compose->gtkaspell);
2903 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2905 quote_fmt_scan_string(qmark);
2908 buf = quote_fmt_get_buffer();
2910 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2912 Xstrdup_a(quote_str, buf, goto error)
2915 if (fmt && *fmt != '\0') {
2918 while (*trimmed_body == '\n')
2922 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2923 compose->gtkaspell);
2925 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2927 if (need_unescape) {
2930 /* decode \-escape sequences in the internal representation of the quote format */
2931 tmp = malloc(strlen(fmt)+1);
2932 pref_get_unescaped_pref(tmp, fmt);
2933 quote_fmt_scan_string(tmp);
2937 quote_fmt_scan_string(fmt);
2941 buf = quote_fmt_get_buffer();
2943 gint line = quote_fmt_get_line();
2944 alertpanel_error(err_msg, line);
2950 prev_autowrap = compose->autowrap;
2951 compose->autowrap = FALSE;
2953 mark = gtk_text_buffer_get_insert(buffer);
2954 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2955 if (g_utf8_validate(buf, -1, NULL)) {
2956 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2958 gchar *tmpout = NULL;
2959 tmpout = conv_codeset_strdup
2960 (buf, conv_get_locale_charset_str_no_utf8(),
2962 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2964 tmpout = g_malloc(strlen(buf)*2+1);
2965 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2967 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2971 cursor_pos = quote_fmt_get_cursor_pos();
2972 if (cursor_pos == -1)
2973 cursor_pos = gtk_text_iter_get_offset(&iter);
2974 compose->set_cursor_pos = cursor_pos;
2976 gtk_text_buffer_get_start_iter(buffer, &iter);
2977 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2978 gtk_text_buffer_place_cursor(buffer, &iter);
2980 compose->autowrap = prev_autowrap;
2981 if (compose->autowrap && rewrap)
2982 compose_wrap_all(compose);
2989 SIGNAL_UNBLOCK(buffer);
2991 procmsg_msginfo_free( dummyinfo );
2996 /* if ml_post is of type addr@host and from is of type
2997 * addr-anything@host, return TRUE
2999 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3001 gchar *left_ml = NULL;
3002 gchar *right_ml = NULL;
3003 gchar *left_from = NULL;
3004 gchar *right_from = NULL;
3005 gboolean result = FALSE;
3007 if (!ml_post || !from)
3010 left_ml = g_strdup(ml_post);
3011 if (strstr(left_ml, "@")) {
3012 right_ml = strstr(left_ml, "@")+1;
3013 *(strstr(left_ml, "@")) = '\0';
3016 left_from = g_strdup(from);
3017 if (strstr(left_from, "@")) {
3018 right_from = strstr(left_from, "@")+1;
3019 *(strstr(left_from, "@")) = '\0';
3022 if (left_ml && left_from && right_ml && right_from
3023 && !strncmp(left_from, left_ml, strlen(left_ml))
3024 && !strcmp(right_from, right_ml)) {
3033 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3034 gboolean to_all, gboolean to_ml,
3036 gboolean followup_and_reply_to)
3038 GSList *cc_list = NULL;
3041 gchar *replyto = NULL;
3043 gboolean reply_to_ml = FALSE;
3044 gboolean default_reply_to = FALSE;
3046 cm_return_if_fail(compose->account != NULL);
3047 cm_return_if_fail(msginfo != NULL);
3049 reply_to_ml = to_ml && compose->ml_post;
3051 default_reply_to = msginfo->folder &&
3052 msginfo->folder->prefs->enable_default_reply_to;
3054 if (compose->account->protocol != A_NNTP) {
3055 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3056 if (msginfo->folder->prefs->enable_default_replyto) {
3057 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3058 COMPOSE_REPLYTO, PREF_FOLDER);
3060 if (msginfo->folder->prefs->enable_default_bcc) {
3061 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3062 COMPOSE_BCC, PREF_FOLDER);
3064 if (msginfo->folder->prefs->enable_default_cc) {
3065 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3066 COMPOSE_CC, PREF_FOLDER);
3069 if (reply_to_ml && !default_reply_to) {
3071 gboolean is_subscr = is_subscription(compose->ml_post,
3074 /* normal answer to ml post with a reply-to */
3075 compose_entry_append(compose,
3077 COMPOSE_TO, PREF_ML);
3078 if (compose->replyto)
3079 compose_entry_append(compose,
3081 COMPOSE_CC, PREF_ML);
3083 /* answer to subscription confirmation */
3084 if (compose->replyto)
3085 compose_entry_append(compose,
3087 COMPOSE_TO, PREF_ML);
3088 else if (msginfo->from)
3089 compose_entry_append(compose,
3091 COMPOSE_TO, PREF_ML);
3094 else if (!(to_all || to_sender) && default_reply_to) {
3095 compose_entry_append(compose,
3096 msginfo->folder->prefs->default_reply_to,
3097 COMPOSE_TO, PREF_FOLDER);
3098 compose_entry_mark_default_to(compose,
3099 msginfo->folder->prefs->default_reply_to);
3104 Xstrdup_a(tmp1, msginfo->from, return);
3105 extract_address(tmp1);
3106 if (to_all || to_sender ||
3107 !account_find_from_address(tmp1, FALSE))
3108 compose_entry_append(compose,
3109 (compose->replyto && !to_sender)
3110 ? compose->replyto :
3111 msginfo->from ? msginfo->from : "",
3112 COMPOSE_TO, PREF_NONE);
3113 else if (!to_all && !to_sender) {
3114 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3115 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3116 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3117 if (compose->replyto) {
3118 compose_entry_append(compose,
3120 COMPOSE_TO, PREF_NONE);
3122 compose_entry_append(compose,
3123 msginfo->from ? msginfo->from : "",
3124 COMPOSE_TO, PREF_NONE);
3127 /* replying to own mail, use original recp */
3128 compose_entry_append(compose,
3129 msginfo->to ? msginfo->to : "",
3130 COMPOSE_TO, PREF_NONE);
3131 compose_entry_append(compose,
3132 msginfo->cc ? msginfo->cc : "",
3133 COMPOSE_CC, PREF_NONE);
3138 if (to_sender || (compose->followup_to &&
3139 !strncmp(compose->followup_to, "poster", 6)))
3140 compose_entry_append
3142 (compose->replyto ? compose->replyto :
3143 msginfo->from ? msginfo->from : ""),
3144 COMPOSE_TO, PREF_NONE);
3146 else if (followup_and_reply_to || to_all) {
3147 compose_entry_append
3149 (compose->replyto ? compose->replyto :
3150 msginfo->from ? msginfo->from : ""),
3151 COMPOSE_TO, PREF_NONE);
3153 compose_entry_append
3155 compose->followup_to ? compose->followup_to :
3156 compose->newsgroups ? compose->newsgroups : "",
3157 COMPOSE_NEWSGROUPS, PREF_NONE);
3160 compose_entry_append
3162 compose->followup_to ? compose->followup_to :
3163 compose->newsgroups ? compose->newsgroups : "",
3164 COMPOSE_NEWSGROUPS, PREF_NONE);
3167 if (msginfo->subject && *msginfo->subject) {
3171 buf = p = g_strdup(msginfo->subject);
3172 p += subject_get_prefix_length(p);
3173 memmove(buf, p, strlen(p) + 1);
3175 buf2 = g_strdup_printf("Re: %s", buf);
3176 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3181 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3183 if (to_ml && compose->ml_post) return;
3184 if (!to_all || compose->account->protocol == A_NNTP) return;
3186 if (compose->replyto) {
3187 Xstrdup_a(replyto, compose->replyto, return);
3188 extract_address(replyto);
3190 if (msginfo->from) {
3191 Xstrdup_a(from, msginfo->from, return);
3192 extract_address(from);
3195 if (replyto && from)
3196 cc_list = address_list_append_with_comments(cc_list, from);
3197 if (to_all && msginfo->folder &&
3198 msginfo->folder->prefs->enable_default_reply_to)
3199 cc_list = address_list_append_with_comments(cc_list,
3200 msginfo->folder->prefs->default_reply_to);
3201 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3202 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3205 for (cur = cc_list; cur != NULL; cur = cur->next)
3206 compose_entry_append(compose, (gchar *)cur->data,
3207 COMPOSE_CC, PREF_NONE);
3208 slist_free_strings(cc_list);
3209 g_slist_free(cc_list);
3213 #define SET_ENTRY(entry, str) \
3216 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3219 #define SET_ADDRESS(type, str) \
3222 compose_entry_append(compose, str, type, PREF_NONE); \
3225 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3227 cm_return_if_fail(msginfo != NULL);
3229 SET_ENTRY(subject_entry, msginfo->subject);
3230 SET_ENTRY(from_name, msginfo->from);
3231 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3232 SET_ADDRESS(COMPOSE_CC, compose->cc);
3233 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3234 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3235 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3236 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3238 compose_update_priority_menu_item(compose);
3239 compose_update_privacy_system_menu_item(compose, FALSE);
3240 compose_show_first_last_header(compose, TRUE);
3246 static void compose_insert_sig(Compose *compose, gboolean replace)
3248 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3249 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3251 GtkTextIter iter, iter_end;
3252 gint cur_pos, ins_pos;
3253 gboolean prev_autowrap;
3254 gboolean found = FALSE;
3255 gboolean exists = FALSE;
3257 cm_return_if_fail(compose->account != NULL);
3261 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3262 G_CALLBACK(compose_changed_cb),
3265 mark = gtk_text_buffer_get_insert(buffer);
3266 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3267 cur_pos = gtk_text_iter_get_offset (&iter);
3270 gtk_text_buffer_get_end_iter(buffer, &iter);
3272 exists = (compose->sig_str != NULL);
3275 GtkTextIter first_iter, start_iter, end_iter;
3277 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3279 if (!exists || compose->sig_str[0] == '\0')
3282 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3283 compose->signature_tag);
3286 /* include previous \n\n */
3287 gtk_text_iter_backward_chars(&first_iter, 1);
3288 start_iter = first_iter;
3289 end_iter = first_iter;
3291 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3292 compose->signature_tag);
3293 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3294 compose->signature_tag);
3296 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3302 g_free(compose->sig_str);
3303 compose->sig_str = account_get_signature_str(compose->account);
3305 cur_pos = gtk_text_iter_get_offset(&iter);
3307 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3308 g_free(compose->sig_str);
3309 compose->sig_str = NULL;
3311 if (compose->sig_inserted == FALSE)
3312 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3313 compose->sig_inserted = TRUE;
3315 cur_pos = gtk_text_iter_get_offset(&iter);
3316 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3318 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3319 gtk_text_iter_forward_chars(&iter, 1);
3320 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3321 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3323 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3324 cur_pos = gtk_text_buffer_get_char_count (buffer);
3327 /* put the cursor where it should be
3328 * either where the quote_fmt says, either where it was */
3329 if (compose->set_cursor_pos < 0)
3330 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3332 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3333 compose->set_cursor_pos);
3335 compose->set_cursor_pos = -1;
3336 gtk_text_buffer_place_cursor(buffer, &iter);
3337 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3338 G_CALLBACK(compose_changed_cb),
3344 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3347 GtkTextBuffer *buffer;
3350 const gchar *cur_encoding;
3351 gchar buf[BUFFSIZE];
3354 gboolean prev_autowrap;
3355 gboolean badtxt = FALSE;
3356 struct stat file_stat;
3359 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3361 /* get the size of the file we are about to insert */
3362 ret = g_stat(file, &file_stat);
3364 gchar *shortfile = g_path_get_basename(file);
3365 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3367 return COMPOSE_INSERT_NO_FILE;
3368 } else if (prefs_common.warn_large_insert == TRUE) {
3370 /* ask user for confirmation if the file is large */
3371 if (prefs_common.warn_large_insert_size < 0 ||
3372 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3376 msg = g_strdup_printf(_("You are about to insert a file of %s "
3377 "in the message body. Are you sure you want to do that?"),
3378 to_human_readable(file_stat.st_size));
3379 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3380 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3383 /* do we ask for confirmation next time? */
3384 if (aval & G_ALERTDISABLE) {
3385 /* no confirmation next time, disable feature in preferences */
3386 aval &= ~G_ALERTDISABLE;
3387 prefs_common.warn_large_insert = FALSE;
3390 /* abort file insertion if user canceled action */
3391 if (aval != G_ALERTALTERNATE) {
3392 return COMPOSE_INSERT_NO_FILE;
3398 if ((fp = g_fopen(file, "rb")) == NULL) {
3399 FILE_OP_ERROR(file, "fopen");
3400 return COMPOSE_INSERT_READ_ERROR;
3403 prev_autowrap = compose->autowrap;
3404 compose->autowrap = FALSE;
3406 text = GTK_TEXT_VIEW(compose->text);
3407 buffer = gtk_text_view_get_buffer(text);
3408 mark = gtk_text_buffer_get_insert(buffer);
3409 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3411 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3412 G_CALLBACK(text_inserted),
3415 cur_encoding = conv_get_locale_charset_str_no_utf8();
3417 while (fgets(buf, sizeof(buf), fp) != NULL) {
3420 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3421 str = g_strdup(buf);
3423 str = conv_codeset_strdup
3424 (buf, cur_encoding, CS_INTERNAL);
3427 /* strip <CR> if DOS/Windows file,
3428 replace <CR> with <LF> if Macintosh file. */
3431 if (len > 0 && str[len - 1] != '\n') {
3433 if (str[len] == '\r') str[len] = '\n';
3436 gtk_text_buffer_insert(buffer, &iter, str, -1);
3440 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3441 G_CALLBACK(text_inserted),
3443 compose->autowrap = prev_autowrap;
3444 if (compose->autowrap)
3445 compose_wrap_all(compose);
3450 return COMPOSE_INSERT_INVALID_CHARACTER;
3452 return COMPOSE_INSERT_SUCCESS;
3455 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3456 const gchar *filename,
3457 const gchar *content_type)
3465 GtkListStore *store;
3467 gboolean has_binary = FALSE;
3469 if (!is_file_exist(file)) {
3470 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3471 gboolean result = FALSE;
3472 if (file_from_uri && is_file_exist(file_from_uri)) {
3473 result = compose_attach_append(
3474 compose, file_from_uri,
3478 g_free(file_from_uri);
3481 alertpanel_error("File %s doesn't exist\n", filename);
3484 if ((size = get_file_size(file)) < 0) {
3485 alertpanel_error("Can't get file size of %s\n", filename);
3489 alertpanel_error(_("File %s is empty."), filename);
3492 if ((fp = g_fopen(file, "rb")) == NULL) {
3493 alertpanel_error(_("Can't read %s."), filename);
3498 ainfo = g_new0(AttachInfo, 1);
3499 auto_ainfo = g_auto_pointer_new_with_free
3500 (ainfo, (GFreeFunc) compose_attach_info_free);
3501 ainfo->file = g_strdup(file);
3504 ainfo->content_type = g_strdup(content_type);
3505 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3507 MsgFlags flags = {0, 0};
3509 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3510 ainfo->encoding = ENC_7BIT;
3512 ainfo->encoding = ENC_8BIT;
3514 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3515 if (msginfo && msginfo->subject)
3516 name = g_strdup(msginfo->subject);
3518 name = g_path_get_basename(filename ? filename : file);
3520 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3522 procmsg_msginfo_free(msginfo);
3524 if (!g_ascii_strncasecmp(content_type, "text", 4))
3525 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3527 ainfo->encoding = ENC_BASE64;
3528 name = g_path_get_basename(filename ? filename : file);
3529 ainfo->name = g_strdup(name);
3533 ainfo->content_type = procmime_get_mime_type(file);
3534 if (!ainfo->content_type) {
3535 ainfo->content_type =
3536 g_strdup("application/octet-stream");
3537 ainfo->encoding = ENC_BASE64;
3538 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3540 procmime_get_encoding_for_text_file(file, &has_binary);
3542 ainfo->encoding = ENC_BASE64;
3543 name = g_path_get_basename(filename ? filename : file);
3544 ainfo->name = g_strdup(name);
3548 if (ainfo->name != NULL
3549 && !strcmp(ainfo->name, ".")) {
3550 g_free(ainfo->name);
3554 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3555 g_free(ainfo->content_type);
3556 ainfo->content_type = g_strdup("application/octet-stream");
3559 ainfo->size = (goffset)size;
3560 size_text = to_human_readable((goffset)size);
3562 store = GTK_LIST_STORE(gtk_tree_view_get_model
3563 (GTK_TREE_VIEW(compose->attach_clist)));
3565 gtk_list_store_append(store, &iter);
3566 gtk_list_store_set(store, &iter,
3567 COL_MIMETYPE, ainfo->content_type,
3568 COL_SIZE, size_text,
3569 COL_NAME, ainfo->name,
3571 COL_AUTODATA, auto_ainfo,
3574 g_auto_pointer_free(auto_ainfo);
3575 compose_attach_update_label(compose);
3579 static void compose_use_signing(Compose *compose, gboolean use_signing)
3581 compose->use_signing = use_signing;
3582 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3585 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3587 compose->use_encryption = use_encryption;
3588 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3591 #define NEXT_PART_NOT_CHILD(info) \
3593 node = info->node; \
3594 while (node->children) \
3595 node = g_node_last_child(node); \
3596 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3599 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3603 MimeInfo *firsttext = NULL;
3604 MimeInfo *encrypted = NULL;
3607 const gchar *partname = NULL;
3609 mimeinfo = procmime_scan_message(msginfo);
3610 if (!mimeinfo) return;
3612 if (mimeinfo->node->children == NULL) {
3613 procmime_mimeinfo_free_all(mimeinfo);
3617 /* find first content part */
3618 child = (MimeInfo *) mimeinfo->node->children->data;
3619 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3620 child = (MimeInfo *)child->node->children->data;
3623 if (child->type == MIMETYPE_TEXT) {
3625 debug_print("First text part found\n");
3626 } else if (compose->mode == COMPOSE_REEDIT &&
3627 child->type == MIMETYPE_APPLICATION &&
3628 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3629 encrypted = (MimeInfo *)child->node->parent->data;
3632 child = (MimeInfo *) mimeinfo->node->children->data;
3633 while (child != NULL) {
3636 if (child == encrypted) {
3637 /* skip this part of tree */
3638 NEXT_PART_NOT_CHILD(child);
3642 if (child->type == MIMETYPE_MULTIPART) {
3643 /* get the actual content */
3644 child = procmime_mimeinfo_next(child);
3648 if (child == firsttext) {
3649 child = procmime_mimeinfo_next(child);
3653 outfile = procmime_get_tmp_file_name(child);
3654 if ((err = procmime_get_part(outfile, child)) < 0)
3655 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3657 gchar *content_type;
3659 content_type = procmime_get_content_type_str(child->type, child->subtype);
3661 /* if we meet a pgp signature, we don't attach it, but
3662 * we force signing. */
3663 if ((strcmp(content_type, "application/pgp-signature") &&
3664 strcmp(content_type, "application/pkcs7-signature") &&
3665 strcmp(content_type, "application/x-pkcs7-signature"))
3666 || compose->mode == COMPOSE_REDIRECT) {
3667 partname = procmime_mimeinfo_get_parameter(child, "filename");
3668 if (partname == NULL)
3669 partname = procmime_mimeinfo_get_parameter(child, "name");
3670 if (partname == NULL)
3672 compose_attach_append(compose, outfile,
3673 partname, content_type);
3675 compose_force_signing(compose, compose->account, NULL);
3677 g_free(content_type);
3680 NEXT_PART_NOT_CHILD(child);
3682 procmime_mimeinfo_free_all(mimeinfo);
3685 #undef NEXT_PART_NOT_CHILD
3690 WAIT_FOR_INDENT_CHAR,
3691 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3694 /* return indent length, we allow:
3695 indent characters followed by indent characters or spaces/tabs,
3696 alphabets and numbers immediately followed by indent characters,
3697 and the repeating sequences of the above
3698 If quote ends with multiple spaces, only the first one is included. */
3699 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3700 const GtkTextIter *start, gint *len)
3702 GtkTextIter iter = *start;
3706 IndentState state = WAIT_FOR_INDENT_CHAR;
3709 gint alnum_count = 0;
3710 gint space_count = 0;
3713 if (prefs_common.quote_chars == NULL) {
3717 while (!gtk_text_iter_ends_line(&iter)) {
3718 wc = gtk_text_iter_get_char(&iter);
3719 if (g_unichar_iswide(wc))
3721 clen = g_unichar_to_utf8(wc, ch);
3725 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3726 is_space = g_unichar_isspace(wc);
3728 if (state == WAIT_FOR_INDENT_CHAR) {
3729 if (!is_indent && !g_unichar_isalnum(wc))
3732 quote_len += alnum_count + space_count + 1;
3733 alnum_count = space_count = 0;
3734 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3737 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3738 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3742 else if (is_indent) {
3743 quote_len += alnum_count + space_count + 1;
3744 alnum_count = space_count = 0;
3747 state = WAIT_FOR_INDENT_CHAR;
3751 gtk_text_iter_forward_char(&iter);
3754 if (quote_len > 0 && space_count > 0)
3760 if (quote_len > 0) {
3762 gtk_text_iter_forward_chars(&iter, quote_len);
3763 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3769 /* return >0 if the line is itemized */
3770 static int compose_itemized_length(GtkTextBuffer *buffer,
3771 const GtkTextIter *start)
3773 GtkTextIter iter = *start;
3778 if (gtk_text_iter_ends_line(&iter))
3783 wc = gtk_text_iter_get_char(&iter);
3784 if (!g_unichar_isspace(wc))
3786 gtk_text_iter_forward_char(&iter);
3787 if (gtk_text_iter_ends_line(&iter))
3791 clen = g_unichar_to_utf8(wc, ch);
3795 if (!strchr("*-+", ch[0]))
3798 gtk_text_iter_forward_char(&iter);
3799 if (gtk_text_iter_ends_line(&iter))
3801 wc = gtk_text_iter_get_char(&iter);
3802 if (g_unichar_isspace(wc)) {
3808 /* return the string at the start of the itemization */
3809 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3810 const GtkTextIter *start)
3812 GtkTextIter iter = *start;
3815 GString *item_chars = g_string_new("");
3818 if (gtk_text_iter_ends_line(&iter))
3823 wc = gtk_text_iter_get_char(&iter);
3824 if (!g_unichar_isspace(wc))
3826 gtk_text_iter_forward_char(&iter);
3827 if (gtk_text_iter_ends_line(&iter))
3829 g_string_append_unichar(item_chars, wc);
3832 str = item_chars->str;
3833 g_string_free(item_chars, FALSE);
3837 /* return the number of spaces at a line's start */
3838 static int compose_left_offset_length(GtkTextBuffer *buffer,
3839 const GtkTextIter *start)
3841 GtkTextIter iter = *start;
3844 if (gtk_text_iter_ends_line(&iter))
3848 wc = gtk_text_iter_get_char(&iter);
3849 if (!g_unichar_isspace(wc))
3852 gtk_text_iter_forward_char(&iter);
3853 if (gtk_text_iter_ends_line(&iter))
3857 gtk_text_iter_forward_char(&iter);
3858 if (gtk_text_iter_ends_line(&iter))
3863 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3864 const GtkTextIter *start,
3865 GtkTextIter *break_pos,
3869 GtkTextIter iter = *start, line_end = *start;
3870 PangoLogAttr *attrs;
3877 gboolean can_break = FALSE;
3878 gboolean do_break = FALSE;
3879 gboolean was_white = FALSE;
3880 gboolean prev_dont_break = FALSE;
3882 gtk_text_iter_forward_to_line_end(&line_end);
3883 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3884 len = g_utf8_strlen(str, -1);
3888 g_warning("compose_get_line_break_pos: len = 0!\n");
3892 /* g_print("breaking line: %d: %s (len = %d)\n",
3893 gtk_text_iter_get_line(&iter), str, len); */
3895 attrs = g_new(PangoLogAttr, len + 1);
3897 pango_default_break(str, -1, NULL, attrs, len + 1);
3901 /* skip quote and leading spaces */
3902 for (i = 0; *p != '\0' && i < len; i++) {
3905 wc = g_utf8_get_char(p);
3906 if (i >= quote_len && !g_unichar_isspace(wc))
3908 if (g_unichar_iswide(wc))
3910 else if (*p == '\t')
3914 p = g_utf8_next_char(p);
3917 for (; *p != '\0' && i < len; i++) {
3918 PangoLogAttr *attr = attrs + i;
3922 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3925 was_white = attr->is_white;
3927 /* don't wrap URI */
3928 if ((uri_len = get_uri_len(p)) > 0) {
3930 if (pos > 0 && col > max_col) {
3940 wc = g_utf8_get_char(p);
3941 if (g_unichar_iswide(wc)) {
3943 if (prev_dont_break && can_break && attr->is_line_break)
3945 } else if (*p == '\t')
3949 if (pos > 0 && col > max_col) {
3954 if (*p == '-' || *p == '/')
3955 prev_dont_break = TRUE;
3957 prev_dont_break = FALSE;
3959 p = g_utf8_next_char(p);
3963 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3968 *break_pos = *start;
3969 gtk_text_iter_set_line_offset(break_pos, pos);
3974 static gboolean compose_join_next_line(Compose *compose,
3975 GtkTextBuffer *buffer,
3977 const gchar *quote_str)
3979 GtkTextIter iter_ = *iter, cur, prev, next, end;
3980 PangoLogAttr attrs[3];
3982 gchar *next_quote_str;
3985 gboolean keep_cursor = FALSE;
3987 if (!gtk_text_iter_forward_line(&iter_) ||
3988 gtk_text_iter_ends_line(&iter_)) {
3991 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3993 if ((quote_str || next_quote_str) &&
3994 strcmp2(quote_str, next_quote_str) != 0) {
3995 g_free(next_quote_str);
3998 g_free(next_quote_str);
4001 if (quote_len > 0) {
4002 gtk_text_iter_forward_chars(&end, quote_len);
4003 if (gtk_text_iter_ends_line(&end)) {
4008 /* don't join itemized lines */
4009 if (compose_itemized_length(buffer, &end) > 0) {
4013 /* don't join signature separator */
4014 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4017 /* delete quote str */
4019 gtk_text_buffer_delete(buffer, &iter_, &end);
4021 /* don't join line breaks put by the user */
4023 gtk_text_iter_backward_char(&cur);
4024 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4025 gtk_text_iter_forward_char(&cur);
4029 gtk_text_iter_forward_char(&cur);
4030 /* delete linebreak and extra spaces */
4031 while (gtk_text_iter_backward_char(&cur)) {
4032 wc1 = gtk_text_iter_get_char(&cur);
4033 if (!g_unichar_isspace(wc1))
4038 while (!gtk_text_iter_ends_line(&cur)) {
4039 wc1 = gtk_text_iter_get_char(&cur);
4040 if (!g_unichar_isspace(wc1))
4042 gtk_text_iter_forward_char(&cur);
4045 if (!gtk_text_iter_equal(&prev, &next)) {
4048 mark = gtk_text_buffer_get_insert(buffer);
4049 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4050 if (gtk_text_iter_equal(&prev, &cur))
4052 gtk_text_buffer_delete(buffer, &prev, &next);
4056 /* insert space if required */
4057 gtk_text_iter_backward_char(&prev);
4058 wc1 = gtk_text_iter_get_char(&prev);
4059 wc2 = gtk_text_iter_get_char(&next);
4060 gtk_text_iter_forward_char(&next);
4061 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4062 pango_default_break(str, -1, NULL, attrs, 3);
4063 if (!attrs[1].is_line_break ||
4064 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4065 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4067 gtk_text_iter_backward_char(&iter_);
4068 gtk_text_buffer_place_cursor(buffer, &iter_);
4077 #define ADD_TXT_POS(bp_, ep_, pti_) \
4078 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4079 last = last->next; \
4080 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4081 last->next = NULL; \
4083 g_warning("alloc error scanning URIs\n"); \
4086 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4088 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4089 GtkTextBuffer *buffer;
4090 GtkTextIter iter, break_pos, end_of_line;
4091 gchar *quote_str = NULL;
4093 gboolean wrap_quote = prefs_common.linewrap_quote;
4094 gboolean prev_autowrap = compose->autowrap;
4095 gint startq_offset = -1, noq_offset = -1;
4096 gint uri_start = -1, uri_stop = -1;
4097 gint nouri_start = -1, nouri_stop = -1;
4098 gint num_blocks = 0;
4099 gint quotelevel = -1;
4100 gboolean modified = force;
4101 gboolean removed = FALSE;
4102 gboolean modified_before_remove = FALSE;
4104 gboolean start = TRUE;
4105 gint itemized_len = 0, rem_item_len = 0;
4106 gchar *itemized_chars = NULL;
4107 gboolean item_continuation = FALSE;
4112 if (compose->draft_timeout_tag == -2) {
4116 compose->autowrap = FALSE;
4118 buffer = gtk_text_view_get_buffer(text);
4119 undo_wrapping(compose->undostruct, TRUE);
4124 mark = gtk_text_buffer_get_insert(buffer);
4125 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4129 if (compose->draft_timeout_tag == -2) {
4130 if (gtk_text_iter_ends_line(&iter)) {
4131 while (gtk_text_iter_ends_line(&iter) &&
4132 gtk_text_iter_forward_line(&iter))
4135 while (gtk_text_iter_backward_line(&iter)) {
4136 if (gtk_text_iter_ends_line(&iter)) {
4137 gtk_text_iter_forward_line(&iter);
4143 /* move to line start */
4144 gtk_text_iter_set_line_offset(&iter, 0);
4147 itemized_len = compose_itemized_length(buffer, &iter);
4149 if (!itemized_len) {
4150 itemized_len = compose_left_offset_length(buffer, &iter);
4151 item_continuation = TRUE;
4155 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4157 /* go until paragraph end (empty line) */
4158 while (start || !gtk_text_iter_ends_line(&iter)) {
4159 gchar *scanpos = NULL;
4160 /* parse table - in order of priority */
4162 const gchar *needle; /* token */
4164 /* token search function */
4165 gchar *(*search) (const gchar *haystack,
4166 const gchar *needle);
4167 /* part parsing function */
4168 gboolean (*parse) (const gchar *start,
4169 const gchar *scanpos,
4173 /* part to URI function */
4174 gchar *(*build_uri) (const gchar *bp,
4178 static struct table parser[] = {
4179 {"http://", strcasestr, get_uri_part, make_uri_string},
4180 {"https://", strcasestr, get_uri_part, make_uri_string},
4181 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4182 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4183 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4184 {"www.", strcasestr, get_uri_part, make_http_string},
4185 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4186 {"@", strcasestr, get_email_part, make_email_string}
4188 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4189 gint last_index = PARSE_ELEMS;
4191 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4195 if (!prev_autowrap && num_blocks == 0) {
4197 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4198 G_CALLBACK(text_inserted),
4201 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4204 uri_start = uri_stop = -1;
4206 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4209 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4210 if (startq_offset == -1)
4211 startq_offset = gtk_text_iter_get_offset(&iter);
4212 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4213 if (quotelevel > 2) {
4214 /* recycle colors */
4215 if (prefs_common.recycle_quote_colors)
4224 if (startq_offset == -1)
4225 noq_offset = gtk_text_iter_get_offset(&iter);
4229 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4232 if (gtk_text_iter_ends_line(&iter)) {
4234 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4235 prefs_common.linewrap_len,
4237 GtkTextIter prev, next, cur;
4238 if (prev_autowrap != FALSE || force) {
4239 compose->automatic_break = TRUE;
4241 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4242 compose->automatic_break = FALSE;
4243 if (itemized_len && compose->autoindent) {
4244 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4245 if (!item_continuation)
4246 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4248 } else if (quote_str && wrap_quote) {
4249 compose->automatic_break = TRUE;
4251 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4252 compose->automatic_break = FALSE;
4253 if (itemized_len && compose->autoindent) {
4254 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4255 if (!item_continuation)
4256 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4260 /* remove trailing spaces */
4262 rem_item_len = itemized_len;
4263 while (compose->autoindent && rem_item_len-- > 0)
4264 gtk_text_iter_backward_char(&cur);
4265 gtk_text_iter_backward_char(&cur);
4268 while (!gtk_text_iter_starts_line(&cur)) {
4271 gtk_text_iter_backward_char(&cur);
4272 wc = gtk_text_iter_get_char(&cur);
4273 if (!g_unichar_isspace(wc))
4277 if (!gtk_text_iter_equal(&prev, &next)) {
4278 gtk_text_buffer_delete(buffer, &prev, &next);
4280 gtk_text_iter_forward_char(&break_pos);
4284 gtk_text_buffer_insert(buffer, &break_pos,
4288 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4290 /* move iter to current line start */
4291 gtk_text_iter_set_line_offset(&iter, 0);
4298 /* move iter to next line start */
4304 if (!prev_autowrap && num_blocks > 0) {
4306 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4307 G_CALLBACK(text_inserted),
4311 while (!gtk_text_iter_ends_line(&end_of_line)) {
4312 gtk_text_iter_forward_char(&end_of_line);
4314 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4316 nouri_start = gtk_text_iter_get_offset(&iter);
4317 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4319 walk_pos = gtk_text_iter_get_offset(&iter);
4320 /* FIXME: this looks phony. scanning for anything in the parse table */
4321 for (n = 0; n < PARSE_ELEMS; n++) {
4324 tmp = parser[n].search(walk, parser[n].needle);
4326 if (scanpos == NULL || tmp < scanpos) {
4335 /* check if URI can be parsed */
4336 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4337 (const gchar **)&ep, FALSE)
4338 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4342 strlen(parser[last_index].needle);
4345 uri_start = walk_pos + (bp - o_walk);
4346 uri_stop = walk_pos + (ep - o_walk);
4350 gtk_text_iter_forward_line(&iter);
4353 if (startq_offset != -1) {
4354 GtkTextIter startquote, endquote;
4355 gtk_text_buffer_get_iter_at_offset(
4356 buffer, &startquote, startq_offset);
4359 switch (quotelevel) {
4361 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4362 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4363 gtk_text_buffer_apply_tag_by_name(
4364 buffer, "quote0", &startquote, &endquote);
4365 gtk_text_buffer_remove_tag_by_name(
4366 buffer, "quote1", &startquote, &endquote);
4367 gtk_text_buffer_remove_tag_by_name(
4368 buffer, "quote2", &startquote, &endquote);
4373 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4374 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4375 gtk_text_buffer_apply_tag_by_name(
4376 buffer, "quote1", &startquote, &endquote);
4377 gtk_text_buffer_remove_tag_by_name(
4378 buffer, "quote0", &startquote, &endquote);
4379 gtk_text_buffer_remove_tag_by_name(
4380 buffer, "quote2", &startquote, &endquote);
4385 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4386 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4387 gtk_text_buffer_apply_tag_by_name(
4388 buffer, "quote2", &startquote, &endquote);
4389 gtk_text_buffer_remove_tag_by_name(
4390 buffer, "quote0", &startquote, &endquote);
4391 gtk_text_buffer_remove_tag_by_name(
4392 buffer, "quote1", &startquote, &endquote);
4398 } else if (noq_offset != -1) {
4399 GtkTextIter startnoquote, endnoquote;
4400 gtk_text_buffer_get_iter_at_offset(
4401 buffer, &startnoquote, noq_offset);
4404 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4405 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4406 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4407 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4408 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4409 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4410 gtk_text_buffer_remove_tag_by_name(
4411 buffer, "quote0", &startnoquote, &endnoquote);
4412 gtk_text_buffer_remove_tag_by_name(
4413 buffer, "quote1", &startnoquote, &endnoquote);
4414 gtk_text_buffer_remove_tag_by_name(
4415 buffer, "quote2", &startnoquote, &endnoquote);
4421 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4422 GtkTextIter nouri_start_iter, nouri_end_iter;
4423 gtk_text_buffer_get_iter_at_offset(
4424 buffer, &nouri_start_iter, nouri_start);
4425 gtk_text_buffer_get_iter_at_offset(
4426 buffer, &nouri_end_iter, nouri_stop);
4427 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4428 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4429 gtk_text_buffer_remove_tag_by_name(
4430 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4431 modified_before_remove = modified;
4436 if (uri_start >= 0 && uri_stop > 0) {
4437 GtkTextIter uri_start_iter, uri_end_iter, back;
4438 gtk_text_buffer_get_iter_at_offset(
4439 buffer, &uri_start_iter, uri_start);
4440 gtk_text_buffer_get_iter_at_offset(
4441 buffer, &uri_end_iter, uri_stop);
4442 back = uri_end_iter;
4443 gtk_text_iter_backward_char(&back);
4444 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4445 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4446 gtk_text_buffer_apply_tag_by_name(
4447 buffer, "link", &uri_start_iter, &uri_end_iter);
4449 if (removed && !modified_before_remove) {
4455 // debug_print("not modified, out after %d lines\n", lines);
4459 // debug_print("modified, out after %d lines\n", lines);
4461 g_free(itemized_chars);
4464 undo_wrapping(compose->undostruct, FALSE);
4465 compose->autowrap = prev_autowrap;
4470 void compose_action_cb(void *data)
4472 Compose *compose = (Compose *)data;
4473 compose_wrap_all(compose);
4476 static void compose_wrap_all(Compose *compose)
4478 compose_wrap_all_full(compose, FALSE);
4481 static void compose_wrap_all_full(Compose *compose, gboolean force)
4483 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4484 GtkTextBuffer *buffer;
4486 gboolean modified = TRUE;
4488 buffer = gtk_text_view_get_buffer(text);
4490 gtk_text_buffer_get_start_iter(buffer, &iter);
4491 while (!gtk_text_iter_is_end(&iter) && modified)
4492 modified = compose_beautify_paragraph(compose, &iter, force);
4496 static void compose_set_title(Compose *compose)
4502 edited = compose->modified ? _(" [Edited]") : "";
4504 subject = gtk_editable_get_chars(
4505 GTK_EDITABLE(compose->subject_entry), 0, -1);
4507 #ifndef GENERIC_UMPC
4508 if (subject && strlen(subject))
4509 str = g_strdup_printf(_("%s - Compose message%s"),
4512 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4514 str = g_strdup(_("Compose message"));
4517 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4523 * compose_current_mail_account:
4525 * Find a current mail account (the currently selected account, or the
4526 * default account, if a news account is currently selected). If a
4527 * mail account cannot be found, display an error message.
4529 * Return value: Mail account, or NULL if not found.
4531 static PrefsAccount *
4532 compose_current_mail_account(void)
4536 if (cur_account && cur_account->protocol != A_NNTP)
4539 ac = account_get_default();
4540 if (!ac || ac->protocol == A_NNTP) {
4541 alertpanel_error(_("Account for sending mail is not specified.\n"
4542 "Please select a mail account before sending."));
4549 #define QUOTE_IF_REQUIRED(out, str) \
4551 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4555 len = strlen(str) + 3; \
4556 if ((__tmp = alloca(len)) == NULL) { \
4557 g_warning("can't allocate memory\n"); \
4558 g_string_free(header, TRUE); \
4561 g_snprintf(__tmp, len, "\"%s\"", str); \
4566 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4567 g_warning("can't allocate memory\n"); \
4568 g_string_free(header, TRUE); \
4571 strcpy(__tmp, str); \
4577 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4579 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4583 len = strlen(str) + 3; \
4584 if ((__tmp = alloca(len)) == NULL) { \
4585 g_warning("can't allocate memory\n"); \
4588 g_snprintf(__tmp, len, "\"%s\"", str); \
4593 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4594 g_warning("can't allocate memory\n"); \
4597 strcpy(__tmp, str); \
4603 static void compose_select_account(Compose *compose, PrefsAccount *account,
4606 gchar *from = NULL, *email, *header;
4607 ComposeHeaderEntry *header_entry;
4609 cm_return_if_fail(account != NULL);
4611 email = g_utf8_strdown(compose->account->address, -1);
4612 g_hash_table_remove(compose->email_hashtable, email);
4615 compose->account = account;
4616 g_hash_table_insert(compose->email_hashtable,
4617 g_utf8_strdown(account->address, -1),
4618 GUINT_TO_POINTER(1));
4620 if (account->name && *account->name) {
4622 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4623 from = g_strdup_printf("%s <%s>",
4624 buf, account->address);
4625 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4627 from = g_strdup_printf("<%s>",
4629 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4634 compose_set_title(compose);
4636 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4637 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4639 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4640 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4641 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4643 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4645 activate_privacy_system(compose, account, FALSE);
4647 if (!init && compose->mode != COMPOSE_REDIRECT) {
4648 undo_block(compose->undostruct);
4649 compose_insert_sig(compose, TRUE);
4650 undo_unblock(compose->undostruct);
4653 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4654 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4656 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4657 if (account->protocol == A_NNTP) {
4658 if (!strcmp(header, _("To:")))
4659 combobox_select_by_text(
4660 GTK_COMBO_BOX(header_entry->combo),
4663 if (!strcmp(header, _("Newsgroups:")))
4664 combobox_select_by_text(
4665 GTK_COMBO_BOX(header_entry->combo),
4673 /* use account's dict info if set */
4674 if (compose->gtkaspell) {
4675 if (account->enable_default_dictionary)
4676 gtkaspell_change_dict(compose->gtkaspell,
4677 account->default_dictionary, FALSE);
4678 if (account->enable_default_alt_dictionary)
4679 gtkaspell_change_alt_dict(compose->gtkaspell,
4680 account->default_alt_dictionary);
4681 if (account->enable_default_dictionary
4682 || account->enable_default_alt_dictionary)
4683 compose_spell_menu_changed(compose);
4688 gboolean compose_check_for_valid_recipient(Compose *compose) {
4689 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4690 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4691 gboolean recipient_found = FALSE;
4695 /* free to and newsgroup list */
4696 slist_free_strings(compose->to_list);
4697 g_slist_free(compose->to_list);
4698 compose->to_list = NULL;
4700 slist_free_strings(compose->newsgroup_list);
4701 g_slist_free(compose->newsgroup_list);
4702 compose->newsgroup_list = NULL;
4704 /* search header entries for to and newsgroup entries */
4705 for (list = compose->header_list; list; list = list->next) {
4708 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4709 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4712 if (entry[0] != '\0') {
4713 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4714 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4715 compose->to_list = address_list_append(compose->to_list, entry);
4716 recipient_found = TRUE;
4719 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4720 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4721 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4722 recipient_found = TRUE;
4729 return recipient_found;
4732 static gboolean compose_check_for_set_recipients(Compose *compose)
4734 if (compose->account->set_autocc && compose->account->auto_cc) {
4735 gboolean found_other = FALSE;
4737 /* search header entries for to and newsgroup entries */
4738 for (list = compose->header_list; list; list = list->next) {
4741 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4742 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4745 if (strcmp(entry, compose->account->auto_cc)
4746 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4756 if (compose->batch) {
4757 gtk_widget_show_all(compose->window);
4759 aval = alertpanel(_("Send"),
4760 _("The only recipient is the default CC address. Send anyway?"),
4761 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4762 if (aval != G_ALERTALTERNATE)
4766 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4767 gboolean found_other = FALSE;
4769 /* search header entries for to and newsgroup entries */
4770 for (list = compose->header_list; list; list = list->next) {
4773 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4774 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4777 if (strcmp(entry, compose->account->auto_bcc)
4778 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4788 if (compose->batch) {
4789 gtk_widget_show_all(compose->window);
4791 aval = alertpanel(_("Send"),
4792 _("The only recipient is the default BCC address. Send anyway?"),
4793 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4794 if (aval != G_ALERTALTERNATE)
4801 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4805 if (compose_check_for_valid_recipient(compose) == FALSE) {
4806 if (compose->batch) {
4807 gtk_widget_show_all(compose->window);
4809 alertpanel_error(_("Recipient is not specified."));
4813 if (compose_check_for_set_recipients(compose) == FALSE) {
4817 if (!compose->batch) {
4818 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4819 if (*str == '\0' && check_everything == TRUE &&
4820 compose->mode != COMPOSE_REDIRECT) {
4822 gchar *button_label;
4825 if (compose->sending)
4826 button_label = _("+_Send");
4828 button_label = _("+_Queue");
4829 message = g_strdup_printf(_("Subject is empty. %s"),
4830 compose->sending?_("Send it anyway?"):
4831 _("Queue it anyway?"));
4833 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4834 GTK_STOCK_CANCEL, button_label, NULL);
4836 if (aval != G_ALERTALTERNATE)
4841 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4847 gint compose_send(Compose *compose)
4850 FolderItem *folder = NULL;
4852 gchar *msgpath = NULL;
4853 gboolean discard_window = FALSE;
4854 gchar *errstr = NULL;
4855 gchar *tmsgid = NULL;
4856 MainWindow *mainwin = mainwindow_get_mainwindow();
4857 gboolean queued_removed = FALSE;
4859 if (prefs_common.send_dialog_invisible
4860 || compose->batch == TRUE)
4861 discard_window = TRUE;
4863 compose_allow_user_actions (compose, FALSE);
4864 compose->sending = TRUE;
4866 if (compose_check_entries(compose, TRUE) == FALSE) {
4867 if (compose->batch) {
4868 gtk_widget_show_all(compose->window);
4874 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4877 if (compose->batch) {
4878 gtk_widget_show_all(compose->window);
4881 alertpanel_error(_("Could not queue message for sending:\n\n"
4882 "Charset conversion failed."));
4883 } else if (val == -5) {
4884 alertpanel_error(_("Could not queue message for sending:\n\n"
4885 "Couldn't get recipient encryption key."));
4886 } else if (val == -6) {
4888 } else if (val == -3) {
4889 if (privacy_peek_error())
4890 alertpanel_error(_("Could not queue message for sending:\n\n"
4891 "Signature failed: %s"), privacy_get_error());
4892 } else if (val == -2 && errno != 0) {
4893 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4895 alertpanel_error(_("Could not queue message for sending."));
4900 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4901 if (discard_window) {
4902 compose->sending = FALSE;
4903 compose_close(compose);
4904 /* No more compose access in the normal codepath
4905 * after this point! */
4910 alertpanel_error(_("The message was queued but could not be "
4911 "sent.\nUse \"Send queued messages\" from "
4912 "the main window to retry."));
4913 if (!discard_window) {
4920 if (msgpath == NULL) {
4921 msgpath = folder_item_fetch_msg(folder, msgnum);
4922 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4925 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4926 claws_unlink(msgpath);
4929 if (!discard_window) {
4931 if (!queued_removed)
4932 folder_item_remove_msg(folder, msgnum);
4933 folder_item_scan(folder);
4935 /* make sure we delete that */
4936 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4938 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4939 folder_item_remove_msg(folder, tmp->msgnum);
4940 procmsg_msginfo_free(tmp);
4947 if (!queued_removed)
4948 folder_item_remove_msg(folder, msgnum);
4949 folder_item_scan(folder);
4951 /* make sure we delete that */
4952 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4954 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4955 folder_item_remove_msg(folder, tmp->msgnum);
4956 procmsg_msginfo_free(tmp);
4959 if (!discard_window) {
4960 compose->sending = FALSE;
4961 compose_allow_user_actions (compose, TRUE);
4962 compose_close(compose);
4966 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4967 "the main window to retry."), errstr);
4970 alertpanel_error_log(_("The message was queued but could not be "
4971 "sent.\nUse \"Send queued messages\" from "
4972 "the main window to retry."));
4974 if (!discard_window) {
4983 toolbar_main_set_sensitive(mainwin);
4984 main_window_set_menu_sensitive(mainwin);
4990 compose_allow_user_actions (compose, TRUE);
4991 compose->sending = FALSE;
4992 compose->modified = TRUE;
4993 toolbar_main_set_sensitive(mainwin);
4994 main_window_set_menu_sensitive(mainwin);
4999 static gboolean compose_use_attach(Compose *compose)
5001 GtkTreeModel *model = gtk_tree_view_get_model
5002 (GTK_TREE_VIEW(compose->attach_clist));
5003 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5006 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5009 gchar buf[BUFFSIZE];
5011 gboolean first_to_address;
5012 gboolean first_cc_address;
5014 ComposeHeaderEntry *headerentry;
5015 const gchar *headerentryname;
5016 const gchar *cc_hdr;
5017 const gchar *to_hdr;
5018 gboolean err = FALSE;
5020 debug_print("Writing redirect header\n");
5022 cc_hdr = prefs_common_translated_header_name("Cc:");
5023 to_hdr = prefs_common_translated_header_name("To:");
5025 first_to_address = TRUE;
5026 for (list = compose->header_list; list; list = list->next) {
5027 headerentry = ((ComposeHeaderEntry *)list->data);
5028 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5030 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5031 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5032 Xstrdup_a(str, entstr, return -1);
5034 if (str[0] != '\0') {
5035 compose_convert_header
5036 (compose, buf, sizeof(buf), str,
5037 strlen("Resent-To") + 2, TRUE);
5039 if (first_to_address) {
5040 err |= (fprintf(fp, "Resent-To: ") < 0);
5041 first_to_address = FALSE;
5043 err |= (fprintf(fp, ",") < 0);
5045 err |= (fprintf(fp, "%s", buf) < 0);
5049 if (!first_to_address) {
5050 err |= (fprintf(fp, "\n") < 0);
5053 first_cc_address = TRUE;
5054 for (list = compose->header_list; list; list = list->next) {
5055 headerentry = ((ComposeHeaderEntry *)list->data);
5056 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5058 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5059 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5060 Xstrdup_a(str, strg, return -1);
5062 if (str[0] != '\0') {
5063 compose_convert_header
5064 (compose, buf, sizeof(buf), str,
5065 strlen("Resent-Cc") + 2, TRUE);
5067 if (first_cc_address) {
5068 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5069 first_cc_address = FALSE;
5071 err |= (fprintf(fp, ",") < 0);
5073 err |= (fprintf(fp, "%s", buf) < 0);
5077 if (!first_cc_address) {
5078 err |= (fprintf(fp, "\n") < 0);
5081 return (err ? -1:0);
5084 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5086 gchar buf[BUFFSIZE];
5088 const gchar *entstr;
5089 /* struct utsname utsbuf; */
5090 gboolean err = FALSE;
5092 cm_return_val_if_fail(fp != NULL, -1);
5093 cm_return_val_if_fail(compose->account != NULL, -1);
5094 cm_return_val_if_fail(compose->account->address != NULL, -1);
5097 get_rfc822_date(buf, sizeof(buf));
5098 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5101 if (compose->account->name && *compose->account->name) {
5102 compose_convert_header
5103 (compose, buf, sizeof(buf), compose->account->name,
5104 strlen("From: "), TRUE);
5105 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5106 buf, compose->account->address) < 0);
5108 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5111 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5112 if (*entstr != '\0') {
5113 Xstrdup_a(str, entstr, return -1);
5116 compose_convert_header(compose, buf, sizeof(buf), str,
5117 strlen("Subject: "), FALSE);
5118 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5122 /* Resent-Message-ID */
5123 if (compose->account->set_domain && compose->account->domain) {
5124 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5125 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5126 g_snprintf(buf, sizeof(buf), "%s",
5127 strchr(compose->account->address, '@') ?
5128 strchr(compose->account->address, '@')+1 :
5129 compose->account->address);
5131 g_snprintf(buf, sizeof(buf), "%s", "");
5134 if (compose->account->gen_msgid) {
5136 if (compose->account->msgid_with_addr) {
5137 addr = compose->account->address;
5139 generate_msgid(buf, sizeof(buf), addr);
5140 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5141 compose->msgid = g_strdup(buf);
5143 compose->msgid = NULL;
5146 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5149 /* separator between header and body */
5150 err |= (fputs("\n", fp) == EOF);
5152 return (err ? -1:0);
5155 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5159 gchar buf[BUFFSIZE];
5161 gboolean skip = FALSE;
5162 gboolean err = FALSE;
5163 gchar *not_included[]={
5164 "Return-Path:", "Delivered-To:", "Received:",
5165 "Subject:", "X-UIDL:", "AF:",
5166 "NF:", "PS:", "SRH:",
5167 "SFN:", "DSR:", "MID:",
5168 "CFG:", "PT:", "S:",
5169 "RQ:", "SSV:", "NSV:",
5170 "SSH:", "R:", "MAID:",
5171 "NAID:", "RMID:", "FMID:",
5172 "SCF:", "RRCPT:", "NG:",
5173 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5174 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5175 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5176 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5177 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5180 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5181 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5185 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5187 for (i = 0; not_included[i] != NULL; i++) {
5188 if (g_ascii_strncasecmp(buf, not_included[i],
5189 strlen(not_included[i])) == 0) {
5196 if (fputs(buf, fdest) == -1)
5199 if (!prefs_common.redirect_keep_from) {
5200 if (g_ascii_strncasecmp(buf, "From:",
5201 strlen("From:")) == 0) {
5202 err |= (fputs(" (by way of ", fdest) == EOF);
5203 if (compose->account->name
5204 && *compose->account->name) {
5205 compose_convert_header
5206 (compose, buf, sizeof(buf),
5207 compose->account->name,
5210 err |= (fprintf(fdest, "%s <%s>",
5212 compose->account->address) < 0);
5214 err |= (fprintf(fdest, "%s",
5215 compose->account->address) < 0);
5216 err |= (fputs(")", fdest) == EOF);
5220 if (fputs("\n", fdest) == -1)
5227 if (compose_redirect_write_headers(compose, fdest))
5230 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5231 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5244 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5246 GtkTextBuffer *buffer;
5247 GtkTextIter start, end;
5250 const gchar *out_codeset;
5251 EncodingType encoding = ENC_UNKNOWN;
5252 MimeInfo *mimemsg, *mimetext;
5254 const gchar *src_codeset = CS_INTERNAL;
5255 gchar *from_addr = NULL;
5256 gchar *from_name = NULL;
5258 if (action == COMPOSE_WRITE_FOR_SEND)
5259 attach_parts = TRUE;
5261 /* create message MimeInfo */
5262 mimemsg = procmime_mimeinfo_new();
5263 mimemsg->type = MIMETYPE_MESSAGE;
5264 mimemsg->subtype = g_strdup("rfc822");
5265 mimemsg->content = MIMECONTENT_MEM;
5266 mimemsg->tmp = TRUE; /* must free content later */
5267 mimemsg->data.mem = compose_get_header(compose);
5269 /* Create text part MimeInfo */
5270 /* get all composed text */
5271 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5272 gtk_text_buffer_get_start_iter(buffer, &start);
5273 gtk_text_buffer_get_end_iter(buffer, &end);
5274 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5276 out_codeset = conv_get_charset_str(compose->out_encoding);
5278 if (!out_codeset && is_ascii_str(chars)) {
5279 out_codeset = CS_US_ASCII;
5280 } else if (prefs_common.outgoing_fallback_to_ascii &&
5281 is_ascii_str(chars)) {
5282 out_codeset = CS_US_ASCII;
5283 encoding = ENC_7BIT;
5287 gchar *test_conv_global_out = NULL;
5288 gchar *test_conv_reply = NULL;
5290 /* automatic mode. be automatic. */
5291 codeconv_set_strict(TRUE);
5293 out_codeset = conv_get_outgoing_charset_str();
5295 debug_print("trying to convert to %s\n", out_codeset);
5296 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5299 if (!test_conv_global_out && compose->orig_charset
5300 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5301 out_codeset = compose->orig_charset;
5302 debug_print("failure; trying to convert to %s\n", out_codeset);
5303 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5306 if (!test_conv_global_out && !test_conv_reply) {
5308 out_codeset = CS_INTERNAL;
5309 debug_print("failure; finally using %s\n", out_codeset);
5311 g_free(test_conv_global_out);
5312 g_free(test_conv_reply);
5313 codeconv_set_strict(FALSE);
5316 if (encoding == ENC_UNKNOWN) {
5317 if (prefs_common.encoding_method == CTE_BASE64)
5318 encoding = ENC_BASE64;
5319 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5320 encoding = ENC_QUOTED_PRINTABLE;
5321 else if (prefs_common.encoding_method == CTE_8BIT)
5322 encoding = ENC_8BIT;
5324 encoding = procmime_get_encoding_for_charset(out_codeset);
5327 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5328 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5330 if (action == COMPOSE_WRITE_FOR_SEND) {
5331 codeconv_set_strict(TRUE);
5332 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5333 codeconv_set_strict(FALSE);
5339 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5340 "to the specified %s charset.\n"
5341 "Send it as %s?"), out_codeset, src_codeset);
5342 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5343 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5346 if (aval != G_ALERTALTERNATE) {
5351 out_codeset = src_codeset;
5357 out_codeset = src_codeset;
5362 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5363 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5364 strstr(buf, "\nFrom ") != NULL) {
5365 encoding = ENC_QUOTED_PRINTABLE;
5369 mimetext = procmime_mimeinfo_new();
5370 mimetext->content = MIMECONTENT_MEM;
5371 mimetext->tmp = TRUE; /* must free content later */
5372 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5373 * and free the data, which we need later. */
5374 mimetext->data.mem = g_strdup(buf);
5375 mimetext->type = MIMETYPE_TEXT;
5376 mimetext->subtype = g_strdup("plain");
5377 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5378 g_strdup(out_codeset));
5380 /* protect trailing spaces when signing message */
5381 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5382 privacy_system_can_sign(compose->privacy_system)) {
5383 encoding = ENC_QUOTED_PRINTABLE;
5386 debug_print("main text: %zd bytes encoded as %s in %d\n",
5387 strlen(buf), out_codeset, encoding);
5389 /* check for line length limit */
5390 if (action == COMPOSE_WRITE_FOR_SEND &&
5391 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5392 check_line_length(buf, 1000, &line) < 0) {
5396 msg = g_strdup_printf
5397 (_("Line %d exceeds the line length limit (998 bytes).\n"
5398 "The contents of the message might be broken on the way to the delivery.\n"
5400 "Send it anyway?"), line + 1);
5401 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5403 if (aval != G_ALERTALTERNATE) {
5409 if (encoding != ENC_UNKNOWN)
5410 procmime_encode_content(mimetext, encoding);
5412 /* append attachment parts */
5413 if (compose_use_attach(compose) && attach_parts) {
5414 MimeInfo *mimempart;
5415 gchar *boundary = NULL;
5416 mimempart = procmime_mimeinfo_new();
5417 mimempart->content = MIMECONTENT_EMPTY;
5418 mimempart->type = MIMETYPE_MULTIPART;
5419 mimempart->subtype = g_strdup("mixed");
5423 boundary = generate_mime_boundary(NULL);
5424 } while (strstr(buf, boundary) != NULL);
5426 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5429 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5431 g_node_append(mimempart->node, mimetext->node);
5432 g_node_append(mimemsg->node, mimempart->node);
5434 if (compose_add_attachments(compose, mimempart) < 0)
5437 g_node_append(mimemsg->node, mimetext->node);
5441 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5442 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5443 /* extract name and address */
5444 if (strstr(spec, " <") && strstr(spec, ">")) {
5445 from_addr = g_strdup(strrchr(spec, '<')+1);
5446 *(strrchr(from_addr, '>')) = '\0';
5447 from_name = g_strdup(spec);
5448 *(strrchr(from_name, '<')) = '\0';
5455 /* sign message if sending */
5456 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5457 privacy_system_can_sign(compose->privacy_system))
5458 if (!privacy_sign(compose->privacy_system, mimemsg,
5459 compose->account, from_addr)) {
5466 procmime_write_mimeinfo(mimemsg, fp);
5468 procmime_mimeinfo_free_all(mimemsg);
5473 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5475 GtkTextBuffer *buffer;
5476 GtkTextIter start, end;
5481 if ((fp = g_fopen(file, "wb")) == NULL) {
5482 FILE_OP_ERROR(file, "fopen");
5486 /* chmod for security */
5487 if (change_file_mode_rw(fp, file) < 0) {
5488 FILE_OP_ERROR(file, "chmod");
5489 g_warning("can't change file mode\n");
5492 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5493 gtk_text_buffer_get_start_iter(buffer, &start);
5494 gtk_text_buffer_get_end_iter(buffer, &end);
5495 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5497 chars = conv_codeset_strdup
5498 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5501 if (!chars) return -1;
5504 len = strlen(chars);
5505 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5506 FILE_OP_ERROR(file, "fwrite");
5515 if (fclose(fp) == EOF) {
5516 FILE_OP_ERROR(file, "fclose");
5523 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5526 MsgInfo *msginfo = compose->targetinfo;
5528 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5529 if (!msginfo) return -1;
5531 if (!force && MSG_IS_LOCKED(msginfo->flags))
5534 item = msginfo->folder;
5535 cm_return_val_if_fail(item != NULL, -1);
5537 if (procmsg_msg_exist(msginfo) &&
5538 (folder_has_parent_of_type(item, F_QUEUE) ||
5539 folder_has_parent_of_type(item, F_DRAFT)
5540 || msginfo == compose->autosaved_draft)) {
5541 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5542 g_warning("can't remove the old message\n");
5545 debug_print("removed reedit target %d\n", msginfo->msgnum);
5552 static void compose_remove_draft(Compose *compose)
5555 MsgInfo *msginfo = compose->targetinfo;
5556 drafts = account_get_special_folder(compose->account, F_DRAFT);
5558 if (procmsg_msg_exist(msginfo)) {
5559 folder_item_remove_msg(drafts, msginfo->msgnum);
5564 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5565 gboolean remove_reedit_target)
5567 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5570 static gboolean compose_warn_encryption(Compose *compose)
5572 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5573 AlertValue val = G_ALERTALTERNATE;
5575 if (warning == NULL)
5578 val = alertpanel_full(_("Encryption warning"), warning,
5579 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5580 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5581 if (val & G_ALERTDISABLE) {
5582 val &= ~G_ALERTDISABLE;
5583 if (val == G_ALERTALTERNATE)
5584 privacy_inhibit_encrypt_warning(compose->privacy_system,
5588 if (val == G_ALERTALTERNATE) {
5595 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5596 gchar **msgpath, gboolean check_subject,
5597 gboolean remove_reedit_target)
5604 static gboolean lock = FALSE;
5605 PrefsAccount *mailac = NULL, *newsac = NULL;
5606 gboolean err = FALSE;
5608 debug_print("queueing message...\n");
5609 cm_return_val_if_fail(compose->account != NULL, -1);
5613 if (compose_check_entries(compose, check_subject) == FALSE) {
5615 if (compose->batch) {
5616 gtk_widget_show_all(compose->window);
5621 if (!compose->to_list && !compose->newsgroup_list) {
5622 g_warning("can't get recipient list.");
5627 if (compose->to_list) {
5628 if (compose->account->protocol != A_NNTP)
5629 mailac = compose->account;
5630 else if (cur_account && cur_account->protocol != A_NNTP)
5631 mailac = cur_account;
5632 else if (!(mailac = compose_current_mail_account())) {
5634 alertpanel_error(_("No account for sending mails available!"));
5639 if (compose->newsgroup_list) {
5640 if (compose->account->protocol == A_NNTP)
5641 newsac = compose->account;
5644 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5649 /* write queue header */
5650 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5651 G_DIR_SEPARATOR, compose, (guint) rand());
5652 debug_print("queuing to %s\n", tmp);
5653 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5654 FILE_OP_ERROR(tmp, "fopen");
5660 if (change_file_mode_rw(fp, tmp) < 0) {
5661 FILE_OP_ERROR(tmp, "chmod");
5662 g_warning("can't change file mode\n");
5665 /* queueing variables */
5666 err |= (fprintf(fp, "AF:\n") < 0);
5667 err |= (fprintf(fp, "NF:0\n") < 0);
5668 err |= (fprintf(fp, "PS:10\n") < 0);
5669 err |= (fprintf(fp, "SRH:1\n") < 0);
5670 err |= (fprintf(fp, "SFN:\n") < 0);
5671 err |= (fprintf(fp, "DSR:\n") < 0);
5673 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5675 err |= (fprintf(fp, "MID:\n") < 0);
5676 err |= (fprintf(fp, "CFG:\n") < 0);
5677 err |= (fprintf(fp, "PT:0\n") < 0);
5678 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5679 err |= (fprintf(fp, "RQ:\n") < 0);
5681 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5683 err |= (fprintf(fp, "SSV:\n") < 0);
5685 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5687 err |= (fprintf(fp, "NSV:\n") < 0);
5688 err |= (fprintf(fp, "SSH:\n") < 0);
5689 /* write recepient list */
5690 if (compose->to_list) {
5691 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5692 for (cur = compose->to_list->next; cur != NULL;
5694 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5695 err |= (fprintf(fp, "\n") < 0);
5697 /* write newsgroup list */
5698 if (compose->newsgroup_list) {
5699 err |= (fprintf(fp, "NG:") < 0);
5700 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5701 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5702 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5703 err |= (fprintf(fp, "\n") < 0);
5705 /* Sylpheed account IDs */
5707 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5709 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5712 if (compose->privacy_system != NULL) {
5713 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5714 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5715 if (compose->use_encryption) {
5717 if (!compose_warn_encryption(compose)) {
5724 if (mailac && mailac->encrypt_to_self) {
5725 GSList *tmp_list = g_slist_copy(compose->to_list);
5726 tmp_list = g_slist_append(tmp_list, compose->account->address);
5727 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5728 g_slist_free(tmp_list);
5730 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5732 if (encdata != NULL) {
5733 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5734 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5735 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5737 } /* else we finally dont want to encrypt */
5739 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5740 /* and if encdata was null, it means there's been a problem in
5752 /* Save copy folder */
5753 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5754 gchar *savefolderid;
5756 savefolderid = compose_get_save_to(compose);
5757 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5758 g_free(savefolderid);
5760 /* Save copy folder */
5761 if (compose->return_receipt) {
5762 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5764 /* Message-ID of message replying to */
5765 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5768 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5769 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5772 /* Message-ID of message forwarding to */
5773 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5776 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5777 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5781 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5782 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5784 /* end of headers */
5785 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5787 if (compose->redirect_filename != NULL) {
5788 if (compose_redirect_write_to_file(compose, fp) < 0) {
5797 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5802 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5806 g_warning("failed to write queue message\n");
5813 if (fclose(fp) == EOF) {
5814 FILE_OP_ERROR(tmp, "fclose");
5821 if (item && *item) {
5824 queue = account_get_special_folder(compose->account, F_QUEUE);
5827 g_warning("can't find queue folder\n");
5833 folder_item_scan(queue);
5834 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5835 g_warning("can't queue the message\n");
5842 if (msgpath == NULL) {
5848 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5849 compose_remove_reedit_target(compose, FALSE);
5852 if ((msgnum != NULL) && (item != NULL)) {
5860 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5863 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5865 struct stat statbuf;
5866 gchar *type, *subtype;
5867 GtkTreeModel *model;
5870 model = gtk_tree_view_get_model(tree_view);
5872 if (!gtk_tree_model_get_iter_first(model, &iter))
5875 gtk_tree_model_get(model, &iter,
5879 if (!is_file_exist(ainfo->file)) {
5880 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5881 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5882 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5884 if (val == G_ALERTDEFAULT) {
5889 mimepart = procmime_mimeinfo_new();
5890 mimepart->content = MIMECONTENT_FILE;
5891 mimepart->data.filename = g_strdup(ainfo->file);
5892 mimepart->tmp = FALSE; /* or we destroy our attachment */
5893 mimepart->offset = 0;
5895 g_stat(ainfo->file, &statbuf);
5896 mimepart->length = statbuf.st_size;
5898 type = g_strdup(ainfo->content_type);
5900 if (!strchr(type, '/')) {
5902 type = g_strdup("application/octet-stream");
5905 subtype = strchr(type, '/') + 1;
5906 *(subtype - 1) = '\0';
5907 mimepart->type = procmime_get_media_type(type);
5908 mimepart->subtype = g_strdup(subtype);
5911 if (mimepart->type == MIMETYPE_MESSAGE &&
5912 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5913 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5916 if (mimepart->type == MIMETYPE_APPLICATION &&
5917 !strcmp2(mimepart->subtype, "octet-stream"))
5918 g_hash_table_insert(mimepart->typeparameters,
5919 g_strdup("name"), g_strdup(ainfo->name));
5920 g_hash_table_insert(mimepart->dispositionparameters,
5921 g_strdup("filename"), g_strdup(ainfo->name));
5922 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5926 if (compose->use_signing) {
5927 if (ainfo->encoding == ENC_7BIT)
5928 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5929 else if (ainfo->encoding == ENC_8BIT)
5930 ainfo->encoding = ENC_BASE64;
5933 procmime_encode_content(mimepart, ainfo->encoding);
5935 g_node_append(parent->node, mimepart->node);
5936 } while (gtk_tree_model_iter_next(model, &iter));
5941 #define IS_IN_CUSTOM_HEADER(header) \
5942 (compose->account->add_customhdr && \
5943 custom_header_find(compose->account->customhdr_list, header) != NULL)
5945 static void compose_add_headerfield_from_headerlist(Compose *compose,
5947 const gchar *fieldname,
5948 const gchar *seperator)
5950 gchar *str, *fieldname_w_colon;
5951 gboolean add_field = FALSE;
5953 ComposeHeaderEntry *headerentry;
5954 const gchar *headerentryname;
5955 const gchar *trans_fieldname;
5958 if (IS_IN_CUSTOM_HEADER(fieldname))
5961 debug_print("Adding %s-fields\n", fieldname);
5963 fieldstr = g_string_sized_new(64);
5965 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5966 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5968 for (list = compose->header_list; list; list = list->next) {
5969 headerentry = ((ComposeHeaderEntry *)list->data);
5970 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5972 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5973 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5975 if (str[0] != '\0') {
5977 g_string_append(fieldstr, seperator);
5978 g_string_append(fieldstr, str);
5987 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5988 compose_convert_header
5989 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5990 strlen(fieldname) + 2, TRUE);
5991 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5995 g_free(fieldname_w_colon);
5996 g_string_free(fieldstr, TRUE);
6001 static gchar *compose_get_header(Compose *compose)
6003 gchar buf[BUFFSIZE];
6004 const gchar *entry_str;
6008 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6010 gchar *from_name = NULL, *from_address = NULL;
6013 cm_return_val_if_fail(compose->account != NULL, NULL);
6014 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6016 header = g_string_sized_new(64);
6019 get_rfc822_date(buf, sizeof(buf));
6020 g_string_append_printf(header, "Date: %s\n", buf);
6024 if (compose->account->name && *compose->account->name) {
6026 QUOTE_IF_REQUIRED(buf, compose->account->name);
6027 tmp = g_strdup_printf("%s <%s>",
6028 buf, compose->account->address);
6030 tmp = g_strdup_printf("%s",
6031 compose->account->address);
6033 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6034 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6036 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6037 from_address = g_strdup(compose->account->address);
6039 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6040 /* extract name and address */
6041 if (strstr(spec, " <") && strstr(spec, ">")) {
6042 from_address = g_strdup(strrchr(spec, '<')+1);
6043 *(strrchr(from_address, '>')) = '\0';
6044 from_name = g_strdup(spec);
6045 *(strrchr(from_name, '<')) = '\0';
6048 from_address = g_strdup(spec);
6055 if (from_name && *from_name) {
6056 compose_convert_header
6057 (compose, buf, sizeof(buf), from_name,
6058 strlen("From: "), TRUE);
6059 QUOTE_IF_REQUIRED(name, buf);
6061 g_string_append_printf(header, "From: %s <%s>\n",
6062 name, from_address);
6064 g_string_append_printf(header, "From: %s\n", from_address);
6067 g_free(from_address);
6070 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6073 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6076 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6079 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6082 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6084 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6087 compose_convert_header(compose, buf, sizeof(buf), str,
6088 strlen("Subject: "), FALSE);
6089 g_string_append_printf(header, "Subject: %s\n", buf);
6095 if (compose->account->set_domain && compose->account->domain) {
6096 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6097 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6098 g_snprintf(buf, sizeof(buf), "%s",
6099 strchr(compose->account->address, '@') ?
6100 strchr(compose->account->address, '@')+1 :
6101 compose->account->address);
6103 g_snprintf(buf, sizeof(buf), "%s", "");
6106 if (compose->account->gen_msgid) {
6108 if (compose->account->msgid_with_addr) {
6109 addr = compose->account->address;
6111 generate_msgid(buf, sizeof(buf), addr);
6112 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6113 compose->msgid = g_strdup(buf);
6115 compose->msgid = NULL;
6118 if (compose->remove_references == FALSE) {
6120 if (compose->inreplyto && compose->to_list)
6121 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6124 if (compose->references)
6125 g_string_append_printf(header, "References: %s\n", compose->references);
6129 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6132 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6135 if (compose->account->organization &&
6136 strlen(compose->account->organization) &&
6137 !IS_IN_CUSTOM_HEADER("Organization")) {
6138 compose_convert_header(compose, buf, sizeof(buf),
6139 compose->account->organization,
6140 strlen("Organization: "), FALSE);
6141 g_string_append_printf(header, "Organization: %s\n", buf);
6144 /* Program version and system info */
6145 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6146 !compose->newsgroup_list) {
6147 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6149 gtk_major_version, gtk_minor_version, gtk_micro_version,
6152 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6153 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6155 gtk_major_version, gtk_minor_version, gtk_micro_version,
6159 /* custom headers */
6160 if (compose->account->add_customhdr) {
6163 for (cur = compose->account->customhdr_list; cur != NULL;
6165 CustomHeader *chdr = (CustomHeader *)cur->data;
6167 if (custom_header_is_allowed(chdr->name)) {
6168 compose_convert_header
6169 (compose, buf, sizeof(buf),
6170 chdr->value ? chdr->value : "",
6171 strlen(chdr->name) + 2, FALSE);
6172 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6177 /* Automatic Faces and X-Faces */
6178 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6179 g_string_append_printf(header, "X-Face: %s\n", buf);
6181 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6182 g_string_append_printf(header, "X-Face: %s\n", buf);
6184 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6185 g_string_append_printf(header, "Face: %s\n", buf);
6187 else if (get_default_face (buf, sizeof(buf)) == 0) {
6188 g_string_append_printf(header, "Face: %s\n", buf);
6192 switch (compose->priority) {
6193 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6194 "X-Priority: 1 (Highest)\n");
6196 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6197 "X-Priority: 2 (High)\n");
6199 case PRIORITY_NORMAL: break;
6200 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6201 "X-Priority: 4 (Low)\n");
6203 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6204 "X-Priority: 5 (Lowest)\n");
6206 default: debug_print("compose: priority unknown : %d\n",
6210 /* Request Return Receipt */
6211 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6212 if (compose->return_receipt) {
6213 if (compose->account->name
6214 && *compose->account->name) {
6215 compose_convert_header(compose, buf, sizeof(buf),
6216 compose->account->name,
6217 strlen("Disposition-Notification-To: "),
6219 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6221 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6225 /* get special headers */
6226 for (list = compose->header_list; list; list = list->next) {
6227 ComposeHeaderEntry *headerentry;
6230 gchar *headername_wcolon;
6231 const gchar *headername_trans;
6234 gboolean standard_header = FALSE;
6236 headerentry = ((ComposeHeaderEntry *)list->data);
6238 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6240 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6245 if (!strstr(tmp, ":")) {
6246 headername_wcolon = g_strconcat(tmp, ":", NULL);
6247 headername = g_strdup(tmp);
6249 headername_wcolon = g_strdup(tmp);
6250 headername = g_strdup(strtok(tmp, ":"));
6254 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6255 Xstrdup_a(headervalue, entry_str, return NULL);
6256 subst_char(headervalue, '\r', ' ');
6257 subst_char(headervalue, '\n', ' ');
6258 string = std_headers;
6259 while (*string != NULL) {
6260 headername_trans = prefs_common_translated_header_name(*string);
6261 if (!strcmp(headername_trans, headername_wcolon))
6262 standard_header = TRUE;
6265 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6266 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6269 g_free(headername_wcolon);
6273 g_string_free(header, FALSE);
6278 #undef IS_IN_CUSTOM_HEADER
6280 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6281 gint header_len, gboolean addr_field)
6283 gchar *tmpstr = NULL;
6284 const gchar *out_codeset = NULL;
6286 cm_return_if_fail(src != NULL);
6287 cm_return_if_fail(dest != NULL);
6289 if (len < 1) return;
6291 tmpstr = g_strdup(src);
6293 subst_char(tmpstr, '\n', ' ');
6294 subst_char(tmpstr, '\r', ' ');
6297 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6298 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6299 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6304 codeconv_set_strict(TRUE);
6305 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6306 conv_get_charset_str(compose->out_encoding));
6307 codeconv_set_strict(FALSE);
6309 if (!dest || *dest == '\0') {
6310 gchar *test_conv_global_out = NULL;
6311 gchar *test_conv_reply = NULL;
6313 /* automatic mode. be automatic. */
6314 codeconv_set_strict(TRUE);
6316 out_codeset = conv_get_outgoing_charset_str();
6318 debug_print("trying to convert to %s\n", out_codeset);
6319 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6322 if (!test_conv_global_out && compose->orig_charset
6323 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6324 out_codeset = compose->orig_charset;
6325 debug_print("failure; trying to convert to %s\n", out_codeset);
6326 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6329 if (!test_conv_global_out && !test_conv_reply) {
6331 out_codeset = CS_INTERNAL;
6332 debug_print("finally using %s\n", out_codeset);
6334 g_free(test_conv_global_out);
6335 g_free(test_conv_reply);
6336 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6338 codeconv_set_strict(FALSE);
6343 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6347 cm_return_if_fail(user_data != NULL);
6349 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6350 g_strstrip(address);
6351 if (*address != '\0') {
6352 gchar *name = procheader_get_fromname(address);
6353 extract_address(address);
6354 addressbook_add_contact(name, address, NULL, NULL);
6359 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6361 GtkWidget *menuitem;
6364 cm_return_if_fail(menu != NULL);
6365 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6367 menuitem = gtk_separator_menu_item_new();
6368 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6369 gtk_widget_show(menuitem);
6371 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6372 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6374 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6375 g_strstrip(address);
6376 if (*address == '\0') {
6377 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6380 g_signal_connect(G_OBJECT(menuitem), "activate",
6381 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6382 gtk_widget_show(menuitem);
6385 static void compose_create_header_entry(Compose *compose)
6387 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6394 const gchar *header = NULL;
6395 ComposeHeaderEntry *headerentry;
6396 gboolean standard_header = FALSE;
6397 GtkListStore *model;
6399 #if !(GTK_CHECK_VERSION(2,12,0))
6400 GtkTooltips *tips = compose->tooltips;
6403 headerentry = g_new0(ComposeHeaderEntry, 1);
6406 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6407 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6408 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6410 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6412 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6414 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6415 COMPOSE_NEWSGROUPS);
6416 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6418 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6419 COMPOSE_FOLLOWUPTO);
6421 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6422 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6423 G_CALLBACK(compose_grab_focus_cb), compose);
6424 gtk_widget_show(combo);
6425 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6426 compose->header_nextrow, compose->header_nextrow+1,
6427 GTK_SHRINK, GTK_FILL, 0, 0);
6428 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6429 const gchar *last_header_entry = gtk_entry_get_text(
6430 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6432 while (*string != NULL) {
6433 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6434 standard_header = TRUE;
6437 if (standard_header)
6438 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6440 if (!compose->header_last || !standard_header) {
6441 switch(compose->account->protocol) {
6443 header = prefs_common_translated_header_name("Newsgroups:");
6446 header = prefs_common_translated_header_name("To:");
6451 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6453 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6454 G_CALLBACK(compose_grab_focus_cb), compose);
6456 /* Entry field with cleanup button */
6457 #if GTK_CHECK_VERSION(2, 8, 0)
6458 button = gtk_button_new();
6459 gtk_button_set_image(GTK_BUTTON(button),
6460 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6462 button = gtk_button_new_with_label(_("Clear"));
6464 gtk_widget_show(button);
6465 CLAWS_SET_TIP(button,
6466 _("Delete entry contents"));
6467 entry = gtk_entry_new();
6468 gtk_widget_show(entry);
6469 CLAWS_SET_TIP(entry,
6470 _("Use <tab> to autocomplete from addressbook"));
6471 hbox = gtk_hbox_new (FALSE, 0);
6472 gtk_widget_show(hbox);
6473 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6474 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6475 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6476 compose->header_nextrow, compose->header_nextrow+1,
6477 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6479 g_signal_connect(G_OBJECT(entry), "key-press-event",
6480 G_CALLBACK(compose_headerentry_key_press_event_cb),
6482 g_signal_connect(G_OBJECT(entry), "changed",
6483 G_CALLBACK(compose_headerentry_changed_cb),
6485 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6486 G_CALLBACK(compose_grab_focus_cb), compose);
6488 g_signal_connect(G_OBJECT(button), "clicked",
6489 G_CALLBACK(compose_headerentry_button_clicked_cb),
6493 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6494 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6495 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6496 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6497 G_CALLBACK(compose_header_drag_received_cb),
6499 g_signal_connect(G_OBJECT(entry), "drag-drop",
6500 G_CALLBACK(compose_drag_drop),
6502 g_signal_connect(G_OBJECT(entry), "populate-popup",
6503 G_CALLBACK(compose_entry_popup_extend),
6506 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6508 headerentry->compose = compose;
6509 headerentry->combo = combo;
6510 headerentry->entry = entry;
6511 headerentry->button = button;
6512 headerentry->hbox = hbox;
6513 headerentry->headernum = compose->header_nextrow;
6514 headerentry->type = PREF_NONE;
6516 compose->header_nextrow++;
6517 compose->header_last = headerentry;
6518 compose->header_list =
6519 g_slist_append(compose->header_list,
6523 static void compose_add_header_entry(Compose *compose, const gchar *header,
6524 gchar *text, ComposePrefType pref_type)
6526 ComposeHeaderEntry *last_header = compose->header_last;
6527 gchar *tmp = g_strdup(text), *email;
6529 extract_address(tmp);
6530 email = g_utf8_strdown(tmp, -1);
6532 if (!(!strcmp(header, "To:") && !strcasecmp(compose->account->address, email))
6533 && g_hash_table_lookup(compose->email_hashtable, email) != NULL) {
6534 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6535 header, text, (gint) pref_type);
6541 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6542 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6543 last_header->type = pref_type;
6545 g_hash_table_insert(compose->email_hashtable, email,
6546 GUINT_TO_POINTER(1));
6550 static void compose_destroy_headerentry(Compose *compose,
6551 ComposeHeaderEntry *headerentry)
6553 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6556 extract_address(text);
6557 email = g_utf8_strdown(text, -1);
6558 g_hash_table_remove(compose->email_hashtable, email);
6562 gtk_widget_destroy(headerentry->combo);
6563 gtk_widget_destroy(headerentry->entry);
6564 gtk_widget_destroy(headerentry->button);
6565 gtk_widget_destroy(headerentry->hbox);
6566 g_free(headerentry);
6569 static void compose_remove_header_entries(Compose *compose)
6572 for (list = compose->header_list; list; list = list->next)
6573 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6575 compose->header_last = NULL;
6576 g_slist_free(compose->header_list);
6577 compose->header_list = NULL;
6578 compose->header_nextrow = 1;
6579 compose_create_header_entry(compose);
6582 static GtkWidget *compose_create_header(Compose *compose)
6584 GtkWidget *from_optmenu_hbox;
6585 GtkWidget *header_scrolledwin;
6586 GtkWidget *header_table;
6590 /* header labels and entries */
6591 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6592 gtk_widget_show(header_scrolledwin);
6593 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6595 header_table = gtk_table_new(2, 2, FALSE);
6596 gtk_widget_show(header_table);
6597 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6598 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6599 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6602 /* option menu for selecting accounts */
6603 from_optmenu_hbox = compose_account_option_menu_create(compose);
6604 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6605 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6608 compose->header_table = header_table;
6609 compose->header_list = NULL;
6610 compose->header_nextrow = count;
6612 compose_create_header_entry(compose);
6614 compose->table = NULL;
6616 return header_scrolledwin ;
6619 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6621 Compose *compose = (Compose *)data;
6622 GdkEventButton event;
6625 event.time = gtk_get_current_event_time();
6627 return attach_button_pressed(compose->attach_clist, &event, compose);
6630 static GtkWidget *compose_create_attach(Compose *compose)
6632 GtkWidget *attach_scrwin;
6633 GtkWidget *attach_clist;
6635 GtkListStore *store;
6636 GtkCellRenderer *renderer;
6637 GtkTreeViewColumn *column;
6638 GtkTreeSelection *selection;
6640 /* attachment list */
6641 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6642 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6643 GTK_POLICY_AUTOMATIC,
6644 GTK_POLICY_AUTOMATIC);
6645 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6647 store = gtk_list_store_new(N_ATTACH_COLS,
6652 G_TYPE_AUTO_POINTER,
6654 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6655 (GTK_TREE_MODEL(store)));
6656 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6657 g_object_unref(store);
6659 renderer = gtk_cell_renderer_text_new();
6660 column = gtk_tree_view_column_new_with_attributes
6661 (_("Mime type"), renderer, "text",
6662 COL_MIMETYPE, NULL);
6663 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6665 renderer = gtk_cell_renderer_text_new();
6666 column = gtk_tree_view_column_new_with_attributes
6667 (_("Size"), renderer, "text",
6669 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6671 renderer = gtk_cell_renderer_text_new();
6672 column = gtk_tree_view_column_new_with_attributes
6673 (_("Name"), renderer, "text",
6675 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6677 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6678 prefs_common.use_stripes_everywhere);
6679 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6680 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6682 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6683 G_CALLBACK(attach_selected), compose);
6684 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6685 G_CALLBACK(attach_button_pressed), compose);
6687 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6688 G_CALLBACK(popup_attach_button_pressed), compose);
6690 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6691 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6692 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6693 G_CALLBACK(popup_attach_button_pressed), compose);
6695 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6696 G_CALLBACK(attach_key_pressed), compose);
6699 gtk_drag_dest_set(attach_clist,
6700 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6701 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6702 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6703 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6704 G_CALLBACK(compose_attach_drag_received_cb),
6706 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6707 G_CALLBACK(compose_drag_drop),
6710 compose->attach_scrwin = attach_scrwin;
6711 compose->attach_clist = attach_clist;
6713 return attach_scrwin;
6716 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6717 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6719 static GtkWidget *compose_create_others(Compose *compose)
6722 GtkWidget *savemsg_checkbtn;
6723 GtkWidget *savemsg_combo;
6724 GtkWidget *savemsg_select;
6727 gchar *folderidentifier;
6729 /* Table for settings */
6730 table = gtk_table_new(3, 1, FALSE);
6731 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6732 gtk_widget_show(table);
6733 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6736 /* Save Message to folder */
6737 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6738 gtk_widget_show(savemsg_checkbtn);
6739 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6740 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6741 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6743 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6744 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6746 savemsg_combo = gtk_combo_box_entry_new_text();
6747 compose->savemsg_checkbtn = savemsg_checkbtn;
6748 compose->savemsg_combo = savemsg_combo;
6749 gtk_widget_show(savemsg_combo);
6751 if (prefs_common.compose_save_to_history)
6752 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6753 prefs_common.compose_save_to_history);
6755 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6756 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6757 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6758 G_CALLBACK(compose_grab_focus_cb), compose);
6759 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6760 folderidentifier = folder_item_get_identifier(account_get_special_folder
6761 (compose->account, F_OUTBOX));
6762 compose_set_save_to(compose, folderidentifier);
6763 g_free(folderidentifier);
6766 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6767 gtk_widget_show(savemsg_select);
6768 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6769 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6770 G_CALLBACK(compose_savemsg_select_cb),
6778 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6780 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6781 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6784 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6789 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6792 path = folder_item_get_identifier(dest);
6794 compose_set_save_to(compose, path);
6798 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6799 GdkAtom clip, GtkTextIter *insert_place);
6802 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6806 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6808 if (event->button == 3) {
6810 GtkTextIter sel_start, sel_end;
6811 gboolean stuff_selected;
6813 /* move the cursor to allow GtkAspell to check the word
6814 * under the mouse */
6815 if (event->x && event->y) {
6816 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6817 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6819 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6822 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6823 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6826 stuff_selected = gtk_text_buffer_get_selection_bounds(
6828 &sel_start, &sel_end);
6830 gtk_text_buffer_place_cursor (buffer, &iter);
6831 /* reselect stuff */
6833 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6834 gtk_text_buffer_select_range(buffer,
6835 &sel_start, &sel_end);
6837 return FALSE; /* pass the event so that the right-click goes through */
6840 if (event->button == 2) {
6845 /* get the middle-click position to paste at the correct place */
6846 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6847 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6849 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6852 entry_paste_clipboard(compose, text,
6853 prefs_common.linewrap_pastes,
6854 GDK_SELECTION_PRIMARY, &iter);
6862 static void compose_spell_menu_changed(void *data)
6864 Compose *compose = (Compose *)data;
6866 GtkWidget *menuitem;
6867 GtkWidget *parent_item;
6868 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6871 if (compose->gtkaspell == NULL)
6874 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6875 "/Menu/Spelling/Options");
6877 /* setting the submenu removes /Spelling/Options from the factory
6878 * so we need to save it */
6880 if (parent_item == NULL) {
6881 parent_item = compose->aspell_options_menu;
6882 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6884 compose->aspell_options_menu = parent_item;
6886 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6888 spell_menu = g_slist_reverse(spell_menu);
6889 for (items = spell_menu;
6890 items; items = items->next) {
6891 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6892 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6893 gtk_widget_show(GTK_WIDGET(menuitem));
6895 g_slist_free(spell_menu);
6897 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6898 gtk_widget_show(parent_item);
6901 static void compose_dict_changed(void *data)
6903 Compose *compose = (Compose *) data;
6905 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6908 gtkaspell_highlight_all(compose->gtkaspell);
6909 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6913 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6915 Compose *compose = (Compose *)data;
6916 GdkEventButton event;
6919 event.time = gtk_get_current_event_time();
6923 return text_clicked(compose->text, &event, compose);
6926 static gboolean compose_force_window_origin = TRUE;
6927 static Compose *compose_create(PrefsAccount *account,
6936 GtkWidget *handlebox;
6938 GtkWidget *notebook;
6940 GtkWidget *attach_hbox;
6941 GtkWidget *attach_lab1;
6942 GtkWidget *attach_lab2;
6947 GtkWidget *subject_hbox;
6948 GtkWidget *subject_frame;
6949 GtkWidget *subject_entry;
6953 GtkWidget *edit_vbox;
6954 GtkWidget *ruler_hbox;
6956 GtkWidget *scrolledwin;
6958 GtkTextBuffer *buffer;
6959 GtkClipboard *clipboard;
6962 UndoMain *undostruct;
6964 gchar *titles[N_ATTACH_COLS];
6965 GtkWidget *popupmenu;
6966 GtkWidget *tmpl_menu;
6967 GtkActionGroup *action_group = NULL;
6970 GtkAspell * gtkaspell = NULL;
6973 static GdkGeometry geometry;
6975 cm_return_val_if_fail(account != NULL, NULL);
6977 debug_print("Creating compose window...\n");
6978 compose = g_new0(Compose, 1);
6980 titles[COL_MIMETYPE] = _("MIME type");
6981 titles[COL_SIZE] = _("Size");
6982 titles[COL_NAME] = _("Name");
6984 compose->batch = batch;
6985 compose->account = account;
6986 compose->folder = folder;
6988 compose->mutex = g_mutex_new();
6989 compose->set_cursor_pos = -1;
6991 #if !(GTK_CHECK_VERSION(2,12,0))
6992 compose->tooltips = tips;
6995 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6997 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6998 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7000 if (!geometry.max_width) {
7001 geometry.max_width = gdk_screen_width();
7002 geometry.max_height = gdk_screen_height();
7005 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7006 &geometry, GDK_HINT_MAX_SIZE);
7007 if (!geometry.min_width) {
7008 geometry.min_width = 600;
7009 geometry.min_height = 440;
7011 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7012 &geometry, GDK_HINT_MIN_SIZE);
7014 #ifndef GENERIC_UMPC
7015 if (compose_force_window_origin)
7016 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7017 prefs_common.compose_y);
7019 g_signal_connect(G_OBJECT(window), "delete_event",
7020 G_CALLBACK(compose_delete_cb), compose);
7021 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7022 gtk_widget_realize(window);
7024 gtkut_widget_set_composer_icon(window);
7026 vbox = gtk_vbox_new(FALSE, 0);
7027 gtk_container_add(GTK_CONTAINER(window), vbox);
7029 compose->ui_manager = gtk_ui_manager_new();
7030 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7031 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7032 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7033 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7034 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7035 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7036 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7037 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7038 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7039 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7042 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7047 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7048 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7050 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7052 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7053 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7054 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7057 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7059 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7060 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7061 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7062 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7063 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7064 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7065 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7066 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7069 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7070 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7073 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7077 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7078 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7079 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7080 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7082 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7084 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7085 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7086 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7087 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7088 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7089 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7091 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7092 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7093 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7094 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7096 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7097 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7098 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7100 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7102 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7103 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7104 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7105 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7108 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7110 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7114 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7115 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7116 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7117 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7118 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7119 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7123 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7124 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7125 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7127 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7129 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7130 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7131 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7132 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7133 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7136 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7137 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7139 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7140 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7144 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7145 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7146 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7155 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7159 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)
7160 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)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7166 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)
7167 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)
7169 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7172 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)
7173 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7176 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)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7182 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)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7187 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7188 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)
7189 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)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7199 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7201 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)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7213 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7217 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7219 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7220 gtk_widget_show_all(menubar);
7222 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7224 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7226 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7229 if (prefs_common.toolbar_detachable) {
7230 handlebox = gtk_handle_box_new();
7232 handlebox = gtk_hbox_new(FALSE, 0);
7234 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7236 gtk_widget_realize(handlebox);
7238 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7241 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7245 vbox2 = gtk_vbox_new(FALSE, 2);
7246 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7247 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7250 notebook = gtk_notebook_new();
7251 gtk_widget_set_size_request(notebook, -1, 130);
7252 gtk_widget_show(notebook);
7254 /* header labels and entries */
7255 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7256 compose_create_header(compose),
7257 gtk_label_new_with_mnemonic(_("Hea_der")));
7258 /* attachment list */
7259 attach_hbox = gtk_hbox_new(FALSE, 0);
7260 gtk_widget_show(attach_hbox);
7262 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7263 gtk_widget_show(attach_lab1);
7264 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7266 attach_lab2 = gtk_label_new("");
7267 gtk_widget_show(attach_lab2);
7268 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7270 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7271 compose_create_attach(compose),
7274 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7275 compose_create_others(compose),
7276 gtk_label_new_with_mnemonic(_("Othe_rs")));
7279 subject_hbox = gtk_hbox_new(FALSE, 0);
7280 gtk_widget_show(subject_hbox);
7282 subject_frame = gtk_frame_new(NULL);
7283 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7284 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7285 gtk_widget_show(subject_frame);
7287 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7288 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7289 gtk_widget_show(subject);
7291 label = gtk_label_new(_("Subject:"));
7292 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7293 gtk_widget_show(label);
7296 subject_entry = claws_spell_entry_new();
7298 subject_entry = gtk_entry_new();
7300 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7301 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7302 G_CALLBACK(compose_grab_focus_cb), compose);
7303 gtk_widget_show(subject_entry);
7304 compose->subject_entry = subject_entry;
7305 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7307 edit_vbox = gtk_vbox_new(FALSE, 0);
7309 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7312 ruler_hbox = gtk_hbox_new(FALSE, 0);
7313 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7315 ruler = gtk_shruler_new();
7316 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7317 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7321 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7322 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7323 GTK_POLICY_AUTOMATIC,
7324 GTK_POLICY_AUTOMATIC);
7325 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7327 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7328 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7330 text = gtk_text_view_new();
7331 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7332 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7333 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7334 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7335 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7337 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7339 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7340 G_CALLBACK(compose_edit_size_alloc),
7342 g_signal_connect(G_OBJECT(buffer), "changed",
7343 G_CALLBACK(compose_changed_cb), compose);
7344 g_signal_connect(G_OBJECT(text), "grab_focus",
7345 G_CALLBACK(compose_grab_focus_cb), compose);
7346 g_signal_connect(G_OBJECT(buffer), "insert_text",
7347 G_CALLBACK(text_inserted), compose);
7348 g_signal_connect(G_OBJECT(text), "button_press_event",
7349 G_CALLBACK(text_clicked), compose);
7351 g_signal_connect(G_OBJECT(text), "popup-menu",
7352 G_CALLBACK(compose_popup_menu), compose);
7354 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7355 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7356 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7357 G_CALLBACK(compose_popup_menu), compose);
7359 g_signal_connect(G_OBJECT(subject_entry), "changed",
7360 G_CALLBACK(compose_changed_cb), compose);
7363 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7364 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7365 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7366 g_signal_connect(G_OBJECT(text), "drag_data_received",
7367 G_CALLBACK(compose_insert_drag_received_cb),
7369 g_signal_connect(G_OBJECT(text), "drag-drop",
7370 G_CALLBACK(compose_drag_drop),
7372 gtk_widget_show_all(vbox);
7374 /* pane between attach clist and text */
7375 paned = gtk_vpaned_new();
7376 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7378 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7379 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7381 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7383 gtk_paned_add1(GTK_PANED(paned), notebook);
7384 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7385 gtk_widget_show_all(paned);
7388 if (prefs_common.textfont) {
7389 PangoFontDescription *font_desc;
7391 font_desc = pango_font_description_from_string
7392 (prefs_common.textfont);
7394 gtk_widget_modify_font(text, font_desc);
7395 pango_font_description_free(font_desc);
7399 gtk_action_group_add_actions(action_group, compose_popup_entries,
7400 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7401 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7404 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7405 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7406 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7408 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7410 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7411 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7412 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7414 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7416 undostruct = undo_init(text);
7417 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7420 address_completion_start(window);
7422 compose->window = window;
7423 compose->vbox = vbox;
7424 compose->menubar = menubar;
7425 compose->handlebox = handlebox;
7427 compose->vbox2 = vbox2;
7429 compose->paned = paned;
7431 compose->attach_label = attach_lab2;
7433 compose->notebook = notebook;
7434 compose->edit_vbox = edit_vbox;
7435 compose->ruler_hbox = ruler_hbox;
7436 compose->ruler = ruler;
7437 compose->scrolledwin = scrolledwin;
7438 compose->text = text;
7440 compose->focused_editable = NULL;
7442 compose->popupmenu = popupmenu;
7444 compose->tmpl_menu = tmpl_menu;
7446 compose->mode = mode;
7447 compose->rmode = mode;
7449 compose->targetinfo = NULL;
7450 compose->replyinfo = NULL;
7451 compose->fwdinfo = NULL;
7453 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7454 g_str_equal, (GDestroyNotify) g_free, NULL);
7456 compose->replyto = NULL;
7458 compose->bcc = NULL;
7459 compose->followup_to = NULL;
7461 compose->ml_post = NULL;
7463 compose->inreplyto = NULL;
7464 compose->references = NULL;
7465 compose->msgid = NULL;
7466 compose->boundary = NULL;
7468 compose->autowrap = prefs_common.autowrap;
7469 compose->autoindent = prefs_common.auto_indent;
7470 compose->use_signing = FALSE;
7471 compose->use_encryption = FALSE;
7472 compose->privacy_system = NULL;
7474 compose->modified = FALSE;
7476 compose->return_receipt = FALSE;
7478 compose->to_list = NULL;
7479 compose->newsgroup_list = NULL;
7481 compose->undostruct = undostruct;
7483 compose->sig_str = NULL;
7485 compose->exteditor_file = NULL;
7486 compose->exteditor_pid = -1;
7487 compose->exteditor_tag = -1;
7488 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7491 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7492 if (mode != COMPOSE_REDIRECT) {
7493 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7494 strcmp(prefs_common.dictionary, "")) {
7495 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7496 prefs_common.alt_dictionary,
7497 conv_get_locale_charset_str(),
7498 prefs_common.misspelled_col,
7499 prefs_common.check_while_typing,
7500 prefs_common.recheck_when_changing_dict,
7501 prefs_common.use_alternate,
7502 prefs_common.use_both_dicts,
7503 GTK_TEXT_VIEW(text),
7504 GTK_WINDOW(compose->window),
7505 compose_dict_changed,
7506 compose_spell_menu_changed,
7509 alertpanel_error(_("Spell checker could not "
7511 gtkaspell_checkers_strerror());
7512 gtkaspell_checkers_reset_error();
7514 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7518 compose->gtkaspell = gtkaspell;
7519 compose_spell_menu_changed(compose);
7520 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7523 compose_select_account(compose, account, TRUE);
7525 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7526 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7528 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7529 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7531 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7532 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7534 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7535 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7537 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7538 if (account->protocol != A_NNTP)
7539 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7540 prefs_common_translated_header_name("To:"));
7542 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7543 prefs_common_translated_header_name("Newsgroups:"));
7545 addressbook_set_target_compose(compose);
7547 if (mode != COMPOSE_REDIRECT)
7548 compose_set_template_menu(compose);
7550 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7553 compose_list = g_list_append(compose_list, compose);
7555 if (!prefs_common.show_ruler)
7556 gtk_widget_hide(ruler_hbox);
7558 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7561 compose->priority = PRIORITY_NORMAL;
7562 compose_update_priority_menu_item(compose);
7564 compose_set_out_encoding(compose);
7567 compose_update_actions_menu(compose);
7569 /* Privacy Systems menu */
7570 compose_update_privacy_systems_menu(compose);
7572 activate_privacy_system(compose, account, TRUE);
7573 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7575 gtk_widget_realize(window);
7577 gtk_widget_show(window);
7579 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7580 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7587 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7592 GtkWidget *optmenubox;
7595 GtkWidget *from_name = NULL;
7596 #if !(GTK_CHECK_VERSION(2,12,0))
7597 GtkTooltips *tips = compose->tooltips;
7600 gint num = 0, def_menu = 0;
7602 accounts = account_get_list();
7603 cm_return_val_if_fail(accounts != NULL, NULL);
7605 optmenubox = gtk_event_box_new();
7606 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7607 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7609 hbox = gtk_hbox_new(FALSE, 6);
7610 from_name = gtk_entry_new();
7612 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7613 G_CALLBACK(compose_grab_focus_cb), compose);
7615 for (; accounts != NULL; accounts = accounts->next, num++) {
7616 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7617 gchar *name, *from = NULL;
7619 if (ac == compose->account) def_menu = num;
7621 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7624 if (ac == compose->account) {
7625 if (ac->name && *ac->name) {
7627 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7628 from = g_strdup_printf("%s <%s>",
7630 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7632 from = g_strdup_printf("%s",
7634 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7637 COMBOBOX_ADD(menu, name, ac->account_id);
7642 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7644 g_signal_connect(G_OBJECT(optmenu), "changed",
7645 G_CALLBACK(account_activated),
7647 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7648 G_CALLBACK(compose_entry_popup_extend),
7651 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7652 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7654 CLAWS_SET_TIP(optmenubox,
7655 _("Account to use for this email"));
7656 CLAWS_SET_TIP(from_name,
7657 _("Sender address to be used"));
7659 compose->from_name = from_name;
7664 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7666 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7667 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7668 Compose *compose = (Compose *) data;
7670 compose->priority = value;
7674 static void compose_reply_change_mode(Compose *compose,
7677 gboolean was_modified = compose->modified;
7679 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7681 cm_return_if_fail(compose->replyinfo != NULL);
7683 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7685 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7687 if (action == COMPOSE_REPLY_TO_ALL)
7689 if (action == COMPOSE_REPLY_TO_SENDER)
7691 if (action == COMPOSE_REPLY_TO_LIST)
7694 compose_remove_header_entries(compose);
7695 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7696 if (compose->account->set_autocc && compose->account->auto_cc)
7697 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7699 if (compose->account->set_autobcc && compose->account->auto_bcc)
7700 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7702 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7703 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7704 compose_show_first_last_header(compose, TRUE);
7705 compose->modified = was_modified;
7706 compose_set_title(compose);
7709 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7711 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7712 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7713 Compose *compose = (Compose *) data;
7716 compose_reply_change_mode(compose, value);
7719 static void compose_update_priority_menu_item(Compose * compose)
7721 GtkWidget *menuitem = NULL;
7722 switch (compose->priority) {
7723 case PRIORITY_HIGHEST:
7724 menuitem = gtk_ui_manager_get_widget
7725 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7728 menuitem = gtk_ui_manager_get_widget
7729 (compose->ui_manager, "/Menu/Options/Priority/High");
7731 case PRIORITY_NORMAL:
7732 menuitem = gtk_ui_manager_get_widget
7733 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7736 menuitem = gtk_ui_manager_get_widget
7737 (compose->ui_manager, "/Menu/Options/Priority/Low");
7739 case PRIORITY_LOWEST:
7740 menuitem = gtk_ui_manager_get_widget
7741 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7744 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7747 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7749 Compose *compose = (Compose *) data;
7751 gboolean can_sign = FALSE, can_encrypt = FALSE;
7753 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7755 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7758 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7759 g_free(compose->privacy_system);
7760 compose->privacy_system = NULL;
7761 if (systemid != NULL) {
7762 compose->privacy_system = g_strdup(systemid);
7764 can_sign = privacy_system_can_sign(systemid);
7765 can_encrypt = privacy_system_can_encrypt(systemid);
7768 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7770 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7771 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7774 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7776 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7777 GtkWidget *menuitem = NULL;
7779 gboolean can_sign = FALSE, can_encrypt = FALSE;
7780 gboolean found = FALSE;
7782 if (compose->privacy_system != NULL) {
7784 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7785 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7786 cm_return_if_fail(menuitem != NULL);
7788 amenu = GTK_MENU_SHELL(menuitem)->children;
7790 while (amenu != NULL) {
7791 GList *alist = amenu->next;
7793 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7794 if (systemid != NULL) {
7795 if (strcmp(systemid, compose->privacy_system) == 0 &&
7796 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7797 menuitem = GTK_WIDGET(amenu->data);
7799 can_sign = privacy_system_can_sign(systemid);
7800 can_encrypt = privacy_system_can_encrypt(systemid);
7804 } else if (strlen(compose->privacy_system) == 0 &&
7805 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7806 menuitem = GTK_WIDGET(amenu->data);
7809 can_encrypt = FALSE;
7816 if (menuitem != NULL)
7817 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7819 if (warn && !found && strlen(compose->privacy_system)) {
7820 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7821 "will not be able to sign or encrypt this message."),
7822 compose->privacy_system);
7826 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7827 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7830 static void compose_set_out_encoding(Compose *compose)
7832 CharSet out_encoding;
7833 const gchar *branch = NULL;
7834 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7836 switch(out_encoding) {
7837 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7838 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7839 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7840 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7841 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7842 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7843 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7844 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7845 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7846 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7847 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7848 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7849 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7850 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7851 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7852 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7853 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7854 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7855 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7856 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7857 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7858 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7859 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7860 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7861 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7862 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7863 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7864 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7865 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7866 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7867 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7868 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7870 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7873 static void compose_set_template_menu(Compose *compose)
7875 GSList *tmpl_list, *cur;
7879 tmpl_list = template_get_config();
7881 menu = gtk_menu_new();
7883 gtk_menu_set_accel_group (GTK_MENU (menu),
7884 gtk_ui_manager_get_accel_group(compose->ui_manager));
7885 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7886 Template *tmpl = (Template *)cur->data;
7887 gchar *accel_path = NULL;
7888 item = gtk_menu_item_new_with_label(tmpl->name);
7889 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7890 g_signal_connect(G_OBJECT(item), "activate",
7891 G_CALLBACK(compose_template_activate_cb),
7893 g_object_set_data(G_OBJECT(item), "template", tmpl);
7894 gtk_widget_show(item);
7895 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7896 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7900 gtk_widget_show(menu);
7901 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7904 void compose_update_actions_menu(Compose *compose)
7906 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7909 static void compose_update_privacy_systems_menu(Compose *compose)
7911 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7912 GSList *systems, *cur;
7914 GtkWidget *system_none;
7916 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7917 GtkWidget *privacy_menu = gtk_menu_new();
7919 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7920 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7922 g_signal_connect(G_OBJECT(system_none), "activate",
7923 G_CALLBACK(compose_set_privacy_system_cb), compose);
7925 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7926 gtk_widget_show(system_none);
7928 systems = privacy_get_system_ids();
7929 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7930 gchar *systemid = cur->data;
7932 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7933 widget = gtk_radio_menu_item_new_with_label(group,
7934 privacy_system_get_name(systemid));
7935 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7936 g_strdup(systemid), g_free);
7937 g_signal_connect(G_OBJECT(widget), "activate",
7938 G_CALLBACK(compose_set_privacy_system_cb), compose);
7940 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7941 gtk_widget_show(widget);
7944 g_slist_free(systems);
7945 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7946 gtk_widget_show_all(privacy_menu);
7947 gtk_widget_show_all(privacy_menuitem);
7950 void compose_reflect_prefs_all(void)
7955 for (cur = compose_list; cur != NULL; cur = cur->next) {
7956 compose = (Compose *)cur->data;
7957 compose_set_template_menu(compose);
7961 void compose_reflect_prefs_pixmap_theme(void)
7966 for (cur = compose_list; cur != NULL; cur = cur->next) {
7967 compose = (Compose *)cur->data;
7968 toolbar_update(TOOLBAR_COMPOSE, compose);
7972 static const gchar *compose_quote_char_from_context(Compose *compose)
7974 const gchar *qmark = NULL;
7976 cm_return_val_if_fail(compose != NULL, NULL);
7978 switch (compose->mode) {
7979 /* use forward-specific quote char */
7980 case COMPOSE_FORWARD:
7981 case COMPOSE_FORWARD_AS_ATTACH:
7982 case COMPOSE_FORWARD_INLINE:
7983 if (compose->folder && compose->folder->prefs &&
7984 compose->folder->prefs->forward_with_format)
7985 qmark = compose->folder->prefs->forward_quotemark;
7986 else if (compose->account->forward_with_format)
7987 qmark = compose->account->forward_quotemark;
7989 qmark = prefs_common.fw_quotemark;
7992 /* use reply-specific quote char in all other modes */
7994 if (compose->folder && compose->folder->prefs &&
7995 compose->folder->prefs->reply_with_format)
7996 qmark = compose->folder->prefs->reply_quotemark;
7997 else if (compose->account->reply_with_format)
7998 qmark = compose->account->reply_quotemark;
8000 qmark = prefs_common.quotemark;
8004 if (qmark == NULL || *qmark == '\0')
8010 static void compose_template_apply(Compose *compose, Template *tmpl,
8014 GtkTextBuffer *buffer;
8018 gchar *parsed_str = NULL;
8019 gint cursor_pos = 0;
8020 const gchar *err_msg = _("The body of the template has an error at line %d.");
8023 /* process the body */
8025 text = GTK_TEXT_VIEW(compose->text);
8026 buffer = gtk_text_view_get_buffer(text);
8029 qmark = compose_quote_char_from_context(compose);
8031 if (compose->replyinfo != NULL) {
8034 gtk_text_buffer_set_text(buffer, "", -1);
8035 mark = gtk_text_buffer_get_insert(buffer);
8036 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8038 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8039 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8041 } else if (compose->fwdinfo != NULL) {
8044 gtk_text_buffer_set_text(buffer, "", -1);
8045 mark = gtk_text_buffer_get_insert(buffer);
8046 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8048 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8049 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8052 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8054 GtkTextIter start, end;
8057 gtk_text_buffer_get_start_iter(buffer, &start);
8058 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8059 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8061 /* clear the buffer now */
8063 gtk_text_buffer_set_text(buffer, "", -1);
8065 parsed_str = compose_quote_fmt(compose, dummyinfo,
8066 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8067 procmsg_msginfo_free( dummyinfo );
8073 gtk_text_buffer_set_text(buffer, "", -1);
8074 mark = gtk_text_buffer_get_insert(buffer);
8075 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8078 if (replace && parsed_str && compose->account->auto_sig)
8079 compose_insert_sig(compose, FALSE);
8081 if (replace && parsed_str) {
8082 gtk_text_buffer_get_start_iter(buffer, &iter);
8083 gtk_text_buffer_place_cursor(buffer, &iter);
8087 cursor_pos = quote_fmt_get_cursor_pos();
8088 compose->set_cursor_pos = cursor_pos;
8089 if (cursor_pos == -1)
8091 gtk_text_buffer_get_start_iter(buffer, &iter);
8092 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8093 gtk_text_buffer_place_cursor(buffer, &iter);
8096 /* process the other fields */
8098 compose_template_apply_fields(compose, tmpl);
8099 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8100 quote_fmt_reset_vartable();
8101 compose_changed_cb(NULL, compose);
8104 if (compose->gtkaspell->check_while_typing)
8105 gtkaspell_highlight_all(compose->gtkaspell);
8109 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8111 MsgInfo* dummyinfo = NULL;
8112 MsgInfo *msginfo = NULL;
8115 if (compose->replyinfo != NULL)
8116 msginfo = compose->replyinfo;
8117 else if (compose->fwdinfo != NULL)
8118 msginfo = compose->fwdinfo;
8120 dummyinfo = compose_msginfo_new_from_compose(compose);
8121 msginfo = dummyinfo;
8124 if (tmpl->from && *tmpl->from != '\0') {
8126 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8127 compose->gtkaspell);
8129 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8131 quote_fmt_scan_string(tmpl->from);
8134 buf = quote_fmt_get_buffer();
8136 alertpanel_error(_("Template From format error."));
8138 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8142 if (tmpl->to && *tmpl->to != '\0') {
8144 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8145 compose->gtkaspell);
8147 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8149 quote_fmt_scan_string(tmpl->to);
8152 buf = quote_fmt_get_buffer();
8154 alertpanel_error(_("Template To format error."));
8156 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8160 if (tmpl->cc && *tmpl->cc != '\0') {
8162 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8163 compose->gtkaspell);
8165 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8167 quote_fmt_scan_string(tmpl->cc);
8170 buf = quote_fmt_get_buffer();
8172 alertpanel_error(_("Template Cc format error."));
8174 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8178 if (tmpl->bcc && *tmpl->bcc != '\0') {
8180 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8181 compose->gtkaspell);
8183 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8185 quote_fmt_scan_string(tmpl->bcc);
8188 buf = quote_fmt_get_buffer();
8190 alertpanel_error(_("Template Bcc format error."));
8192 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8196 /* process the subject */
8197 if (tmpl->subject && *tmpl->subject != '\0') {
8199 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8200 compose->gtkaspell);
8202 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8204 quote_fmt_scan_string(tmpl->subject);
8207 buf = quote_fmt_get_buffer();
8209 alertpanel_error(_("Template subject format error."));
8211 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8215 procmsg_msginfo_free( dummyinfo );
8218 static void compose_destroy(Compose *compose)
8220 GtkTextBuffer *buffer;
8221 GtkClipboard *clipboard;
8223 compose_list = g_list_remove(compose_list, compose);
8225 if (compose->updating) {
8226 debug_print("danger, not destroying anything now\n");
8227 compose->deferred_destroy = TRUE;
8230 /* NOTE: address_completion_end() does nothing with the window
8231 * however this may change. */
8232 address_completion_end(compose->window);
8234 slist_free_strings(compose->to_list);
8235 g_slist_free(compose->to_list);
8236 slist_free_strings(compose->newsgroup_list);
8237 g_slist_free(compose->newsgroup_list);
8238 slist_free_strings(compose->header_list);
8239 g_slist_free(compose->header_list);
8241 g_hash_table_destroy(compose->email_hashtable);
8243 procmsg_msginfo_free(compose->targetinfo);
8244 procmsg_msginfo_free(compose->replyinfo);
8245 procmsg_msginfo_free(compose->fwdinfo);
8247 g_free(compose->replyto);
8248 g_free(compose->cc);
8249 g_free(compose->bcc);
8250 g_free(compose->newsgroups);
8251 g_free(compose->followup_to);
8253 g_free(compose->ml_post);
8255 g_free(compose->inreplyto);
8256 g_free(compose->references);
8257 g_free(compose->msgid);
8258 g_free(compose->boundary);
8260 g_free(compose->redirect_filename);
8261 if (compose->undostruct)
8262 undo_destroy(compose->undostruct);
8264 g_free(compose->sig_str);
8266 g_free(compose->exteditor_file);
8268 g_free(compose->orig_charset);
8270 g_free(compose->privacy_system);
8272 if (addressbook_get_target_compose() == compose)
8273 addressbook_set_target_compose(NULL);
8276 if (compose->gtkaspell) {
8277 gtkaspell_delete(compose->gtkaspell);
8278 compose->gtkaspell = NULL;
8282 if (!compose->batch) {
8283 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8284 prefs_common.compose_height = compose->window->allocation.height;
8287 if (!gtk_widget_get_parent(compose->paned))
8288 gtk_widget_destroy(compose->paned);
8289 gtk_widget_destroy(compose->popupmenu);
8291 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8292 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8293 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8295 gtk_widget_destroy(compose->window);
8296 toolbar_destroy(compose->toolbar);
8297 g_free(compose->toolbar);
8298 g_mutex_free(compose->mutex);
8302 static void compose_attach_info_free(AttachInfo *ainfo)
8304 g_free(ainfo->file);
8305 g_free(ainfo->content_type);
8306 g_free(ainfo->name);
8310 static void compose_attach_update_label(Compose *compose)
8315 GtkTreeModel *model;
8320 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8321 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8322 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8326 while(gtk_tree_model_iter_next(model, &iter))
8329 text = g_strdup_printf("(%d)", i);
8330 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8334 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8336 Compose *compose = (Compose *)data;
8337 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8338 GtkTreeSelection *selection;
8340 GtkTreeModel *model;
8342 selection = gtk_tree_view_get_selection(tree_view);
8343 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8348 for (cur = sel; cur != NULL; cur = cur->next) {
8349 GtkTreePath *path = cur->data;
8350 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8353 gtk_tree_path_free(path);
8356 for (cur = sel; cur != NULL; cur = cur->next) {
8357 GtkTreeRowReference *ref = cur->data;
8358 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8361 if (gtk_tree_model_get_iter(model, &iter, path))
8362 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8364 gtk_tree_path_free(path);
8365 gtk_tree_row_reference_free(ref);
8369 compose_attach_update_label(compose);
8372 static struct _AttachProperty
8375 GtkWidget *mimetype_entry;
8376 GtkWidget *encoding_optmenu;
8377 GtkWidget *path_entry;
8378 GtkWidget *filename_entry;
8380 GtkWidget *cancel_btn;
8383 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8385 gtk_tree_path_free((GtkTreePath *)ptr);
8388 static void compose_attach_property(GtkAction *action, gpointer data)
8390 Compose *compose = (Compose *)data;
8391 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8393 GtkComboBox *optmenu;
8394 GtkTreeSelection *selection;
8396 GtkTreeModel *model;
8399 static gboolean cancelled;
8401 /* only if one selected */
8402 selection = gtk_tree_view_get_selection(tree_view);
8403 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8406 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8410 path = (GtkTreePath *) sel->data;
8411 gtk_tree_model_get_iter(model, &iter, path);
8412 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8415 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8421 if (!attach_prop.window)
8422 compose_attach_property_create(&cancelled);
8423 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8424 gtk_widget_grab_focus(attach_prop.ok_btn);
8425 gtk_widget_show(attach_prop.window);
8426 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8428 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8429 if (ainfo->encoding == ENC_UNKNOWN)
8430 combobox_select_by_data(optmenu, ENC_BASE64);
8432 combobox_select_by_data(optmenu, ainfo->encoding);
8434 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8435 ainfo->content_type ? ainfo->content_type : "");
8436 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8437 ainfo->file ? ainfo->file : "");
8438 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8439 ainfo->name ? ainfo->name : "");
8442 const gchar *entry_text;
8444 gchar *cnttype = NULL;
8451 gtk_widget_hide(attach_prop.window);
8452 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8457 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8458 if (*entry_text != '\0') {
8461 text = g_strstrip(g_strdup(entry_text));
8462 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8463 cnttype = g_strdup(text);
8466 alertpanel_error(_("Invalid MIME type."));
8472 ainfo->encoding = combobox_get_active_data(optmenu);
8474 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8475 if (*entry_text != '\0') {
8476 if (is_file_exist(entry_text) &&
8477 (size = get_file_size(entry_text)) > 0)
8478 file = g_strdup(entry_text);
8481 (_("File doesn't exist or is empty."));
8487 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8488 if (*entry_text != '\0') {
8489 g_free(ainfo->name);
8490 ainfo->name = g_strdup(entry_text);
8494 g_free(ainfo->content_type);
8495 ainfo->content_type = cnttype;
8498 g_free(ainfo->file);
8502 ainfo->size = (goffset)size;
8504 /* update tree store */
8505 text = to_human_readable(ainfo->size);
8506 gtk_tree_model_get_iter(model, &iter, path);
8507 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8508 COL_MIMETYPE, ainfo->content_type,
8510 COL_NAME, ainfo->name,
8516 gtk_tree_path_free(path);
8519 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8521 label = gtk_label_new(str); \
8522 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8523 GTK_FILL, 0, 0, 0); \
8524 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8526 entry = gtk_entry_new(); \
8527 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8528 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8531 static void compose_attach_property_create(gboolean *cancelled)
8537 GtkWidget *mimetype_entry;
8540 GtkListStore *optmenu_menu;
8541 GtkWidget *path_entry;
8542 GtkWidget *filename_entry;
8545 GtkWidget *cancel_btn;
8546 GList *mime_type_list, *strlist;
8549 debug_print("Creating attach_property window...\n");
8551 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8552 gtk_widget_set_size_request(window, 480, -1);
8553 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8554 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8555 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8556 g_signal_connect(G_OBJECT(window), "delete_event",
8557 G_CALLBACK(attach_property_delete_event),
8559 g_signal_connect(G_OBJECT(window), "key_press_event",
8560 G_CALLBACK(attach_property_key_pressed),
8563 vbox = gtk_vbox_new(FALSE, 8);
8564 gtk_container_add(GTK_CONTAINER(window), vbox);
8566 table = gtk_table_new(4, 2, FALSE);
8567 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8568 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8569 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8571 label = gtk_label_new(_("MIME type"));
8572 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8574 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8575 mimetype_entry = gtk_combo_box_entry_new_text();
8576 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8577 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8579 /* stuff with list */
8580 mime_type_list = procmime_get_mime_type_list();
8582 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8583 MimeType *type = (MimeType *) mime_type_list->data;
8586 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8588 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8591 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8592 (GCompareFunc)strcmp2);
8595 for (mime_type_list = strlist; mime_type_list != NULL;
8596 mime_type_list = mime_type_list->next) {
8597 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8598 g_free(mime_type_list->data);
8600 g_list_free(strlist);
8601 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8602 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8604 label = gtk_label_new(_("Encoding"));
8605 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8607 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8609 hbox = gtk_hbox_new(FALSE, 0);
8610 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8611 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8613 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8614 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8616 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8617 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8618 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8619 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8620 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8622 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8624 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8625 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8627 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8628 &ok_btn, GTK_STOCK_OK,
8630 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8631 gtk_widget_grab_default(ok_btn);
8633 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8634 G_CALLBACK(attach_property_ok),
8636 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8637 G_CALLBACK(attach_property_cancel),
8640 gtk_widget_show_all(vbox);
8642 attach_prop.window = window;
8643 attach_prop.mimetype_entry = mimetype_entry;
8644 attach_prop.encoding_optmenu = optmenu;
8645 attach_prop.path_entry = path_entry;
8646 attach_prop.filename_entry = filename_entry;
8647 attach_prop.ok_btn = ok_btn;
8648 attach_prop.cancel_btn = cancel_btn;
8651 #undef SET_LABEL_AND_ENTRY
8653 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8659 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8665 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8666 gboolean *cancelled)
8674 static gboolean attach_property_key_pressed(GtkWidget *widget,
8676 gboolean *cancelled)
8678 if (event && event->keyval == GDK_Escape) {
8682 if (event && event->keyval == GDK_Return) {
8690 static void compose_exec_ext_editor(Compose *compose)
8697 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8698 G_DIR_SEPARATOR, compose);
8700 if (pipe(pipe_fds) < 0) {
8706 if ((pid = fork()) < 0) {
8713 /* close the write side of the pipe */
8716 compose->exteditor_file = g_strdup(tmp);
8717 compose->exteditor_pid = pid;
8719 compose_set_ext_editor_sensitive(compose, FALSE);
8722 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8724 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8726 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8730 } else { /* process-monitoring process */
8736 /* close the read side of the pipe */
8739 if (compose_write_body_to_file(compose, tmp) < 0) {
8740 fd_write_all(pipe_fds[1], "2\n", 2);
8744 pid_ed = compose_exec_ext_editor_real(tmp);
8746 fd_write_all(pipe_fds[1], "1\n", 2);
8750 /* wait until editor is terminated */
8751 waitpid(pid_ed, NULL, 0);
8753 fd_write_all(pipe_fds[1], "0\n", 2);
8760 #endif /* G_OS_UNIX */
8764 static gint compose_exec_ext_editor_real(const gchar *file)
8771 cm_return_val_if_fail(file != NULL, -1);
8773 if ((pid = fork()) < 0) {
8778 if (pid != 0) return pid;
8780 /* grandchild process */
8782 if (setpgid(0, getppid()))
8785 if (prefs_common_get_ext_editor_cmd() &&
8786 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8787 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8788 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8790 if (prefs_common_get_ext_editor_cmd())
8791 g_warning("External editor command-line is invalid: '%s'\n",
8792 prefs_common_get_ext_editor_cmd());
8793 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8796 cmdline = strsplit_with_quote(buf, " ", 1024);
8797 execvp(cmdline[0], cmdline);
8800 g_strfreev(cmdline);
8805 static gboolean compose_ext_editor_kill(Compose *compose)
8807 pid_t pgid = compose->exteditor_pid * -1;
8810 ret = kill(pgid, 0);
8812 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8816 msg = g_strdup_printf
8817 (_("The external editor is still working.\n"
8818 "Force terminating the process?\n"
8819 "process group id: %d"), -pgid);
8820 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8821 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8825 if (val == G_ALERTALTERNATE) {
8826 g_source_remove(compose->exteditor_tag);
8827 g_io_channel_shutdown(compose->exteditor_ch,
8829 g_io_channel_unref(compose->exteditor_ch);
8831 if (kill(pgid, SIGTERM) < 0) perror("kill");
8832 waitpid(compose->exteditor_pid, NULL, 0);
8834 g_warning("Terminated process group id: %d", -pgid);
8835 g_warning("Temporary file: %s",
8836 compose->exteditor_file);
8838 compose_set_ext_editor_sensitive(compose, TRUE);
8840 g_free(compose->exteditor_file);
8841 compose->exteditor_file = NULL;
8842 compose->exteditor_pid = -1;
8843 compose->exteditor_ch = NULL;
8844 compose->exteditor_tag = -1;
8852 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8856 Compose *compose = (Compose *)data;
8859 debug_print(_("Compose: input from monitoring process\n"));
8861 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8863 g_io_channel_shutdown(source, FALSE, NULL);
8864 g_io_channel_unref(source);
8866 waitpid(compose->exteditor_pid, NULL, 0);
8868 if (buf[0] == '0') { /* success */
8869 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8870 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8872 gtk_text_buffer_set_text(buffer, "", -1);
8873 compose_insert_file(compose, compose->exteditor_file);
8874 compose_changed_cb(NULL, compose);
8875 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8877 if (claws_unlink(compose->exteditor_file) < 0)
8878 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8879 } else if (buf[0] == '1') { /* failed */
8880 g_warning("Couldn't exec external editor\n");
8881 if (claws_unlink(compose->exteditor_file) < 0)
8882 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8883 } else if (buf[0] == '2') {
8884 g_warning("Couldn't write to file\n");
8885 } else if (buf[0] == '3') {
8886 g_warning("Pipe read failed\n");
8889 compose_set_ext_editor_sensitive(compose, TRUE);
8891 g_free(compose->exteditor_file);
8892 compose->exteditor_file = NULL;
8893 compose->exteditor_pid = -1;
8894 compose->exteditor_ch = NULL;
8895 compose->exteditor_tag = -1;
8900 static void compose_set_ext_editor_sensitive(Compose *compose,
8903 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8904 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8905 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8906 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8907 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8908 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8909 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8911 gtk_widget_set_sensitive(compose->text, sensitive);
8912 if (compose->toolbar->send_btn)
8913 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8914 if (compose->toolbar->sendl_btn)
8915 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8916 if (compose->toolbar->draft_btn)
8917 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8918 if (compose->toolbar->insert_btn)
8919 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8920 if (compose->toolbar->sig_btn)
8921 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8922 if (compose->toolbar->exteditor_btn)
8923 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8924 if (compose->toolbar->linewrap_current_btn)
8925 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8926 if (compose->toolbar->linewrap_all_btn)
8927 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8929 #endif /* G_OS_UNIX */
8932 * compose_undo_state_changed:
8934 * Change the sensivity of the menuentries undo and redo
8936 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8937 gint redo_state, gpointer data)
8939 Compose *compose = (Compose *)data;
8941 switch (undo_state) {
8942 case UNDO_STATE_TRUE:
8943 if (!undostruct->undo_state) {
8944 undostruct->undo_state = TRUE;
8945 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8948 case UNDO_STATE_FALSE:
8949 if (undostruct->undo_state) {
8950 undostruct->undo_state = FALSE;
8951 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8954 case UNDO_STATE_UNCHANGED:
8956 case UNDO_STATE_REFRESH:
8957 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8960 g_warning("Undo state not recognized");
8964 switch (redo_state) {
8965 case UNDO_STATE_TRUE:
8966 if (!undostruct->redo_state) {
8967 undostruct->redo_state = TRUE;
8968 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8971 case UNDO_STATE_FALSE:
8972 if (undostruct->redo_state) {
8973 undostruct->redo_state = FALSE;
8974 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8977 case UNDO_STATE_UNCHANGED:
8979 case UNDO_STATE_REFRESH:
8980 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8983 g_warning("Redo state not recognized");
8988 /* callback functions */
8990 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8991 * includes "non-client" (windows-izm) in calculation, so this calculation
8992 * may not be accurate.
8994 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8995 GtkAllocation *allocation,
8996 GtkSHRuler *shruler)
8998 if (prefs_common.show_ruler) {
8999 gint char_width = 0, char_height = 0;
9000 gint line_width_in_chars;
9002 gtkut_get_font_size(GTK_WIDGET(widget),
9003 &char_width, &char_height);
9004 line_width_in_chars =
9005 (allocation->width - allocation->x) / char_width;
9007 /* got the maximum */
9008 gtk_ruler_set_range(GTK_RULER(shruler),
9009 0.0, line_width_in_chars, 0,
9010 /*line_width_in_chars*/ char_width);
9017 ComposeEntryType header;
9019 ComposePrefType type;
9020 gboolean entry_marked;
9023 static void account_activated(GtkComboBox *optmenu, gpointer data)
9025 Compose *compose = (Compose *)data;
9028 gchar *folderidentifier;
9029 gint account_id = 0;
9032 GSList *list, *saved_list = NULL;
9033 HeaderEntryState *state;
9034 GtkRcStyle *style = NULL;
9035 static GdkColor yellow;
9036 static gboolean color_set = FALSE;
9038 /* Get ID of active account in the combo box */
9039 menu = gtk_combo_box_get_model(optmenu);
9040 gtk_combo_box_get_active_iter(optmenu, &iter);
9041 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9043 ac = account_find_from_id(account_id);
9044 cm_return_if_fail(ac != NULL);
9046 if (ac != compose->account) {
9047 compose_select_account(compose, ac, FALSE);
9049 for (list = compose->header_list; list; list = list->next) {
9050 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9052 if (hentry->type == PREF_ACCOUNT || !list->next) {
9053 compose_destroy_headerentry(compose, hentry);
9057 state = g_malloc0(sizeof(HeaderEntryState));
9058 state->header = combobox_get_active_data(
9059 GTK_COMBO_BOX(hentry->combo));
9060 state->entry = gtk_editable_get_chars(
9061 GTK_EDITABLE(hentry->entry), 0, -1);
9062 state->type = hentry->type;
9065 gdk_color_parse("#f5f6be", &yellow);
9066 color_set = gdk_colormap_alloc_color(
9067 gdk_colormap_get_system(),
9068 &yellow, FALSE, TRUE);
9071 style = gtk_widget_get_modifier_style(hentry->entry);
9072 state->entry_marked = gdk_color_equal(&yellow,
9073 &style->base[GTK_STATE_NORMAL]);
9075 saved_list = g_slist_append(saved_list, state);
9076 compose_destroy_headerentry(compose, hentry);
9079 compose->header_last = NULL;
9080 g_slist_free(compose->header_list);
9081 compose->header_list = NULL;
9082 compose->header_nextrow = 1;
9083 compose_create_header_entry(compose);
9085 if (ac->set_autocc && ac->auto_cc)
9086 compose_entry_append(compose, ac->auto_cc,
9087 COMPOSE_CC, PREF_ACCOUNT);
9089 if (ac->set_autobcc && ac->auto_bcc)
9090 compose_entry_append(compose, ac->auto_bcc,
9091 COMPOSE_BCC, PREF_ACCOUNT);
9093 if (ac->set_autoreplyto && ac->auto_replyto)
9094 compose_entry_append(compose, ac->auto_replyto,
9095 COMPOSE_REPLYTO, PREF_ACCOUNT);
9097 for (list = saved_list; list; list = list->next) {
9098 state = (HeaderEntryState *) list->data;
9100 compose_entry_append(compose, state->entry,
9101 state->header, state->type);
9102 if (state->entry_marked)
9103 compose_entry_mark_default_to(compose, state->entry);
9105 g_free(state->entry);
9107 g_slist_free(saved_list);
9109 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9110 (ac->protocol == A_NNTP) ?
9111 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9114 /* Set message save folder */
9115 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9116 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9118 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9119 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9121 compose_set_save_to(compose, NULL);
9122 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9123 folderidentifier = folder_item_get_identifier(account_get_special_folder
9124 (compose->account, F_OUTBOX));
9125 compose_set_save_to(compose, folderidentifier);
9126 g_free(folderidentifier);
9130 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9131 GtkTreeViewColumn *column, Compose *compose)
9133 compose_attach_property(NULL, compose);
9136 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9139 Compose *compose = (Compose *)data;
9140 GtkTreeSelection *attach_selection;
9141 gint attach_nr_selected;
9143 if (!event) return FALSE;
9145 if (event->button == 3) {
9146 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9147 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9149 if (attach_nr_selected > 0)
9151 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9152 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9154 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9155 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9158 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9159 NULL, NULL, event->button, event->time);
9166 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9169 Compose *compose = (Compose *)data;
9171 if (!event) return FALSE;
9173 switch (event->keyval) {
9175 compose_attach_remove_selected(NULL, compose);
9181 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9183 toolbar_comp_set_sensitive(compose, allow);
9184 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9185 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9187 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9189 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9190 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9191 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9193 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9197 static void compose_send_cb(GtkAction *action, gpointer data)
9199 Compose *compose = (Compose *)data;
9201 if (prefs_common.work_offline &&
9202 !inc_offline_should_override(TRUE,
9203 _("Claws Mail needs network access in order "
9204 "to send this email.")))
9207 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9208 g_source_remove(compose->draft_timeout_tag);
9209 compose->draft_timeout_tag = -1;
9212 compose_send(compose);
9215 static void compose_send_later_cb(GtkAction *action, gpointer data)
9217 Compose *compose = (Compose *)data;
9221 compose_allow_user_actions(compose, FALSE);
9222 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9223 compose_allow_user_actions(compose, TRUE);
9227 compose_close(compose);
9228 } else if (val == -1) {
9229 alertpanel_error(_("Could not queue message."));
9230 } else if (val == -2) {
9231 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9232 } else if (val == -3) {
9233 if (privacy_peek_error())
9234 alertpanel_error(_("Could not queue message for sending:\n\n"
9235 "Signature failed: %s"), privacy_get_error());
9236 } else if (val == -4) {
9237 alertpanel_error(_("Could not queue message for sending:\n\n"
9238 "Charset conversion failed."));
9239 } else if (val == -5) {
9240 alertpanel_error(_("Could not queue message for sending:\n\n"
9241 "Couldn't get recipient encryption key."));
9242 } else if (val == -6) {
9245 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9248 #define DRAFTED_AT_EXIT "drafted_at_exit"
9249 static void compose_register_draft(MsgInfo *info)
9251 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9252 DRAFTED_AT_EXIT, NULL);
9253 FILE *fp = g_fopen(filepath, "ab");
9256 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9264 gboolean compose_draft (gpointer data, guint action)
9266 Compose *compose = (Compose *)data;
9270 MsgFlags flag = {0, 0};
9271 static gboolean lock = FALSE;
9272 MsgInfo *newmsginfo;
9274 gboolean target_locked = FALSE;
9275 gboolean err = FALSE;
9277 if (lock) return FALSE;
9279 if (compose->sending)
9282 draft = account_get_special_folder(compose->account, F_DRAFT);
9283 cm_return_val_if_fail(draft != NULL, FALSE);
9285 if (!g_mutex_trylock(compose->mutex)) {
9286 /* we don't want to lock the mutex once it's available,
9287 * because as the only other part of compose.c locking
9288 * it is compose_close - which means once unlocked,
9289 * the compose struct will be freed */
9290 debug_print("couldn't lock mutex, probably sending\n");
9296 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9297 G_DIR_SEPARATOR, compose);
9298 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9299 FILE_OP_ERROR(tmp, "fopen");
9303 /* chmod for security */
9304 if (change_file_mode_rw(fp, tmp) < 0) {
9305 FILE_OP_ERROR(tmp, "chmod");
9306 g_warning("can't change file mode\n");
9309 /* Save draft infos */
9310 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9311 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9313 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9314 gchar *savefolderid;
9316 savefolderid = compose_get_save_to(compose);
9317 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9318 g_free(savefolderid);
9320 if (compose->return_receipt) {
9321 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9323 if (compose->privacy_system) {
9324 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9325 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9326 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9329 /* Message-ID of message replying to */
9330 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9333 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9334 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9337 /* Message-ID of message forwarding to */
9338 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9341 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9342 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9346 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9347 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9349 /* end of headers */
9350 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9357 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9361 if (fclose(fp) == EOF) {
9365 if (compose->targetinfo) {
9366 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9367 flag.perm_flags = target_locked?MSG_LOCKED:0;
9369 flag.tmp_flags = MSG_DRAFT;
9371 folder_item_scan(draft);
9372 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9373 MsgInfo *tmpinfo = NULL;
9374 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9375 if (compose->msgid) {
9376 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9379 msgnum = tmpinfo->msgnum;
9380 procmsg_msginfo_free(tmpinfo);
9381 debug_print("got draft msgnum %d from scanning\n", msgnum);
9383 debug_print("didn't get draft msgnum after scanning\n");
9386 debug_print("got draft msgnum %d from adding\n", msgnum);
9392 if (action != COMPOSE_AUTO_SAVE) {
9393 if (action != COMPOSE_DRAFT_FOR_EXIT)
9394 alertpanel_error(_("Could not save draft."));
9397 gtkut_window_popup(compose->window);
9398 val = alertpanel_full(_("Could not save draft"),
9399 _("Could not save draft.\n"
9400 "Do you want to cancel exit or discard this email?"),
9401 _("_Cancel exit"), _("_Discard email"), NULL,
9402 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9403 if (val == G_ALERTALTERNATE) {
9405 g_mutex_unlock(compose->mutex); /* must be done before closing */
9406 compose_close(compose);
9410 g_mutex_unlock(compose->mutex); /* must be done before closing */
9419 if (compose->mode == COMPOSE_REEDIT) {
9420 compose_remove_reedit_target(compose, TRUE);
9423 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9426 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9428 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9430 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9431 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9432 procmsg_msginfo_set_flags(newmsginfo, 0,
9433 MSG_HAS_ATTACHMENT);
9435 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9436 compose_register_draft(newmsginfo);
9438 procmsg_msginfo_free(newmsginfo);
9441 folder_item_scan(draft);
9443 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9445 g_mutex_unlock(compose->mutex); /* must be done before closing */
9446 compose_close(compose);
9452 path = folder_item_fetch_msg(draft, msgnum);
9454 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9457 if (g_stat(path, &s) < 0) {
9458 FILE_OP_ERROR(path, "stat");
9464 procmsg_msginfo_free(compose->targetinfo);
9465 compose->targetinfo = procmsg_msginfo_new();
9466 compose->targetinfo->msgnum = msgnum;
9467 compose->targetinfo->size = (goffset)s.st_size;
9468 compose->targetinfo->mtime = s.st_mtime;
9469 compose->targetinfo->folder = draft;
9471 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9472 compose->mode = COMPOSE_REEDIT;
9474 if (action == COMPOSE_AUTO_SAVE) {
9475 compose->autosaved_draft = compose->targetinfo;
9477 compose->modified = FALSE;
9478 compose_set_title(compose);
9482 g_mutex_unlock(compose->mutex);
9486 void compose_clear_exit_drafts(void)
9488 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9489 DRAFTED_AT_EXIT, NULL);
9490 if (is_file_exist(filepath))
9491 claws_unlink(filepath);
9496 void compose_reopen_exit_drafts(void)
9498 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9499 DRAFTED_AT_EXIT, NULL);
9500 FILE *fp = g_fopen(filepath, "rb");
9504 while (fgets(buf, sizeof(buf), fp)) {
9505 gchar **parts = g_strsplit(buf, "\t", 2);
9506 const gchar *folder = parts[0];
9507 int msgnum = parts[1] ? atoi(parts[1]):-1;
9509 if (folder && *folder && msgnum > -1) {
9510 FolderItem *item = folder_find_item_from_identifier(folder);
9511 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9513 compose_reedit(info, FALSE);
9520 compose_clear_exit_drafts();
9523 static void compose_save_cb(GtkAction *action, gpointer data)
9525 Compose *compose = (Compose *)data;
9526 compose_draft(compose, COMPOSE_KEEP_EDITING);
9527 compose->rmode = COMPOSE_REEDIT;
9530 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9532 if (compose && file_list) {
9535 for ( tmp = file_list; tmp; tmp = tmp->next) {
9536 gchar *file = (gchar *) tmp->data;
9537 gchar *utf8_filename = conv_filename_to_utf8(file);
9538 compose_attach_append(compose, file, utf8_filename, NULL);
9539 compose_changed_cb(NULL, compose);
9544 g_free(utf8_filename);
9549 static void compose_attach_cb(GtkAction *action, gpointer data)
9551 Compose *compose = (Compose *)data;
9554 if (compose->redirect_filename != NULL)
9557 file_list = filesel_select_multiple_files_open(_("Select file"));
9560 compose_attach_from_list(compose, file_list, TRUE);
9561 g_list_free(file_list);
9565 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9567 Compose *compose = (Compose *)data;
9569 gint files_inserted = 0;
9571 file_list = filesel_select_multiple_files_open(_("Select file"));
9576 for ( tmp = file_list; tmp; tmp = tmp->next) {
9577 gchar *file = (gchar *) tmp->data;
9578 gchar *filedup = g_strdup(file);
9579 gchar *shortfile = g_path_get_basename(filedup);
9580 ComposeInsertResult res;
9581 /* insert the file if the file is short or if the user confirmed that
9582 he/she wants to insert the large file */
9583 res = compose_insert_file(compose, file);
9584 if (res == COMPOSE_INSERT_READ_ERROR) {
9585 alertpanel_error(_("File '%s' could not be read."), shortfile);
9586 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9587 alertpanel_error(_("File '%s' contained invalid characters\n"
9588 "for the current encoding, insertion may be incorrect."),
9590 } else if (res == COMPOSE_INSERT_SUCCESS)
9597 g_list_free(file_list);
9601 if (files_inserted > 0 && compose->gtkaspell->check_while_typing)
9602 gtkaspell_highlight_all(compose->gtkaspell);
9606 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9608 Compose *compose = (Compose *)data;
9610 compose_insert_sig(compose, FALSE);
9613 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9617 Compose *compose = (Compose *)data;
9619 gtkut_widget_get_uposition(widget, &x, &y);
9620 if (!compose->batch) {
9621 prefs_common.compose_x = x;
9622 prefs_common.compose_y = y;
9624 if (compose->sending || compose->updating)
9626 compose_close_cb(NULL, compose);
9630 void compose_close_toolbar(Compose *compose)
9632 compose_close_cb(NULL, compose);
9635 static void compose_close_cb(GtkAction *action, gpointer data)
9637 Compose *compose = (Compose *)data;
9641 if (compose->exteditor_tag != -1) {
9642 if (!compose_ext_editor_kill(compose))
9647 if (compose->modified) {
9648 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9649 if (!g_mutex_trylock(compose->mutex)) {
9650 /* we don't want to lock the mutex once it's available,
9651 * because as the only other part of compose.c locking
9652 * it is compose_close - which means once unlocked,
9653 * the compose struct will be freed */
9654 debug_print("couldn't lock mutex, probably sending\n");
9658 val = alertpanel(_("Discard message"),
9659 _("This message has been modified. Discard it?"),
9660 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9662 val = alertpanel(_("Save changes"),
9663 _("This message has been modified. Save the latest changes?"),
9664 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9666 g_mutex_unlock(compose->mutex);
9668 case G_ALERTDEFAULT:
9669 if (prefs_common.autosave && !reedit)
9670 compose_remove_draft(compose);
9672 case G_ALERTALTERNATE:
9673 compose_draft(data, COMPOSE_QUIT_EDITING);
9680 compose_close(compose);
9683 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9685 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9686 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9687 Compose *compose = (Compose *) data;
9690 compose->out_encoding = (CharSet)value;
9693 static void compose_address_cb(GtkAction *action, gpointer data)
9695 Compose *compose = (Compose *)data;
9697 addressbook_open(compose);
9700 static void about_show_cb(GtkAction *action, gpointer data)
9705 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9707 Compose *compose = (Compose *)data;
9712 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9713 cm_return_if_fail(tmpl != NULL);
9715 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9717 val = alertpanel(_("Apply template"), msg,
9718 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9721 if (val == G_ALERTDEFAULT)
9722 compose_template_apply(compose, tmpl, TRUE);
9723 else if (val == G_ALERTALTERNATE)
9724 compose_template_apply(compose, tmpl, FALSE);
9727 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9729 Compose *compose = (Compose *)data;
9731 compose_exec_ext_editor(compose);
9734 static void compose_undo_cb(GtkAction *action, gpointer data)
9736 Compose *compose = (Compose *)data;
9737 gboolean prev_autowrap = compose->autowrap;
9739 compose->autowrap = FALSE;
9740 undo_undo(compose->undostruct);
9741 compose->autowrap = prev_autowrap;
9744 static void compose_redo_cb(GtkAction *action, gpointer data)
9746 Compose *compose = (Compose *)data;
9747 gboolean prev_autowrap = compose->autowrap;
9749 compose->autowrap = FALSE;
9750 undo_redo(compose->undostruct);
9751 compose->autowrap = prev_autowrap;
9754 static void entry_cut_clipboard(GtkWidget *entry)
9756 if (GTK_IS_EDITABLE(entry))
9757 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9758 else if (GTK_IS_TEXT_VIEW(entry))
9759 gtk_text_buffer_cut_clipboard(
9760 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9761 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9765 static void entry_copy_clipboard(GtkWidget *entry)
9767 if (GTK_IS_EDITABLE(entry))
9768 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9769 else if (GTK_IS_TEXT_VIEW(entry))
9770 gtk_text_buffer_copy_clipboard(
9771 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9772 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9775 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9776 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9778 if (GTK_IS_TEXT_VIEW(entry)) {
9779 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9780 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9781 GtkTextIter start_iter, end_iter;
9783 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9785 if (contents == NULL)
9788 /* we shouldn't delete the selection when middle-click-pasting, or we
9789 * can't mid-click-paste our own selection */
9790 if (clip != GDK_SELECTION_PRIMARY) {
9791 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9792 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9795 if (insert_place == NULL) {
9796 /* if insert_place isn't specified, insert at the cursor.
9797 * used for Ctrl-V pasting */
9798 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9799 start = gtk_text_iter_get_offset(&start_iter);
9800 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9802 /* if insert_place is specified, paste here.
9803 * used for mid-click-pasting */
9804 start = gtk_text_iter_get_offset(insert_place);
9805 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9806 if (prefs_common.primary_paste_unselects)
9807 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9811 /* paste unwrapped: mark the paste so it's not wrapped later */
9812 end = start + strlen(contents);
9813 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9814 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9815 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9816 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9817 /* rewrap paragraph now (after a mid-click-paste) */
9818 mark_start = gtk_text_buffer_get_insert(buffer);
9819 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9820 gtk_text_iter_backward_char(&start_iter);
9821 compose_beautify_paragraph(compose, &start_iter, TRUE);
9823 } else if (GTK_IS_EDITABLE(entry))
9824 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9826 compose->modified = TRUE;
9829 static void entry_allsel(GtkWidget *entry)
9831 if (GTK_IS_EDITABLE(entry))
9832 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9833 else if (GTK_IS_TEXT_VIEW(entry)) {
9834 GtkTextIter startiter, enditer;
9835 GtkTextBuffer *textbuf;
9837 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9838 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9839 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9841 gtk_text_buffer_move_mark_by_name(textbuf,
9842 "selection_bound", &startiter);
9843 gtk_text_buffer_move_mark_by_name(textbuf,
9844 "insert", &enditer);
9848 static void compose_cut_cb(GtkAction *action, gpointer data)
9850 Compose *compose = (Compose *)data;
9851 if (compose->focused_editable
9852 #ifndef GENERIC_UMPC
9853 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9856 entry_cut_clipboard(compose->focused_editable);
9859 static void compose_copy_cb(GtkAction *action, gpointer data)
9861 Compose *compose = (Compose *)data;
9862 if (compose->focused_editable
9863 #ifndef GENERIC_UMPC
9864 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9867 entry_copy_clipboard(compose->focused_editable);
9870 static void compose_paste_cb(GtkAction *action, gpointer data)
9872 Compose *compose = (Compose *)data;
9874 GtkTextBuffer *buffer;
9876 if (compose->focused_editable &&
9877 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9878 entry_paste_clipboard(compose, compose->focused_editable,
9879 prefs_common.linewrap_pastes,
9880 GDK_SELECTION_CLIPBOARD, NULL);
9884 if (GTK_WIDGET_HAS_FOCUS(compose->text) &&
9885 compose->gtkaspell->check_while_typing)
9886 gtkaspell_highlight_all(compose->gtkaspell);
9890 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9892 Compose *compose = (Compose *)data;
9893 gint wrap_quote = prefs_common.linewrap_quote;
9894 if (compose->focused_editable
9895 #ifndef GENERIC_UMPC
9896 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9899 /* let text_insert() (called directly or at a later time
9900 * after the gtk_editable_paste_clipboard) know that
9901 * text is to be inserted as a quotation. implemented
9902 * by using a simple refcount... */
9903 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9904 G_OBJECT(compose->focused_editable),
9905 "paste_as_quotation"));
9906 g_object_set_data(G_OBJECT(compose->focused_editable),
9907 "paste_as_quotation",
9908 GINT_TO_POINTER(paste_as_quotation + 1));
9909 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9910 entry_paste_clipboard(compose, compose->focused_editable,
9911 prefs_common.linewrap_pastes,
9912 GDK_SELECTION_CLIPBOARD, NULL);
9913 prefs_common.linewrap_quote = wrap_quote;
9917 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9919 Compose *compose = (Compose *)data;
9921 GtkTextBuffer *buffer;
9923 if (compose->focused_editable
9924 #ifndef GENERIC_UMPC
9925 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9928 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9929 GDK_SELECTION_CLIPBOARD, NULL);
9933 if (GTK_WIDGET_HAS_FOCUS(compose->text) &&
9934 compose->gtkaspell->check_while_typing)
9935 gtkaspell_highlight_all(compose->gtkaspell);
9939 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9941 Compose *compose = (Compose *)data;
9943 GtkTextBuffer *buffer;
9945 if (compose->focused_editable
9946 #ifndef GENERIC_UMPC
9947 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9950 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9951 GDK_SELECTION_CLIPBOARD, NULL);
9955 if (GTK_WIDGET_HAS_FOCUS(compose->text) &&
9956 compose->gtkaspell->check_while_typing)
9957 gtkaspell_highlight_all(compose->gtkaspell);
9961 static void compose_allsel_cb(GtkAction *action, gpointer data)
9963 Compose *compose = (Compose *)data;
9964 if (compose->focused_editable
9965 #ifndef GENERIC_UMPC
9966 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9969 entry_allsel(compose->focused_editable);
9972 static void textview_move_beginning_of_line (GtkTextView *text)
9974 GtkTextBuffer *buffer;
9978 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9980 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9981 mark = gtk_text_buffer_get_insert(buffer);
9982 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9983 gtk_text_iter_set_line_offset(&ins, 0);
9984 gtk_text_buffer_place_cursor(buffer, &ins);
9987 static void textview_move_forward_character (GtkTextView *text)
9989 GtkTextBuffer *buffer;
9993 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9995 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9996 mark = gtk_text_buffer_get_insert(buffer);
9997 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9998 if (gtk_text_iter_forward_cursor_position(&ins))
9999 gtk_text_buffer_place_cursor(buffer, &ins);
10002 static void textview_move_backward_character (GtkTextView *text)
10004 GtkTextBuffer *buffer;
10008 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10010 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10011 mark = gtk_text_buffer_get_insert(buffer);
10012 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10013 if (gtk_text_iter_backward_cursor_position(&ins))
10014 gtk_text_buffer_place_cursor(buffer, &ins);
10017 static void textview_move_forward_word (GtkTextView *text)
10019 GtkTextBuffer *buffer;
10024 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10026 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10027 mark = gtk_text_buffer_get_insert(buffer);
10028 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10029 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10030 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10031 gtk_text_iter_backward_word_start(&ins);
10032 gtk_text_buffer_place_cursor(buffer, &ins);
10036 static void textview_move_backward_word (GtkTextView *text)
10038 GtkTextBuffer *buffer;
10043 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10045 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10046 mark = gtk_text_buffer_get_insert(buffer);
10047 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10048 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10049 if (gtk_text_iter_backward_word_starts(&ins, 1))
10050 gtk_text_buffer_place_cursor(buffer, &ins);
10053 static void textview_move_end_of_line (GtkTextView *text)
10055 GtkTextBuffer *buffer;
10059 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10061 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10062 mark = gtk_text_buffer_get_insert(buffer);
10063 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10064 if (gtk_text_iter_forward_to_line_end(&ins))
10065 gtk_text_buffer_place_cursor(buffer, &ins);
10068 static void textview_move_next_line (GtkTextView *text)
10070 GtkTextBuffer *buffer;
10075 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10077 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10078 mark = gtk_text_buffer_get_insert(buffer);
10079 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10080 offset = gtk_text_iter_get_line_offset(&ins);
10081 if (gtk_text_iter_forward_line(&ins)) {
10082 gtk_text_iter_set_line_offset(&ins, offset);
10083 gtk_text_buffer_place_cursor(buffer, &ins);
10087 static void textview_move_previous_line (GtkTextView *text)
10089 GtkTextBuffer *buffer;
10094 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10096 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10097 mark = gtk_text_buffer_get_insert(buffer);
10098 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10099 offset = gtk_text_iter_get_line_offset(&ins);
10100 if (gtk_text_iter_backward_line(&ins)) {
10101 gtk_text_iter_set_line_offset(&ins, offset);
10102 gtk_text_buffer_place_cursor(buffer, &ins);
10106 static void textview_delete_forward_character (GtkTextView *text)
10108 GtkTextBuffer *buffer;
10110 GtkTextIter ins, end_iter;
10112 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10114 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10115 mark = gtk_text_buffer_get_insert(buffer);
10116 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10118 if (gtk_text_iter_forward_char(&end_iter)) {
10119 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10123 static void textview_delete_backward_character (GtkTextView *text)
10125 GtkTextBuffer *buffer;
10127 GtkTextIter ins, end_iter;
10129 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10131 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10132 mark = gtk_text_buffer_get_insert(buffer);
10133 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10135 if (gtk_text_iter_backward_char(&end_iter)) {
10136 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10140 static void textview_delete_forward_word (GtkTextView *text)
10142 GtkTextBuffer *buffer;
10144 GtkTextIter ins, end_iter;
10146 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10148 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10149 mark = gtk_text_buffer_get_insert(buffer);
10150 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10152 if (gtk_text_iter_forward_word_end(&end_iter)) {
10153 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10157 static void textview_delete_backward_word (GtkTextView *text)
10159 GtkTextBuffer *buffer;
10161 GtkTextIter ins, end_iter;
10163 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10165 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10166 mark = gtk_text_buffer_get_insert(buffer);
10167 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10169 if (gtk_text_iter_backward_word_start(&end_iter)) {
10170 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10174 static void textview_delete_line (GtkTextView *text)
10176 GtkTextBuffer *buffer;
10178 GtkTextIter ins, start_iter, end_iter;
10180 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10182 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10183 mark = gtk_text_buffer_get_insert(buffer);
10184 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10187 gtk_text_iter_set_line_offset(&start_iter, 0);
10190 if (gtk_text_iter_ends_line(&end_iter)){
10191 if (!gtk_text_iter_forward_char(&end_iter))
10192 gtk_text_iter_backward_char(&start_iter);
10195 gtk_text_iter_forward_to_line_end(&end_iter);
10196 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10199 static void textview_delete_to_line_end (GtkTextView *text)
10201 GtkTextBuffer *buffer;
10203 GtkTextIter ins, end_iter;
10205 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10207 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10208 mark = gtk_text_buffer_get_insert(buffer);
10209 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10211 if (gtk_text_iter_ends_line(&end_iter))
10212 gtk_text_iter_forward_char(&end_iter);
10214 gtk_text_iter_forward_to_line_end(&end_iter);
10215 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10218 #define DO_ACTION(name, act) { \
10219 if(!strcmp(name, a_name)) { \
10223 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10225 const gchar *a_name = gtk_action_get_name(action);
10226 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10227 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10228 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10229 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10230 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10231 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10232 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10233 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10234 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10235 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10236 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10237 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10238 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10239 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10243 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10245 Compose *compose = (Compose *)data;
10246 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10247 ComposeCallAdvancedAction action = -1;
10249 action = compose_call_advanced_action_from_path(gaction);
10252 void (*do_action) (GtkTextView *text);
10253 } action_table[] = {
10254 {textview_move_beginning_of_line},
10255 {textview_move_forward_character},
10256 {textview_move_backward_character},
10257 {textview_move_forward_word},
10258 {textview_move_backward_word},
10259 {textview_move_end_of_line},
10260 {textview_move_next_line},
10261 {textview_move_previous_line},
10262 {textview_delete_forward_character},
10263 {textview_delete_backward_character},
10264 {textview_delete_forward_word},
10265 {textview_delete_backward_word},
10266 {textview_delete_line},
10267 {textview_delete_to_line_end}
10270 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10272 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10273 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10274 if (action_table[action].do_action)
10275 action_table[action].do_action(text);
10277 g_warning("Not implemented yet.");
10281 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10285 if (GTK_IS_EDITABLE(widget)) {
10286 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10287 gtk_editable_set_position(GTK_EDITABLE(widget),
10290 if (widget->parent && widget->parent->parent
10291 && widget->parent->parent->parent) {
10292 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10293 gint y = widget->allocation.y;
10294 gint height = widget->allocation.height;
10295 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10296 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10298 if (y < (int)shown->value) {
10299 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10301 if (y + height > (int)shown->value + (int)shown->page_size) {
10302 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10303 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10304 y + height - (int)shown->page_size - 1);
10306 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10307 (int)shown->upper - (int)shown->page_size - 1);
10314 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10315 compose->focused_editable = widget;
10317 #ifdef GENERIC_UMPC
10318 if (GTK_IS_TEXT_VIEW(widget)
10319 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10320 g_object_ref(compose->notebook);
10321 g_object_ref(compose->edit_vbox);
10322 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10323 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10324 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10325 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10326 g_object_unref(compose->notebook);
10327 g_object_unref(compose->edit_vbox);
10328 g_signal_handlers_block_by_func(G_OBJECT(widget),
10329 G_CALLBACK(compose_grab_focus_cb),
10331 gtk_widget_grab_focus(widget);
10332 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10333 G_CALLBACK(compose_grab_focus_cb),
10335 } else if (!GTK_IS_TEXT_VIEW(widget)
10336 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10337 g_object_ref(compose->notebook);
10338 g_object_ref(compose->edit_vbox);
10339 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10340 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10341 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10342 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10343 g_object_unref(compose->notebook);
10344 g_object_unref(compose->edit_vbox);
10345 g_signal_handlers_block_by_func(G_OBJECT(widget),
10346 G_CALLBACK(compose_grab_focus_cb),
10348 gtk_widget_grab_focus(widget);
10349 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10350 G_CALLBACK(compose_grab_focus_cb),
10356 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10358 compose->modified = TRUE;
10359 // compose_beautify_paragraph(compose, NULL, TRUE);
10360 #ifndef GENERIC_UMPC
10361 compose_set_title(compose);
10365 static void compose_wrap_cb(GtkAction *action, gpointer data)
10367 Compose *compose = (Compose *)data;
10368 compose_beautify_paragraph(compose, NULL, TRUE);
10371 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10373 Compose *compose = (Compose *)data;
10374 compose_wrap_all_full(compose, TRUE);
10377 static void compose_find_cb(GtkAction *action, gpointer data)
10379 Compose *compose = (Compose *)data;
10381 message_search_compose(compose);
10384 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10387 Compose *compose = (Compose *)data;
10388 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10389 if (compose->autowrap)
10390 compose_wrap_all_full(compose, TRUE);
10391 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10394 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10397 Compose *compose = (Compose *)data;
10398 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10401 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10403 Compose *compose = (Compose *)data;
10405 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10408 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10410 Compose *compose = (Compose *)data;
10412 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10415 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10417 g_free(compose->privacy_system);
10419 compose->privacy_system = g_strdup(account->default_privacy_system);
10420 compose_update_privacy_system_menu_item(compose, warn);
10423 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10425 Compose *compose = (Compose *)data;
10427 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10428 gtk_widget_show(compose->ruler_hbox);
10429 prefs_common.show_ruler = TRUE;
10431 gtk_widget_hide(compose->ruler_hbox);
10432 gtk_widget_queue_resize(compose->edit_vbox);
10433 prefs_common.show_ruler = FALSE;
10437 static void compose_attach_drag_received_cb (GtkWidget *widget,
10438 GdkDragContext *context,
10441 GtkSelectionData *data,
10444 gpointer user_data)
10446 Compose *compose = (Compose *)user_data;
10449 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10451 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10453 ) && gtk_drag_get_source_widget(context) !=
10454 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10455 list = uri_list_extract_filenames((const gchar *)data->data);
10456 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10457 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10458 compose_attach_append
10459 (compose, (const gchar *)tmp->data,
10460 utf8_filename, NULL);
10461 g_free(utf8_filename);
10463 if (list) compose_changed_cb(NULL, compose);
10464 list_free_strings(list);
10466 } else if (gtk_drag_get_source_widget(context)
10467 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10468 /* comes from our summaryview */
10469 SummaryView * summaryview = NULL;
10470 GSList * list = NULL, *cur = NULL;
10472 if (mainwindow_get_mainwindow())
10473 summaryview = mainwindow_get_mainwindow()->summaryview;
10476 list = summary_get_selected_msg_list(summaryview);
10478 for (cur = list; cur; cur = cur->next) {
10479 MsgInfo *msginfo = (MsgInfo *)cur->data;
10480 gchar *file = NULL;
10482 file = procmsg_get_message_file_full(msginfo,
10485 compose_attach_append(compose, (const gchar *)file,
10486 (const gchar *)file, "message/rfc822");
10490 g_slist_free(list);
10494 static gboolean compose_drag_drop(GtkWidget *widget,
10495 GdkDragContext *drag_context,
10497 guint time, gpointer user_data)
10499 /* not handling this signal makes compose_insert_drag_received_cb
10504 static void compose_insert_drag_received_cb (GtkWidget *widget,
10505 GdkDragContext *drag_context,
10508 GtkSelectionData *data,
10511 gpointer user_data)
10513 Compose *compose = (Compose *)user_data;
10516 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10519 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10521 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10523 AlertValue val = G_ALERTDEFAULT;
10525 list = uri_list_extract_filenames((const gchar *)data->data);
10526 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10527 /* Assume a list of no files, and data has ://, is a remote link */
10528 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10529 gchar *tmpfile = get_tmp_file();
10530 str_write_to_file(tmpdata, tmpfile);
10532 compose_insert_file(compose, tmpfile);
10533 claws_unlink(tmpfile);
10535 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10536 compose_beautify_paragraph(compose, NULL, TRUE);
10539 switch (prefs_common.compose_dnd_mode) {
10540 case COMPOSE_DND_ASK:
10541 val = alertpanel_full(_("Insert or attach?"),
10542 _("Do you want to insert the contents of the file(s) "
10543 "into the message body, or attach it to the email?"),
10544 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10545 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10547 case COMPOSE_DND_INSERT:
10548 val = G_ALERTALTERNATE;
10550 case COMPOSE_DND_ATTACH:
10551 val = G_ALERTOTHER;
10554 /* unexpected case */
10555 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10558 if (val & G_ALERTDISABLE) {
10559 val &= ~G_ALERTDISABLE;
10560 /* remember what action to perform by default, only if we don't click Cancel */
10561 if (val == G_ALERTALTERNATE)
10562 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10563 else if (val == G_ALERTOTHER)
10564 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10567 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10568 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10569 list_free_strings(list);
10572 } else if (val == G_ALERTOTHER) {
10573 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10574 list_free_strings(list);
10579 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10580 compose_insert_file(compose, (const gchar *)tmp->data);
10582 list_free_strings(list);
10584 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10587 #if GTK_CHECK_VERSION(2, 8, 0)
10588 /* do nothing, handled by GTK */
10590 gchar *tmpfile = get_tmp_file();
10591 str_write_to_file((const gchar *)data->data, tmpfile);
10592 compose_insert_file(compose, tmpfile);
10593 claws_unlink(tmpfile);
10595 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10599 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10602 static void compose_header_drag_received_cb (GtkWidget *widget,
10603 GdkDragContext *drag_context,
10606 GtkSelectionData *data,
10609 gpointer user_data)
10611 GtkEditable *entry = (GtkEditable *)user_data;
10612 gchar *email = (gchar *)data->data;
10614 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10617 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10618 gchar *decoded=g_new(gchar, strlen(email));
10621 email += strlen("mailto:");
10622 decode_uri(decoded, email); /* will fit */
10623 gtk_editable_delete_text(entry, 0, -1);
10624 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10625 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10629 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10632 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10634 Compose *compose = (Compose *)data;
10636 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10637 compose->return_receipt = TRUE;
10639 compose->return_receipt = FALSE;
10642 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10644 Compose *compose = (Compose *)data;
10646 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10647 compose->remove_references = TRUE;
10649 compose->remove_references = FALSE;
10652 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10653 ComposeHeaderEntry *headerentry)
10655 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10659 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10660 GdkEventKey *event,
10661 ComposeHeaderEntry *headerentry)
10663 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10664 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10665 !(event->state & GDK_MODIFIER_MASK) &&
10666 (event->keyval == GDK_BackSpace) &&
10667 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10668 gtk_container_remove
10669 (GTK_CONTAINER(headerentry->compose->header_table),
10670 headerentry->combo);
10671 gtk_container_remove
10672 (GTK_CONTAINER(headerentry->compose->header_table),
10673 headerentry->entry);
10674 headerentry->compose->header_list =
10675 g_slist_remove(headerentry->compose->header_list,
10677 g_free(headerentry);
10678 } else if (event->keyval == GDK_Tab) {
10679 if (headerentry->compose->header_last == headerentry) {
10680 /* Override default next focus, and give it to subject_entry
10681 * instead of notebook tabs
10683 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10684 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10691 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10692 ComposeHeaderEntry *headerentry)
10694 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10695 compose_create_header_entry(headerentry->compose);
10696 g_signal_handlers_disconnect_matched
10697 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10698 0, 0, NULL, NULL, headerentry);
10700 /* Automatically scroll down */
10701 GTK_EVENTS_FLUSH();
10702 compose_show_first_last_header(headerentry->compose, FALSE);
10708 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10710 GtkAdjustment *vadj;
10712 cm_return_if_fail(compose);
10713 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10714 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10715 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10716 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10717 gtk_adjustment_changed(vadj);
10720 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10721 const gchar *text, gint len, Compose *compose)
10723 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10724 (G_OBJECT(compose->text), "paste_as_quotation"));
10727 cm_return_if_fail(text != NULL);
10729 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10730 G_CALLBACK(text_inserted),
10732 if (paste_as_quotation) {
10734 const gchar *qmark;
10736 GtkTextIter start_iter;
10739 len = strlen(text);
10741 new_text = g_strndup(text, len);
10743 qmark = compose_quote_char_from_context(compose);
10745 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10746 gtk_text_buffer_place_cursor(buffer, iter);
10748 pos = gtk_text_iter_get_offset(iter);
10750 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10751 _("Quote format error at line %d."));
10752 quote_fmt_reset_vartable();
10754 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10755 GINT_TO_POINTER(paste_as_quotation - 1));
10757 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10758 gtk_text_buffer_place_cursor(buffer, iter);
10759 gtk_text_buffer_delete_mark(buffer, mark);
10761 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10762 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10763 compose_beautify_paragraph(compose, &start_iter, FALSE);
10764 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10765 gtk_text_buffer_delete_mark(buffer, mark);
10767 if (strcmp(text, "\n") || compose->automatic_break
10768 || gtk_text_iter_starts_line(iter)) {
10769 GtkTextIter before_ins;
10770 gtk_text_buffer_insert(buffer, iter, text, len);
10771 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10772 before_ins = *iter;
10773 gtk_text_iter_backward_chars(&before_ins, len);
10774 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10777 /* check if the preceding is just whitespace or quote */
10778 GtkTextIter start_line;
10779 gchar *tmp = NULL, *quote = NULL;
10780 gint quote_len = 0, is_normal = 0;
10781 start_line = *iter;
10782 gtk_text_iter_set_line_offset(&start_line, 0);
10783 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10786 if (*tmp == '\0') {
10789 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10797 gtk_text_buffer_insert(buffer, iter, text, len);
10799 gtk_text_buffer_insert_with_tags_by_name(buffer,
10800 iter, text, len, "no_join", NULL);
10805 if (!paste_as_quotation) {
10806 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10807 compose_beautify_paragraph(compose, iter, FALSE);
10808 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10809 gtk_text_buffer_delete_mark(buffer, mark);
10812 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10813 G_CALLBACK(text_inserted),
10815 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10817 if (prefs_common.autosave &&
10818 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10819 compose->draft_timeout_tag != -2 /* disabled while loading */)
10820 compose->draft_timeout_tag = g_timeout_add
10821 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10823 static gint compose_defer_auto_save_draft(Compose *compose)
10825 compose->draft_timeout_tag = -1;
10826 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10831 static void compose_check_all(GtkAction *action, gpointer data)
10833 Compose *compose = (Compose *)data;
10834 if (!compose->gtkaspell)
10837 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10838 claws_spell_entry_check_all(
10839 CLAWS_SPELL_ENTRY(compose->subject_entry));
10841 gtkaspell_check_all(compose->gtkaspell);
10844 static void compose_highlight_all(GtkAction *action, gpointer data)
10846 Compose *compose = (Compose *)data;
10847 if (compose->gtkaspell) {
10848 claws_spell_entry_recheck_all(
10849 CLAWS_SPELL_ENTRY(compose->subject_entry));
10850 gtkaspell_highlight_all(compose->gtkaspell);
10854 static void compose_check_backwards(GtkAction *action, gpointer data)
10856 Compose *compose = (Compose *)data;
10857 if (!compose->gtkaspell) {
10858 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10862 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10863 claws_spell_entry_check_backwards(
10864 CLAWS_SPELL_ENTRY(compose->subject_entry));
10866 gtkaspell_check_backwards(compose->gtkaspell);
10869 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10871 Compose *compose = (Compose *)data;
10872 if (!compose->gtkaspell) {
10873 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10877 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10878 claws_spell_entry_check_forwards_go(
10879 CLAWS_SPELL_ENTRY(compose->subject_entry));
10881 gtkaspell_check_forwards_go(compose->gtkaspell);
10886 *\brief Guess originating forward account from MsgInfo and several
10887 * "common preference" settings. Return NULL if no guess.
10889 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10891 PrefsAccount *account = NULL;
10893 cm_return_val_if_fail(msginfo, NULL);
10894 cm_return_val_if_fail(msginfo->folder, NULL);
10895 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10897 if (msginfo->folder->prefs->enable_default_account)
10898 account = account_find_from_id(msginfo->folder->prefs->default_account);
10901 account = msginfo->folder->folder->account;
10903 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10905 Xstrdup_a(to, msginfo->to, return NULL);
10906 extract_address(to);
10907 account = account_find_from_address(to, FALSE);
10910 if (!account && prefs_common.forward_account_autosel) {
10911 gchar cc[BUFFSIZE];
10912 if (!procheader_get_header_from_msginfo
10913 (msginfo, cc,sizeof cc , "Cc:")) {
10914 gchar *buf = cc + strlen("Cc:");
10915 extract_address(buf);
10916 account = account_find_from_address(buf, FALSE);
10920 if (!account && prefs_common.forward_account_autosel) {
10921 gchar deliveredto[BUFFSIZE];
10922 if (!procheader_get_header_from_msginfo
10923 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10924 gchar *buf = deliveredto + strlen("Delivered-To:");
10925 extract_address(buf);
10926 account = account_find_from_address(buf, FALSE);
10933 gboolean compose_close(Compose *compose)
10937 if (!g_mutex_trylock(compose->mutex)) {
10938 /* we have to wait for the (possibly deferred by auto-save)
10939 * drafting to be done, before destroying the compose under
10941 debug_print("waiting for drafting to finish...\n");
10942 compose_allow_user_actions(compose, FALSE);
10943 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10946 cm_return_val_if_fail(compose, FALSE);
10947 gtkut_widget_get_uposition(compose->window, &x, &y);
10948 if (!compose->batch) {
10949 prefs_common.compose_x = x;
10950 prefs_common.compose_y = y;
10952 g_mutex_unlock(compose->mutex);
10953 compose_destroy(compose);
10958 * Add entry field for each address in list.
10959 * \param compose E-Mail composition object.
10960 * \param listAddress List of (formatted) E-Mail addresses.
10962 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10965 node = listAddress;
10967 addr = ( gchar * ) node->data;
10968 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
10969 node = g_list_next( node );
10973 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10974 guint action, gboolean opening_multiple)
10976 gchar *body = NULL;
10977 GSList *new_msglist = NULL;
10978 MsgInfo *tmp_msginfo = NULL;
10979 gboolean originally_enc = FALSE;
10980 gboolean originally_sig = FALSE;
10981 Compose *compose = NULL;
10982 gchar *s_system = NULL;
10984 cm_return_if_fail(msgview != NULL);
10986 cm_return_if_fail(msginfo_list != NULL);
10988 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10989 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10990 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10992 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10993 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10994 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10995 orig_msginfo, mimeinfo);
10996 if (tmp_msginfo != NULL) {
10997 new_msglist = g_slist_append(NULL, tmp_msginfo);
10999 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11000 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11001 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11003 tmp_msginfo->folder = orig_msginfo->folder;
11004 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11005 if (orig_msginfo->tags) {
11006 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11007 tmp_msginfo->folder->tags_dirty = TRUE;
11013 if (!opening_multiple)
11014 body = messageview_get_selection(msgview);
11017 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11018 procmsg_msginfo_free(tmp_msginfo);
11019 g_slist_free(new_msglist);
11021 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11023 if (compose && originally_enc) {
11024 compose_force_encryption(compose, compose->account, FALSE, s_system);
11027 if (compose && originally_sig && compose->account->default_sign_reply) {
11028 compose_force_signing(compose, compose->account, s_system);
11034 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11037 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11038 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11039 GSList *cur = msginfo_list;
11040 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11041 "messages. Opening the windows "
11042 "could take some time. Do you "
11043 "want to continue?"),
11044 g_slist_length(msginfo_list));
11045 if (g_slist_length(msginfo_list) > 9
11046 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11047 != G_ALERTALTERNATE) {
11052 /* We'll open multiple compose windows */
11053 /* let the WM place the next windows */
11054 compose_force_window_origin = FALSE;
11055 for (; cur; cur = cur->next) {
11057 tmplist.data = cur->data;
11058 tmplist.next = NULL;
11059 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11061 compose_force_window_origin = TRUE;
11063 /* forwarding multiple mails as attachments is done via a
11064 * single compose window */
11065 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11069 void compose_set_position(Compose *compose, gint pos)
11071 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11073 gtkut_text_view_set_position(text, pos);
11076 gboolean compose_search_string(Compose *compose,
11077 const gchar *str, gboolean case_sens)
11079 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11081 return gtkut_text_view_search_string(text, str, case_sens);
11084 gboolean compose_search_string_backward(Compose *compose,
11085 const gchar *str, gboolean case_sens)
11087 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11089 return gtkut_text_view_search_string_backward(text, str, case_sens);
11092 /* allocate a msginfo structure and populate its data from a compose data structure */
11093 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11095 MsgInfo *newmsginfo;
11097 gchar buf[BUFFSIZE];
11099 cm_return_val_if_fail( compose != NULL, NULL );
11101 newmsginfo = procmsg_msginfo_new();
11104 get_rfc822_date(buf, sizeof(buf));
11105 newmsginfo->date = g_strdup(buf);
11108 if (compose->from_name) {
11109 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11110 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11114 if (compose->subject_entry)
11115 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11117 /* to, cc, reply-to, newsgroups */
11118 for (list = compose->header_list; list; list = list->next) {
11119 gchar *header = gtk_editable_get_chars(
11121 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11122 gchar *entry = gtk_editable_get_chars(
11123 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11125 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11126 if ( newmsginfo->to == NULL ) {
11127 newmsginfo->to = g_strdup(entry);
11128 } else if (entry && *entry) {
11129 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11130 g_free(newmsginfo->to);
11131 newmsginfo->to = tmp;
11134 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11135 if ( newmsginfo->cc == NULL ) {
11136 newmsginfo->cc = g_strdup(entry);
11137 } else if (entry && *entry) {
11138 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11139 g_free(newmsginfo->cc);
11140 newmsginfo->cc = tmp;
11143 if ( strcasecmp(header,
11144 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11145 if ( newmsginfo->newsgroups == NULL ) {
11146 newmsginfo->newsgroups = g_strdup(entry);
11147 } else if (entry && *entry) {
11148 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11149 g_free(newmsginfo->newsgroups);
11150 newmsginfo->newsgroups = tmp;
11158 /* other data is unset */
11164 /* update compose's dictionaries from folder dict settings */
11165 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11166 FolderItem *folder_item)
11168 cm_return_if_fail(compose != NULL);
11170 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11171 FolderItemPrefs *prefs = folder_item->prefs;
11173 if (prefs->enable_default_dictionary)
11174 gtkaspell_change_dict(compose->gtkaspell,
11175 prefs->default_dictionary, FALSE);
11176 if (folder_item->prefs->enable_default_alt_dictionary)
11177 gtkaspell_change_alt_dict(compose->gtkaspell,
11178 prefs->default_alt_dictionary);
11179 if (prefs->enable_default_dictionary
11180 || prefs->enable_default_alt_dictionary)
11181 compose_spell_menu_changed(compose);