2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
115 #define N_ATTACH_COLS (N_COL_COLUMNS)
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
133 } ComposeCallAdvancedAction;
137 PRIORITY_HIGHEST = 1,
146 COMPOSE_INSERT_SUCCESS,
147 COMPOSE_INSERT_READ_ERROR,
148 COMPOSE_INSERT_INVALID_CHARACTER,
149 COMPOSE_INSERT_NO_FILE
150 } ComposeInsertResult;
154 COMPOSE_WRITE_FOR_SEND,
155 COMPOSE_WRITE_FOR_STORE
160 COMPOSE_QUOTE_FORCED,
167 SUBJECT_FIELD_PRESENT,
172 #define B64_LINE_SIZE 57
173 #define B64_BUFFSIZE 77
175 #define MAX_REFERENCES_LEN 999
177 static GList *compose_list = NULL;
179 static Compose *compose_generic_new (PrefsAccount *account,
182 GPtrArray *attach_files,
183 GList *listAddress );
185 static Compose *compose_create (PrefsAccount *account,
190 static void compose_entry_mark_default_to (Compose *compose,
191 const gchar *address);
192 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
193 ComposeQuoteMode quote_mode,
197 static Compose *compose_forward_multiple (PrefsAccount *account,
198 GSList *msginfo_list);
199 static Compose *compose_reply (MsgInfo *msginfo,
200 ComposeQuoteMode quote_mode,
205 static Compose *compose_reply_mode (ComposeMode mode,
206 GSList *msginfo_list,
208 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
209 static void compose_update_privacy_systems_menu(Compose *compose);
211 static GtkWidget *compose_account_option_menu_create
213 static void compose_set_out_encoding (Compose *compose);
214 static void compose_set_template_menu (Compose *compose);
215 static void compose_destroy (Compose *compose);
217 static MailField compose_entries_set (Compose *compose,
219 ComposeEntryType to_type);
220 static gint compose_parse_header (Compose *compose,
222 static gchar *compose_parse_references (const gchar *ref,
225 static gchar *compose_quote_fmt (Compose *compose,
231 gboolean need_unescape,
232 const gchar *err_msg);
234 static void compose_reply_set_entry (Compose *compose,
240 followup_and_reply_to);
241 static void compose_reedit_set_entry (Compose *compose,
244 static void compose_insert_sig (Compose *compose,
246 static ComposeInsertResult compose_insert_file (Compose *compose,
249 static gboolean compose_attach_append (Compose *compose,
252 const gchar *content_type);
253 static void compose_attach_parts (Compose *compose,
256 static gboolean compose_beautify_paragraph (Compose *compose,
257 GtkTextIter *par_iter,
259 static void compose_wrap_all (Compose *compose);
260 static void compose_wrap_all_full (Compose *compose,
263 static void compose_set_title (Compose *compose);
264 static void compose_select_account (Compose *compose,
265 PrefsAccount *account,
268 static PrefsAccount *compose_current_mail_account(void);
269 /* static gint compose_send (Compose *compose); */
270 static gboolean compose_check_for_valid_recipient
272 static gboolean compose_check_entries (Compose *compose,
273 gboolean check_everything);
274 static gint compose_write_to_file (Compose *compose,
277 gboolean attach_parts);
278 static gint compose_write_body_to_file (Compose *compose,
280 static gint compose_remove_reedit_target (Compose *compose,
282 static void compose_remove_draft (Compose *compose);
283 static gint compose_queue_sub (Compose *compose,
287 gboolean check_subject,
288 gboolean remove_reedit_target);
289 static int compose_add_attachments (Compose *compose,
291 static gchar *compose_get_header (Compose *compose);
293 static void compose_convert_header (Compose *compose,
298 gboolean addr_field);
300 static void compose_attach_info_free (AttachInfo *ainfo);
301 static void compose_attach_remove_selected (GtkAction *action,
304 static void compose_template_apply (Compose *compose,
307 static void compose_attach_property (GtkAction *action,
309 static void compose_attach_property_create (gboolean *cancelled);
310 static void attach_property_ok (GtkWidget *widget,
311 gboolean *cancelled);
312 static void attach_property_cancel (GtkWidget *widget,
313 gboolean *cancelled);
314 static gint attach_property_delete_event (GtkWidget *widget,
316 gboolean *cancelled);
317 static gboolean attach_property_key_pressed (GtkWidget *widget,
319 gboolean *cancelled);
321 static void compose_exec_ext_editor (Compose *compose);
323 static gint compose_exec_ext_editor_real (const gchar *file);
324 static gboolean compose_ext_editor_kill (Compose *compose);
325 static gboolean compose_input_cb (GIOChannel *source,
326 GIOCondition condition,
328 static void compose_set_ext_editor_sensitive (Compose *compose,
330 #endif /* G_OS_UNIX */
332 static void compose_undo_state_changed (UndoMain *undostruct,
337 static void compose_create_header_entry (Compose *compose);
338 static void compose_add_header_entry (Compose *compose, const gchar *header,
339 gchar *text, ComposePrefType pref_type);
340 static void compose_remove_header_entries(Compose *compose);
342 static void compose_update_priority_menu_item(Compose * compose);
344 static void compose_spell_menu_changed (void *data);
345 static void compose_dict_changed (void *data);
347 static void compose_add_field_list ( Compose *compose,
348 GList *listAddress );
350 /* callback functions */
352 static gboolean compose_edit_size_alloc (GtkEditable *widget,
353 GtkAllocation *allocation,
354 GtkSHRuler *shruler);
355 static void account_activated (GtkComboBox *optmenu,
357 static void attach_selected (GtkTreeView *tree_view,
358 GtkTreePath *tree_path,
359 GtkTreeViewColumn *column,
361 static gboolean attach_button_pressed (GtkWidget *widget,
362 GdkEventButton *event,
364 static gboolean attach_key_pressed (GtkWidget *widget,
367 static void compose_send_cb (GtkAction *action, gpointer data);
368 static void compose_send_later_cb (GtkAction *action, gpointer data);
370 static void compose_save_cb (GtkAction *action,
373 static void compose_attach_cb (GtkAction *action,
375 static void compose_insert_file_cb (GtkAction *action,
377 static void compose_insert_sig_cb (GtkAction *action,
380 static void compose_close_cb (GtkAction *action,
383 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
385 static void compose_address_cb (GtkAction *action,
387 static void about_show_cb (GtkAction *action,
389 static void compose_template_activate_cb(GtkWidget *widget,
392 static void compose_ext_editor_cb (GtkAction *action,
395 static gint compose_delete_cb (GtkWidget *widget,
399 static void compose_undo_cb (GtkAction *action,
401 static void compose_redo_cb (GtkAction *action,
403 static void compose_cut_cb (GtkAction *action,
405 static void compose_copy_cb (GtkAction *action,
407 static void compose_paste_cb (GtkAction *action,
409 static void compose_paste_as_quote_cb (GtkAction *action,
411 static void compose_paste_no_wrap_cb (GtkAction *action,
413 static void compose_paste_wrap_cb (GtkAction *action,
415 static void compose_allsel_cb (GtkAction *action,
418 static void compose_advanced_action_cb (GtkAction *action,
421 static void compose_grab_focus_cb (GtkWidget *widget,
424 static void compose_changed_cb (GtkTextBuffer *textbuf,
427 static void compose_wrap_cb (GtkAction *action,
429 static void compose_wrap_all_cb (GtkAction *action,
431 static void compose_find_cb (GtkAction *action,
433 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
435 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
438 static void compose_toggle_ruler_cb (GtkToggleAction *action,
440 static void compose_toggle_sign_cb (GtkToggleAction *action,
442 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
444 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
445 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
446 static void activate_privacy_system (Compose *compose,
447 PrefsAccount *account,
449 static void compose_use_signing(Compose *compose, gboolean use_signing);
450 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
451 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
453 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
455 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
456 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
457 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
459 static void compose_attach_drag_received_cb (GtkWidget *widget,
460 GdkDragContext *drag_context,
463 GtkSelectionData *data,
467 static void compose_insert_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
475 static void compose_header_drag_received_cb (GtkWidget *widget,
476 GdkDragContext *drag_context,
479 GtkSelectionData *data,
484 static gboolean compose_drag_drop (GtkWidget *widget,
485 GdkDragContext *drag_context,
487 guint time, gpointer user_data);
489 static void text_inserted (GtkTextBuffer *buffer,
494 static Compose *compose_generic_reply(MsgInfo *msginfo,
495 ComposeQuoteMode quote_mode,
499 gboolean followup_and_reply_to,
502 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
503 ComposeHeaderEntry *headerentry);
504 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
506 ComposeHeaderEntry *headerentry);
507 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
508 ComposeHeaderEntry *headerentry);
510 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
512 static void compose_allow_user_actions (Compose *compose, gboolean allow);
514 static void compose_nothing_cb (GtkAction *action, gpointer data)
520 static void compose_check_all (GtkAction *action, gpointer data);
521 static void compose_highlight_all (GtkAction *action, gpointer data);
522 static void compose_check_backwards (GtkAction *action, gpointer data);
523 static void compose_check_forwards_go (GtkAction *action, gpointer data);
526 static gint compose_defer_auto_save_draft (Compose *compose);
527 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
529 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
532 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
533 FolderItem *folder_item);
535 static void compose_attach_update_label(Compose *compose);
537 static GtkActionEntry compose_popup_entries[] =
539 {"Compose", NULL, "Compose" },
540 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
541 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
542 {"Compose/---", NULL, "---", NULL, NULL, NULL },
543 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
546 static GtkActionEntry compose_entries[] =
548 {"Menu", NULL, "Menu" },
550 {"Message", NULL, N_("_Message") },
551 {"Edit", NULL, N_("_Edit") },
553 {"Spelling", NULL, N_("_Spelling") },
555 {"Options", NULL, N_("_Options") },
556 {"Tools", NULL, N_("_Tools") },
557 {"Help", NULL, N_("_Help") },
559 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
560 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
561 {"Message/---", NULL, "---" },
563 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
564 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
565 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
566 /* {"Message/---", NULL, "---" }, */
567 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
568 /* {"Message/---", NULL, "---" }, */
569 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
572 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
573 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
574 {"Edit/---", NULL, "---" },
576 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
577 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
578 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
580 {"Edit/SpecialPaste", NULL, N_("Special paste") },
581 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
582 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
583 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
585 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
587 {"Edit/Advanced", NULL, N_("A_dvanced") },
588 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
589 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
590 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
591 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
592 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
593 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
594 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
595 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
596 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
597 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
598 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
599 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
600 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
601 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
603 /* {"Edit/---", NULL, "---" }, */
604 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
606 /* {"Edit/---", NULL, "---" }, */
607 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
608 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
609 /* {"Edit/---", NULL, "---" }, */
610 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
613 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
614 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
615 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
616 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
618 {"Spelling/---", NULL, "---" },
619 {"Spelling/Options", NULL, N_("_Options") },
624 {"Options/ReplyMode", NULL, N_("Reply _mode") },
625 {"Options/---", NULL, "---" },
626 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
627 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
629 /* {"Options/---", NULL, "---" }, */
631 {"Options/Priority", NULL, N_("_Priority") },
633 {"Options/Encoding", NULL, N_("Character _encoding") },
634 {"Options/Encoding/---", NULL, "---" },
635 #define ENC_ACTION(cs_char,c_char,string) \
636 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
638 {"Options/Encoding/Western", NULL, N_("Western European") },
639 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
640 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
641 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
642 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
643 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
644 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
645 {"Options/Encoding/Korean", NULL, N_("Korean") },
646 {"Options/Encoding/Thai", NULL, N_("Thai") },
649 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
651 {"Tools/Template", NULL, N_("_Template") },
652 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
653 {"Tools/Actions", NULL, N_("Actio_ns") },
654 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
657 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
660 static GtkToggleActionEntry compose_toggle_entries[] =
662 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
663 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
664 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
665 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
666 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
667 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
668 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
671 static GtkRadioActionEntry compose_radio_rm_entries[] =
673 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
674 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
675 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
676 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
679 static GtkRadioActionEntry compose_radio_prio_entries[] =
681 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
682 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
683 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
684 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
685 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
688 static GtkRadioActionEntry compose_radio_enc_entries[] =
690 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
723 static GtkTargetEntry compose_mime_types[] =
725 {"text/uri-list", 0, 0},
726 {"UTF8_STRING", 0, 0},
730 static gboolean compose_put_existing_to_front(MsgInfo *info)
732 GList *compose_list = compose_get_compose_list();
736 for (elem = compose_list; elem != NULL && elem->data != NULL;
738 Compose *c = (Compose*)elem->data;
740 if (!c->targetinfo || !c->targetinfo->msgid ||
744 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
745 gtkut_window_popup(c->window);
753 static GdkColor quote_color1 =
754 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
755 static GdkColor quote_color2 =
756 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
757 static GdkColor quote_color3 =
758 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
760 static GdkColor quote_bgcolor1 =
761 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
762 static GdkColor quote_bgcolor2 =
763 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
764 static GdkColor quote_bgcolor3 =
765 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor signature_color = {
774 static GdkColor uri_color = {
781 static void compose_create_tags(GtkTextView *text, Compose *compose)
783 GtkTextBuffer *buffer;
784 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 buffer = gtk_text_view_get_buffer(text);
792 if (prefs_common.enable_color) {
793 /* grab the quote colors, converting from an int to a GdkColor */
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
796 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
798 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
800 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
802 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
804 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
806 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
808 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
811 signature_color = quote_color1 = quote_color2 = quote_color3 =
812 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
815 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
816 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
817 "foreground-gdk", "e_color1,
818 "paragraph-background-gdk", "e_bgcolor1,
820 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
821 "foreground-gdk", "e_color2,
822 "paragraph-background-gdk", "e_bgcolor2,
824 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
825 "foreground-gdk", "e_color3,
826 "paragraph-background-gdk", "e_bgcolor3,
829 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
830 "foreground-gdk", "e_color1,
832 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
833 "foreground-gdk", "e_color2,
835 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
836 "foreground-gdk", "e_color3,
840 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
841 "foreground-gdk", &signature_color,
844 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
845 "foreground-gdk", &uri_color,
847 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
848 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
850 color[0] = quote_color1;
851 color[1] = quote_color2;
852 color[2] = quote_color3;
853 color[3] = quote_bgcolor1;
854 color[4] = quote_bgcolor2;
855 color[5] = quote_bgcolor3;
856 color[6] = signature_color;
857 color[7] = uri_color;
858 cmap = gdk_drawable_get_colormap(compose->window->window);
859 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
861 for (i = 0; i < 8; i++) {
862 if (success[i] == FALSE) {
865 g_warning("Compose: color allocation failed.\n");
866 style = gtk_widget_get_style(GTK_WIDGET(text));
867 quote_color1 = quote_color2 = quote_color3 =
868 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
869 signature_color = uri_color = black;
874 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
875 GPtrArray *attach_files)
877 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
880 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
882 return compose_generic_new(account, mailto, item, NULL, NULL);
885 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
887 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
890 #define SCROLL_TO_CURSOR(compose) { \
891 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
892 gtk_text_view_get_buffer( \
893 GTK_TEXT_VIEW(compose->text))); \
894 gtk_text_view_scroll_mark_onscreen( \
895 GTK_TEXT_VIEW(compose->text), \
899 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
902 if (folderidentifier) {
903 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
904 prefs_common.compose_save_to_history = add_history(
905 prefs_common.compose_save_to_history, folderidentifier);
906 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
907 prefs_common.compose_save_to_history);
910 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
911 if (folderidentifier)
912 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
914 gtk_entry_set_text(GTK_ENTRY(entry), "");
917 static gchar *compose_get_save_to(Compose *compose)
920 gchar *result = NULL;
921 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
922 result = gtk_editable_get_chars(entry, 0, -1);
925 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
926 prefs_common.compose_save_to_history = add_history(
927 prefs_common.compose_save_to_history, result);
928 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
929 prefs_common.compose_save_to_history);
934 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
935 GPtrArray *attach_files, GList *listAddress )
938 GtkTextView *textview;
939 GtkTextBuffer *textbuf;
941 const gchar *subject_format = NULL;
942 const gchar *body_format = NULL;
943 gchar *mailto_from = NULL;
944 PrefsAccount *mailto_account = NULL;
945 MsgInfo* dummyinfo = NULL;
946 MailField mfield = NO_FIELD_PRESENT;
950 /* check if mailto defines a from */
951 if (mailto && *mailto != '\0') {
952 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
953 /* mailto defines a from, check if we can get account prefs from it,
954 if not, the account prefs will be guessed using other ways, but we'll keep
957 mailto_account = account_find_from_address(mailto_from, TRUE);
959 account = mailto_account;
962 /* if no account prefs set from mailto, set if from folder prefs (if any) */
963 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
964 account = account_find_from_id(item->prefs->default_account);
966 /* if no account prefs set, fallback to the current one */
967 if (!account) account = cur_account;
968 cm_return_val_if_fail(account != NULL, NULL);
970 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
972 /* override from name if mailto asked for it */
974 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
977 /* override from name according to folder properties */
978 if (item && item->prefs &&
979 item->prefs->compose_with_format &&
980 item->prefs->compose_override_from_format &&
981 *item->prefs->compose_override_from_format != '\0') {
986 dummyinfo = compose_msginfo_new_from_compose(compose);
988 /* decode \-escape sequences in the internal representation of the quote format */
989 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
990 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
993 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
996 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
998 quote_fmt_scan_string(tmp);
1001 buf = quote_fmt_get_buffer();
1003 alertpanel_error(_("New message From format error."));
1005 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1006 quote_fmt_reset_vartable();
1011 compose->replyinfo = NULL;
1012 compose->fwdinfo = NULL;
1014 textview = GTK_TEXT_VIEW(compose->text);
1015 textbuf = gtk_text_view_get_buffer(textview);
1016 compose_create_tags(textview, compose);
1018 undo_block(compose->undostruct);
1020 compose_set_dictionaries_from_folder_prefs(compose, item);
1023 if (account->auto_sig)
1024 compose_insert_sig(compose, FALSE);
1025 gtk_text_buffer_get_start_iter(textbuf, &iter);
1026 gtk_text_buffer_place_cursor(textbuf, &iter);
1028 if (account->protocol != A_NNTP) {
1029 if (mailto && *mailto != '\0') {
1030 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1032 } else if (item && item->prefs) {
1033 if (item->prefs->enable_default_bcc) {
1034 compose_entry_append(compose, item->prefs->default_bcc,
1035 COMPOSE_BCC, PREF_FOLDER);
1037 if (item->prefs->enable_default_cc) {
1038 compose_entry_append(compose, item->prefs->default_cc,
1039 COMPOSE_CC, PREF_FOLDER);
1041 if (item->prefs->enable_default_replyto) {
1042 compose_entry_append(compose, item->prefs->default_replyto,
1043 COMPOSE_REPLYTO, PREF_FOLDER);
1045 if (item->prefs->enable_default_to) {
1046 compose_entry_append(compose, item->prefs->default_to,
1047 COMPOSE_TO, PREF_FOLDER);
1048 compose_entry_mark_default_to(compose, item->prefs->default_to);
1051 if (item && item->ret_rcpt) {
1052 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1055 if (mailto && *mailto != '\0') {
1056 if (!strchr(mailto, '@'))
1057 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1059 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1060 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1061 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1064 * CLAWS: just don't allow return receipt request, even if the user
1065 * may want to send an email. simple but foolproof.
1067 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1069 compose_add_field_list( compose, listAddress );
1071 if (item && item->prefs && item->prefs->compose_with_format) {
1072 subject_format = item->prefs->compose_subject_format;
1073 body_format = item->prefs->compose_body_format;
1074 } else if (account->compose_with_format) {
1075 subject_format = account->compose_subject_format;
1076 body_format = account->compose_body_format;
1077 } else if (prefs_common.compose_with_format) {
1078 subject_format = prefs_common.compose_subject_format;
1079 body_format = prefs_common.compose_body_format;
1082 if (subject_format || body_format) {
1085 && *subject_format != '\0' )
1087 gchar *subject = NULL;
1092 dummyinfo = compose_msginfo_new_from_compose(compose);
1094 /* decode \-escape sequences in the internal representation of the quote format */
1095 tmp = malloc(strlen(subject_format)+1);
1096 pref_get_unescaped_pref(tmp, subject_format);
1098 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1100 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1101 compose->gtkaspell);
1103 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1105 quote_fmt_scan_string(tmp);
1108 buf = quote_fmt_get_buffer();
1110 alertpanel_error(_("New message subject format error."));
1112 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1113 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1114 quote_fmt_reset_vartable();
1121 && *body_format != '\0' )
1124 GtkTextBuffer *buffer;
1125 GtkTextIter start, end;
1129 dummyinfo = compose_msginfo_new_from_compose(compose);
1131 text = GTK_TEXT_VIEW(compose->text);
1132 buffer = gtk_text_view_get_buffer(text);
1133 gtk_text_buffer_get_start_iter(buffer, &start);
1134 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1135 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1137 compose_quote_fmt(compose, dummyinfo,
1139 NULL, tmp, FALSE, TRUE,
1140 _("The body of the \"New message\" template has an error at line %d."));
1141 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1142 quote_fmt_reset_vartable();
1148 procmsg_msginfo_free( dummyinfo );
1154 for (i = 0; i < attach_files->len; i++) {
1155 file = g_ptr_array_index(attach_files, i);
1156 compose_attach_append(compose, file, file, NULL);
1160 compose_show_first_last_header(compose, TRUE);
1162 /* Set save folder */
1163 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1164 gchar *folderidentifier;
1166 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1167 folderidentifier = folder_item_get_identifier(item);
1168 compose_set_save_to(compose, folderidentifier);
1169 g_free(folderidentifier);
1172 /* Place cursor according to provided input (mfield) */
1174 case NO_FIELD_PRESENT:
1175 gtk_widget_grab_focus(compose->header_last->entry);
1177 case TO_FIELD_PRESENT:
1179 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1180 gtk_widget_grab_focus(compose->subject_entry);
1182 case SUBJECT_FIELD_PRESENT:
1183 textview = GTK_TEXT_VIEW(compose->text);
1184 textbuf = gtk_text_view_get_buffer(textview);
1185 mark = gtk_text_buffer_get_insert(textbuf);
1186 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1187 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1189 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1190 * only defers where it comes to the variable body
1191 * is not null. If no body is present compose->text
1192 * will be null in which case you cannot place the
1193 * cursor inside the component so. An empty component
1194 * is therefore created before placing the cursor
1196 case BODY_FIELD_PRESENT:
1197 gtk_widget_grab_focus(compose->text);
1201 undo_unblock(compose->undostruct);
1203 if (prefs_common.auto_exteditor)
1204 compose_exec_ext_editor(compose);
1206 compose->draft_timeout_tag = -1;
1207 SCROLL_TO_CURSOR(compose);
1209 compose->modified = FALSE;
1210 compose_set_title(compose);
1214 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1215 gboolean override_pref, const gchar *system)
1217 const gchar *privacy = NULL;
1219 cm_return_if_fail(compose != NULL);
1220 cm_return_if_fail(account != NULL);
1222 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1227 else if (account->default_privacy_system
1228 && strlen(account->default_privacy_system)) {
1229 privacy = account->default_privacy_system;
1231 GSList *privacy_avail = privacy_get_system_ids();
1232 if (privacy_avail && g_slist_length(privacy_avail)) {
1233 privacy = (gchar *)(privacy_avail->data);
1236 if (privacy != NULL) {
1238 g_free(compose->privacy_system);
1239 compose->privacy_system = NULL;
1241 if (compose->privacy_system == NULL)
1242 compose->privacy_system = g_strdup(privacy);
1243 else if (*(compose->privacy_system) == '\0') {
1244 g_free(compose->privacy_system);
1245 compose->privacy_system = g_strdup(privacy);
1247 compose_update_privacy_system_menu_item(compose, FALSE);
1248 compose_use_encryption(compose, TRUE);
1252 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1254 const gchar *privacy = NULL;
1258 else if (account->default_privacy_system
1259 && strlen(account->default_privacy_system)) {
1260 privacy = account->default_privacy_system;
1262 GSList *privacy_avail = privacy_get_system_ids();
1263 if (privacy_avail && g_slist_length(privacy_avail)) {
1264 privacy = (gchar *)(privacy_avail->data);
1268 if (privacy != NULL) {
1270 g_free(compose->privacy_system);
1271 compose->privacy_system = NULL;
1273 if (compose->privacy_system == NULL)
1274 compose->privacy_system = g_strdup(privacy);
1275 compose_update_privacy_system_menu_item(compose, FALSE);
1276 compose_use_signing(compose, TRUE);
1280 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1284 Compose *compose = NULL;
1286 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1288 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1289 cm_return_val_if_fail(msginfo != NULL, NULL);
1291 list_len = g_slist_length(msginfo_list);
1295 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1296 FALSE, prefs_common.default_reply_list, FALSE, body);
1298 case COMPOSE_REPLY_WITH_QUOTE:
1299 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1300 FALSE, prefs_common.default_reply_list, FALSE, body);
1302 case COMPOSE_REPLY_WITHOUT_QUOTE:
1303 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1304 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1306 case COMPOSE_REPLY_TO_SENDER:
1307 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1308 FALSE, FALSE, TRUE, body);
1310 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1311 compose = compose_followup_and_reply_to(msginfo,
1312 COMPOSE_QUOTE_CHECK,
1313 FALSE, FALSE, body);
1315 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1316 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1317 FALSE, FALSE, TRUE, body);
1319 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1320 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1321 FALSE, FALSE, TRUE, NULL);
1323 case COMPOSE_REPLY_TO_ALL:
1324 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1325 TRUE, FALSE, FALSE, body);
1327 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1328 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1329 TRUE, FALSE, FALSE, body);
1331 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1333 TRUE, FALSE, FALSE, NULL);
1335 case COMPOSE_REPLY_TO_LIST:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1337 FALSE, TRUE, FALSE, body);
1339 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1341 FALSE, TRUE, FALSE, body);
1343 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1345 FALSE, TRUE, FALSE, NULL);
1347 case COMPOSE_FORWARD:
1348 if (prefs_common.forward_as_attachment) {
1349 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1352 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1356 case COMPOSE_FORWARD_INLINE:
1357 /* check if we reply to more than one Message */
1358 if (list_len == 1) {
1359 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1362 /* more messages FALL THROUGH */
1363 case COMPOSE_FORWARD_AS_ATTACH:
1364 compose = compose_forward_multiple(NULL, msginfo_list);
1366 case COMPOSE_REDIRECT:
1367 compose = compose_redirect(NULL, msginfo, FALSE);
1370 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1373 if (compose == NULL) {
1374 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1378 compose->rmode = mode;
1379 switch (compose->rmode) {
1381 case COMPOSE_REPLY_WITH_QUOTE:
1382 case COMPOSE_REPLY_WITHOUT_QUOTE:
1383 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1384 debug_print("reply mode Normal\n");
1385 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1386 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1388 case COMPOSE_REPLY_TO_SENDER:
1389 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1390 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1391 debug_print("reply mode Sender\n");
1392 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1394 case COMPOSE_REPLY_TO_ALL:
1395 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1396 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1397 debug_print("reply mode All\n");
1398 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1400 case COMPOSE_REPLY_TO_LIST:
1401 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1402 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1403 debug_print("reply mode List\n");
1404 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1412 static Compose *compose_reply(MsgInfo *msginfo,
1413 ComposeQuoteMode quote_mode,
1419 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1420 to_sender, FALSE, body);
1423 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1424 ComposeQuoteMode quote_mode,
1429 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1430 to_sender, TRUE, body);
1433 static void compose_extract_original_charset(Compose *compose)
1435 MsgInfo *info = NULL;
1436 if (compose->replyinfo) {
1437 info = compose->replyinfo;
1438 } else if (compose->fwdinfo) {
1439 info = compose->fwdinfo;
1440 } else if (compose->targetinfo) {
1441 info = compose->targetinfo;
1444 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1445 MimeInfo *partinfo = mimeinfo;
1446 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1447 partinfo = procmime_mimeinfo_next(partinfo);
1449 compose->orig_charset =
1450 g_strdup(procmime_mimeinfo_get_parameter(
1451 partinfo, "charset"));
1453 procmime_mimeinfo_free_all(mimeinfo);
1457 #define SIGNAL_BLOCK(buffer) { \
1458 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1459 G_CALLBACK(compose_changed_cb), \
1461 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1462 G_CALLBACK(text_inserted), \
1466 #define SIGNAL_UNBLOCK(buffer) { \
1467 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1468 G_CALLBACK(compose_changed_cb), \
1470 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1471 G_CALLBACK(text_inserted), \
1475 static Compose *compose_generic_reply(MsgInfo *msginfo,
1476 ComposeQuoteMode quote_mode,
1477 gboolean to_all, gboolean to_ml,
1479 gboolean followup_and_reply_to,
1483 PrefsAccount *account = NULL;
1484 GtkTextView *textview;
1485 GtkTextBuffer *textbuf;
1486 gboolean quote = FALSE;
1487 const gchar *qmark = NULL;
1488 const gchar *body_fmt = NULL;
1489 gchar *s_system = NULL;
1491 cm_return_val_if_fail(msginfo != NULL, NULL);
1492 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1494 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1496 cm_return_val_if_fail(account != NULL, NULL);
1498 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1500 compose->updating = TRUE;
1502 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1503 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1505 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1506 if (!compose->replyinfo)
1507 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1509 compose_extract_original_charset(compose);
1511 if (msginfo->folder && msginfo->folder->ret_rcpt)
1512 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1514 /* Set save folder */
1515 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1516 gchar *folderidentifier;
1518 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1519 folderidentifier = folder_item_get_identifier(msginfo->folder);
1520 compose_set_save_to(compose, folderidentifier);
1521 g_free(folderidentifier);
1524 if (compose_parse_header(compose, msginfo) < 0) {
1525 compose->updating = FALSE;
1526 compose_destroy(compose);
1530 /* override from name according to folder properties */
1531 if (msginfo->folder && msginfo->folder->prefs &&
1532 msginfo->folder->prefs->reply_with_format &&
1533 msginfo->folder->prefs->reply_override_from_format &&
1534 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1539 /* decode \-escape sequences in the internal representation of the quote format */
1540 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1541 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1544 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1545 compose->gtkaspell);
1547 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1549 quote_fmt_scan_string(tmp);
1552 buf = quote_fmt_get_buffer();
1554 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1556 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1557 quote_fmt_reset_vartable();
1562 textview = (GTK_TEXT_VIEW(compose->text));
1563 textbuf = gtk_text_view_get_buffer(textview);
1564 compose_create_tags(textview, compose);
1566 undo_block(compose->undostruct);
1568 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1571 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1572 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1573 /* use the reply format of folder (if enabled), or the account's one
1574 (if enabled) or fallback to the global reply format, which is always
1575 enabled (even if empty), and use the relevant quotemark */
1577 if (msginfo->folder && msginfo->folder->prefs &&
1578 msginfo->folder->prefs->reply_with_format) {
1579 qmark = msginfo->folder->prefs->reply_quotemark;
1580 body_fmt = msginfo->folder->prefs->reply_body_format;
1582 } else if (account->reply_with_format) {
1583 qmark = account->reply_quotemark;
1584 body_fmt = account->reply_body_format;
1587 qmark = prefs_common.quotemark;
1588 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1589 body_fmt = gettext(prefs_common.quotefmt);
1596 /* empty quotemark is not allowed */
1597 if (qmark == NULL || *qmark == '\0')
1599 compose_quote_fmt(compose, compose->replyinfo,
1600 body_fmt, qmark, body, FALSE, TRUE,
1601 _("The body of the \"Reply\" template has an error at line %d."));
1602 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1603 quote_fmt_reset_vartable();
1606 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1607 compose_force_encryption(compose, account, FALSE, s_system);
1610 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1611 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1612 compose_force_signing(compose, account, s_system);
1616 SIGNAL_BLOCK(textbuf);
1618 if (account->auto_sig)
1619 compose_insert_sig(compose, FALSE);
1621 compose_wrap_all(compose);
1623 SIGNAL_UNBLOCK(textbuf);
1625 gtk_widget_grab_focus(compose->text);
1627 undo_unblock(compose->undostruct);
1629 if (prefs_common.auto_exteditor)
1630 compose_exec_ext_editor(compose);
1632 compose->modified = FALSE;
1633 compose_set_title(compose);
1635 compose->updating = FALSE;
1636 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1637 SCROLL_TO_CURSOR(compose);
1639 if (compose->deferred_destroy) {
1640 compose_destroy(compose);
1647 #define INSERT_FW_HEADER(var, hdr) \
1648 if (msginfo->var && *msginfo->var) { \
1649 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1650 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1651 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1654 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1655 gboolean as_attach, const gchar *body,
1656 gboolean no_extedit,
1660 GtkTextView *textview;
1661 GtkTextBuffer *textbuf;
1664 cm_return_val_if_fail(msginfo != NULL, NULL);
1665 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1668 !(account = compose_guess_forward_account_from_msginfo
1670 account = cur_account;
1672 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1674 compose->updating = TRUE;
1675 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1676 if (!compose->fwdinfo)
1677 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1679 compose_extract_original_charset(compose);
1681 if (msginfo->subject && *msginfo->subject) {
1682 gchar *buf, *buf2, *p;
1684 buf = p = g_strdup(msginfo->subject);
1685 p += subject_get_prefix_length(p);
1686 memmove(buf, p, strlen(p) + 1);
1688 buf2 = g_strdup_printf("Fw: %s", buf);
1689 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1695 /* override from name according to folder properties */
1696 if (msginfo->folder && msginfo->folder->prefs &&
1697 msginfo->folder->prefs->forward_with_format &&
1698 msginfo->folder->prefs->forward_override_from_format &&
1699 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1703 MsgInfo *full_msginfo = NULL;
1706 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1708 full_msginfo = procmsg_msginfo_copy(msginfo);
1710 /* decode \-escape sequences in the internal representation of the quote format */
1711 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1712 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1715 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1716 compose->gtkaspell);
1718 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1720 quote_fmt_scan_string(tmp);
1723 buf = quote_fmt_get_buffer();
1725 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1727 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1728 quote_fmt_reset_vartable();
1731 procmsg_msginfo_free(full_msginfo);
1734 textview = GTK_TEXT_VIEW(compose->text);
1735 textbuf = gtk_text_view_get_buffer(textview);
1736 compose_create_tags(textview, compose);
1738 undo_block(compose->undostruct);
1742 msgfile = procmsg_get_message_file(msginfo);
1743 if (!is_file_exist(msgfile))
1744 g_warning("%s: file not exist\n", msgfile);
1746 compose_attach_append(compose, msgfile, msgfile,
1751 const gchar *qmark = NULL;
1752 const gchar *body_fmt = NULL;
1753 MsgInfo *full_msginfo;
1755 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1756 body_fmt = gettext(prefs_common.fw_quotefmt);
1760 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1762 full_msginfo = procmsg_msginfo_copy(msginfo);
1764 /* use the forward format of folder (if enabled), or the account's one
1765 (if enabled) or fallback to the global forward format, which is always
1766 enabled (even if empty), and use the relevant quotemark */
1767 if (msginfo->folder && msginfo->folder->prefs &&
1768 msginfo->folder->prefs->forward_with_format) {
1769 qmark = msginfo->folder->prefs->forward_quotemark;
1770 body_fmt = msginfo->folder->prefs->forward_body_format;
1772 } else if (account->forward_with_format) {
1773 qmark = account->forward_quotemark;
1774 body_fmt = account->forward_body_format;
1777 qmark = prefs_common.fw_quotemark;
1778 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1779 body_fmt = gettext(prefs_common.fw_quotefmt);
1784 /* empty quotemark is not allowed */
1785 if (qmark == NULL || *qmark == '\0')
1788 compose_quote_fmt(compose, full_msginfo,
1789 body_fmt, qmark, body, FALSE, TRUE,
1790 _("The body of the \"Forward\" template has an error at line %d."));
1791 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1792 quote_fmt_reset_vartable();
1793 compose_attach_parts(compose, msginfo);
1795 procmsg_msginfo_free(full_msginfo);
1798 SIGNAL_BLOCK(textbuf);
1800 if (account->auto_sig)
1801 compose_insert_sig(compose, FALSE);
1803 compose_wrap_all(compose);
1805 SIGNAL_UNBLOCK(textbuf);
1807 gtk_text_buffer_get_start_iter(textbuf, &iter);
1808 gtk_text_buffer_place_cursor(textbuf, &iter);
1810 gtk_widget_grab_focus(compose->header_last->entry);
1812 if (!no_extedit && prefs_common.auto_exteditor)
1813 compose_exec_ext_editor(compose);
1816 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1817 gchar *folderidentifier;
1819 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1820 folderidentifier = folder_item_get_identifier(msginfo->folder);
1821 compose_set_save_to(compose, folderidentifier);
1822 g_free(folderidentifier);
1825 undo_unblock(compose->undostruct);
1827 compose->modified = FALSE;
1828 compose_set_title(compose);
1830 compose->updating = FALSE;
1831 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1832 SCROLL_TO_CURSOR(compose);
1834 if (compose->deferred_destroy) {
1835 compose_destroy(compose);
1842 #undef INSERT_FW_HEADER
1844 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1847 GtkTextView *textview;
1848 GtkTextBuffer *textbuf;
1852 gboolean single_mail = TRUE;
1854 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1856 if (g_slist_length(msginfo_list) > 1)
1857 single_mail = FALSE;
1859 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1860 if (((MsgInfo *)msginfo->data)->folder == NULL)
1863 /* guess account from first selected message */
1865 !(account = compose_guess_forward_account_from_msginfo
1866 (msginfo_list->data)))
1867 account = cur_account;
1869 cm_return_val_if_fail(account != NULL, NULL);
1871 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1872 if (msginfo->data) {
1873 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1874 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1878 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1879 g_warning("no msginfo_list");
1883 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1885 compose->updating = TRUE;
1887 /* override from name according to folder properties */
1888 if (msginfo_list->data) {
1889 MsgInfo *msginfo = msginfo_list->data;
1891 if (msginfo->folder && msginfo->folder->prefs &&
1892 msginfo->folder->prefs->forward_with_format &&
1893 msginfo->folder->prefs->forward_override_from_format &&
1894 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1899 /* decode \-escape sequences in the internal representation of the quote format */
1900 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1901 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1904 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1905 compose->gtkaspell);
1907 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1909 quote_fmt_scan_string(tmp);
1912 buf = quote_fmt_get_buffer();
1914 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1916 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1917 quote_fmt_reset_vartable();
1923 textview = GTK_TEXT_VIEW(compose->text);
1924 textbuf = gtk_text_view_get_buffer(textview);
1925 compose_create_tags(textview, compose);
1927 undo_block(compose->undostruct);
1928 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1929 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1931 if (!is_file_exist(msgfile))
1932 g_warning("%s: file not exist\n", msgfile);
1934 compose_attach_append(compose, msgfile, msgfile,
1940 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1941 if (info->subject && *info->subject) {
1942 gchar *buf, *buf2, *p;
1944 buf = p = g_strdup(info->subject);
1945 p += subject_get_prefix_length(p);
1946 memmove(buf, p, strlen(p) + 1);
1948 buf2 = g_strdup_printf("Fw: %s", buf);
1949 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1955 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1956 _("Fw: multiple emails"));
1959 SIGNAL_BLOCK(textbuf);
1961 if (account->auto_sig)
1962 compose_insert_sig(compose, FALSE);
1964 compose_wrap_all(compose);
1966 SIGNAL_UNBLOCK(textbuf);
1968 gtk_text_buffer_get_start_iter(textbuf, &iter);
1969 gtk_text_buffer_place_cursor(textbuf, &iter);
1971 gtk_widget_grab_focus(compose->header_last->entry);
1972 undo_unblock(compose->undostruct);
1973 compose->modified = FALSE;
1974 compose_set_title(compose);
1976 compose->updating = FALSE;
1977 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1978 SCROLL_TO_CURSOR(compose);
1980 if (compose->deferred_destroy) {
1981 compose_destroy(compose);
1988 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1990 GtkTextIter start = *iter;
1991 GtkTextIter end_iter;
1992 int start_pos = gtk_text_iter_get_offset(&start);
1994 if (!compose->account->sig_sep)
1997 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1998 start_pos+strlen(compose->account->sig_sep));
2000 /* check sig separator */
2001 str = gtk_text_iter_get_text(&start, &end_iter);
2002 if (!strcmp(str, compose->account->sig_sep)) {
2004 /* check end of line (\n) */
2005 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2006 start_pos+strlen(compose->account->sig_sep));
2007 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2008 start_pos+strlen(compose->account->sig_sep)+1);
2009 tmp = gtk_text_iter_get_text(&start, &end_iter);
2010 if (!strcmp(tmp,"\n")) {
2022 static void compose_colorize_signature(Compose *compose)
2024 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2026 GtkTextIter end_iter;
2027 gtk_text_buffer_get_start_iter(buffer, &iter);
2028 while (gtk_text_iter_forward_line(&iter))
2029 if (compose_is_sig_separator(compose, buffer, &iter)) {
2030 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2031 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2035 #define BLOCK_WRAP() { \
2036 prev_autowrap = compose->autowrap; \
2037 buffer = gtk_text_view_get_buffer( \
2038 GTK_TEXT_VIEW(compose->text)); \
2039 compose->autowrap = FALSE; \
2041 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2042 G_CALLBACK(compose_changed_cb), \
2044 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2045 G_CALLBACK(text_inserted), \
2048 #define UNBLOCK_WRAP() { \
2049 compose->autowrap = prev_autowrap; \
2050 if (compose->autowrap) { \
2051 gint old = compose->draft_timeout_tag; \
2052 compose->draft_timeout_tag = -2; \
2053 compose_wrap_all(compose); \
2054 compose->draft_timeout_tag = old; \
2057 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2058 G_CALLBACK(compose_changed_cb), \
2060 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2061 G_CALLBACK(text_inserted), \
2065 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2067 Compose *compose = NULL;
2068 PrefsAccount *account = NULL;
2069 GtkTextView *textview;
2070 GtkTextBuffer *textbuf;
2074 gchar buf[BUFFSIZE];
2075 gboolean use_signing = FALSE;
2076 gboolean use_encryption = FALSE;
2077 gchar *privacy_system = NULL;
2078 int priority = PRIORITY_NORMAL;
2079 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2080 gboolean autowrap = prefs_common.autowrap;
2081 gboolean autoindent = prefs_common.auto_indent;
2083 cm_return_val_if_fail(msginfo != NULL, NULL);
2084 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2086 if (compose_put_existing_to_front(msginfo)) {
2090 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2091 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2092 gchar queueheader_buf[BUFFSIZE];
2095 /* Select Account from queue headers */
2096 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2097 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2098 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2099 account = account_find_from_id(id);
2101 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2102 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2103 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2104 account = account_find_from_id(id);
2106 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2107 sizeof(queueheader_buf), "NAID:")) {
2108 id = atoi(&queueheader_buf[strlen("NAID:")]);
2109 account = account_find_from_id(id);
2111 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2112 sizeof(queueheader_buf), "MAID:")) {
2113 id = atoi(&queueheader_buf[strlen("MAID:")]);
2114 account = account_find_from_id(id);
2116 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2117 sizeof(queueheader_buf), "S:")) {
2118 account = account_find_from_address(queueheader_buf, FALSE);
2120 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2121 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2122 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2123 use_signing = param;
2126 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2127 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2128 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2129 use_signing = param;
2132 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2133 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2134 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2135 use_encryption = param;
2137 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2138 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2139 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2140 use_encryption = param;
2142 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2143 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2144 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2147 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2148 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2149 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2152 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2153 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2154 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2156 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2157 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2158 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2160 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2161 sizeof(queueheader_buf), "X-Priority: ")) {
2162 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2165 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2166 sizeof(queueheader_buf), "RMID:")) {
2167 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2168 if (tokens[0] && tokens[1] && tokens[2]) {
2169 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2170 if (orig_item != NULL) {
2171 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2176 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2177 sizeof(queueheader_buf), "FMID:")) {
2178 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2179 if (tokens[0] && tokens[1] && tokens[2]) {
2180 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2181 if (orig_item != NULL) {
2182 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2188 account = msginfo->folder->folder->account;
2191 if (!account && prefs_common.reedit_account_autosel) {
2192 gchar from[BUFFSIZE];
2193 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2194 extract_address(from);
2195 account = account_find_from_address(from, FALSE);
2199 account = cur_account;
2201 cm_return_val_if_fail(account != NULL, NULL);
2203 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2205 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2206 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2207 compose->autowrap = autowrap;
2208 compose->replyinfo = replyinfo;
2209 compose->fwdinfo = fwdinfo;
2211 compose->updating = TRUE;
2212 compose->priority = priority;
2214 if (privacy_system != NULL) {
2215 compose->privacy_system = privacy_system;
2216 compose_use_signing(compose, use_signing);
2217 compose_use_encryption(compose, use_encryption);
2218 compose_update_privacy_system_menu_item(compose, FALSE);
2220 activate_privacy_system(compose, account, FALSE);
2223 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2225 compose_extract_original_charset(compose);
2227 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2228 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2229 gchar queueheader_buf[BUFFSIZE];
2231 /* Set message save folder */
2232 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2233 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2234 compose_set_save_to(compose, &queueheader_buf[4]);
2236 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2237 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2239 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2244 if (compose_parse_header(compose, msginfo) < 0) {
2245 compose->updating = FALSE;
2246 compose_destroy(compose);
2249 compose_reedit_set_entry(compose, msginfo);
2251 textview = GTK_TEXT_VIEW(compose->text);
2252 textbuf = gtk_text_view_get_buffer(textview);
2253 compose_create_tags(textview, compose);
2255 mark = gtk_text_buffer_get_insert(textbuf);
2256 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2258 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2259 G_CALLBACK(compose_changed_cb),
2262 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2263 fp = procmime_get_first_encrypted_text_content(msginfo);
2265 compose_force_encryption(compose, account, TRUE, NULL);
2268 fp = procmime_get_first_text_content(msginfo);
2271 g_warning("Can't get text part\n");
2275 gboolean prev_autowrap = compose->autowrap;
2276 GtkTextBuffer *buffer = textbuf;
2278 while (fgets(buf, sizeof(buf), fp) != NULL) {
2280 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2286 compose_attach_parts(compose, msginfo);
2288 compose_colorize_signature(compose);
2290 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2291 G_CALLBACK(compose_changed_cb),
2294 gtk_widget_grab_focus(compose->text);
2296 if (prefs_common.auto_exteditor) {
2297 compose_exec_ext_editor(compose);
2299 compose->modified = FALSE;
2300 compose_set_title(compose);
2302 compose->updating = FALSE;
2303 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2304 SCROLL_TO_CURSOR(compose);
2306 if (compose->deferred_destroy) {
2307 compose_destroy(compose);
2311 compose->sig_str = account_get_signature_str(compose->account);
2316 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2323 cm_return_val_if_fail(msginfo != NULL, NULL);
2326 account = account_get_reply_account(msginfo,
2327 prefs_common.reply_account_autosel);
2328 cm_return_val_if_fail(account != NULL, NULL);
2330 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2332 compose->updating = TRUE;
2334 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2335 compose->replyinfo = NULL;
2336 compose->fwdinfo = NULL;
2338 compose_show_first_last_header(compose, TRUE);
2340 gtk_widget_grab_focus(compose->header_last->entry);
2342 filename = procmsg_get_message_file(msginfo);
2344 if (filename == NULL) {
2345 compose->updating = FALSE;
2346 compose_destroy(compose);
2351 compose->redirect_filename = filename;
2353 /* Set save folder */
2354 item = msginfo->folder;
2355 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2356 gchar *folderidentifier;
2358 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2359 folderidentifier = folder_item_get_identifier(item);
2360 compose_set_save_to(compose, folderidentifier);
2361 g_free(folderidentifier);
2364 compose_attach_parts(compose, msginfo);
2366 if (msginfo->subject)
2367 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2369 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2371 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2372 _("The body of the \"Redirect\" template has an error at line %d."));
2373 quote_fmt_reset_vartable();
2374 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2376 compose_colorize_signature(compose);
2379 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2380 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2381 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2383 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2384 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2385 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2386 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2387 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2388 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2389 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2390 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2392 if (compose->toolbar->draft_btn)
2393 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2394 if (compose->toolbar->insert_btn)
2395 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2396 if (compose->toolbar->attach_btn)
2397 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2398 if (compose->toolbar->sig_btn)
2399 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2400 if (compose->toolbar->exteditor_btn)
2401 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2402 if (compose->toolbar->linewrap_current_btn)
2403 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2404 if (compose->toolbar->linewrap_all_btn)
2405 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2407 compose->modified = FALSE;
2408 compose_set_title(compose);
2409 compose->updating = FALSE;
2410 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2411 SCROLL_TO_CURSOR(compose);
2413 if (compose->deferred_destroy) {
2414 compose_destroy(compose);
2421 GList *compose_get_compose_list(void)
2423 return compose_list;
2426 void compose_entry_append(Compose *compose, const gchar *address,
2427 ComposeEntryType type, ComposePrefType pref_type)
2429 const gchar *header;
2431 gboolean in_quote = FALSE;
2432 if (!address || *address == '\0') return;
2439 header = N_("Bcc:");
2441 case COMPOSE_REPLYTO:
2442 header = N_("Reply-To:");
2444 case COMPOSE_NEWSGROUPS:
2445 header = N_("Newsgroups:");
2447 case COMPOSE_FOLLOWUPTO:
2448 header = N_( "Followup-To:");
2455 header = prefs_common_translated_header_name(header);
2457 cur = begin = (gchar *)address;
2459 /* we separate the line by commas, but not if we're inside a quoted
2461 while (*cur != '\0') {
2463 in_quote = !in_quote;
2464 if (*cur == ',' && !in_quote) {
2465 gchar *tmp = g_strdup(begin);
2467 tmp[cur-begin]='\0';
2470 while (*tmp == ' ' || *tmp == '\t')
2472 compose_add_header_entry(compose, header, tmp, pref_type);
2479 gchar *tmp = g_strdup(begin);
2481 tmp[cur-begin]='\0';
2484 while (*tmp == ' ' || *tmp == '\t')
2486 compose_add_header_entry(compose, header, tmp, pref_type);
2491 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2493 static GdkColor yellow;
2494 static GdkColor black;
2495 static gboolean yellow_initialised = FALSE;
2499 if (!yellow_initialised) {
2500 gdk_color_parse("#f5f6be", &yellow);
2501 gdk_color_parse("#000000", &black);
2502 yellow_initialised = gdk_colormap_alloc_color(
2503 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2504 yellow_initialised &= gdk_colormap_alloc_color(
2505 gdk_colormap_get_system(), &black, FALSE, TRUE);
2508 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2509 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2510 if (gtk_entry_get_text(entry) &&
2511 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2512 if (yellow_initialised) {
2513 gtk_widget_modify_base(
2514 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2515 GTK_STATE_NORMAL, &yellow);
2516 gtk_widget_modify_text(
2517 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2518 GTK_STATE_NORMAL, &black);
2524 void compose_toolbar_cb(gint action, gpointer data)
2526 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2527 Compose *compose = (Compose*)toolbar_item->parent;
2529 cm_return_if_fail(compose != NULL);
2533 compose_send_cb(NULL, compose);
2536 compose_send_later_cb(NULL, compose);
2539 compose_draft(compose, COMPOSE_QUIT_EDITING);
2542 compose_insert_file_cb(NULL, compose);
2545 compose_attach_cb(NULL, compose);
2548 compose_insert_sig(compose, FALSE);
2551 compose_ext_editor_cb(NULL, compose);
2553 case A_LINEWRAP_CURRENT:
2554 compose_beautify_paragraph(compose, NULL, TRUE);
2556 case A_LINEWRAP_ALL:
2557 compose_wrap_all_full(compose, TRUE);
2560 compose_address_cb(NULL, compose);
2563 case A_CHECK_SPELLING:
2564 compose_check_all(NULL, compose);
2572 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2577 gchar *subject = NULL;
2581 gchar **attach = NULL;
2582 MailField mfield = NO_FIELD_PRESENT;
2584 /* get mailto parts but skip from */
2585 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2588 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2589 mfield = TO_FIELD_PRESENT;
2592 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2594 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2596 if (!g_utf8_validate (subject, -1, NULL)) {
2597 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2598 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2601 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2603 mfield = SUBJECT_FIELD_PRESENT;
2606 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2607 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2610 gboolean prev_autowrap = compose->autowrap;
2612 compose->autowrap = FALSE;
2614 mark = gtk_text_buffer_get_insert(buffer);
2615 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2617 if (!g_utf8_validate (body, -1, NULL)) {
2618 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2619 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2622 gtk_text_buffer_insert(buffer, &iter, body, -1);
2624 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2626 compose->autowrap = prev_autowrap;
2627 if (compose->autowrap)
2628 compose_wrap_all(compose);
2629 mfield = BODY_FIELD_PRESENT;
2633 gint i = 0, att = 0;
2634 gchar *warn_files = NULL;
2635 while (attach[i] != NULL) {
2636 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2637 if (utf8_filename) {
2638 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2639 gchar *tmp = g_strdup_printf("%s%s\n",
2640 warn_files?warn_files:"",
2646 g_free(utf8_filename);
2648 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2653 alertpanel_notice(ngettext(
2654 "The following file has been attached: \n%s",
2655 "The following files have been attached: \n%s", att), warn_files);
2669 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2671 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2672 {"Cc:", NULL, TRUE},
2673 {"References:", NULL, FALSE},
2674 {"Bcc:", NULL, TRUE},
2675 {"Newsgroups:", NULL, TRUE},
2676 {"Followup-To:", NULL, TRUE},
2677 {"List-Post:", NULL, FALSE},
2678 {"X-Priority:", NULL, FALSE},
2679 {NULL, NULL, FALSE}};
2695 cm_return_val_if_fail(msginfo != NULL, -1);
2697 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2698 procheader_get_header_fields(fp, hentry);
2701 if (hentry[H_REPLY_TO].body != NULL) {
2702 if (hentry[H_REPLY_TO].body[0] != '\0') {
2704 conv_unmime_header(hentry[H_REPLY_TO].body,
2707 g_free(hentry[H_REPLY_TO].body);
2708 hentry[H_REPLY_TO].body = NULL;
2710 if (hentry[H_CC].body != NULL) {
2711 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2712 g_free(hentry[H_CC].body);
2713 hentry[H_CC].body = NULL;
2715 if (hentry[H_REFERENCES].body != NULL) {
2716 if (compose->mode == COMPOSE_REEDIT)
2717 compose->references = hentry[H_REFERENCES].body;
2719 compose->references = compose_parse_references
2720 (hentry[H_REFERENCES].body, msginfo->msgid);
2721 g_free(hentry[H_REFERENCES].body);
2723 hentry[H_REFERENCES].body = NULL;
2725 if (hentry[H_BCC].body != NULL) {
2726 if (compose->mode == COMPOSE_REEDIT)
2728 conv_unmime_header(hentry[H_BCC].body, NULL);
2729 g_free(hentry[H_BCC].body);
2730 hentry[H_BCC].body = NULL;
2732 if (hentry[H_NEWSGROUPS].body != NULL) {
2733 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2734 hentry[H_NEWSGROUPS].body = NULL;
2736 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2737 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2738 compose->followup_to =
2739 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2742 g_free(hentry[H_FOLLOWUP_TO].body);
2743 hentry[H_FOLLOWUP_TO].body = NULL;
2745 if (hentry[H_LIST_POST].body != NULL) {
2748 extract_address(hentry[H_LIST_POST].body);
2749 if (hentry[H_LIST_POST].body[0] != '\0') {
2750 scan_mailto_url(hentry[H_LIST_POST].body,
2751 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2753 g_free(compose->ml_post);
2754 compose->ml_post = to;
2757 g_free(hentry[H_LIST_POST].body);
2758 hentry[H_LIST_POST].body = NULL;
2761 /* CLAWS - X-Priority */
2762 if (compose->mode == COMPOSE_REEDIT)
2763 if (hentry[H_X_PRIORITY].body != NULL) {
2766 priority = atoi(hentry[H_X_PRIORITY].body);
2767 g_free(hentry[H_X_PRIORITY].body);
2769 hentry[H_X_PRIORITY].body = NULL;
2771 if (priority < PRIORITY_HIGHEST ||
2772 priority > PRIORITY_LOWEST)
2773 priority = PRIORITY_NORMAL;
2775 compose->priority = priority;
2778 if (compose->mode == COMPOSE_REEDIT) {
2779 if (msginfo->inreplyto && *msginfo->inreplyto)
2780 compose->inreplyto = g_strdup(msginfo->inreplyto);
2784 if (msginfo->msgid && *msginfo->msgid)
2785 compose->inreplyto = g_strdup(msginfo->msgid);
2787 if (!compose->references) {
2788 if (msginfo->msgid && *msginfo->msgid) {
2789 if (msginfo->inreplyto && *msginfo->inreplyto)
2790 compose->references =
2791 g_strdup_printf("<%s>\n\t<%s>",
2795 compose->references =
2796 g_strconcat("<", msginfo->msgid, ">",
2798 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2799 compose->references =
2800 g_strconcat("<", msginfo->inreplyto, ">",
2808 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2810 GSList *ref_id_list, *cur;
2814 ref_id_list = references_list_append(NULL, ref);
2815 if (!ref_id_list) return NULL;
2816 if (msgid && *msgid)
2817 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2822 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2823 /* "<" + Message-ID + ">" + CR+LF+TAB */
2824 len += strlen((gchar *)cur->data) + 5;
2826 if (len > MAX_REFERENCES_LEN) {
2827 /* remove second message-ID */
2828 if (ref_id_list && ref_id_list->next &&
2829 ref_id_list->next->next) {
2830 g_free(ref_id_list->next->data);
2831 ref_id_list = g_slist_remove
2832 (ref_id_list, ref_id_list->next->data);
2834 slist_free_strings(ref_id_list);
2835 g_slist_free(ref_id_list);
2842 new_ref = g_string_new("");
2843 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2844 if (new_ref->len > 0)
2845 g_string_append(new_ref, "\n\t");
2846 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2849 slist_free_strings(ref_id_list);
2850 g_slist_free(ref_id_list);
2852 new_ref_str = new_ref->str;
2853 g_string_free(new_ref, FALSE);
2858 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2859 const gchar *fmt, const gchar *qmark,
2860 const gchar *body, gboolean rewrap,
2861 gboolean need_unescape,
2862 const gchar *err_msg)
2864 MsgInfo* dummyinfo = NULL;
2865 gchar *quote_str = NULL;
2867 gboolean prev_autowrap;
2868 const gchar *trimmed_body = body;
2869 gint cursor_pos = -1;
2870 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2871 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2876 SIGNAL_BLOCK(buffer);
2879 dummyinfo = compose_msginfo_new_from_compose(compose);
2880 msginfo = dummyinfo;
2883 if (qmark != NULL) {
2885 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2886 compose->gtkaspell);
2888 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2890 quote_fmt_scan_string(qmark);
2893 buf = quote_fmt_get_buffer();
2895 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2897 Xstrdup_a(quote_str, buf, goto error)
2900 if (fmt && *fmt != '\0') {
2903 while (*trimmed_body == '\n')
2907 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2908 compose->gtkaspell);
2910 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2912 if (need_unescape) {
2915 /* decode \-escape sequences in the internal representation of the quote format */
2916 tmp = malloc(strlen(fmt)+1);
2917 pref_get_unescaped_pref(tmp, fmt);
2918 quote_fmt_scan_string(tmp);
2922 quote_fmt_scan_string(fmt);
2926 buf = quote_fmt_get_buffer();
2928 gint line = quote_fmt_get_line();
2929 alertpanel_error(err_msg, line);
2935 prev_autowrap = compose->autowrap;
2936 compose->autowrap = FALSE;
2938 mark = gtk_text_buffer_get_insert(buffer);
2939 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2940 if (g_utf8_validate(buf, -1, NULL)) {
2941 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2943 gchar *tmpout = NULL;
2944 tmpout = conv_codeset_strdup
2945 (buf, conv_get_locale_charset_str_no_utf8(),
2947 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2949 tmpout = g_malloc(strlen(buf)*2+1);
2950 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2952 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2956 cursor_pos = quote_fmt_get_cursor_pos();
2957 if (cursor_pos == -1)
2958 cursor_pos = gtk_text_iter_get_offset(&iter);
2959 compose->set_cursor_pos = cursor_pos;
2961 gtk_text_buffer_get_start_iter(buffer, &iter);
2962 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2963 gtk_text_buffer_place_cursor(buffer, &iter);
2965 compose->autowrap = prev_autowrap;
2966 if (compose->autowrap && rewrap)
2967 compose_wrap_all(compose);
2974 SIGNAL_UNBLOCK(buffer);
2976 procmsg_msginfo_free( dummyinfo );
2981 /* if ml_post is of type addr@host and from is of type
2982 * addr-anything@host, return TRUE
2984 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2986 gchar *left_ml = NULL;
2987 gchar *right_ml = NULL;
2988 gchar *left_from = NULL;
2989 gchar *right_from = NULL;
2990 gboolean result = FALSE;
2992 if (!ml_post || !from)
2995 left_ml = g_strdup(ml_post);
2996 if (strstr(left_ml, "@")) {
2997 right_ml = strstr(left_ml, "@")+1;
2998 *(strstr(left_ml, "@")) = '\0';
3001 left_from = g_strdup(from);
3002 if (strstr(left_from, "@")) {
3003 right_from = strstr(left_from, "@")+1;
3004 *(strstr(left_from, "@")) = '\0';
3007 if (left_ml && left_from && right_ml && right_from
3008 && !strncmp(left_from, left_ml, strlen(left_ml))
3009 && !strcmp(right_from, right_ml)) {
3018 static gboolean same_address(const gchar *addr1, const gchar *addr2)
3020 gchar *my_addr1, *my_addr2;
3022 if (!addr1 || !addr2)
3025 Xstrdup_a(my_addr1, addr1, return FALSE);
3026 Xstrdup_a(my_addr2, addr2, return FALSE);
3028 extract_address(my_addr1);
3029 extract_address(my_addr2);
3031 return !strcasecmp(my_addr1, my_addr2);
3034 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3035 gboolean to_all, gboolean to_ml,
3037 gboolean followup_and_reply_to)
3039 GSList *cc_list = NULL;
3042 gchar *replyto = NULL;
3043 GHashTable *to_table;
3045 gboolean reply_to_ml = FALSE;
3046 gboolean default_reply_to = FALSE;
3048 cm_return_if_fail(compose->account != NULL);
3049 cm_return_if_fail(msginfo != NULL);
3051 reply_to_ml = to_ml && compose->ml_post;
3053 default_reply_to = msginfo->folder &&
3054 msginfo->folder->prefs->enable_default_reply_to;
3056 if (compose->account->protocol != A_NNTP) {
3057 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3058 if (msginfo->folder->prefs->enable_default_replyto) {
3059 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3060 COMPOSE_REPLYTO, PREF_FOLDER);
3062 if (msginfo->folder->prefs->enable_default_bcc) {
3063 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3064 COMPOSE_BCC, PREF_FOLDER);
3066 if (msginfo->folder->prefs->enable_default_cc) {
3067 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3068 COMPOSE_CC, PREF_FOLDER);
3071 if (reply_to_ml && !default_reply_to) {
3073 gboolean is_subscr = is_subscription(compose->ml_post,
3076 /* normal answer to ml post with a reply-to */
3077 compose_entry_append(compose,
3079 COMPOSE_TO, PREF_ML);
3080 if (compose->replyto
3081 && !same_address(compose->ml_post, compose->replyto))
3082 compose_entry_append(compose,
3084 COMPOSE_CC, PREF_ML);
3086 /* answer to subscription confirmation */
3087 if (compose->replyto)
3088 compose_entry_append(compose,
3090 COMPOSE_TO, PREF_ML);
3091 else if (msginfo->from)
3092 compose_entry_append(compose,
3094 COMPOSE_TO, PREF_ML);
3097 else if (!(to_all || to_sender) && default_reply_to) {
3098 compose_entry_append(compose,
3099 msginfo->folder->prefs->default_reply_to,
3100 COMPOSE_TO, PREF_FOLDER);
3101 compose_entry_mark_default_to(compose,
3102 msginfo->folder->prefs->default_reply_to);
3107 Xstrdup_a(tmp1, msginfo->from, return);
3108 extract_address(tmp1);
3109 if (to_all || to_sender ||
3110 !account_find_from_address(tmp1, FALSE))
3111 compose_entry_append(compose,
3112 (compose->replyto && !to_sender)
3113 ? compose->replyto :
3114 msginfo->from ? msginfo->from : "",
3115 COMPOSE_TO, PREF_NONE);
3116 else if (!to_all && !to_sender) {
3117 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3118 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3119 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3120 if (compose->replyto) {
3121 compose_entry_append(compose,
3123 COMPOSE_TO, PREF_NONE);
3125 compose_entry_append(compose,
3126 msginfo->from ? msginfo->from : "",
3127 COMPOSE_TO, PREF_NONE);
3130 /* replying to own mail, use original recp */
3131 compose_entry_append(compose,
3132 msginfo->to ? msginfo->to : "",
3133 COMPOSE_TO, PREF_NONE);
3134 compose_entry_append(compose,
3135 msginfo->cc ? msginfo->cc : "",
3136 COMPOSE_CC, PREF_NONE);
3141 if (to_sender || (compose->followup_to &&
3142 !strncmp(compose->followup_to, "poster", 6)))
3143 compose_entry_append
3145 (compose->replyto ? compose->replyto :
3146 msginfo->from ? msginfo->from : ""),
3147 COMPOSE_TO, PREF_NONE);
3149 else if (followup_and_reply_to || to_all) {
3150 compose_entry_append
3152 (compose->replyto ? compose->replyto :
3153 msginfo->from ? msginfo->from : ""),
3154 COMPOSE_TO, PREF_NONE);
3156 compose_entry_append
3158 compose->followup_to ? compose->followup_to :
3159 compose->newsgroups ? compose->newsgroups : "",
3160 COMPOSE_NEWSGROUPS, PREF_NONE);
3163 compose_entry_append
3165 compose->followup_to ? compose->followup_to :
3166 compose->newsgroups ? compose->newsgroups : "",
3167 COMPOSE_NEWSGROUPS, PREF_NONE);
3170 if (msginfo->subject && *msginfo->subject) {
3174 buf = p = g_strdup(msginfo->subject);
3175 p += subject_get_prefix_length(p);
3176 memmove(buf, p, strlen(p) + 1);
3178 buf2 = g_strdup_printf("Re: %s", buf);
3179 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3184 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3186 if (to_ml && compose->ml_post) return;
3187 if (!to_all || compose->account->protocol == A_NNTP) return;
3189 if (compose->replyto) {
3190 Xstrdup_a(replyto, compose->replyto, return);
3191 extract_address(replyto);
3193 if (msginfo->from) {
3194 Xstrdup_a(from, msginfo->from, return);
3195 extract_address(from);
3198 if (replyto && from)
3199 cc_list = address_list_append_with_comments(cc_list, from);
3200 if (to_all && msginfo->folder &&
3201 msginfo->folder->prefs->enable_default_reply_to)
3202 cc_list = address_list_append_with_comments(cc_list,
3203 msginfo->folder->prefs->default_reply_to);
3204 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3205 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3207 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3209 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3210 if (compose->account) {
3211 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3212 GINT_TO_POINTER(1));
3214 /* remove address on To: and that of current account */
3215 for (cur = cc_list; cur != NULL; ) {
3216 GSList *next = cur->next;
3219 addr = g_utf8_strdown(cur->data, -1);
3220 extract_address(addr);
3222 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3223 cc_list = g_slist_remove(cc_list, cur->data);
3225 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3229 hash_free_strings(to_table);
3230 g_hash_table_destroy(to_table);
3233 for (cur = cc_list; cur != NULL; cur = cur->next)
3234 compose_entry_append(compose, (gchar *)cur->data,
3235 COMPOSE_CC, PREF_NONE);
3236 slist_free_strings(cc_list);
3237 g_slist_free(cc_list);
3242 #define SET_ENTRY(entry, str) \
3245 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3248 #define SET_ADDRESS(type, str) \
3251 compose_entry_append(compose, str, type, PREF_NONE); \
3254 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3256 cm_return_if_fail(msginfo != NULL);
3258 SET_ENTRY(subject_entry, msginfo->subject);
3259 SET_ENTRY(from_name, msginfo->from);
3260 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3261 SET_ADDRESS(COMPOSE_CC, compose->cc);
3262 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3263 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3264 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3265 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3267 compose_update_priority_menu_item(compose);
3268 compose_update_privacy_system_menu_item(compose, FALSE);
3269 compose_show_first_last_header(compose, TRUE);
3275 static void compose_insert_sig(Compose *compose, gboolean replace)
3277 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3278 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3280 GtkTextIter iter, iter_end;
3281 gint cur_pos, ins_pos;
3282 gboolean prev_autowrap;
3283 gboolean found = FALSE;
3284 gboolean exists = FALSE;
3286 cm_return_if_fail(compose->account != NULL);
3290 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3291 G_CALLBACK(compose_changed_cb),
3294 mark = gtk_text_buffer_get_insert(buffer);
3295 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3296 cur_pos = gtk_text_iter_get_offset (&iter);
3299 gtk_text_buffer_get_end_iter(buffer, &iter);
3301 exists = (compose->sig_str != NULL);
3304 GtkTextIter first_iter, start_iter, end_iter;
3306 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3308 if (!exists || compose->sig_str[0] == '\0')
3311 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3312 compose->signature_tag);
3315 /* include previous \n\n */
3316 gtk_text_iter_backward_chars(&first_iter, 1);
3317 start_iter = first_iter;
3318 end_iter = first_iter;
3320 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3321 compose->signature_tag);
3322 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3323 compose->signature_tag);
3325 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3331 g_free(compose->sig_str);
3332 compose->sig_str = account_get_signature_str(compose->account);
3334 cur_pos = gtk_text_iter_get_offset(&iter);
3336 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3337 g_free(compose->sig_str);
3338 compose->sig_str = NULL;
3340 if (compose->sig_inserted == FALSE)
3341 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3342 compose->sig_inserted = TRUE;
3344 cur_pos = gtk_text_iter_get_offset(&iter);
3345 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3347 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3348 gtk_text_iter_forward_chars(&iter, 1);
3349 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3350 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3352 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3353 cur_pos = gtk_text_buffer_get_char_count (buffer);
3356 /* put the cursor where it should be
3357 * either where the quote_fmt says, either where it was */
3358 if (compose->set_cursor_pos < 0)
3359 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3361 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3362 compose->set_cursor_pos);
3364 compose->set_cursor_pos = -1;
3365 gtk_text_buffer_place_cursor(buffer, &iter);
3366 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3367 G_CALLBACK(compose_changed_cb),
3373 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3376 GtkTextBuffer *buffer;
3379 const gchar *cur_encoding;
3380 gchar buf[BUFFSIZE];
3383 gboolean prev_autowrap;
3384 gboolean badtxt = FALSE;
3385 struct stat file_stat;
3388 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3390 /* get the size of the file we are about to insert */
3391 ret = g_stat(file, &file_stat);
3393 gchar *shortfile = g_path_get_basename(file);
3394 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3396 return COMPOSE_INSERT_NO_FILE;
3397 } else if (prefs_common.warn_large_insert == TRUE) {
3399 /* ask user for confirmation if the file is large */
3400 if (prefs_common.warn_large_insert_size < 0 ||
3401 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3405 msg = g_strdup_printf(_("You are about to insert a file of %s "
3406 "in the message body. Are you sure you want to do that?"),
3407 to_human_readable(file_stat.st_size));
3408 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3409 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3412 /* do we ask for confirmation next time? */
3413 if (aval & G_ALERTDISABLE) {
3414 /* no confirmation next time, disable feature in preferences */
3415 aval &= ~G_ALERTDISABLE;
3416 prefs_common.warn_large_insert = FALSE;
3419 /* abort file insertion if user canceled action */
3420 if (aval != G_ALERTALTERNATE) {
3421 return COMPOSE_INSERT_NO_FILE;
3427 if ((fp = g_fopen(file, "rb")) == NULL) {
3428 FILE_OP_ERROR(file, "fopen");
3429 return COMPOSE_INSERT_READ_ERROR;
3432 prev_autowrap = compose->autowrap;
3433 compose->autowrap = FALSE;
3435 text = GTK_TEXT_VIEW(compose->text);
3436 buffer = gtk_text_view_get_buffer(text);
3437 mark = gtk_text_buffer_get_insert(buffer);
3438 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3440 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3441 G_CALLBACK(text_inserted),
3444 cur_encoding = conv_get_locale_charset_str_no_utf8();
3446 while (fgets(buf, sizeof(buf), fp) != NULL) {
3449 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3450 str = g_strdup(buf);
3452 str = conv_codeset_strdup
3453 (buf, cur_encoding, CS_INTERNAL);
3456 /* strip <CR> if DOS/Windows file,
3457 replace <CR> with <LF> if Macintosh file. */
3460 if (len > 0 && str[len - 1] != '\n') {
3462 if (str[len] == '\r') str[len] = '\n';
3465 gtk_text_buffer_insert(buffer, &iter, str, -1);
3469 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3470 G_CALLBACK(text_inserted),
3472 compose->autowrap = prev_autowrap;
3473 if (compose->autowrap)
3474 compose_wrap_all(compose);
3479 return COMPOSE_INSERT_INVALID_CHARACTER;
3481 return COMPOSE_INSERT_SUCCESS;
3484 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3485 const gchar *filename,
3486 const gchar *content_type)
3494 GtkListStore *store;
3496 gboolean has_binary = FALSE;
3498 if (!is_file_exist(file)) {
3499 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3500 gboolean result = FALSE;
3501 if (file_from_uri && is_file_exist(file_from_uri)) {
3502 result = compose_attach_append(
3503 compose, file_from_uri,
3507 g_free(file_from_uri);
3510 alertpanel_error("File %s doesn't exist\n", filename);
3513 if ((size = get_file_size(file)) < 0) {
3514 alertpanel_error("Can't get file size of %s\n", filename);
3518 alertpanel_error(_("File %s is empty."), filename);
3521 if ((fp = g_fopen(file, "rb")) == NULL) {
3522 alertpanel_error(_("Can't read %s."), filename);
3527 ainfo = g_new0(AttachInfo, 1);
3528 auto_ainfo = g_auto_pointer_new_with_free
3529 (ainfo, (GFreeFunc) compose_attach_info_free);
3530 ainfo->file = g_strdup(file);
3533 ainfo->content_type = g_strdup(content_type);
3534 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3536 MsgFlags flags = {0, 0};
3538 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3539 ainfo->encoding = ENC_7BIT;
3541 ainfo->encoding = ENC_8BIT;
3543 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3544 if (msginfo && msginfo->subject)
3545 name = g_strdup(msginfo->subject);
3547 name = g_path_get_basename(filename ? filename : file);
3549 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3551 procmsg_msginfo_free(msginfo);
3553 if (!g_ascii_strncasecmp(content_type, "text", 4))
3554 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3556 ainfo->encoding = ENC_BASE64;
3557 name = g_path_get_basename(filename ? filename : file);
3558 ainfo->name = g_strdup(name);
3562 ainfo->content_type = procmime_get_mime_type(file);
3563 if (!ainfo->content_type) {
3564 ainfo->content_type =
3565 g_strdup("application/octet-stream");
3566 ainfo->encoding = ENC_BASE64;
3567 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3569 procmime_get_encoding_for_text_file(file, &has_binary);
3571 ainfo->encoding = ENC_BASE64;
3572 name = g_path_get_basename(filename ? filename : file);
3573 ainfo->name = g_strdup(name);
3577 if (ainfo->name != NULL
3578 && !strcmp(ainfo->name, ".")) {
3579 g_free(ainfo->name);
3583 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3584 g_free(ainfo->content_type);
3585 ainfo->content_type = g_strdup("application/octet-stream");
3588 ainfo->size = (goffset)size;
3589 size_text = to_human_readable((goffset)size);
3591 store = GTK_LIST_STORE(gtk_tree_view_get_model
3592 (GTK_TREE_VIEW(compose->attach_clist)));
3594 gtk_list_store_append(store, &iter);
3595 gtk_list_store_set(store, &iter,
3596 COL_MIMETYPE, ainfo->content_type,
3597 COL_SIZE, size_text,
3598 COL_NAME, ainfo->name,
3600 COL_AUTODATA, auto_ainfo,
3603 g_auto_pointer_free(auto_ainfo);
3604 compose_attach_update_label(compose);
3608 static void compose_use_signing(Compose *compose, gboolean use_signing)
3610 compose->use_signing = use_signing;
3611 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3614 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3616 compose->use_encryption = use_encryption;
3617 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3620 #define NEXT_PART_NOT_CHILD(info) \
3622 node = info->node; \
3623 while (node->children) \
3624 node = g_node_last_child(node); \
3625 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3628 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3632 MimeInfo *firsttext = NULL;
3633 MimeInfo *encrypted = NULL;
3636 const gchar *partname = NULL;
3638 mimeinfo = procmime_scan_message(msginfo);
3639 if (!mimeinfo) return;
3641 if (mimeinfo->node->children == NULL) {
3642 procmime_mimeinfo_free_all(mimeinfo);
3646 /* find first content part */
3647 child = (MimeInfo *) mimeinfo->node->children->data;
3648 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3649 child = (MimeInfo *)child->node->children->data;
3652 if (child->type == MIMETYPE_TEXT) {
3654 debug_print("First text part found\n");
3655 } else if (compose->mode == COMPOSE_REEDIT &&
3656 child->type == MIMETYPE_APPLICATION &&
3657 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3658 encrypted = (MimeInfo *)child->node->parent->data;
3661 child = (MimeInfo *) mimeinfo->node->children->data;
3662 while (child != NULL) {
3665 if (child == encrypted) {
3666 /* skip this part of tree */
3667 NEXT_PART_NOT_CHILD(child);
3671 if (child->type == MIMETYPE_MULTIPART) {
3672 /* get the actual content */
3673 child = procmime_mimeinfo_next(child);
3677 if (child == firsttext) {
3678 child = procmime_mimeinfo_next(child);
3682 outfile = procmime_get_tmp_file_name(child);
3683 if ((err = procmime_get_part(outfile, child)) < 0)
3684 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3686 gchar *content_type;
3688 content_type = procmime_get_content_type_str(child->type, child->subtype);
3690 /* if we meet a pgp signature, we don't attach it, but
3691 * we force signing. */
3692 if ((strcmp(content_type, "application/pgp-signature") &&
3693 strcmp(content_type, "application/pkcs7-signature") &&
3694 strcmp(content_type, "application/x-pkcs7-signature"))
3695 || compose->mode == COMPOSE_REDIRECT) {
3696 partname = procmime_mimeinfo_get_parameter(child, "filename");
3697 if (partname == NULL)
3698 partname = procmime_mimeinfo_get_parameter(child, "name");
3699 if (partname == NULL)
3701 compose_attach_append(compose, outfile,
3702 partname, content_type);
3704 compose_force_signing(compose, compose->account, NULL);
3706 g_free(content_type);
3709 NEXT_PART_NOT_CHILD(child);
3711 procmime_mimeinfo_free_all(mimeinfo);
3714 #undef NEXT_PART_NOT_CHILD
3719 WAIT_FOR_INDENT_CHAR,
3720 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3723 /* return indent length, we allow:
3724 indent characters followed by indent characters or spaces/tabs,
3725 alphabets and numbers immediately followed by indent characters,
3726 and the repeating sequences of the above
3727 If quote ends with multiple spaces, only the first one is included. */
3728 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3729 const GtkTextIter *start, gint *len)
3731 GtkTextIter iter = *start;
3735 IndentState state = WAIT_FOR_INDENT_CHAR;
3738 gint alnum_count = 0;
3739 gint space_count = 0;
3742 if (prefs_common.quote_chars == NULL) {
3746 while (!gtk_text_iter_ends_line(&iter)) {
3747 wc = gtk_text_iter_get_char(&iter);
3748 if (g_unichar_iswide(wc))
3750 clen = g_unichar_to_utf8(wc, ch);
3754 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3755 is_space = g_unichar_isspace(wc);
3757 if (state == WAIT_FOR_INDENT_CHAR) {
3758 if (!is_indent && !g_unichar_isalnum(wc))
3761 quote_len += alnum_count + space_count + 1;
3762 alnum_count = space_count = 0;
3763 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3766 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3767 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3771 else if (is_indent) {
3772 quote_len += alnum_count + space_count + 1;
3773 alnum_count = space_count = 0;
3776 state = WAIT_FOR_INDENT_CHAR;
3780 gtk_text_iter_forward_char(&iter);
3783 if (quote_len > 0 && space_count > 0)
3789 if (quote_len > 0) {
3791 gtk_text_iter_forward_chars(&iter, quote_len);
3792 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3798 /* return >0 if the line is itemized */
3799 static int compose_itemized_length(GtkTextBuffer *buffer,
3800 const GtkTextIter *start)
3802 GtkTextIter iter = *start;
3807 if (gtk_text_iter_ends_line(&iter))
3812 wc = gtk_text_iter_get_char(&iter);
3813 if (!g_unichar_isspace(wc))
3815 gtk_text_iter_forward_char(&iter);
3816 if (gtk_text_iter_ends_line(&iter))
3820 clen = g_unichar_to_utf8(wc, ch);
3824 if (!strchr("*-+", ch[0]))
3827 gtk_text_iter_forward_char(&iter);
3828 if (gtk_text_iter_ends_line(&iter))
3830 wc = gtk_text_iter_get_char(&iter);
3831 if (g_unichar_isspace(wc)) {
3837 /* return the string at the start of the itemization */
3838 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3839 const GtkTextIter *start)
3841 GtkTextIter iter = *start;
3844 GString *item_chars = g_string_new("");
3847 if (gtk_text_iter_ends_line(&iter))
3852 wc = gtk_text_iter_get_char(&iter);
3853 if (!g_unichar_isspace(wc))
3855 gtk_text_iter_forward_char(&iter);
3856 if (gtk_text_iter_ends_line(&iter))
3858 g_string_append_unichar(item_chars, wc);
3861 str = item_chars->str;
3862 g_string_free(item_chars, FALSE);
3866 /* return the number of spaces at a line's start */
3867 static int compose_left_offset_length(GtkTextBuffer *buffer,
3868 const GtkTextIter *start)
3870 GtkTextIter iter = *start;
3873 if (gtk_text_iter_ends_line(&iter))
3877 wc = gtk_text_iter_get_char(&iter);
3878 if (!g_unichar_isspace(wc))
3881 gtk_text_iter_forward_char(&iter);
3882 if (gtk_text_iter_ends_line(&iter))
3886 gtk_text_iter_forward_char(&iter);
3887 if (gtk_text_iter_ends_line(&iter))
3892 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3893 const GtkTextIter *start,
3894 GtkTextIter *break_pos,
3898 GtkTextIter iter = *start, line_end = *start;
3899 PangoLogAttr *attrs;
3906 gboolean can_break = FALSE;
3907 gboolean do_break = FALSE;
3908 gboolean was_white = FALSE;
3909 gboolean prev_dont_break = FALSE;
3911 gtk_text_iter_forward_to_line_end(&line_end);
3912 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3913 len = g_utf8_strlen(str, -1);
3917 g_warning("compose_get_line_break_pos: len = 0!\n");
3921 /* g_print("breaking line: %d: %s (len = %d)\n",
3922 gtk_text_iter_get_line(&iter), str, len); */
3924 attrs = g_new(PangoLogAttr, len + 1);
3926 pango_default_break(str, -1, NULL, attrs, len + 1);
3930 /* skip quote and leading spaces */
3931 for (i = 0; *p != '\0' && i < len; i++) {
3934 wc = g_utf8_get_char(p);
3935 if (i >= quote_len && !g_unichar_isspace(wc))
3937 if (g_unichar_iswide(wc))
3939 else if (*p == '\t')
3943 p = g_utf8_next_char(p);
3946 for (; *p != '\0' && i < len; i++) {
3947 PangoLogAttr *attr = attrs + i;
3951 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3954 was_white = attr->is_white;
3956 /* don't wrap URI */
3957 if ((uri_len = get_uri_len(p)) > 0) {
3959 if (pos > 0 && col > max_col) {
3969 wc = g_utf8_get_char(p);
3970 if (g_unichar_iswide(wc)) {
3972 if (prev_dont_break && can_break && attr->is_line_break)
3974 } else if (*p == '\t')
3978 if (pos > 0 && col > max_col) {
3983 if (*p == '-' || *p == '/')
3984 prev_dont_break = TRUE;
3986 prev_dont_break = FALSE;
3988 p = g_utf8_next_char(p);
3992 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3997 *break_pos = *start;
3998 gtk_text_iter_set_line_offset(break_pos, pos);
4003 static gboolean compose_join_next_line(Compose *compose,
4004 GtkTextBuffer *buffer,
4006 const gchar *quote_str)
4008 GtkTextIter iter_ = *iter, cur, prev, next, end;
4009 PangoLogAttr attrs[3];
4011 gchar *next_quote_str;
4014 gboolean keep_cursor = FALSE;
4016 if (!gtk_text_iter_forward_line(&iter_) ||
4017 gtk_text_iter_ends_line(&iter_)) {
4020 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4022 if ((quote_str || next_quote_str) &&
4023 strcmp2(quote_str, next_quote_str) != 0) {
4024 g_free(next_quote_str);
4027 g_free(next_quote_str);
4030 if (quote_len > 0) {
4031 gtk_text_iter_forward_chars(&end, quote_len);
4032 if (gtk_text_iter_ends_line(&end)) {
4037 /* don't join itemized lines */
4038 if (compose_itemized_length(buffer, &end) > 0) {
4042 /* don't join signature separator */
4043 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4046 /* delete quote str */
4048 gtk_text_buffer_delete(buffer, &iter_, &end);
4050 /* don't join line breaks put by the user */
4052 gtk_text_iter_backward_char(&cur);
4053 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4054 gtk_text_iter_forward_char(&cur);
4058 gtk_text_iter_forward_char(&cur);
4059 /* delete linebreak and extra spaces */
4060 while (gtk_text_iter_backward_char(&cur)) {
4061 wc1 = gtk_text_iter_get_char(&cur);
4062 if (!g_unichar_isspace(wc1))
4067 while (!gtk_text_iter_ends_line(&cur)) {
4068 wc1 = gtk_text_iter_get_char(&cur);
4069 if (!g_unichar_isspace(wc1))
4071 gtk_text_iter_forward_char(&cur);
4074 if (!gtk_text_iter_equal(&prev, &next)) {
4077 mark = gtk_text_buffer_get_insert(buffer);
4078 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4079 if (gtk_text_iter_equal(&prev, &cur))
4081 gtk_text_buffer_delete(buffer, &prev, &next);
4085 /* insert space if required */
4086 gtk_text_iter_backward_char(&prev);
4087 wc1 = gtk_text_iter_get_char(&prev);
4088 wc2 = gtk_text_iter_get_char(&next);
4089 gtk_text_iter_forward_char(&next);
4090 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4091 pango_default_break(str, -1, NULL, attrs, 3);
4092 if (!attrs[1].is_line_break ||
4093 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4094 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4096 gtk_text_iter_backward_char(&iter_);
4097 gtk_text_buffer_place_cursor(buffer, &iter_);
4106 #define ADD_TXT_POS(bp_, ep_, pti_) \
4107 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4108 last = last->next; \
4109 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4110 last->next = NULL; \
4112 g_warning("alloc error scanning URIs\n"); \
4115 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4117 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4118 GtkTextBuffer *buffer;
4119 GtkTextIter iter, break_pos, end_of_line;
4120 gchar *quote_str = NULL;
4122 gboolean wrap_quote = prefs_common.linewrap_quote;
4123 gboolean prev_autowrap = compose->autowrap;
4124 gint startq_offset = -1, noq_offset = -1;
4125 gint uri_start = -1, uri_stop = -1;
4126 gint nouri_start = -1, nouri_stop = -1;
4127 gint num_blocks = 0;
4128 gint quotelevel = -1;
4129 gboolean modified = force;
4130 gboolean removed = FALSE;
4131 gboolean modified_before_remove = FALSE;
4133 gboolean start = TRUE;
4134 gint itemized_len = 0, rem_item_len = 0;
4135 gchar *itemized_chars = NULL;
4136 gboolean item_continuation = FALSE;
4141 if (compose->draft_timeout_tag == -2) {
4145 compose->autowrap = FALSE;
4147 buffer = gtk_text_view_get_buffer(text);
4148 undo_wrapping(compose->undostruct, TRUE);
4153 mark = gtk_text_buffer_get_insert(buffer);
4154 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4158 if (compose->draft_timeout_tag == -2) {
4159 if (gtk_text_iter_ends_line(&iter)) {
4160 while (gtk_text_iter_ends_line(&iter) &&
4161 gtk_text_iter_forward_line(&iter))
4164 while (gtk_text_iter_backward_line(&iter)) {
4165 if (gtk_text_iter_ends_line(&iter)) {
4166 gtk_text_iter_forward_line(&iter);
4172 /* move to line start */
4173 gtk_text_iter_set_line_offset(&iter, 0);
4176 itemized_len = compose_itemized_length(buffer, &iter);
4178 if (!itemized_len) {
4179 itemized_len = compose_left_offset_length(buffer, &iter);
4180 item_continuation = TRUE;
4184 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4186 /* go until paragraph end (empty line) */
4187 while (start || !gtk_text_iter_ends_line(&iter)) {
4188 gchar *scanpos = NULL;
4189 /* parse table - in order of priority */
4191 const gchar *needle; /* token */
4193 /* token search function */
4194 gchar *(*search) (const gchar *haystack,
4195 const gchar *needle);
4196 /* part parsing function */
4197 gboolean (*parse) (const gchar *start,
4198 const gchar *scanpos,
4202 /* part to URI function */
4203 gchar *(*build_uri) (const gchar *bp,
4207 static struct table parser[] = {
4208 {"http://", strcasestr, get_uri_part, make_uri_string},
4209 {"https://", strcasestr, get_uri_part, make_uri_string},
4210 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4211 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4212 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4213 {"www.", strcasestr, get_uri_part, make_http_string},
4214 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4215 {"@", strcasestr, get_email_part, make_email_string}
4217 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4218 gint last_index = PARSE_ELEMS;
4220 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4224 if (!prev_autowrap && num_blocks == 0) {
4226 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4227 G_CALLBACK(text_inserted),
4230 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4233 uri_start = uri_stop = -1;
4235 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4238 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4239 if (startq_offset == -1)
4240 startq_offset = gtk_text_iter_get_offset(&iter);
4241 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4242 if (quotelevel > 2) {
4243 /* recycle colors */
4244 if (prefs_common.recycle_quote_colors)
4253 if (startq_offset == -1)
4254 noq_offset = gtk_text_iter_get_offset(&iter);
4258 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4261 if (gtk_text_iter_ends_line(&iter)) {
4263 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4264 prefs_common.linewrap_len,
4266 GtkTextIter prev, next, cur;
4267 if (prev_autowrap != FALSE || force) {
4268 compose->automatic_break = TRUE;
4270 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4271 compose->automatic_break = FALSE;
4272 if (itemized_len && compose->autoindent) {
4273 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4274 if (!item_continuation)
4275 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4277 } else if (quote_str && wrap_quote) {
4278 compose->automatic_break = TRUE;
4280 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4281 compose->automatic_break = FALSE;
4282 if (itemized_len && compose->autoindent) {
4283 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4284 if (!item_continuation)
4285 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4289 /* remove trailing spaces */
4291 rem_item_len = itemized_len;
4292 while (compose->autoindent && rem_item_len-- > 0)
4293 gtk_text_iter_backward_char(&cur);
4294 gtk_text_iter_backward_char(&cur);
4297 while (!gtk_text_iter_starts_line(&cur)) {
4300 gtk_text_iter_backward_char(&cur);
4301 wc = gtk_text_iter_get_char(&cur);
4302 if (!g_unichar_isspace(wc))
4306 if (!gtk_text_iter_equal(&prev, &next)) {
4307 gtk_text_buffer_delete(buffer, &prev, &next);
4309 gtk_text_iter_forward_char(&break_pos);
4313 gtk_text_buffer_insert(buffer, &break_pos,
4317 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4319 /* move iter to current line start */
4320 gtk_text_iter_set_line_offset(&iter, 0);
4327 /* move iter to next line start */
4333 if (!prev_autowrap && num_blocks > 0) {
4335 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4336 G_CALLBACK(text_inserted),
4340 while (!gtk_text_iter_ends_line(&end_of_line)) {
4341 gtk_text_iter_forward_char(&end_of_line);
4343 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4345 nouri_start = gtk_text_iter_get_offset(&iter);
4346 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4348 walk_pos = gtk_text_iter_get_offset(&iter);
4349 /* FIXME: this looks phony. scanning for anything in the parse table */
4350 for (n = 0; n < PARSE_ELEMS; n++) {
4353 tmp = parser[n].search(walk, parser[n].needle);
4355 if (scanpos == NULL || tmp < scanpos) {
4364 /* check if URI can be parsed */
4365 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4366 (const gchar **)&ep, FALSE)
4367 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4371 strlen(parser[last_index].needle);
4374 uri_start = walk_pos + (bp - o_walk);
4375 uri_stop = walk_pos + (ep - o_walk);
4379 gtk_text_iter_forward_line(&iter);
4382 if (startq_offset != -1) {
4383 GtkTextIter startquote, endquote;
4384 gtk_text_buffer_get_iter_at_offset(
4385 buffer, &startquote, startq_offset);
4388 switch (quotelevel) {
4390 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4391 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4392 gtk_text_buffer_apply_tag_by_name(
4393 buffer, "quote0", &startquote, &endquote);
4394 gtk_text_buffer_remove_tag_by_name(
4395 buffer, "quote1", &startquote, &endquote);
4396 gtk_text_buffer_remove_tag_by_name(
4397 buffer, "quote2", &startquote, &endquote);
4402 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4403 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4404 gtk_text_buffer_apply_tag_by_name(
4405 buffer, "quote1", &startquote, &endquote);
4406 gtk_text_buffer_remove_tag_by_name(
4407 buffer, "quote0", &startquote, &endquote);
4408 gtk_text_buffer_remove_tag_by_name(
4409 buffer, "quote2", &startquote, &endquote);
4414 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4415 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4416 gtk_text_buffer_apply_tag_by_name(
4417 buffer, "quote2", &startquote, &endquote);
4418 gtk_text_buffer_remove_tag_by_name(
4419 buffer, "quote0", &startquote, &endquote);
4420 gtk_text_buffer_remove_tag_by_name(
4421 buffer, "quote1", &startquote, &endquote);
4427 } else if (noq_offset != -1) {
4428 GtkTextIter startnoquote, endnoquote;
4429 gtk_text_buffer_get_iter_at_offset(
4430 buffer, &startnoquote, noq_offset);
4433 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4434 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4435 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4436 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4437 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4438 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4439 gtk_text_buffer_remove_tag_by_name(
4440 buffer, "quote0", &startnoquote, &endnoquote);
4441 gtk_text_buffer_remove_tag_by_name(
4442 buffer, "quote1", &startnoquote, &endnoquote);
4443 gtk_text_buffer_remove_tag_by_name(
4444 buffer, "quote2", &startnoquote, &endnoquote);
4450 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4451 GtkTextIter nouri_start_iter, nouri_end_iter;
4452 gtk_text_buffer_get_iter_at_offset(
4453 buffer, &nouri_start_iter, nouri_start);
4454 gtk_text_buffer_get_iter_at_offset(
4455 buffer, &nouri_end_iter, nouri_stop);
4456 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4457 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4458 gtk_text_buffer_remove_tag_by_name(
4459 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4460 modified_before_remove = modified;
4465 if (uri_start >= 0 && uri_stop > 0) {
4466 GtkTextIter uri_start_iter, uri_end_iter, back;
4467 gtk_text_buffer_get_iter_at_offset(
4468 buffer, &uri_start_iter, uri_start);
4469 gtk_text_buffer_get_iter_at_offset(
4470 buffer, &uri_end_iter, uri_stop);
4471 back = uri_end_iter;
4472 gtk_text_iter_backward_char(&back);
4473 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4474 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4475 gtk_text_buffer_apply_tag_by_name(
4476 buffer, "link", &uri_start_iter, &uri_end_iter);
4478 if (removed && !modified_before_remove) {
4484 // debug_print("not modified, out after %d lines\n", lines);
4488 // debug_print("modified, out after %d lines\n", lines);
4490 g_free(itemized_chars);
4493 undo_wrapping(compose->undostruct, FALSE);
4494 compose->autowrap = prev_autowrap;
4499 void compose_action_cb(void *data)
4501 Compose *compose = (Compose *)data;
4502 compose_wrap_all(compose);
4505 static void compose_wrap_all(Compose *compose)
4507 compose_wrap_all_full(compose, FALSE);
4510 static void compose_wrap_all_full(Compose *compose, gboolean force)
4512 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4513 GtkTextBuffer *buffer;
4515 gboolean modified = TRUE;
4517 buffer = gtk_text_view_get_buffer(text);
4519 gtk_text_buffer_get_start_iter(buffer, &iter);
4520 while (!gtk_text_iter_is_end(&iter) && modified)
4521 modified = compose_beautify_paragraph(compose, &iter, force);
4525 static void compose_set_title(Compose *compose)
4531 edited = compose->modified ? _(" [Edited]") : "";
4533 subject = gtk_editable_get_chars(
4534 GTK_EDITABLE(compose->subject_entry), 0, -1);
4536 #ifndef GENERIC_UMPC
4537 if (subject && strlen(subject))
4538 str = g_strdup_printf(_("%s - Compose message%s"),
4541 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4543 str = g_strdup(_("Compose message"));
4546 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4552 * compose_current_mail_account:
4554 * Find a current mail account (the currently selected account, or the
4555 * default account, if a news account is currently selected). If a
4556 * mail account cannot be found, display an error message.
4558 * Return value: Mail account, or NULL if not found.
4560 static PrefsAccount *
4561 compose_current_mail_account(void)
4565 if (cur_account && cur_account->protocol != A_NNTP)
4568 ac = account_get_default();
4569 if (!ac || ac->protocol == A_NNTP) {
4570 alertpanel_error(_("Account for sending mail is not specified.\n"
4571 "Please select a mail account before sending."));
4578 #define QUOTE_IF_REQUIRED(out, str) \
4580 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4584 len = strlen(str) + 3; \
4585 if ((__tmp = alloca(len)) == NULL) { \
4586 g_warning("can't allocate memory\n"); \
4587 g_string_free(header, TRUE); \
4590 g_snprintf(__tmp, len, "\"%s\"", str); \
4595 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4596 g_warning("can't allocate memory\n"); \
4597 g_string_free(header, TRUE); \
4600 strcpy(__tmp, str); \
4606 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4608 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4612 len = strlen(str) + 3; \
4613 if ((__tmp = alloca(len)) == NULL) { \
4614 g_warning("can't allocate memory\n"); \
4617 g_snprintf(__tmp, len, "\"%s\"", str); \
4622 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4623 g_warning("can't allocate memory\n"); \
4626 strcpy(__tmp, str); \
4632 static void compose_select_account(Compose *compose, PrefsAccount *account,
4635 gchar *from = NULL, *header;
4636 ComposeHeaderEntry *header_entry;
4638 cm_return_if_fail(account != NULL);
4640 compose->account = account;
4642 if (account->name && *account->name) {
4644 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4645 from = g_strdup_printf("%s <%s>",
4646 buf, account->address);
4647 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4649 from = g_strdup_printf("<%s>",
4651 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4656 compose_set_title(compose);
4658 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4659 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4661 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4662 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4663 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4665 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4667 activate_privacy_system(compose, account, FALSE);
4669 if (!init && compose->mode != COMPOSE_REDIRECT) {
4670 undo_block(compose->undostruct);
4671 compose_insert_sig(compose, TRUE);
4672 undo_unblock(compose->undostruct);
4675 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4676 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4678 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4679 if (account->protocol == A_NNTP) {
4680 if (!strcmp(header, _("To:")))
4681 combobox_select_by_text(
4682 GTK_COMBO_BOX(header_entry->combo),
4685 if (!strcmp(header, _("Newsgroups:")))
4686 combobox_select_by_text(
4687 GTK_COMBO_BOX(header_entry->combo),
4695 /* use account's dict info if set */
4696 if (compose->gtkaspell) {
4697 if (account->enable_default_dictionary)
4698 gtkaspell_change_dict(compose->gtkaspell,
4699 account->default_dictionary, FALSE);
4700 if (account->enable_default_alt_dictionary)
4701 gtkaspell_change_alt_dict(compose->gtkaspell,
4702 account->default_alt_dictionary);
4703 if (account->enable_default_dictionary
4704 || account->enable_default_alt_dictionary)
4705 compose_spell_menu_changed(compose);
4710 gboolean compose_check_for_valid_recipient(Compose *compose) {
4711 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4712 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4713 gboolean recipient_found = FALSE;
4717 /* free to and newsgroup list */
4718 slist_free_strings(compose->to_list);
4719 g_slist_free(compose->to_list);
4720 compose->to_list = NULL;
4722 slist_free_strings(compose->newsgroup_list);
4723 g_slist_free(compose->newsgroup_list);
4724 compose->newsgroup_list = NULL;
4726 /* search header entries for to and newsgroup entries */
4727 for (list = compose->header_list; list; list = list->next) {
4730 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4731 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4734 if (entry[0] != '\0') {
4735 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4736 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4737 compose->to_list = address_list_append(compose->to_list, entry);
4738 recipient_found = TRUE;
4741 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4742 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4743 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4744 recipient_found = TRUE;
4751 return recipient_found;
4754 static gboolean compose_check_for_set_recipients(Compose *compose)
4756 if (compose->account->set_autocc && compose->account->auto_cc) {
4757 gboolean found_other = FALSE;
4759 /* search header entries for to and newsgroup entries */
4760 for (list = compose->header_list; list; list = list->next) {
4763 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4764 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4767 if (strcmp(entry, compose->account->auto_cc)
4768 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4778 if (compose->batch) {
4779 gtk_widget_show_all(compose->window);
4781 aval = alertpanel(_("Send"),
4782 _("The only recipient is the default CC address. Send anyway?"),
4783 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4784 if (aval != G_ALERTALTERNATE)
4788 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4789 gboolean found_other = FALSE;
4791 /* search header entries for to and newsgroup entries */
4792 for (list = compose->header_list; list; list = list->next) {
4795 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4796 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4799 if (strcmp(entry, compose->account->auto_bcc)
4800 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4810 if (compose->batch) {
4811 gtk_widget_show_all(compose->window);
4813 aval = alertpanel(_("Send"),
4814 _("The only recipient is the default BCC address. Send anyway?"),
4815 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4816 if (aval != G_ALERTALTERNATE)
4823 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4827 if (compose_check_for_valid_recipient(compose) == FALSE) {
4828 if (compose->batch) {
4829 gtk_widget_show_all(compose->window);
4831 alertpanel_error(_("Recipient is not specified."));
4835 if (compose_check_for_set_recipients(compose) == FALSE) {
4839 if (!compose->batch) {
4840 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4841 if (*str == '\0' && check_everything == TRUE &&
4842 compose->mode != COMPOSE_REDIRECT) {
4844 gchar *button_label;
4847 if (compose->sending)
4848 button_label = _("+_Send");
4850 button_label = _("+_Queue");
4851 message = g_strdup_printf(_("Subject is empty. %s"),
4852 compose->sending?_("Send it anyway?"):
4853 _("Queue it anyway?"));
4855 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4856 GTK_STOCK_CANCEL, button_label, NULL);
4858 if (aval != G_ALERTALTERNATE)
4863 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4869 gint compose_send(Compose *compose)
4872 FolderItem *folder = NULL;
4874 gchar *msgpath = NULL;
4875 gboolean discard_window = FALSE;
4876 gchar *errstr = NULL;
4877 gchar *tmsgid = NULL;
4878 MainWindow *mainwin = mainwindow_get_mainwindow();
4879 gboolean queued_removed = FALSE;
4881 if (prefs_common.send_dialog_invisible
4882 || compose->batch == TRUE)
4883 discard_window = TRUE;
4885 compose_allow_user_actions (compose, FALSE);
4886 compose->sending = TRUE;
4888 if (compose_check_entries(compose, TRUE) == FALSE) {
4889 if (compose->batch) {
4890 gtk_widget_show_all(compose->window);
4896 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4899 if (compose->batch) {
4900 gtk_widget_show_all(compose->window);
4903 alertpanel_error(_("Could not queue message for sending:\n\n"
4904 "Charset conversion failed."));
4905 } else if (val == -5) {
4906 alertpanel_error(_("Could not queue message for sending:\n\n"
4907 "Couldn't get recipient encryption key."));
4908 } else if (val == -6) {
4910 } else if (val == -3) {
4911 if (privacy_peek_error())
4912 alertpanel_error(_("Could not queue message for sending:\n\n"
4913 "Signature failed: %s"), privacy_get_error());
4914 } else if (val == -2 && errno != 0) {
4915 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4917 alertpanel_error(_("Could not queue message for sending."));
4922 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4923 if (discard_window) {
4924 compose->sending = FALSE;
4925 compose_close(compose);
4926 /* No more compose access in the normal codepath
4927 * after this point! */
4932 alertpanel_error(_("The message was queued but could not be "
4933 "sent.\nUse \"Send queued messages\" from "
4934 "the main window to retry."));
4935 if (!discard_window) {
4942 if (msgpath == NULL) {
4943 msgpath = folder_item_fetch_msg(folder, msgnum);
4944 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4947 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4948 claws_unlink(msgpath);
4951 if (!discard_window) {
4953 if (!queued_removed)
4954 folder_item_remove_msg(folder, msgnum);
4955 folder_item_scan(folder);
4957 /* make sure we delete that */
4958 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4960 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4961 folder_item_remove_msg(folder, tmp->msgnum);
4962 procmsg_msginfo_free(tmp);
4969 if (!queued_removed)
4970 folder_item_remove_msg(folder, msgnum);
4971 folder_item_scan(folder);
4973 /* make sure we delete that */
4974 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4976 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4977 folder_item_remove_msg(folder, tmp->msgnum);
4978 procmsg_msginfo_free(tmp);
4981 if (!discard_window) {
4982 compose->sending = FALSE;
4983 compose_allow_user_actions (compose, TRUE);
4984 compose_close(compose);
4988 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4989 "the main window to retry."), errstr);
4992 alertpanel_error_log(_("The message was queued but could not be "
4993 "sent.\nUse \"Send queued messages\" from "
4994 "the main window to retry."));
4996 if (!discard_window) {
5005 toolbar_main_set_sensitive(mainwin);
5006 main_window_set_menu_sensitive(mainwin);
5012 compose_allow_user_actions (compose, TRUE);
5013 compose->sending = FALSE;
5014 compose->modified = TRUE;
5015 toolbar_main_set_sensitive(mainwin);
5016 main_window_set_menu_sensitive(mainwin);
5021 static gboolean compose_use_attach(Compose *compose)
5023 GtkTreeModel *model = gtk_tree_view_get_model
5024 (GTK_TREE_VIEW(compose->attach_clist));
5025 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5028 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5031 gchar buf[BUFFSIZE];
5033 gboolean first_to_address;
5034 gboolean first_cc_address;
5036 ComposeHeaderEntry *headerentry;
5037 const gchar *headerentryname;
5038 const gchar *cc_hdr;
5039 const gchar *to_hdr;
5040 gboolean err = FALSE;
5042 debug_print("Writing redirect header\n");
5044 cc_hdr = prefs_common_translated_header_name("Cc:");
5045 to_hdr = prefs_common_translated_header_name("To:");
5047 first_to_address = TRUE;
5048 for (list = compose->header_list; list; list = list->next) {
5049 headerentry = ((ComposeHeaderEntry *)list->data);
5050 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5052 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5053 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5054 Xstrdup_a(str, entstr, return -1);
5056 if (str[0] != '\0') {
5057 compose_convert_header
5058 (compose, buf, sizeof(buf), str,
5059 strlen("Resent-To") + 2, TRUE);
5061 if (first_to_address) {
5062 err |= (fprintf(fp, "Resent-To: ") < 0);
5063 first_to_address = FALSE;
5065 err |= (fprintf(fp, ",") < 0);
5067 err |= (fprintf(fp, "%s", buf) < 0);
5071 if (!first_to_address) {
5072 err |= (fprintf(fp, "\n") < 0);
5075 first_cc_address = TRUE;
5076 for (list = compose->header_list; list; list = list->next) {
5077 headerentry = ((ComposeHeaderEntry *)list->data);
5078 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5080 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5081 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5082 Xstrdup_a(str, strg, return -1);
5084 if (str[0] != '\0') {
5085 compose_convert_header
5086 (compose, buf, sizeof(buf), str,
5087 strlen("Resent-Cc") + 2, TRUE);
5089 if (first_cc_address) {
5090 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5091 first_cc_address = FALSE;
5093 err |= (fprintf(fp, ",") < 0);
5095 err |= (fprintf(fp, "%s", buf) < 0);
5099 if (!first_cc_address) {
5100 err |= (fprintf(fp, "\n") < 0);
5103 return (err ? -1:0);
5106 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5108 gchar buf[BUFFSIZE];
5110 const gchar *entstr;
5111 /* struct utsname utsbuf; */
5112 gboolean err = FALSE;
5114 cm_return_val_if_fail(fp != NULL, -1);
5115 cm_return_val_if_fail(compose->account != NULL, -1);
5116 cm_return_val_if_fail(compose->account->address != NULL, -1);
5119 get_rfc822_date(buf, sizeof(buf));
5120 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5123 if (compose->account->name && *compose->account->name) {
5124 compose_convert_header
5125 (compose, buf, sizeof(buf), compose->account->name,
5126 strlen("From: "), TRUE);
5127 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5128 buf, compose->account->address) < 0);
5130 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5133 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5134 if (*entstr != '\0') {
5135 Xstrdup_a(str, entstr, return -1);
5138 compose_convert_header(compose, buf, sizeof(buf), str,
5139 strlen("Subject: "), FALSE);
5140 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5144 /* Resent-Message-ID */
5145 if (compose->account->set_domain && compose->account->domain) {
5146 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5147 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5148 g_snprintf(buf, sizeof(buf), "%s",
5149 strchr(compose->account->address, '@') ?
5150 strchr(compose->account->address, '@')+1 :
5151 compose->account->address);
5153 g_snprintf(buf, sizeof(buf), "%s", "");
5156 if (compose->account->gen_msgid) {
5158 if (compose->account->msgid_with_addr) {
5159 addr = compose->account->address;
5161 generate_msgid(buf, sizeof(buf), addr);
5162 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5163 compose->msgid = g_strdup(buf);
5165 compose->msgid = NULL;
5168 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5171 /* separator between header and body */
5172 err |= (fputs("\n", fp) == EOF);
5174 return (err ? -1:0);
5177 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5181 gchar buf[BUFFSIZE];
5183 gboolean skip = FALSE;
5184 gboolean err = FALSE;
5185 gchar *not_included[]={
5186 "Return-Path:", "Delivered-To:", "Received:",
5187 "Subject:", "X-UIDL:", "AF:",
5188 "NF:", "PS:", "SRH:",
5189 "SFN:", "DSR:", "MID:",
5190 "CFG:", "PT:", "S:",
5191 "RQ:", "SSV:", "NSV:",
5192 "SSH:", "R:", "MAID:",
5193 "NAID:", "RMID:", "FMID:",
5194 "SCF:", "RRCPT:", "NG:",
5195 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5196 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5197 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5198 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5199 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5202 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5203 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5207 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5209 for (i = 0; not_included[i] != NULL; i++) {
5210 if (g_ascii_strncasecmp(buf, not_included[i],
5211 strlen(not_included[i])) == 0) {
5218 if (fputs(buf, fdest) == -1)
5221 if (!prefs_common.redirect_keep_from) {
5222 if (g_ascii_strncasecmp(buf, "From:",
5223 strlen("From:")) == 0) {
5224 err |= (fputs(" (by way of ", fdest) == EOF);
5225 if (compose->account->name
5226 && *compose->account->name) {
5227 compose_convert_header
5228 (compose, buf, sizeof(buf),
5229 compose->account->name,
5232 err |= (fprintf(fdest, "%s <%s>",
5234 compose->account->address) < 0);
5236 err |= (fprintf(fdest, "%s",
5237 compose->account->address) < 0);
5238 err |= (fputs(")", fdest) == EOF);
5242 if (fputs("\n", fdest) == -1)
5249 if (compose_redirect_write_headers(compose, fdest))
5252 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5253 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5266 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5268 GtkTextBuffer *buffer;
5269 GtkTextIter start, end;
5272 const gchar *out_codeset;
5273 EncodingType encoding = ENC_UNKNOWN;
5274 MimeInfo *mimemsg, *mimetext;
5276 const gchar *src_codeset = CS_INTERNAL;
5277 gchar *from_addr = NULL;
5278 gchar *from_name = NULL;
5280 if (action == COMPOSE_WRITE_FOR_SEND)
5281 attach_parts = TRUE;
5283 /* create message MimeInfo */
5284 mimemsg = procmime_mimeinfo_new();
5285 mimemsg->type = MIMETYPE_MESSAGE;
5286 mimemsg->subtype = g_strdup("rfc822");
5287 mimemsg->content = MIMECONTENT_MEM;
5288 mimemsg->tmp = TRUE; /* must free content later */
5289 mimemsg->data.mem = compose_get_header(compose);
5291 /* Create text part MimeInfo */
5292 /* get all composed text */
5293 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5294 gtk_text_buffer_get_start_iter(buffer, &start);
5295 gtk_text_buffer_get_end_iter(buffer, &end);
5296 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5298 out_codeset = conv_get_charset_str(compose->out_encoding);
5300 if (!out_codeset && is_ascii_str(chars)) {
5301 out_codeset = CS_US_ASCII;
5302 } else if (prefs_common.outgoing_fallback_to_ascii &&
5303 is_ascii_str(chars)) {
5304 out_codeset = CS_US_ASCII;
5305 encoding = ENC_7BIT;
5309 gchar *test_conv_global_out = NULL;
5310 gchar *test_conv_reply = NULL;
5312 /* automatic mode. be automatic. */
5313 codeconv_set_strict(TRUE);
5315 out_codeset = conv_get_outgoing_charset_str();
5317 debug_print("trying to convert to %s\n", out_codeset);
5318 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5321 if (!test_conv_global_out && compose->orig_charset
5322 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5323 out_codeset = compose->orig_charset;
5324 debug_print("failure; trying to convert to %s\n", out_codeset);
5325 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5328 if (!test_conv_global_out && !test_conv_reply) {
5330 out_codeset = CS_INTERNAL;
5331 debug_print("failure; finally using %s\n", out_codeset);
5333 g_free(test_conv_global_out);
5334 g_free(test_conv_reply);
5335 codeconv_set_strict(FALSE);
5338 if (encoding == ENC_UNKNOWN) {
5339 if (prefs_common.encoding_method == CTE_BASE64)
5340 encoding = ENC_BASE64;
5341 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5342 encoding = ENC_QUOTED_PRINTABLE;
5343 else if (prefs_common.encoding_method == CTE_8BIT)
5344 encoding = ENC_8BIT;
5346 encoding = procmime_get_encoding_for_charset(out_codeset);
5349 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5350 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5352 if (action == COMPOSE_WRITE_FOR_SEND) {
5353 codeconv_set_strict(TRUE);
5354 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5355 codeconv_set_strict(FALSE);
5361 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5362 "to the specified %s charset.\n"
5363 "Send it as %s?"), out_codeset, src_codeset);
5364 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5365 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5368 if (aval != G_ALERTALTERNATE) {
5373 out_codeset = src_codeset;
5379 out_codeset = src_codeset;
5384 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5385 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5386 strstr(buf, "\nFrom ") != NULL) {
5387 encoding = ENC_QUOTED_PRINTABLE;
5391 mimetext = procmime_mimeinfo_new();
5392 mimetext->content = MIMECONTENT_MEM;
5393 mimetext->tmp = TRUE; /* must free content later */
5394 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5395 * and free the data, which we need later. */
5396 mimetext->data.mem = g_strdup(buf);
5397 mimetext->type = MIMETYPE_TEXT;
5398 mimetext->subtype = g_strdup("plain");
5399 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5400 g_strdup(out_codeset));
5402 /* protect trailing spaces when signing message */
5403 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5404 privacy_system_can_sign(compose->privacy_system)) {
5405 encoding = ENC_QUOTED_PRINTABLE;
5408 debug_print("main text: %zd bytes encoded as %s in %d\n",
5409 strlen(buf), out_codeset, encoding);
5411 /* check for line length limit */
5412 if (action == COMPOSE_WRITE_FOR_SEND &&
5413 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5414 check_line_length(buf, 1000, &line) < 0) {
5418 msg = g_strdup_printf
5419 (_("Line %d exceeds the line length limit (998 bytes).\n"
5420 "The contents of the message might be broken on the way to the delivery.\n"
5422 "Send it anyway?"), line + 1);
5423 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5425 if (aval != G_ALERTALTERNATE) {
5431 if (encoding != ENC_UNKNOWN)
5432 procmime_encode_content(mimetext, encoding);
5434 /* append attachment parts */
5435 if (compose_use_attach(compose) && attach_parts) {
5436 MimeInfo *mimempart;
5437 gchar *boundary = NULL;
5438 mimempart = procmime_mimeinfo_new();
5439 mimempart->content = MIMECONTENT_EMPTY;
5440 mimempart->type = MIMETYPE_MULTIPART;
5441 mimempart->subtype = g_strdup("mixed");
5445 boundary = generate_mime_boundary(NULL);
5446 } while (strstr(buf, boundary) != NULL);
5448 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5451 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5453 g_node_append(mimempart->node, mimetext->node);
5454 g_node_append(mimemsg->node, mimempart->node);
5456 if (compose_add_attachments(compose, mimempart) < 0)
5459 g_node_append(mimemsg->node, mimetext->node);
5463 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5464 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5465 /* extract name and address */
5466 if (strstr(spec, " <") && strstr(spec, ">")) {
5467 from_addr = g_strdup(strrchr(spec, '<')+1);
5468 *(strrchr(from_addr, '>')) = '\0';
5469 from_name = g_strdup(spec);
5470 *(strrchr(from_name, '<')) = '\0';
5477 /* sign message if sending */
5478 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5479 privacy_system_can_sign(compose->privacy_system))
5480 if (!privacy_sign(compose->privacy_system, mimemsg,
5481 compose->account, from_addr)) {
5488 procmime_write_mimeinfo(mimemsg, fp);
5490 procmime_mimeinfo_free_all(mimemsg);
5495 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5497 GtkTextBuffer *buffer;
5498 GtkTextIter start, end;
5503 if ((fp = g_fopen(file, "wb")) == NULL) {
5504 FILE_OP_ERROR(file, "fopen");
5508 /* chmod for security */
5509 if (change_file_mode_rw(fp, file) < 0) {
5510 FILE_OP_ERROR(file, "chmod");
5511 g_warning("can't change file mode\n");
5514 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5515 gtk_text_buffer_get_start_iter(buffer, &start);
5516 gtk_text_buffer_get_end_iter(buffer, &end);
5517 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5519 chars = conv_codeset_strdup
5520 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5523 if (!chars) return -1;
5526 len = strlen(chars);
5527 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5528 FILE_OP_ERROR(file, "fwrite");
5537 if (fclose(fp) == EOF) {
5538 FILE_OP_ERROR(file, "fclose");
5545 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5548 MsgInfo *msginfo = compose->targetinfo;
5550 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5551 if (!msginfo) return -1;
5553 if (!force && MSG_IS_LOCKED(msginfo->flags))
5556 item = msginfo->folder;
5557 cm_return_val_if_fail(item != NULL, -1);
5559 if (procmsg_msg_exist(msginfo) &&
5560 (folder_has_parent_of_type(item, F_QUEUE) ||
5561 folder_has_parent_of_type(item, F_DRAFT)
5562 || msginfo == compose->autosaved_draft)) {
5563 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5564 g_warning("can't remove the old message\n");
5567 debug_print("removed reedit target %d\n", msginfo->msgnum);
5574 static void compose_remove_draft(Compose *compose)
5577 MsgInfo *msginfo = compose->targetinfo;
5578 drafts = account_get_special_folder(compose->account, F_DRAFT);
5580 if (procmsg_msg_exist(msginfo)) {
5581 folder_item_remove_msg(drafts, msginfo->msgnum);
5586 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5587 gboolean remove_reedit_target)
5589 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5592 static gboolean compose_warn_encryption(Compose *compose)
5594 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5595 AlertValue val = G_ALERTALTERNATE;
5597 if (warning == NULL)
5600 val = alertpanel_full(_("Encryption warning"), warning,
5601 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5602 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5603 if (val & G_ALERTDISABLE) {
5604 val &= ~G_ALERTDISABLE;
5605 if (val == G_ALERTALTERNATE)
5606 privacy_inhibit_encrypt_warning(compose->privacy_system,
5610 if (val == G_ALERTALTERNATE) {
5617 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5618 gchar **msgpath, gboolean check_subject,
5619 gboolean remove_reedit_target)
5626 static gboolean lock = FALSE;
5627 PrefsAccount *mailac = NULL, *newsac = NULL;
5628 gboolean err = FALSE;
5630 debug_print("queueing message...\n");
5631 cm_return_val_if_fail(compose->account != NULL, -1);
5635 if (compose_check_entries(compose, check_subject) == FALSE) {
5637 if (compose->batch) {
5638 gtk_widget_show_all(compose->window);
5643 if (!compose->to_list && !compose->newsgroup_list) {
5644 g_warning("can't get recipient list.");
5649 if (compose->to_list) {
5650 if (compose->account->protocol != A_NNTP)
5651 mailac = compose->account;
5652 else if (cur_account && cur_account->protocol != A_NNTP)
5653 mailac = cur_account;
5654 else if (!(mailac = compose_current_mail_account())) {
5656 alertpanel_error(_("No account for sending mails available!"));
5661 if (compose->newsgroup_list) {
5662 if (compose->account->protocol == A_NNTP)
5663 newsac = compose->account;
5666 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5671 /* write queue header */
5672 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5673 G_DIR_SEPARATOR, compose, (guint) rand());
5674 debug_print("queuing to %s\n", tmp);
5675 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5676 FILE_OP_ERROR(tmp, "fopen");
5682 if (change_file_mode_rw(fp, tmp) < 0) {
5683 FILE_OP_ERROR(tmp, "chmod");
5684 g_warning("can't change file mode\n");
5687 /* queueing variables */
5688 err |= (fprintf(fp, "AF:\n") < 0);
5689 err |= (fprintf(fp, "NF:0\n") < 0);
5690 err |= (fprintf(fp, "PS:10\n") < 0);
5691 err |= (fprintf(fp, "SRH:1\n") < 0);
5692 err |= (fprintf(fp, "SFN:\n") < 0);
5693 err |= (fprintf(fp, "DSR:\n") < 0);
5695 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5697 err |= (fprintf(fp, "MID:\n") < 0);
5698 err |= (fprintf(fp, "CFG:\n") < 0);
5699 err |= (fprintf(fp, "PT:0\n") < 0);
5700 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5701 err |= (fprintf(fp, "RQ:\n") < 0);
5703 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5705 err |= (fprintf(fp, "SSV:\n") < 0);
5707 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5709 err |= (fprintf(fp, "NSV:\n") < 0);
5710 err |= (fprintf(fp, "SSH:\n") < 0);
5711 /* write recepient list */
5712 if (compose->to_list) {
5713 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5714 for (cur = compose->to_list->next; cur != NULL;
5716 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5717 err |= (fprintf(fp, "\n") < 0);
5719 /* write newsgroup list */
5720 if (compose->newsgroup_list) {
5721 err |= (fprintf(fp, "NG:") < 0);
5722 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5723 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5724 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5725 err |= (fprintf(fp, "\n") < 0);
5727 /* Sylpheed account IDs */
5729 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5731 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5734 if (compose->privacy_system != NULL) {
5735 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5736 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5737 if (compose->use_encryption) {
5739 if (!compose_warn_encryption(compose)) {
5746 if (mailac && mailac->encrypt_to_self) {
5747 GSList *tmp_list = g_slist_copy(compose->to_list);
5748 tmp_list = g_slist_append(tmp_list, compose->account->address);
5749 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5750 g_slist_free(tmp_list);
5752 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5754 if (encdata != NULL) {
5755 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5756 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5757 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5759 } /* else we finally dont want to encrypt */
5761 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5762 /* and if encdata was null, it means there's been a problem in
5774 /* Save copy folder */
5775 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5776 gchar *savefolderid;
5778 savefolderid = compose_get_save_to(compose);
5779 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5780 g_free(savefolderid);
5782 /* Save copy folder */
5783 if (compose->return_receipt) {
5784 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5786 /* Message-ID of message replying to */
5787 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5790 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5791 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5794 /* Message-ID of message forwarding to */
5795 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5798 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5799 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5803 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5804 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5806 /* end of headers */
5807 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5809 if (compose->redirect_filename != NULL) {
5810 if (compose_redirect_write_to_file(compose, fp) < 0) {
5819 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5824 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5828 g_warning("failed to write queue message\n");
5835 if (fclose(fp) == EOF) {
5836 FILE_OP_ERROR(tmp, "fclose");
5843 if (item && *item) {
5846 queue = account_get_special_folder(compose->account, F_QUEUE);
5849 g_warning("can't find queue folder\n");
5855 folder_item_scan(queue);
5856 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5857 g_warning("can't queue the message\n");
5864 if (msgpath == NULL) {
5870 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5871 compose_remove_reedit_target(compose, FALSE);
5874 if ((msgnum != NULL) && (item != NULL)) {
5882 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5885 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5887 struct stat statbuf;
5888 gchar *type, *subtype;
5889 GtkTreeModel *model;
5892 model = gtk_tree_view_get_model(tree_view);
5894 if (!gtk_tree_model_get_iter_first(model, &iter))
5897 gtk_tree_model_get(model, &iter,
5901 if (!is_file_exist(ainfo->file)) {
5902 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5903 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5904 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5906 if (val == G_ALERTDEFAULT) {
5911 mimepart = procmime_mimeinfo_new();
5912 mimepart->content = MIMECONTENT_FILE;
5913 mimepart->data.filename = g_strdup(ainfo->file);
5914 mimepart->tmp = FALSE; /* or we destroy our attachment */
5915 mimepart->offset = 0;
5917 g_stat(ainfo->file, &statbuf);
5918 mimepart->length = statbuf.st_size;
5920 type = g_strdup(ainfo->content_type);
5922 if (!strchr(type, '/')) {
5924 type = g_strdup("application/octet-stream");
5927 subtype = strchr(type, '/') + 1;
5928 *(subtype - 1) = '\0';
5929 mimepart->type = procmime_get_media_type(type);
5930 mimepart->subtype = g_strdup(subtype);
5933 if (mimepart->type == MIMETYPE_MESSAGE &&
5934 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5935 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5938 if (mimepart->type == MIMETYPE_APPLICATION &&
5939 !strcmp2(mimepart->subtype, "octet-stream"))
5940 g_hash_table_insert(mimepart->typeparameters,
5941 g_strdup("name"), g_strdup(ainfo->name));
5942 g_hash_table_insert(mimepart->dispositionparameters,
5943 g_strdup("filename"), g_strdup(ainfo->name));
5944 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5948 if (compose->use_signing) {
5949 if (ainfo->encoding == ENC_7BIT)
5950 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5951 else if (ainfo->encoding == ENC_8BIT)
5952 ainfo->encoding = ENC_BASE64;
5955 procmime_encode_content(mimepart, ainfo->encoding);
5957 g_node_append(parent->node, mimepart->node);
5958 } while (gtk_tree_model_iter_next(model, &iter));
5963 #define IS_IN_CUSTOM_HEADER(header) \
5964 (compose->account->add_customhdr && \
5965 custom_header_find(compose->account->customhdr_list, header) != NULL)
5967 static void compose_add_headerfield_from_headerlist(Compose *compose,
5969 const gchar *fieldname,
5970 const gchar *seperator)
5972 gchar *str, *fieldname_w_colon;
5973 gboolean add_field = FALSE;
5975 ComposeHeaderEntry *headerentry;
5976 const gchar *headerentryname;
5977 const gchar *trans_fieldname;
5980 if (IS_IN_CUSTOM_HEADER(fieldname))
5983 debug_print("Adding %s-fields\n", fieldname);
5985 fieldstr = g_string_sized_new(64);
5987 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5988 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5990 for (list = compose->header_list; list; list = list->next) {
5991 headerentry = ((ComposeHeaderEntry *)list->data);
5992 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5994 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5995 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5997 if (str[0] != '\0') {
5999 g_string_append(fieldstr, seperator);
6000 g_string_append(fieldstr, str);
6009 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6010 compose_convert_header
6011 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6012 strlen(fieldname) + 2, TRUE);
6013 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6017 g_free(fieldname_w_colon);
6018 g_string_free(fieldstr, TRUE);
6023 static gchar *compose_get_header(Compose *compose)
6025 gchar buf[BUFFSIZE];
6026 const gchar *entry_str;
6030 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6032 gchar *from_name = NULL, *from_address = NULL;
6035 cm_return_val_if_fail(compose->account != NULL, NULL);
6036 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6038 header = g_string_sized_new(64);
6041 get_rfc822_date(buf, sizeof(buf));
6042 g_string_append_printf(header, "Date: %s\n", buf);
6046 if (compose->account->name && *compose->account->name) {
6048 QUOTE_IF_REQUIRED(buf, compose->account->name);
6049 tmp = g_strdup_printf("%s <%s>",
6050 buf, compose->account->address);
6052 tmp = g_strdup_printf("%s",
6053 compose->account->address);
6055 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6056 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6058 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6059 from_address = g_strdup(compose->account->address);
6061 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6062 /* extract name and address */
6063 if (strstr(spec, " <") && strstr(spec, ">")) {
6064 from_address = g_strdup(strrchr(spec, '<')+1);
6065 *(strrchr(from_address, '>')) = '\0';
6066 from_name = g_strdup(spec);
6067 *(strrchr(from_name, '<')) = '\0';
6070 from_address = g_strdup(spec);
6077 if (from_name && *from_name) {
6078 compose_convert_header
6079 (compose, buf, sizeof(buf), from_name,
6080 strlen("From: "), TRUE);
6081 QUOTE_IF_REQUIRED(name, buf);
6083 g_string_append_printf(header, "From: %s <%s>\n",
6084 name, from_address);
6086 g_string_append_printf(header, "From: %s\n", from_address);
6089 g_free(from_address);
6092 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6095 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6098 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6101 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6104 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6106 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6109 compose_convert_header(compose, buf, sizeof(buf), str,
6110 strlen("Subject: "), FALSE);
6111 g_string_append_printf(header, "Subject: %s\n", buf);
6117 if (compose->account->set_domain && compose->account->domain) {
6118 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6119 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6120 g_snprintf(buf, sizeof(buf), "%s",
6121 strchr(compose->account->address, '@') ?
6122 strchr(compose->account->address, '@')+1 :
6123 compose->account->address);
6125 g_snprintf(buf, sizeof(buf), "%s", "");
6128 if (compose->account->gen_msgid) {
6130 if (compose->account->msgid_with_addr) {
6131 addr = compose->account->address;
6133 generate_msgid(buf, sizeof(buf), addr);
6134 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6135 compose->msgid = g_strdup(buf);
6137 compose->msgid = NULL;
6140 if (compose->remove_references == FALSE) {
6142 if (compose->inreplyto && compose->to_list)
6143 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6146 if (compose->references)
6147 g_string_append_printf(header, "References: %s\n", compose->references);
6151 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6154 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6157 if (compose->account->organization &&
6158 strlen(compose->account->organization) &&
6159 !IS_IN_CUSTOM_HEADER("Organization")) {
6160 compose_convert_header(compose, buf, sizeof(buf),
6161 compose->account->organization,
6162 strlen("Organization: "), FALSE);
6163 g_string_append_printf(header, "Organization: %s\n", buf);
6166 /* Program version and system info */
6167 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6168 !compose->newsgroup_list) {
6169 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6171 gtk_major_version, gtk_minor_version, gtk_micro_version,
6174 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6175 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6177 gtk_major_version, gtk_minor_version, gtk_micro_version,
6181 /* custom headers */
6182 if (compose->account->add_customhdr) {
6185 for (cur = compose->account->customhdr_list; cur != NULL;
6187 CustomHeader *chdr = (CustomHeader *)cur->data;
6189 if (custom_header_is_allowed(chdr->name)) {
6190 compose_convert_header
6191 (compose, buf, sizeof(buf),
6192 chdr->value ? chdr->value : "",
6193 strlen(chdr->name) + 2, FALSE);
6194 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6199 /* Automatic Faces and X-Faces */
6200 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6201 g_string_append_printf(header, "X-Face: %s\n", buf);
6203 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6204 g_string_append_printf(header, "X-Face: %s\n", buf);
6206 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6207 g_string_append_printf(header, "Face: %s\n", buf);
6209 else if (get_default_face (buf, sizeof(buf)) == 0) {
6210 g_string_append_printf(header, "Face: %s\n", buf);
6214 switch (compose->priority) {
6215 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6216 "X-Priority: 1 (Highest)\n");
6218 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6219 "X-Priority: 2 (High)\n");
6221 case PRIORITY_NORMAL: break;
6222 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6223 "X-Priority: 4 (Low)\n");
6225 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6226 "X-Priority: 5 (Lowest)\n");
6228 default: debug_print("compose: priority unknown : %d\n",
6232 /* Request Return Receipt */
6233 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6234 if (compose->return_receipt) {
6235 if (compose->account->name
6236 && *compose->account->name) {
6237 compose_convert_header(compose, buf, sizeof(buf),
6238 compose->account->name,
6239 strlen("Disposition-Notification-To: "),
6241 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6243 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6247 /* get special headers */
6248 for (list = compose->header_list; list; list = list->next) {
6249 ComposeHeaderEntry *headerentry;
6252 gchar *headername_wcolon;
6253 const gchar *headername_trans;
6256 gboolean standard_header = FALSE;
6258 headerentry = ((ComposeHeaderEntry *)list->data);
6260 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6262 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6267 if (!strstr(tmp, ":")) {
6268 headername_wcolon = g_strconcat(tmp, ":", NULL);
6269 headername = g_strdup(tmp);
6271 headername_wcolon = g_strdup(tmp);
6272 headername = g_strdup(strtok(tmp, ":"));
6276 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6277 Xstrdup_a(headervalue, entry_str, return NULL);
6278 subst_char(headervalue, '\r', ' ');
6279 subst_char(headervalue, '\n', ' ');
6280 string = std_headers;
6281 while (*string != NULL) {
6282 headername_trans = prefs_common_translated_header_name(*string);
6283 if (!strcmp(headername_trans, headername_wcolon))
6284 standard_header = TRUE;
6287 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6288 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6291 g_free(headername_wcolon);
6295 g_string_free(header, FALSE);
6300 #undef IS_IN_CUSTOM_HEADER
6302 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6303 gint header_len, gboolean addr_field)
6305 gchar *tmpstr = NULL;
6306 const gchar *out_codeset = NULL;
6308 cm_return_if_fail(src != NULL);
6309 cm_return_if_fail(dest != NULL);
6311 if (len < 1) return;
6313 tmpstr = g_strdup(src);
6315 subst_char(tmpstr, '\n', ' ');
6316 subst_char(tmpstr, '\r', ' ');
6319 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6320 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6321 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6326 codeconv_set_strict(TRUE);
6327 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6328 conv_get_charset_str(compose->out_encoding));
6329 codeconv_set_strict(FALSE);
6331 if (!dest || *dest == '\0') {
6332 gchar *test_conv_global_out = NULL;
6333 gchar *test_conv_reply = NULL;
6335 /* automatic mode. be automatic. */
6336 codeconv_set_strict(TRUE);
6338 out_codeset = conv_get_outgoing_charset_str();
6340 debug_print("trying to convert to %s\n", out_codeset);
6341 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6344 if (!test_conv_global_out && compose->orig_charset
6345 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6346 out_codeset = compose->orig_charset;
6347 debug_print("failure; trying to convert to %s\n", out_codeset);
6348 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6351 if (!test_conv_global_out && !test_conv_reply) {
6353 out_codeset = CS_INTERNAL;
6354 debug_print("finally using %s\n", out_codeset);
6356 g_free(test_conv_global_out);
6357 g_free(test_conv_reply);
6358 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6360 codeconv_set_strict(FALSE);
6365 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6369 cm_return_if_fail(user_data != NULL);
6371 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6372 g_strstrip(address);
6373 if (*address != '\0') {
6374 gchar *name = procheader_get_fromname(address);
6375 extract_address(address);
6376 addressbook_add_contact(name, address, NULL, NULL);
6381 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6383 GtkWidget *menuitem;
6386 cm_return_if_fail(menu != NULL);
6387 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6389 menuitem = gtk_separator_menu_item_new();
6390 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6391 gtk_widget_show(menuitem);
6393 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6394 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6396 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6397 g_strstrip(address);
6398 if (*address == '\0') {
6399 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6402 g_signal_connect(G_OBJECT(menuitem), "activate",
6403 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6404 gtk_widget_show(menuitem);
6407 static void compose_create_header_entry(Compose *compose)
6409 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6416 const gchar *header = NULL;
6417 ComposeHeaderEntry *headerentry;
6418 gboolean standard_header = FALSE;
6419 GtkListStore *model;
6421 #if !(GTK_CHECK_VERSION(2,12,0))
6422 GtkTooltips *tips = compose->tooltips;
6425 headerentry = g_new0(ComposeHeaderEntry, 1);
6428 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6429 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6430 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6432 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6434 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6436 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6437 COMPOSE_NEWSGROUPS);
6438 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6440 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6441 COMPOSE_FOLLOWUPTO);
6443 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6444 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6445 G_CALLBACK(compose_grab_focus_cb), compose);
6446 gtk_widget_show(combo);
6447 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6448 compose->header_nextrow, compose->header_nextrow+1,
6449 GTK_SHRINK, GTK_FILL, 0, 0);
6450 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6451 const gchar *last_header_entry = gtk_entry_get_text(
6452 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6454 while (*string != NULL) {
6455 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6456 standard_header = TRUE;
6459 if (standard_header)
6460 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6462 if (!compose->header_last || !standard_header) {
6463 switch(compose->account->protocol) {
6465 header = prefs_common_translated_header_name("Newsgroups:");
6468 header = prefs_common_translated_header_name("To:");
6473 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6475 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6476 G_CALLBACK(compose_grab_focus_cb), compose);
6478 /* Entry field with cleanup button */
6479 #if GTK_CHECK_VERSION(2, 8, 0)
6480 button = gtk_button_new();
6481 gtk_button_set_image(GTK_BUTTON(button),
6482 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6484 button = gtk_button_new_with_label(_("Clear"));
6486 gtk_widget_show(button);
6487 CLAWS_SET_TIP(button,
6488 _("Delete entry contents"));
6489 entry = gtk_entry_new();
6490 gtk_widget_show(entry);
6491 CLAWS_SET_TIP(entry,
6492 _("Use <tab> to autocomplete from addressbook"));
6493 hbox = gtk_hbox_new (FALSE, 0);
6494 gtk_widget_show(hbox);
6495 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6496 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6497 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6498 compose->header_nextrow, compose->header_nextrow+1,
6499 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6501 g_signal_connect(G_OBJECT(entry), "key-press-event",
6502 G_CALLBACK(compose_headerentry_key_press_event_cb),
6504 g_signal_connect(G_OBJECT(entry), "changed",
6505 G_CALLBACK(compose_headerentry_changed_cb),
6507 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6508 G_CALLBACK(compose_grab_focus_cb), compose);
6510 g_signal_connect(G_OBJECT(button), "clicked",
6511 G_CALLBACK(compose_headerentry_button_clicked_cb),
6515 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6516 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6517 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6518 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6519 G_CALLBACK(compose_header_drag_received_cb),
6521 g_signal_connect(G_OBJECT(entry), "drag-drop",
6522 G_CALLBACK(compose_drag_drop),
6524 g_signal_connect(G_OBJECT(entry), "populate-popup",
6525 G_CALLBACK(compose_entry_popup_extend),
6528 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6530 headerentry->compose = compose;
6531 headerentry->combo = combo;
6532 headerentry->entry = entry;
6533 headerentry->button = button;
6534 headerentry->hbox = hbox;
6535 headerentry->headernum = compose->header_nextrow;
6536 headerentry->type = PREF_NONE;
6538 compose->header_nextrow++;
6539 compose->header_last = headerentry;
6540 compose->header_list =
6541 g_slist_append(compose->header_list,
6545 static void compose_add_header_entry(Compose *compose, const gchar *header,
6546 gchar *text, ComposePrefType pref_type)
6548 ComposeHeaderEntry *last_header;
6550 last_header = compose->header_last;
6552 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6553 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6554 last_header->type = pref_type;
6557 static void compose_destroy_headerentry(ComposeHeaderEntry *headerentry)
6559 gtk_widget_destroy(headerentry->combo);
6560 gtk_widget_destroy(headerentry->entry);
6561 gtk_widget_destroy(headerentry->button);
6562 gtk_widget_destroy(headerentry->hbox);
6563 g_free(headerentry);
6566 static void compose_remove_header_entries(Compose *compose)
6569 for (list = compose->header_list; list; list = list->next)
6570 compose_destroy_headerentry((ComposeHeaderEntry *)list->data);
6572 compose->header_last = NULL;
6573 g_slist_free(compose->header_list);
6574 compose->header_list = NULL;
6575 compose->header_nextrow = 1;
6576 compose_create_header_entry(compose);
6579 static GtkWidget *compose_create_header(Compose *compose)
6581 GtkWidget *from_optmenu_hbox;
6582 GtkWidget *header_scrolledwin;
6583 GtkWidget *header_table;
6587 /* header labels and entries */
6588 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6589 gtk_widget_show(header_scrolledwin);
6590 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6592 header_table = gtk_table_new(2, 2, FALSE);
6593 gtk_widget_show(header_table);
6594 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6595 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6596 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6599 /* option menu for selecting accounts */
6600 from_optmenu_hbox = compose_account_option_menu_create(compose);
6601 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6602 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6605 compose->header_table = header_table;
6606 compose->header_list = NULL;
6607 compose->header_nextrow = count;
6609 compose_create_header_entry(compose);
6611 compose->table = NULL;
6613 return header_scrolledwin ;
6616 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6618 Compose *compose = (Compose *)data;
6619 GdkEventButton event;
6622 event.time = gtk_get_current_event_time();
6624 return attach_button_pressed(compose->attach_clist, &event, compose);
6627 static GtkWidget *compose_create_attach(Compose *compose)
6629 GtkWidget *attach_scrwin;
6630 GtkWidget *attach_clist;
6632 GtkListStore *store;
6633 GtkCellRenderer *renderer;
6634 GtkTreeViewColumn *column;
6635 GtkTreeSelection *selection;
6637 /* attachment list */
6638 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6639 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6640 GTK_POLICY_AUTOMATIC,
6641 GTK_POLICY_AUTOMATIC);
6642 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6644 store = gtk_list_store_new(N_ATTACH_COLS,
6649 G_TYPE_AUTO_POINTER,
6651 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6652 (GTK_TREE_MODEL(store)));
6653 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6654 g_object_unref(store);
6656 renderer = gtk_cell_renderer_text_new();
6657 column = gtk_tree_view_column_new_with_attributes
6658 (_("Mime type"), renderer, "text",
6659 COL_MIMETYPE, NULL);
6660 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6662 renderer = gtk_cell_renderer_text_new();
6663 column = gtk_tree_view_column_new_with_attributes
6664 (_("Size"), renderer, "text",
6666 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6668 renderer = gtk_cell_renderer_text_new();
6669 column = gtk_tree_view_column_new_with_attributes
6670 (_("Name"), renderer, "text",
6672 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6674 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6675 prefs_common.use_stripes_everywhere);
6676 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6677 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6679 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6680 G_CALLBACK(attach_selected), compose);
6681 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6682 G_CALLBACK(attach_button_pressed), compose);
6684 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6685 G_CALLBACK(popup_attach_button_pressed), compose);
6687 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6688 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6689 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6690 G_CALLBACK(popup_attach_button_pressed), compose);
6692 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6693 G_CALLBACK(attach_key_pressed), compose);
6696 gtk_drag_dest_set(attach_clist,
6697 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6698 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6699 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6700 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6701 G_CALLBACK(compose_attach_drag_received_cb),
6703 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6704 G_CALLBACK(compose_drag_drop),
6707 compose->attach_scrwin = attach_scrwin;
6708 compose->attach_clist = attach_clist;
6710 return attach_scrwin;
6713 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6714 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6716 static GtkWidget *compose_create_others(Compose *compose)
6719 GtkWidget *savemsg_checkbtn;
6720 GtkWidget *savemsg_combo;
6721 GtkWidget *savemsg_select;
6724 gchar *folderidentifier;
6726 /* Table for settings */
6727 table = gtk_table_new(3, 1, FALSE);
6728 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6729 gtk_widget_show(table);
6730 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6733 /* Save Message to folder */
6734 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6735 gtk_widget_show(savemsg_checkbtn);
6736 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6737 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6738 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6740 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6741 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6743 savemsg_combo = gtk_combo_box_entry_new_text();
6744 compose->savemsg_checkbtn = savemsg_checkbtn;
6745 compose->savemsg_combo = savemsg_combo;
6746 gtk_widget_show(savemsg_combo);
6748 if (prefs_common.compose_save_to_history)
6749 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6750 prefs_common.compose_save_to_history);
6752 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6753 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6754 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6755 G_CALLBACK(compose_grab_focus_cb), compose);
6756 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6757 folderidentifier = folder_item_get_identifier(account_get_special_folder
6758 (compose->account, F_OUTBOX));
6759 compose_set_save_to(compose, folderidentifier);
6760 g_free(folderidentifier);
6763 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6764 gtk_widget_show(savemsg_select);
6765 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6766 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6767 G_CALLBACK(compose_savemsg_select_cb),
6775 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6777 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6778 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6781 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6786 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6789 path = folder_item_get_identifier(dest);
6791 compose_set_save_to(compose, path);
6795 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6796 GdkAtom clip, GtkTextIter *insert_place);
6799 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6803 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6805 if (event->button == 3) {
6807 GtkTextIter sel_start, sel_end;
6808 gboolean stuff_selected;
6810 /* move the cursor to allow GtkAspell to check the word
6811 * under the mouse */
6812 if (event->x && event->y) {
6813 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6814 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6816 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6819 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6820 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6823 stuff_selected = gtk_text_buffer_get_selection_bounds(
6825 &sel_start, &sel_end);
6827 gtk_text_buffer_place_cursor (buffer, &iter);
6828 /* reselect stuff */
6830 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6831 gtk_text_buffer_select_range(buffer,
6832 &sel_start, &sel_end);
6834 return FALSE; /* pass the event so that the right-click goes through */
6837 if (event->button == 2) {
6842 /* get the middle-click position to paste at the correct place */
6843 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6844 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6846 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6849 entry_paste_clipboard(compose, text,
6850 prefs_common.linewrap_pastes,
6851 GDK_SELECTION_PRIMARY, &iter);
6859 static void compose_spell_menu_changed(void *data)
6861 Compose *compose = (Compose *)data;
6863 GtkWidget *menuitem;
6864 GtkWidget *parent_item;
6865 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6868 if (compose->gtkaspell == NULL)
6871 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6872 "/Menu/Spelling/Options");
6874 /* setting the submenu removes /Spelling/Options from the factory
6875 * so we need to save it */
6877 if (parent_item == NULL) {
6878 parent_item = compose->aspell_options_menu;
6879 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6881 compose->aspell_options_menu = parent_item;
6883 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6885 spell_menu = g_slist_reverse(spell_menu);
6886 for (items = spell_menu;
6887 items; items = items->next) {
6888 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6889 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6890 gtk_widget_show(GTK_WIDGET(menuitem));
6892 g_slist_free(spell_menu);
6894 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6895 gtk_widget_show(parent_item);
6898 static void compose_dict_changed(void *data)
6900 Compose *compose = (Compose *) data;
6902 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6905 gtkaspell_highlight_all(compose->gtkaspell);
6906 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6910 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6912 Compose *compose = (Compose *)data;
6913 GdkEventButton event;
6916 event.time = gtk_get_current_event_time();
6920 return text_clicked(compose->text, &event, compose);
6923 static gboolean compose_force_window_origin = TRUE;
6924 static Compose *compose_create(PrefsAccount *account,
6933 GtkWidget *handlebox;
6935 GtkWidget *notebook;
6937 GtkWidget *attach_hbox;
6938 GtkWidget *attach_lab1;
6939 GtkWidget *attach_lab2;
6944 GtkWidget *subject_hbox;
6945 GtkWidget *subject_frame;
6946 GtkWidget *subject_entry;
6950 GtkWidget *edit_vbox;
6951 GtkWidget *ruler_hbox;
6953 GtkWidget *scrolledwin;
6955 GtkTextBuffer *buffer;
6956 GtkClipboard *clipboard;
6959 UndoMain *undostruct;
6961 gchar *titles[N_ATTACH_COLS];
6962 GtkWidget *popupmenu;
6963 GtkWidget *tmpl_menu;
6964 GtkActionGroup *action_group = NULL;
6967 GtkAspell * gtkaspell = NULL;
6970 static GdkGeometry geometry;
6972 cm_return_val_if_fail(account != NULL, NULL);
6974 debug_print("Creating compose window...\n");
6975 compose = g_new0(Compose, 1);
6977 titles[COL_MIMETYPE] = _("MIME type");
6978 titles[COL_SIZE] = _("Size");
6979 titles[COL_NAME] = _("Name");
6981 compose->batch = batch;
6982 compose->account = account;
6983 compose->folder = folder;
6985 compose->mutex = g_mutex_new();
6986 compose->set_cursor_pos = -1;
6988 #if !(GTK_CHECK_VERSION(2,12,0))
6989 compose->tooltips = tips;
6992 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6994 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6995 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6997 if (!geometry.max_width) {
6998 geometry.max_width = gdk_screen_width();
6999 geometry.max_height = gdk_screen_height();
7002 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7003 &geometry, GDK_HINT_MAX_SIZE);
7004 if (!geometry.min_width) {
7005 geometry.min_width = 600;
7006 geometry.min_height = 440;
7008 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7009 &geometry, GDK_HINT_MIN_SIZE);
7011 #ifndef GENERIC_UMPC
7012 if (compose_force_window_origin)
7013 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7014 prefs_common.compose_y);
7016 g_signal_connect(G_OBJECT(window), "delete_event",
7017 G_CALLBACK(compose_delete_cb), compose);
7018 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7019 gtk_widget_realize(window);
7021 gtkut_widget_set_composer_icon(window);
7023 vbox = gtk_vbox_new(FALSE, 0);
7024 gtk_container_add(GTK_CONTAINER(window), vbox);
7026 compose->ui_manager = gtk_ui_manager_new();
7027 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7028 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7029 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7030 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7031 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7032 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7033 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7034 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7035 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7036 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7039 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7041 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7045 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7047 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7049 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7050 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7051 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7054 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7055 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7056 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7057 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7059 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7060 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7061 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7062 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7063 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7066 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7067 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7068 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7070 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7072 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7076 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7077 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7079 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7081 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7082 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7083 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7084 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7085 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7086 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7087 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7088 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7089 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7091 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7092 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7093 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7094 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7097 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7099 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7100 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7101 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7102 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7103 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7105 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7107 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7111 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7112 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7113 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7114 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7115 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7116 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7120 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7121 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7122 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7123 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7124 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7127 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7128 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7129 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7130 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7133 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7134 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7135 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7136 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7137 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7139 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7144 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7145 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7149 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7155 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7156 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)
7157 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)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7162 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7163 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)
7164 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)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7169 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)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7173 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)
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7179 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)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7185 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)
7186 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)
7187 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7198 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)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7216 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7217 gtk_widget_show_all(menubar);
7219 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7221 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7223 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7226 if (prefs_common.toolbar_detachable) {
7227 handlebox = gtk_handle_box_new();
7229 handlebox = gtk_hbox_new(FALSE, 0);
7231 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7233 gtk_widget_realize(handlebox);
7235 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7238 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7242 vbox2 = gtk_vbox_new(FALSE, 2);
7243 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7244 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7247 notebook = gtk_notebook_new();
7248 gtk_widget_set_size_request(notebook, -1, 130);
7249 gtk_widget_show(notebook);
7251 /* header labels and entries */
7252 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7253 compose_create_header(compose),
7254 gtk_label_new_with_mnemonic(_("Hea_der")));
7255 /* attachment list */
7256 attach_hbox = gtk_hbox_new(FALSE, 0);
7257 gtk_widget_show(attach_hbox);
7259 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7260 gtk_widget_show(attach_lab1);
7261 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7263 attach_lab2 = gtk_label_new("");
7264 gtk_widget_show(attach_lab2);
7265 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7267 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7268 compose_create_attach(compose),
7271 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7272 compose_create_others(compose),
7273 gtk_label_new_with_mnemonic(_("Othe_rs")));
7276 subject_hbox = gtk_hbox_new(FALSE, 0);
7277 gtk_widget_show(subject_hbox);
7279 subject_frame = gtk_frame_new(NULL);
7280 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7281 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7282 gtk_widget_show(subject_frame);
7284 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7285 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7286 gtk_widget_show(subject);
7288 label = gtk_label_new(_("Subject:"));
7289 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7290 gtk_widget_show(label);
7293 subject_entry = claws_spell_entry_new();
7295 subject_entry = gtk_entry_new();
7297 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7298 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7299 G_CALLBACK(compose_grab_focus_cb), compose);
7300 gtk_widget_show(subject_entry);
7301 compose->subject_entry = subject_entry;
7302 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7304 edit_vbox = gtk_vbox_new(FALSE, 0);
7306 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7309 ruler_hbox = gtk_hbox_new(FALSE, 0);
7310 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7312 ruler = gtk_shruler_new();
7313 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7314 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7318 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7319 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7320 GTK_POLICY_AUTOMATIC,
7321 GTK_POLICY_AUTOMATIC);
7322 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7324 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7325 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7327 text = gtk_text_view_new();
7328 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7329 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7330 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7331 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7332 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7334 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7336 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7337 G_CALLBACK(compose_edit_size_alloc),
7339 g_signal_connect(G_OBJECT(buffer), "changed",
7340 G_CALLBACK(compose_changed_cb), compose);
7341 g_signal_connect(G_OBJECT(text), "grab_focus",
7342 G_CALLBACK(compose_grab_focus_cb), compose);
7343 g_signal_connect(G_OBJECT(buffer), "insert_text",
7344 G_CALLBACK(text_inserted), compose);
7345 g_signal_connect(G_OBJECT(text), "button_press_event",
7346 G_CALLBACK(text_clicked), compose);
7348 g_signal_connect(G_OBJECT(text), "popup-menu",
7349 G_CALLBACK(compose_popup_menu), compose);
7351 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7352 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7353 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7354 G_CALLBACK(compose_popup_menu), compose);
7356 g_signal_connect(G_OBJECT(subject_entry), "changed",
7357 G_CALLBACK(compose_changed_cb), compose);
7360 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7361 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7362 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7363 g_signal_connect(G_OBJECT(text), "drag_data_received",
7364 G_CALLBACK(compose_insert_drag_received_cb),
7366 g_signal_connect(G_OBJECT(text), "drag-drop",
7367 G_CALLBACK(compose_drag_drop),
7369 gtk_widget_show_all(vbox);
7371 /* pane between attach clist and text */
7372 paned = gtk_vpaned_new();
7373 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7375 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7376 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7378 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7380 gtk_paned_add1(GTK_PANED(paned), notebook);
7381 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7382 gtk_widget_show_all(paned);
7385 if (prefs_common.textfont) {
7386 PangoFontDescription *font_desc;
7388 font_desc = pango_font_description_from_string
7389 (prefs_common.textfont);
7391 gtk_widget_modify_font(text, font_desc);
7392 pango_font_description_free(font_desc);
7396 gtk_action_group_add_actions(action_group, compose_popup_entries,
7397 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7398 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7399 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7400 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7401 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7405 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7407 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7408 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7409 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7411 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7413 undostruct = undo_init(text);
7414 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7417 address_completion_start(window);
7419 compose->window = window;
7420 compose->vbox = vbox;
7421 compose->menubar = menubar;
7422 compose->handlebox = handlebox;
7424 compose->vbox2 = vbox2;
7426 compose->paned = paned;
7428 compose->attach_label = attach_lab2;
7430 compose->notebook = notebook;
7431 compose->edit_vbox = edit_vbox;
7432 compose->ruler_hbox = ruler_hbox;
7433 compose->ruler = ruler;
7434 compose->scrolledwin = scrolledwin;
7435 compose->text = text;
7437 compose->focused_editable = NULL;
7439 compose->popupmenu = popupmenu;
7441 compose->tmpl_menu = tmpl_menu;
7443 compose->mode = mode;
7444 compose->rmode = mode;
7446 compose->targetinfo = NULL;
7447 compose->replyinfo = NULL;
7448 compose->fwdinfo = NULL;
7450 compose->replyto = NULL;
7452 compose->bcc = NULL;
7453 compose->followup_to = NULL;
7455 compose->ml_post = NULL;
7457 compose->inreplyto = NULL;
7458 compose->references = NULL;
7459 compose->msgid = NULL;
7460 compose->boundary = NULL;
7462 compose->autowrap = prefs_common.autowrap;
7463 compose->autoindent = prefs_common.auto_indent;
7464 compose->use_signing = FALSE;
7465 compose->use_encryption = FALSE;
7466 compose->privacy_system = NULL;
7468 compose->modified = FALSE;
7470 compose->return_receipt = FALSE;
7472 compose->to_list = NULL;
7473 compose->newsgroup_list = NULL;
7475 compose->undostruct = undostruct;
7477 compose->sig_str = NULL;
7479 compose->exteditor_file = NULL;
7480 compose->exteditor_pid = -1;
7481 compose->exteditor_tag = -1;
7482 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7485 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7486 if (mode != COMPOSE_REDIRECT) {
7487 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7488 strcmp(prefs_common.dictionary, "")) {
7489 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7490 prefs_common.alt_dictionary,
7491 conv_get_locale_charset_str(),
7492 prefs_common.misspelled_col,
7493 prefs_common.check_while_typing,
7494 prefs_common.recheck_when_changing_dict,
7495 prefs_common.use_alternate,
7496 prefs_common.use_both_dicts,
7497 GTK_TEXT_VIEW(text),
7498 GTK_WINDOW(compose->window),
7499 compose_dict_changed,
7500 compose_spell_menu_changed,
7503 alertpanel_error(_("Spell checker could not "
7505 gtkaspell_checkers_strerror());
7506 gtkaspell_checkers_reset_error();
7508 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7512 compose->gtkaspell = gtkaspell;
7513 compose_spell_menu_changed(compose);
7514 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7517 compose_select_account(compose, account, TRUE);
7519 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7520 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7522 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7523 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7525 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7526 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7528 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7529 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7531 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7532 if (account->protocol != A_NNTP)
7533 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7534 prefs_common_translated_header_name("To:"));
7536 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7537 prefs_common_translated_header_name("Newsgroups:"));
7539 addressbook_set_target_compose(compose);
7541 if (mode != COMPOSE_REDIRECT)
7542 compose_set_template_menu(compose);
7544 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7547 compose_list = g_list_append(compose_list, compose);
7549 if (!prefs_common.show_ruler)
7550 gtk_widget_hide(ruler_hbox);
7552 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7555 compose->priority = PRIORITY_NORMAL;
7556 compose_update_priority_menu_item(compose);
7558 compose_set_out_encoding(compose);
7561 compose_update_actions_menu(compose);
7563 /* Privacy Systems menu */
7564 compose_update_privacy_systems_menu(compose);
7566 activate_privacy_system(compose, account, TRUE);
7567 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7569 gtk_widget_realize(window);
7571 gtk_widget_show(window);
7573 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7574 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7581 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7586 GtkWidget *optmenubox;
7589 GtkWidget *from_name = NULL;
7590 #if !(GTK_CHECK_VERSION(2,12,0))
7591 GtkTooltips *tips = compose->tooltips;
7594 gint num = 0, def_menu = 0;
7596 accounts = account_get_list();
7597 cm_return_val_if_fail(accounts != NULL, NULL);
7599 optmenubox = gtk_event_box_new();
7600 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7601 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7603 hbox = gtk_hbox_new(FALSE, 6);
7604 from_name = gtk_entry_new();
7606 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7607 G_CALLBACK(compose_grab_focus_cb), compose);
7609 for (; accounts != NULL; accounts = accounts->next, num++) {
7610 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7611 gchar *name, *from = NULL;
7613 if (ac == compose->account) def_menu = num;
7615 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7618 if (ac == compose->account) {
7619 if (ac->name && *ac->name) {
7621 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7622 from = g_strdup_printf("%s <%s>",
7624 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7626 from = g_strdup_printf("%s",
7628 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7631 COMBOBOX_ADD(menu, name, ac->account_id);
7636 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7638 g_signal_connect(G_OBJECT(optmenu), "changed",
7639 G_CALLBACK(account_activated),
7641 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7642 G_CALLBACK(compose_entry_popup_extend),
7645 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7646 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7648 CLAWS_SET_TIP(optmenubox,
7649 _("Account to use for this email"));
7650 CLAWS_SET_TIP(from_name,
7651 _("Sender address to be used"));
7653 compose->from_name = from_name;
7658 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7660 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7661 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7662 Compose *compose = (Compose *) data;
7664 compose->priority = value;
7668 static void compose_reply_change_mode(Compose *compose,
7671 gboolean was_modified = compose->modified;
7673 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7675 cm_return_if_fail(compose->replyinfo != NULL);
7677 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7679 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7681 if (action == COMPOSE_REPLY_TO_ALL)
7683 if (action == COMPOSE_REPLY_TO_SENDER)
7685 if (action == COMPOSE_REPLY_TO_LIST)
7688 compose_remove_header_entries(compose);
7689 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7690 if (compose->account->set_autocc && compose->account->auto_cc)
7691 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7693 if (compose->account->set_autobcc && compose->account->auto_bcc)
7694 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7696 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7697 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7698 compose_show_first_last_header(compose, TRUE);
7699 compose->modified = was_modified;
7700 compose_set_title(compose);
7703 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7705 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7706 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7707 Compose *compose = (Compose *) data;
7710 compose_reply_change_mode(compose, value);
7713 static void compose_update_priority_menu_item(Compose * compose)
7715 GtkWidget *menuitem = NULL;
7716 switch (compose->priority) {
7717 case PRIORITY_HIGHEST:
7718 menuitem = gtk_ui_manager_get_widget
7719 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7722 menuitem = gtk_ui_manager_get_widget
7723 (compose->ui_manager, "/Menu/Options/Priority/High");
7725 case PRIORITY_NORMAL:
7726 menuitem = gtk_ui_manager_get_widget
7727 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7730 menuitem = gtk_ui_manager_get_widget
7731 (compose->ui_manager, "/Menu/Options/Priority/Low");
7733 case PRIORITY_LOWEST:
7734 menuitem = gtk_ui_manager_get_widget
7735 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7738 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7741 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7743 Compose *compose = (Compose *) data;
7745 gboolean can_sign = FALSE, can_encrypt = FALSE;
7747 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7749 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7752 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7753 g_free(compose->privacy_system);
7754 compose->privacy_system = NULL;
7755 if (systemid != NULL) {
7756 compose->privacy_system = g_strdup(systemid);
7758 can_sign = privacy_system_can_sign(systemid);
7759 can_encrypt = privacy_system_can_encrypt(systemid);
7762 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7764 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7765 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7768 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7770 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7771 GtkWidget *menuitem = NULL;
7773 gboolean can_sign = FALSE, can_encrypt = FALSE;
7774 gboolean found = FALSE;
7776 if (compose->privacy_system != NULL) {
7778 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7779 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7780 cm_return_if_fail(menuitem != NULL);
7782 amenu = GTK_MENU_SHELL(menuitem)->children;
7784 while (amenu != NULL) {
7785 GList *alist = amenu->next;
7787 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7788 if (systemid != NULL) {
7789 if (strcmp(systemid, compose->privacy_system) == 0 &&
7790 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7791 menuitem = GTK_WIDGET(amenu->data);
7793 can_sign = privacy_system_can_sign(systemid);
7794 can_encrypt = privacy_system_can_encrypt(systemid);
7798 } else if (strlen(compose->privacy_system) == 0 &&
7799 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7800 menuitem = GTK_WIDGET(amenu->data);
7803 can_encrypt = FALSE;
7810 if (menuitem != NULL)
7811 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7813 if (warn && !found && strlen(compose->privacy_system)) {
7814 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7815 "will not be able to sign or encrypt this message."),
7816 compose->privacy_system);
7820 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7821 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7824 static void compose_set_out_encoding(Compose *compose)
7826 CharSet out_encoding;
7827 const gchar *branch = NULL;
7828 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7830 switch(out_encoding) {
7831 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7832 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7833 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7834 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7835 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7836 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7837 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7838 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7839 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7840 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7841 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7842 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7843 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7844 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7845 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7846 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7847 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7848 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7849 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7850 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7851 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7852 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7853 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7854 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7855 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7856 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7857 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7858 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7859 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7860 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7861 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7862 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7864 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7867 static void compose_set_template_menu(Compose *compose)
7869 GSList *tmpl_list, *cur;
7873 tmpl_list = template_get_config();
7875 menu = gtk_menu_new();
7877 gtk_menu_set_accel_group (GTK_MENU (menu),
7878 gtk_ui_manager_get_accel_group(compose->ui_manager));
7879 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7880 Template *tmpl = (Template *)cur->data;
7881 gchar *accel_path = NULL;
7882 item = gtk_menu_item_new_with_label(tmpl->name);
7883 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7884 g_signal_connect(G_OBJECT(item), "activate",
7885 G_CALLBACK(compose_template_activate_cb),
7887 g_object_set_data(G_OBJECT(item), "template", tmpl);
7888 gtk_widget_show(item);
7889 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7890 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7894 gtk_widget_show(menu);
7895 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7898 void compose_update_actions_menu(Compose *compose)
7900 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7903 static void compose_update_privacy_systems_menu(Compose *compose)
7905 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7906 GSList *systems, *cur;
7908 GtkWidget *system_none;
7910 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7911 GtkWidget *privacy_menu = gtk_menu_new();
7913 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7914 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7916 g_signal_connect(G_OBJECT(system_none), "activate",
7917 G_CALLBACK(compose_set_privacy_system_cb), compose);
7919 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7920 gtk_widget_show(system_none);
7922 systems = privacy_get_system_ids();
7923 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7924 gchar *systemid = cur->data;
7926 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7927 widget = gtk_radio_menu_item_new_with_label(group,
7928 privacy_system_get_name(systemid));
7929 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7930 g_strdup(systemid), g_free);
7931 g_signal_connect(G_OBJECT(widget), "activate",
7932 G_CALLBACK(compose_set_privacy_system_cb), compose);
7934 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7935 gtk_widget_show(widget);
7938 g_slist_free(systems);
7939 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7940 gtk_widget_show_all(privacy_menu);
7941 gtk_widget_show_all(privacy_menuitem);
7944 void compose_reflect_prefs_all(void)
7949 for (cur = compose_list; cur != NULL; cur = cur->next) {
7950 compose = (Compose *)cur->data;
7951 compose_set_template_menu(compose);
7955 void compose_reflect_prefs_pixmap_theme(void)
7960 for (cur = compose_list; cur != NULL; cur = cur->next) {
7961 compose = (Compose *)cur->data;
7962 toolbar_update(TOOLBAR_COMPOSE, compose);
7966 static const gchar *compose_quote_char_from_context(Compose *compose)
7968 const gchar *qmark = NULL;
7970 cm_return_val_if_fail(compose != NULL, NULL);
7972 switch (compose->mode) {
7973 /* use forward-specific quote char */
7974 case COMPOSE_FORWARD:
7975 case COMPOSE_FORWARD_AS_ATTACH:
7976 case COMPOSE_FORWARD_INLINE:
7977 if (compose->folder && compose->folder->prefs &&
7978 compose->folder->prefs->forward_with_format)
7979 qmark = compose->folder->prefs->forward_quotemark;
7980 else if (compose->account->forward_with_format)
7981 qmark = compose->account->forward_quotemark;
7983 qmark = prefs_common.fw_quotemark;
7986 /* use reply-specific quote char in all other modes */
7988 if (compose->folder && compose->folder->prefs &&
7989 compose->folder->prefs->reply_with_format)
7990 qmark = compose->folder->prefs->reply_quotemark;
7991 else if (compose->account->reply_with_format)
7992 qmark = compose->account->reply_quotemark;
7994 qmark = prefs_common.quotemark;
7998 if (qmark == NULL || *qmark == '\0')
8004 static void compose_template_apply(Compose *compose, Template *tmpl,
8008 GtkTextBuffer *buffer;
8012 gchar *parsed_str = NULL;
8013 gint cursor_pos = 0;
8014 const gchar *err_msg = _("The body of the template has an error at line %d.");
8017 /* process the body */
8019 text = GTK_TEXT_VIEW(compose->text);
8020 buffer = gtk_text_view_get_buffer(text);
8023 qmark = compose_quote_char_from_context(compose);
8025 if (compose->replyinfo != NULL) {
8028 gtk_text_buffer_set_text(buffer, "", -1);
8029 mark = gtk_text_buffer_get_insert(buffer);
8030 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8032 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8033 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8035 } else if (compose->fwdinfo != NULL) {
8038 gtk_text_buffer_set_text(buffer, "", -1);
8039 mark = gtk_text_buffer_get_insert(buffer);
8040 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8042 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8043 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8046 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8048 GtkTextIter start, end;
8051 gtk_text_buffer_get_start_iter(buffer, &start);
8052 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8053 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8055 /* clear the buffer now */
8057 gtk_text_buffer_set_text(buffer, "", -1);
8059 parsed_str = compose_quote_fmt(compose, dummyinfo,
8060 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8061 procmsg_msginfo_free( dummyinfo );
8067 gtk_text_buffer_set_text(buffer, "", -1);
8068 mark = gtk_text_buffer_get_insert(buffer);
8069 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8072 if (replace && parsed_str && compose->account->auto_sig)
8073 compose_insert_sig(compose, FALSE);
8075 if (replace && parsed_str) {
8076 gtk_text_buffer_get_start_iter(buffer, &iter);
8077 gtk_text_buffer_place_cursor(buffer, &iter);
8081 cursor_pos = quote_fmt_get_cursor_pos();
8082 compose->set_cursor_pos = cursor_pos;
8083 if (cursor_pos == -1)
8085 gtk_text_buffer_get_start_iter(buffer, &iter);
8086 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8087 gtk_text_buffer_place_cursor(buffer, &iter);
8090 /* process the other fields */
8092 compose_template_apply_fields(compose, tmpl);
8093 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8094 quote_fmt_reset_vartable();
8095 compose_changed_cb(NULL, compose);
8098 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8100 MsgInfo* dummyinfo = NULL;
8101 MsgInfo *msginfo = NULL;
8104 if (compose->replyinfo != NULL)
8105 msginfo = compose->replyinfo;
8106 else if (compose->fwdinfo != NULL)
8107 msginfo = compose->fwdinfo;
8109 dummyinfo = compose_msginfo_new_from_compose(compose);
8110 msginfo = dummyinfo;
8113 if (tmpl->from && *tmpl->from != '\0') {
8115 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8116 compose->gtkaspell);
8118 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8120 quote_fmt_scan_string(tmpl->from);
8123 buf = quote_fmt_get_buffer();
8125 alertpanel_error(_("Template From format error."));
8127 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8131 if (tmpl->to && *tmpl->to != '\0') {
8133 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8134 compose->gtkaspell);
8136 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8138 quote_fmt_scan_string(tmpl->to);
8141 buf = quote_fmt_get_buffer();
8143 alertpanel_error(_("Template To format error."));
8145 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8149 if (tmpl->cc && *tmpl->cc != '\0') {
8151 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8152 compose->gtkaspell);
8154 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8156 quote_fmt_scan_string(tmpl->cc);
8159 buf = quote_fmt_get_buffer();
8161 alertpanel_error(_("Template Cc format error."));
8163 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8167 if (tmpl->bcc && *tmpl->bcc != '\0') {
8169 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8170 compose->gtkaspell);
8172 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8174 quote_fmt_scan_string(tmpl->bcc);
8177 buf = quote_fmt_get_buffer();
8179 alertpanel_error(_("Template Bcc format error."));
8181 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8185 /* process the subject */
8186 if (tmpl->subject && *tmpl->subject != '\0') {
8188 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8189 compose->gtkaspell);
8191 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8193 quote_fmt_scan_string(tmpl->subject);
8196 buf = quote_fmt_get_buffer();
8198 alertpanel_error(_("Template subject format error."));
8200 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8204 procmsg_msginfo_free( dummyinfo );
8207 static void compose_destroy(Compose *compose)
8209 GtkTextBuffer *buffer;
8210 GtkClipboard *clipboard;
8212 compose_list = g_list_remove(compose_list, compose);
8214 if (compose->updating) {
8215 debug_print("danger, not destroying anything now\n");
8216 compose->deferred_destroy = TRUE;
8219 /* NOTE: address_completion_end() does nothing with the window
8220 * however this may change. */
8221 address_completion_end(compose->window);
8223 slist_free_strings(compose->to_list);
8224 g_slist_free(compose->to_list);
8225 slist_free_strings(compose->newsgroup_list);
8226 g_slist_free(compose->newsgroup_list);
8227 slist_free_strings(compose->header_list);
8228 g_slist_free(compose->header_list);
8230 procmsg_msginfo_free(compose->targetinfo);
8231 procmsg_msginfo_free(compose->replyinfo);
8232 procmsg_msginfo_free(compose->fwdinfo);
8234 g_free(compose->replyto);
8235 g_free(compose->cc);
8236 g_free(compose->bcc);
8237 g_free(compose->newsgroups);
8238 g_free(compose->followup_to);
8240 g_free(compose->ml_post);
8242 g_free(compose->inreplyto);
8243 g_free(compose->references);
8244 g_free(compose->msgid);
8245 g_free(compose->boundary);
8247 g_free(compose->redirect_filename);
8248 if (compose->undostruct)
8249 undo_destroy(compose->undostruct);
8251 g_free(compose->sig_str);
8253 g_free(compose->exteditor_file);
8255 g_free(compose->orig_charset);
8257 g_free(compose->privacy_system);
8259 if (addressbook_get_target_compose() == compose)
8260 addressbook_set_target_compose(NULL);
8263 if (compose->gtkaspell) {
8264 gtkaspell_delete(compose->gtkaspell);
8265 compose->gtkaspell = NULL;
8269 if (!compose->batch) {
8270 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8271 prefs_common.compose_height = compose->window->allocation.height;
8274 if (!gtk_widget_get_parent(compose->paned))
8275 gtk_widget_destroy(compose->paned);
8276 gtk_widget_destroy(compose->popupmenu);
8278 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8279 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8280 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8282 gtk_widget_destroy(compose->window);
8283 toolbar_destroy(compose->toolbar);
8284 g_free(compose->toolbar);
8285 g_mutex_free(compose->mutex);
8289 static void compose_attach_info_free(AttachInfo *ainfo)
8291 g_free(ainfo->file);
8292 g_free(ainfo->content_type);
8293 g_free(ainfo->name);
8297 static void compose_attach_update_label(Compose *compose)
8302 GtkTreeModel *model;
8307 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8308 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8309 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8313 while(gtk_tree_model_iter_next(model, &iter))
8316 text = g_strdup_printf("(%d)", i);
8317 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8321 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8323 Compose *compose = (Compose *)data;
8324 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8325 GtkTreeSelection *selection;
8327 GtkTreeModel *model;
8329 selection = gtk_tree_view_get_selection(tree_view);
8330 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8335 for (cur = sel; cur != NULL; cur = cur->next) {
8336 GtkTreePath *path = cur->data;
8337 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8340 gtk_tree_path_free(path);
8343 for (cur = sel; cur != NULL; cur = cur->next) {
8344 GtkTreeRowReference *ref = cur->data;
8345 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8348 if (gtk_tree_model_get_iter(model, &iter, path))
8349 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8351 gtk_tree_path_free(path);
8352 gtk_tree_row_reference_free(ref);
8356 compose_attach_update_label(compose);
8359 static struct _AttachProperty
8362 GtkWidget *mimetype_entry;
8363 GtkWidget *encoding_optmenu;
8364 GtkWidget *path_entry;
8365 GtkWidget *filename_entry;
8367 GtkWidget *cancel_btn;
8370 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8372 gtk_tree_path_free((GtkTreePath *)ptr);
8375 static void compose_attach_property(GtkAction *action, gpointer data)
8377 Compose *compose = (Compose *)data;
8378 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8380 GtkComboBox *optmenu;
8381 GtkTreeSelection *selection;
8383 GtkTreeModel *model;
8386 static gboolean cancelled;
8388 /* only if one selected */
8389 selection = gtk_tree_view_get_selection(tree_view);
8390 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8393 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8397 path = (GtkTreePath *) sel->data;
8398 gtk_tree_model_get_iter(model, &iter, path);
8399 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8402 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8408 if (!attach_prop.window)
8409 compose_attach_property_create(&cancelled);
8410 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8411 gtk_widget_grab_focus(attach_prop.ok_btn);
8412 gtk_widget_show(attach_prop.window);
8413 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8415 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8416 if (ainfo->encoding == ENC_UNKNOWN)
8417 combobox_select_by_data(optmenu, ENC_BASE64);
8419 combobox_select_by_data(optmenu, ainfo->encoding);
8421 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8422 ainfo->content_type ? ainfo->content_type : "");
8423 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8424 ainfo->file ? ainfo->file : "");
8425 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8426 ainfo->name ? ainfo->name : "");
8429 const gchar *entry_text;
8431 gchar *cnttype = NULL;
8438 gtk_widget_hide(attach_prop.window);
8439 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8444 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8445 if (*entry_text != '\0') {
8448 text = g_strstrip(g_strdup(entry_text));
8449 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8450 cnttype = g_strdup(text);
8453 alertpanel_error(_("Invalid MIME type."));
8459 ainfo->encoding = combobox_get_active_data(optmenu);
8461 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8462 if (*entry_text != '\0') {
8463 if (is_file_exist(entry_text) &&
8464 (size = get_file_size(entry_text)) > 0)
8465 file = g_strdup(entry_text);
8468 (_("File doesn't exist or is empty."));
8474 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8475 if (*entry_text != '\0') {
8476 g_free(ainfo->name);
8477 ainfo->name = g_strdup(entry_text);
8481 g_free(ainfo->content_type);
8482 ainfo->content_type = cnttype;
8485 g_free(ainfo->file);
8489 ainfo->size = (goffset)size;
8491 /* update tree store */
8492 text = to_human_readable(ainfo->size);
8493 gtk_tree_model_get_iter(model, &iter, path);
8494 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8495 COL_MIMETYPE, ainfo->content_type,
8497 COL_NAME, ainfo->name,
8503 gtk_tree_path_free(path);
8506 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8508 label = gtk_label_new(str); \
8509 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8510 GTK_FILL, 0, 0, 0); \
8511 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8513 entry = gtk_entry_new(); \
8514 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8515 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8518 static void compose_attach_property_create(gboolean *cancelled)
8524 GtkWidget *mimetype_entry;
8527 GtkListStore *optmenu_menu;
8528 GtkWidget *path_entry;
8529 GtkWidget *filename_entry;
8532 GtkWidget *cancel_btn;
8533 GList *mime_type_list, *strlist;
8536 debug_print("Creating attach_property window...\n");
8538 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8539 gtk_widget_set_size_request(window, 480, -1);
8540 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8541 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8542 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8543 g_signal_connect(G_OBJECT(window), "delete_event",
8544 G_CALLBACK(attach_property_delete_event),
8546 g_signal_connect(G_OBJECT(window), "key_press_event",
8547 G_CALLBACK(attach_property_key_pressed),
8550 vbox = gtk_vbox_new(FALSE, 8);
8551 gtk_container_add(GTK_CONTAINER(window), vbox);
8553 table = gtk_table_new(4, 2, FALSE);
8554 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8555 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8556 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8558 label = gtk_label_new(_("MIME type"));
8559 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8561 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8562 mimetype_entry = gtk_combo_box_entry_new_text();
8563 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8564 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8566 /* stuff with list */
8567 mime_type_list = procmime_get_mime_type_list();
8569 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8570 MimeType *type = (MimeType *) mime_type_list->data;
8573 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8575 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8578 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8579 (GCompareFunc)strcmp2);
8582 for (mime_type_list = strlist; mime_type_list != NULL;
8583 mime_type_list = mime_type_list->next) {
8584 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8585 g_free(mime_type_list->data);
8587 g_list_free(strlist);
8588 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8589 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8591 label = gtk_label_new(_("Encoding"));
8592 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8594 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8596 hbox = gtk_hbox_new(FALSE, 0);
8597 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8598 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8600 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8601 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8603 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8604 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8605 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8606 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8607 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8609 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8611 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8612 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8614 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8615 &ok_btn, GTK_STOCK_OK,
8617 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8618 gtk_widget_grab_default(ok_btn);
8620 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8621 G_CALLBACK(attach_property_ok),
8623 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8624 G_CALLBACK(attach_property_cancel),
8627 gtk_widget_show_all(vbox);
8629 attach_prop.window = window;
8630 attach_prop.mimetype_entry = mimetype_entry;
8631 attach_prop.encoding_optmenu = optmenu;
8632 attach_prop.path_entry = path_entry;
8633 attach_prop.filename_entry = filename_entry;
8634 attach_prop.ok_btn = ok_btn;
8635 attach_prop.cancel_btn = cancel_btn;
8638 #undef SET_LABEL_AND_ENTRY
8640 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8646 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8652 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8653 gboolean *cancelled)
8661 static gboolean attach_property_key_pressed(GtkWidget *widget,
8663 gboolean *cancelled)
8665 if (event && event->keyval == GDK_Escape) {
8669 if (event && event->keyval == GDK_Return) {
8677 static void compose_exec_ext_editor(Compose *compose)
8684 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8685 G_DIR_SEPARATOR, compose);
8687 if (pipe(pipe_fds) < 0) {
8693 if ((pid = fork()) < 0) {
8700 /* close the write side of the pipe */
8703 compose->exteditor_file = g_strdup(tmp);
8704 compose->exteditor_pid = pid;
8706 compose_set_ext_editor_sensitive(compose, FALSE);
8709 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8711 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8713 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8717 } else { /* process-monitoring process */
8723 /* close the read side of the pipe */
8726 if (compose_write_body_to_file(compose, tmp) < 0) {
8727 fd_write_all(pipe_fds[1], "2\n", 2);
8731 pid_ed = compose_exec_ext_editor_real(tmp);
8733 fd_write_all(pipe_fds[1], "1\n", 2);
8737 /* wait until editor is terminated */
8738 waitpid(pid_ed, NULL, 0);
8740 fd_write_all(pipe_fds[1], "0\n", 2);
8747 #endif /* G_OS_UNIX */
8751 static gint compose_exec_ext_editor_real(const gchar *file)
8758 cm_return_val_if_fail(file != NULL, -1);
8760 if ((pid = fork()) < 0) {
8765 if (pid != 0) return pid;
8767 /* grandchild process */
8769 if (setpgid(0, getppid()))
8772 if (prefs_common_get_ext_editor_cmd() &&
8773 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8774 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8775 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8777 if (prefs_common_get_ext_editor_cmd())
8778 g_warning("External editor command-line is invalid: '%s'\n",
8779 prefs_common_get_ext_editor_cmd());
8780 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8783 cmdline = strsplit_with_quote(buf, " ", 1024);
8784 execvp(cmdline[0], cmdline);
8787 g_strfreev(cmdline);
8792 static gboolean compose_ext_editor_kill(Compose *compose)
8794 pid_t pgid = compose->exteditor_pid * -1;
8797 ret = kill(pgid, 0);
8799 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8803 msg = g_strdup_printf
8804 (_("The external editor is still working.\n"
8805 "Force terminating the process?\n"
8806 "process group id: %d"), -pgid);
8807 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8808 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8812 if (val == G_ALERTALTERNATE) {
8813 g_source_remove(compose->exteditor_tag);
8814 g_io_channel_shutdown(compose->exteditor_ch,
8816 g_io_channel_unref(compose->exteditor_ch);
8818 if (kill(pgid, SIGTERM) < 0) perror("kill");
8819 waitpid(compose->exteditor_pid, NULL, 0);
8821 g_warning("Terminated process group id: %d", -pgid);
8822 g_warning("Temporary file: %s",
8823 compose->exteditor_file);
8825 compose_set_ext_editor_sensitive(compose, TRUE);
8827 g_free(compose->exteditor_file);
8828 compose->exteditor_file = NULL;
8829 compose->exteditor_pid = -1;
8830 compose->exteditor_ch = NULL;
8831 compose->exteditor_tag = -1;
8839 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8843 Compose *compose = (Compose *)data;
8846 debug_print(_("Compose: input from monitoring process\n"));
8848 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8850 g_io_channel_shutdown(source, FALSE, NULL);
8851 g_io_channel_unref(source);
8853 waitpid(compose->exteditor_pid, NULL, 0);
8855 if (buf[0] == '0') { /* success */
8856 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8857 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8859 gtk_text_buffer_set_text(buffer, "", -1);
8860 compose_insert_file(compose, compose->exteditor_file);
8861 compose_changed_cb(NULL, compose);
8862 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8864 if (claws_unlink(compose->exteditor_file) < 0)
8865 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8866 } else if (buf[0] == '1') { /* failed */
8867 g_warning("Couldn't exec external editor\n");
8868 if (claws_unlink(compose->exteditor_file) < 0)
8869 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8870 } else if (buf[0] == '2') {
8871 g_warning("Couldn't write to file\n");
8872 } else if (buf[0] == '3') {
8873 g_warning("Pipe read failed\n");
8876 compose_set_ext_editor_sensitive(compose, TRUE);
8878 g_free(compose->exteditor_file);
8879 compose->exteditor_file = NULL;
8880 compose->exteditor_pid = -1;
8881 compose->exteditor_ch = NULL;
8882 compose->exteditor_tag = -1;
8887 static void compose_set_ext_editor_sensitive(Compose *compose,
8890 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8891 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8892 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8893 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8894 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8895 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8896 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8898 gtk_widget_set_sensitive(compose->text, sensitive);
8899 if (compose->toolbar->send_btn)
8900 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8901 if (compose->toolbar->sendl_btn)
8902 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8903 if (compose->toolbar->draft_btn)
8904 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8905 if (compose->toolbar->insert_btn)
8906 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8907 if (compose->toolbar->sig_btn)
8908 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8909 if (compose->toolbar->exteditor_btn)
8910 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8911 if (compose->toolbar->linewrap_current_btn)
8912 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8913 if (compose->toolbar->linewrap_all_btn)
8914 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8916 #endif /* G_OS_UNIX */
8919 * compose_undo_state_changed:
8921 * Change the sensivity of the menuentries undo and redo
8923 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8924 gint redo_state, gpointer data)
8926 Compose *compose = (Compose *)data;
8928 switch (undo_state) {
8929 case UNDO_STATE_TRUE:
8930 if (!undostruct->undo_state) {
8931 undostruct->undo_state = TRUE;
8932 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8935 case UNDO_STATE_FALSE:
8936 if (undostruct->undo_state) {
8937 undostruct->undo_state = FALSE;
8938 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8941 case UNDO_STATE_UNCHANGED:
8943 case UNDO_STATE_REFRESH:
8944 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8947 g_warning("Undo state not recognized");
8951 switch (redo_state) {
8952 case UNDO_STATE_TRUE:
8953 if (!undostruct->redo_state) {
8954 undostruct->redo_state = TRUE;
8955 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8958 case UNDO_STATE_FALSE:
8959 if (undostruct->redo_state) {
8960 undostruct->redo_state = FALSE;
8961 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8964 case UNDO_STATE_UNCHANGED:
8966 case UNDO_STATE_REFRESH:
8967 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8970 g_warning("Redo state not recognized");
8975 /* callback functions */
8977 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8978 * includes "non-client" (windows-izm) in calculation, so this calculation
8979 * may not be accurate.
8981 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8982 GtkAllocation *allocation,
8983 GtkSHRuler *shruler)
8985 if (prefs_common.show_ruler) {
8986 gint char_width = 0, char_height = 0;
8987 gint line_width_in_chars;
8989 gtkut_get_font_size(GTK_WIDGET(widget),
8990 &char_width, &char_height);
8991 line_width_in_chars =
8992 (allocation->width - allocation->x) / char_width;
8994 /* got the maximum */
8995 gtk_ruler_set_range(GTK_RULER(shruler),
8996 0.0, line_width_in_chars, 0,
8997 /*line_width_in_chars*/ char_width);
9004 ComposeEntryType header;
9006 ComposePrefType type;
9007 gboolean entry_marked;
9010 static void account_activated(GtkComboBox *optmenu, gpointer data)
9012 Compose *compose = (Compose *)data;
9015 gchar *folderidentifier;
9016 gint account_id = 0;
9019 GSList *list, *saved_list = NULL;
9020 HeaderEntryState *state;
9021 GtkRcStyle *style = NULL;
9022 static GdkColor yellow;
9023 static gboolean color_set = FALSE;
9025 /* Get ID of active account in the combo box */
9026 menu = gtk_combo_box_get_model(optmenu);
9027 gtk_combo_box_get_active_iter(optmenu, &iter);
9028 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9030 ac = account_find_from_id(account_id);
9031 cm_return_if_fail(ac != NULL);
9033 if (ac != compose->account) {
9034 compose_select_account(compose, ac, FALSE);
9036 for (list = compose->header_list; list; list = list->next) {
9037 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9039 if (hentry->type == PREF_ACCOUNT || !list->next) {
9040 compose_destroy_headerentry(hentry);
9044 state = g_malloc0(sizeof(HeaderEntryState));
9045 state->header = combobox_get_active_data(
9046 GTK_COMBO_BOX(hentry->combo));
9047 state->entry = gtk_editable_get_chars(
9048 GTK_EDITABLE(hentry->entry), 0, -1);
9049 state->type = hentry->type;
9052 gdk_color_parse("#f5f6be", &yellow);
9053 color_set = gdk_colormap_alloc_color(
9054 gdk_colormap_get_system(),
9055 &yellow, FALSE, TRUE);
9058 style = gtk_widget_get_modifier_style(hentry->entry);
9059 state->entry_marked = gdk_color_equal(&yellow,
9060 &style->base[GTK_STATE_NORMAL]);
9062 saved_list = g_slist_append(saved_list, state);
9063 compose_destroy_headerentry(hentry);
9066 compose->header_last = NULL;
9067 g_slist_free(compose->header_list);
9068 compose->header_list = NULL;
9069 compose->header_nextrow = 1;
9070 compose_create_header_entry(compose);
9072 if (ac->set_autocc && ac->auto_cc)
9073 compose_entry_append(compose, ac->auto_cc,
9074 COMPOSE_CC, PREF_ACCOUNT);
9076 if (ac->set_autobcc && ac->auto_bcc)
9077 compose_entry_append(compose, ac->auto_bcc,
9078 COMPOSE_BCC, PREF_ACCOUNT);
9080 if (ac->set_autoreplyto && ac->auto_replyto)
9081 compose_entry_append(compose, ac->auto_replyto,
9082 COMPOSE_REPLYTO, PREF_ACCOUNT);
9084 for (list = saved_list; list; list = list->next) {
9085 state = (HeaderEntryState *) list->data;
9087 compose_entry_append(compose, state->entry,
9088 state->header, state->type);
9089 if (state->entry_marked)
9090 compose_entry_mark_default_to(compose, state->entry);
9092 g_free(state->entry);
9094 g_slist_free(saved_list);
9096 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9097 (ac->protocol == A_NNTP) ?
9098 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9101 /* Set message save folder */
9102 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9103 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9105 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9106 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9108 compose_set_save_to(compose, NULL);
9109 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9110 folderidentifier = folder_item_get_identifier(account_get_special_folder
9111 (compose->account, F_OUTBOX));
9112 compose_set_save_to(compose, folderidentifier);
9113 g_free(folderidentifier);
9117 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9118 GtkTreeViewColumn *column, Compose *compose)
9120 compose_attach_property(NULL, compose);
9123 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9126 Compose *compose = (Compose *)data;
9127 GtkTreeSelection *attach_selection;
9128 gint attach_nr_selected;
9130 if (!event) return FALSE;
9132 if (event->button == 3) {
9133 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9134 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9136 if (attach_nr_selected > 0)
9138 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9139 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9141 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9142 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9145 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9146 NULL, NULL, event->button, event->time);
9153 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9156 Compose *compose = (Compose *)data;
9158 if (!event) return FALSE;
9160 switch (event->keyval) {
9162 compose_attach_remove_selected(NULL, compose);
9168 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9170 toolbar_comp_set_sensitive(compose, allow);
9171 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9172 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9174 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9176 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9177 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9178 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9180 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9184 static void compose_send_cb(GtkAction *action, gpointer data)
9186 Compose *compose = (Compose *)data;
9188 if (prefs_common.work_offline &&
9189 !inc_offline_should_override(TRUE,
9190 _("Claws Mail needs network access in order "
9191 "to send this email.")))
9194 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9195 g_source_remove(compose->draft_timeout_tag);
9196 compose->draft_timeout_tag = -1;
9199 compose_send(compose);
9202 static void compose_send_later_cb(GtkAction *action, gpointer data)
9204 Compose *compose = (Compose *)data;
9208 compose_allow_user_actions(compose, FALSE);
9209 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9210 compose_allow_user_actions(compose, TRUE);
9214 compose_close(compose);
9215 } else if (val == -1) {
9216 alertpanel_error(_("Could not queue message."));
9217 } else if (val == -2) {
9218 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9219 } else if (val == -3) {
9220 if (privacy_peek_error())
9221 alertpanel_error(_("Could not queue message for sending:\n\n"
9222 "Signature failed: %s"), privacy_get_error());
9223 } else if (val == -4) {
9224 alertpanel_error(_("Could not queue message for sending:\n\n"
9225 "Charset conversion failed."));
9226 } else if (val == -5) {
9227 alertpanel_error(_("Could not queue message for sending:\n\n"
9228 "Couldn't get recipient encryption key."));
9229 } else if (val == -6) {
9232 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9235 #define DRAFTED_AT_EXIT "drafted_at_exit"
9236 static void compose_register_draft(MsgInfo *info)
9238 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9239 DRAFTED_AT_EXIT, NULL);
9240 FILE *fp = g_fopen(filepath, "ab");
9243 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9251 gboolean compose_draft (gpointer data, guint action)
9253 Compose *compose = (Compose *)data;
9257 MsgFlags flag = {0, 0};
9258 static gboolean lock = FALSE;
9259 MsgInfo *newmsginfo;
9261 gboolean target_locked = FALSE;
9262 gboolean err = FALSE;
9264 if (lock) return FALSE;
9266 if (compose->sending)
9269 draft = account_get_special_folder(compose->account, F_DRAFT);
9270 cm_return_val_if_fail(draft != NULL, FALSE);
9272 if (!g_mutex_trylock(compose->mutex)) {
9273 /* we don't want to lock the mutex once it's available,
9274 * because as the only other part of compose.c locking
9275 * it is compose_close - which means once unlocked,
9276 * the compose struct will be freed */
9277 debug_print("couldn't lock mutex, probably sending\n");
9283 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9284 G_DIR_SEPARATOR, compose);
9285 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9286 FILE_OP_ERROR(tmp, "fopen");
9290 /* chmod for security */
9291 if (change_file_mode_rw(fp, tmp) < 0) {
9292 FILE_OP_ERROR(tmp, "chmod");
9293 g_warning("can't change file mode\n");
9296 /* Save draft infos */
9297 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9298 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9300 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9301 gchar *savefolderid;
9303 savefolderid = compose_get_save_to(compose);
9304 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9305 g_free(savefolderid);
9307 if (compose->return_receipt) {
9308 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9310 if (compose->privacy_system) {
9311 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9312 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9313 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9316 /* Message-ID of message replying to */
9317 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9320 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9321 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9324 /* Message-ID of message forwarding to */
9325 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9328 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9329 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9333 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9334 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9336 /* end of headers */
9337 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9344 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9348 if (fclose(fp) == EOF) {
9352 if (compose->targetinfo) {
9353 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9354 flag.perm_flags = target_locked?MSG_LOCKED:0;
9356 flag.tmp_flags = MSG_DRAFT;
9358 folder_item_scan(draft);
9359 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9360 MsgInfo *tmpinfo = NULL;
9361 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9362 if (compose->msgid) {
9363 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9366 msgnum = tmpinfo->msgnum;
9367 procmsg_msginfo_free(tmpinfo);
9368 debug_print("got draft msgnum %d from scanning\n", msgnum);
9370 debug_print("didn't get draft msgnum after scanning\n");
9373 debug_print("got draft msgnum %d from adding\n", msgnum);
9379 if (action != COMPOSE_AUTO_SAVE) {
9380 if (action != COMPOSE_DRAFT_FOR_EXIT)
9381 alertpanel_error(_("Could not save draft."));
9384 gtkut_window_popup(compose->window);
9385 val = alertpanel_full(_("Could not save draft"),
9386 _("Could not save draft.\n"
9387 "Do you want to cancel exit or discard this email?"),
9388 _("_Cancel exit"), _("_Discard email"), NULL,
9389 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9390 if (val == G_ALERTALTERNATE) {
9392 g_mutex_unlock(compose->mutex); /* must be done before closing */
9393 compose_close(compose);
9397 g_mutex_unlock(compose->mutex); /* must be done before closing */
9406 if (compose->mode == COMPOSE_REEDIT) {
9407 compose_remove_reedit_target(compose, TRUE);
9410 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9413 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9415 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9417 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9418 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9419 procmsg_msginfo_set_flags(newmsginfo, 0,
9420 MSG_HAS_ATTACHMENT);
9422 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9423 compose_register_draft(newmsginfo);
9425 procmsg_msginfo_free(newmsginfo);
9428 folder_item_scan(draft);
9430 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9432 g_mutex_unlock(compose->mutex); /* must be done before closing */
9433 compose_close(compose);
9439 path = folder_item_fetch_msg(draft, msgnum);
9441 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9444 if (g_stat(path, &s) < 0) {
9445 FILE_OP_ERROR(path, "stat");
9451 procmsg_msginfo_free(compose->targetinfo);
9452 compose->targetinfo = procmsg_msginfo_new();
9453 compose->targetinfo->msgnum = msgnum;
9454 compose->targetinfo->size = (goffset)s.st_size;
9455 compose->targetinfo->mtime = s.st_mtime;
9456 compose->targetinfo->folder = draft;
9458 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9459 compose->mode = COMPOSE_REEDIT;
9461 if (action == COMPOSE_AUTO_SAVE) {
9462 compose->autosaved_draft = compose->targetinfo;
9464 compose->modified = FALSE;
9465 compose_set_title(compose);
9469 g_mutex_unlock(compose->mutex);
9473 void compose_clear_exit_drafts(void)
9475 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9476 DRAFTED_AT_EXIT, NULL);
9477 if (is_file_exist(filepath))
9478 claws_unlink(filepath);
9483 void compose_reopen_exit_drafts(void)
9485 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9486 DRAFTED_AT_EXIT, NULL);
9487 FILE *fp = g_fopen(filepath, "rb");
9491 while (fgets(buf, sizeof(buf), fp)) {
9492 gchar **parts = g_strsplit(buf, "\t", 2);
9493 const gchar *folder = parts[0];
9494 int msgnum = parts[1] ? atoi(parts[1]):-1;
9496 if (folder && *folder && msgnum > -1) {
9497 FolderItem *item = folder_find_item_from_identifier(folder);
9498 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9500 compose_reedit(info, FALSE);
9507 compose_clear_exit_drafts();
9510 static void compose_save_cb(GtkAction *action, gpointer data)
9512 Compose *compose = (Compose *)data;
9513 compose_draft(compose, COMPOSE_KEEP_EDITING);
9514 compose->rmode = COMPOSE_REEDIT;
9517 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9519 if (compose && file_list) {
9522 for ( tmp = file_list; tmp; tmp = tmp->next) {
9523 gchar *file = (gchar *) tmp->data;
9524 gchar *utf8_filename = conv_filename_to_utf8(file);
9525 compose_attach_append(compose, file, utf8_filename, NULL);
9526 compose_changed_cb(NULL, compose);
9531 g_free(utf8_filename);
9536 static void compose_attach_cb(GtkAction *action, gpointer data)
9538 Compose *compose = (Compose *)data;
9541 if (compose->redirect_filename != NULL)
9544 file_list = filesel_select_multiple_files_open(_("Select file"));
9547 compose_attach_from_list(compose, file_list, TRUE);
9548 g_list_free(file_list);
9552 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9554 Compose *compose = (Compose *)data;
9557 file_list = filesel_select_multiple_files_open(_("Select file"));
9562 for ( tmp = file_list; tmp; tmp = tmp->next) {
9563 gchar *file = (gchar *) tmp->data;
9564 gchar *filedup = g_strdup(file);
9565 gchar *shortfile = g_path_get_basename(filedup);
9566 ComposeInsertResult res;
9567 /* insert the file if the file is short or if the user confirmed that
9568 he/she wants to insert the large file */
9569 res = compose_insert_file(compose, file);
9570 if (res == COMPOSE_INSERT_READ_ERROR) {
9571 alertpanel_error(_("File '%s' could not be read."), shortfile);
9572 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9573 alertpanel_error(_("File '%s' contained invalid characters\n"
9574 "for the current encoding, insertion may be incorrect."),
9582 g_list_free(file_list);
9586 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9588 Compose *compose = (Compose *)data;
9590 compose_insert_sig(compose, FALSE);
9593 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9597 Compose *compose = (Compose *)data;
9599 gtkut_widget_get_uposition(widget, &x, &y);
9600 if (!compose->batch) {
9601 prefs_common.compose_x = x;
9602 prefs_common.compose_y = y;
9604 if (compose->sending || compose->updating)
9606 compose_close_cb(NULL, compose);
9610 void compose_close_toolbar(Compose *compose)
9612 compose_close_cb(NULL, compose);
9615 static void compose_close_cb(GtkAction *action, gpointer data)
9617 Compose *compose = (Compose *)data;
9621 if (compose->exteditor_tag != -1) {
9622 if (!compose_ext_editor_kill(compose))
9627 if (compose->modified) {
9628 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9629 if (!g_mutex_trylock(compose->mutex)) {
9630 /* we don't want to lock the mutex once it's available,
9631 * because as the only other part of compose.c locking
9632 * it is compose_close - which means once unlocked,
9633 * the compose struct will be freed */
9634 debug_print("couldn't lock mutex, probably sending\n");
9638 val = alertpanel(_("Discard message"),
9639 _("This message has been modified. Discard it?"),
9640 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9642 val = alertpanel(_("Save changes"),
9643 _("This message has been modified. Save the latest changes?"),
9644 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9646 g_mutex_unlock(compose->mutex);
9648 case G_ALERTDEFAULT:
9649 if (prefs_common.autosave && !reedit)
9650 compose_remove_draft(compose);
9652 case G_ALERTALTERNATE:
9653 compose_draft(data, COMPOSE_QUIT_EDITING);
9660 compose_close(compose);
9663 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9665 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9666 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9667 Compose *compose = (Compose *) data;
9670 compose->out_encoding = (CharSet)value;
9673 static void compose_address_cb(GtkAction *action, gpointer data)
9675 Compose *compose = (Compose *)data;
9677 addressbook_open(compose);
9680 static void about_show_cb(GtkAction *action, gpointer data)
9685 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9687 Compose *compose = (Compose *)data;
9692 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9693 cm_return_if_fail(tmpl != NULL);
9695 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9697 val = alertpanel(_("Apply template"), msg,
9698 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9701 if (val == G_ALERTDEFAULT)
9702 compose_template_apply(compose, tmpl, TRUE);
9703 else if (val == G_ALERTALTERNATE)
9704 compose_template_apply(compose, tmpl, FALSE);
9707 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9709 Compose *compose = (Compose *)data;
9711 compose_exec_ext_editor(compose);
9714 static void compose_undo_cb(GtkAction *action, gpointer data)
9716 Compose *compose = (Compose *)data;
9717 gboolean prev_autowrap = compose->autowrap;
9719 compose->autowrap = FALSE;
9720 undo_undo(compose->undostruct);
9721 compose->autowrap = prev_autowrap;
9724 static void compose_redo_cb(GtkAction *action, gpointer data)
9726 Compose *compose = (Compose *)data;
9727 gboolean prev_autowrap = compose->autowrap;
9729 compose->autowrap = FALSE;
9730 undo_redo(compose->undostruct);
9731 compose->autowrap = prev_autowrap;
9734 static void entry_cut_clipboard(GtkWidget *entry)
9736 if (GTK_IS_EDITABLE(entry))
9737 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9738 else if (GTK_IS_TEXT_VIEW(entry))
9739 gtk_text_buffer_cut_clipboard(
9740 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9741 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9745 static void entry_copy_clipboard(GtkWidget *entry)
9747 if (GTK_IS_EDITABLE(entry))
9748 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9749 else if (GTK_IS_TEXT_VIEW(entry))
9750 gtk_text_buffer_copy_clipboard(
9751 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9752 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9755 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9756 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9758 if (GTK_IS_TEXT_VIEW(entry)) {
9759 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9760 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9761 GtkTextIter start_iter, end_iter;
9763 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9765 if (contents == NULL)
9768 /* we shouldn't delete the selection when middle-click-pasting, or we
9769 * can't mid-click-paste our own selection */
9770 if (clip != GDK_SELECTION_PRIMARY) {
9771 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9772 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9775 if (insert_place == NULL) {
9776 /* if insert_place isn't specified, insert at the cursor.
9777 * used for Ctrl-V pasting */
9778 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9779 start = gtk_text_iter_get_offset(&start_iter);
9780 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9782 /* if insert_place is specified, paste here.
9783 * used for mid-click-pasting */
9784 start = gtk_text_iter_get_offset(insert_place);
9785 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9786 if (prefs_common.primary_paste_unselects)
9787 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9791 /* paste unwrapped: mark the paste so it's not wrapped later */
9792 end = start + strlen(contents);
9793 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9794 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9795 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9796 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9797 /* rewrap paragraph now (after a mid-click-paste) */
9798 mark_start = gtk_text_buffer_get_insert(buffer);
9799 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9800 gtk_text_iter_backward_char(&start_iter);
9801 compose_beautify_paragraph(compose, &start_iter, TRUE);
9803 } else if (GTK_IS_EDITABLE(entry))
9804 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9806 compose->modified = TRUE;
9809 static void entry_allsel(GtkWidget *entry)
9811 if (GTK_IS_EDITABLE(entry))
9812 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9813 else if (GTK_IS_TEXT_VIEW(entry)) {
9814 GtkTextIter startiter, enditer;
9815 GtkTextBuffer *textbuf;
9817 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9818 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9819 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9821 gtk_text_buffer_move_mark_by_name(textbuf,
9822 "selection_bound", &startiter);
9823 gtk_text_buffer_move_mark_by_name(textbuf,
9824 "insert", &enditer);
9828 static void compose_cut_cb(GtkAction *action, gpointer data)
9830 Compose *compose = (Compose *)data;
9831 if (compose->focused_editable
9832 #ifndef GENERIC_UMPC
9833 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9836 entry_cut_clipboard(compose->focused_editable);
9839 static void compose_copy_cb(GtkAction *action, gpointer data)
9841 Compose *compose = (Compose *)data;
9842 if (compose->focused_editable
9843 #ifndef GENERIC_UMPC
9844 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9847 entry_copy_clipboard(compose->focused_editable);
9850 static void compose_paste_cb(GtkAction *action, gpointer data)
9852 Compose *compose = (Compose *)data;
9854 GtkTextBuffer *buffer;
9856 if (compose->focused_editable &&
9857 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9858 entry_paste_clipboard(compose, compose->focused_editable,
9859 prefs_common.linewrap_pastes,
9860 GDK_SELECTION_CLIPBOARD, NULL);
9864 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9866 Compose *compose = (Compose *)data;
9867 gint wrap_quote = prefs_common.linewrap_quote;
9868 if (compose->focused_editable
9869 #ifndef GENERIC_UMPC
9870 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9873 /* let text_insert() (called directly or at a later time
9874 * after the gtk_editable_paste_clipboard) know that
9875 * text is to be inserted as a quotation. implemented
9876 * by using a simple refcount... */
9877 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9878 G_OBJECT(compose->focused_editable),
9879 "paste_as_quotation"));
9880 g_object_set_data(G_OBJECT(compose->focused_editable),
9881 "paste_as_quotation",
9882 GINT_TO_POINTER(paste_as_quotation + 1));
9883 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9884 entry_paste_clipboard(compose, compose->focused_editable,
9885 prefs_common.linewrap_pastes,
9886 GDK_SELECTION_CLIPBOARD, NULL);
9887 prefs_common.linewrap_quote = wrap_quote;
9891 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9893 Compose *compose = (Compose *)data;
9895 GtkTextBuffer *buffer;
9897 if (compose->focused_editable
9898 #ifndef GENERIC_UMPC
9899 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9902 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9903 GDK_SELECTION_CLIPBOARD, NULL);
9907 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9909 Compose *compose = (Compose *)data;
9911 GtkTextBuffer *buffer;
9913 if (compose->focused_editable
9914 #ifndef GENERIC_UMPC
9915 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9918 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9919 GDK_SELECTION_CLIPBOARD, NULL);
9923 static void compose_allsel_cb(GtkAction *action, gpointer data)
9925 Compose *compose = (Compose *)data;
9926 if (compose->focused_editable
9927 #ifndef GENERIC_UMPC
9928 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9931 entry_allsel(compose->focused_editable);
9934 static void textview_move_beginning_of_line (GtkTextView *text)
9936 GtkTextBuffer *buffer;
9940 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9942 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9943 mark = gtk_text_buffer_get_insert(buffer);
9944 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9945 gtk_text_iter_set_line_offset(&ins, 0);
9946 gtk_text_buffer_place_cursor(buffer, &ins);
9949 static void textview_move_forward_character (GtkTextView *text)
9951 GtkTextBuffer *buffer;
9955 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9957 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9958 mark = gtk_text_buffer_get_insert(buffer);
9959 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9960 if (gtk_text_iter_forward_cursor_position(&ins))
9961 gtk_text_buffer_place_cursor(buffer, &ins);
9964 static void textview_move_backward_character (GtkTextView *text)
9966 GtkTextBuffer *buffer;
9970 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9972 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9973 mark = gtk_text_buffer_get_insert(buffer);
9974 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9975 if (gtk_text_iter_backward_cursor_position(&ins))
9976 gtk_text_buffer_place_cursor(buffer, &ins);
9979 static void textview_move_forward_word (GtkTextView *text)
9981 GtkTextBuffer *buffer;
9986 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9988 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9989 mark = gtk_text_buffer_get_insert(buffer);
9990 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9991 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9992 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9993 gtk_text_iter_backward_word_start(&ins);
9994 gtk_text_buffer_place_cursor(buffer, &ins);
9998 static void textview_move_backward_word (GtkTextView *text)
10000 GtkTextBuffer *buffer;
10005 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10007 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10008 mark = gtk_text_buffer_get_insert(buffer);
10009 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10010 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10011 if (gtk_text_iter_backward_word_starts(&ins, 1))
10012 gtk_text_buffer_place_cursor(buffer, &ins);
10015 static void textview_move_end_of_line (GtkTextView *text)
10017 GtkTextBuffer *buffer;
10021 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10023 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10024 mark = gtk_text_buffer_get_insert(buffer);
10025 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10026 if (gtk_text_iter_forward_to_line_end(&ins))
10027 gtk_text_buffer_place_cursor(buffer, &ins);
10030 static void textview_move_next_line (GtkTextView *text)
10032 GtkTextBuffer *buffer;
10037 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10039 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10040 mark = gtk_text_buffer_get_insert(buffer);
10041 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10042 offset = gtk_text_iter_get_line_offset(&ins);
10043 if (gtk_text_iter_forward_line(&ins)) {
10044 gtk_text_iter_set_line_offset(&ins, offset);
10045 gtk_text_buffer_place_cursor(buffer, &ins);
10049 static void textview_move_previous_line (GtkTextView *text)
10051 GtkTextBuffer *buffer;
10056 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10058 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10059 mark = gtk_text_buffer_get_insert(buffer);
10060 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10061 offset = gtk_text_iter_get_line_offset(&ins);
10062 if (gtk_text_iter_backward_line(&ins)) {
10063 gtk_text_iter_set_line_offset(&ins, offset);
10064 gtk_text_buffer_place_cursor(buffer, &ins);
10068 static void textview_delete_forward_character (GtkTextView *text)
10070 GtkTextBuffer *buffer;
10072 GtkTextIter ins, end_iter;
10074 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10076 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10077 mark = gtk_text_buffer_get_insert(buffer);
10078 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10080 if (gtk_text_iter_forward_char(&end_iter)) {
10081 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10085 static void textview_delete_backward_character (GtkTextView *text)
10087 GtkTextBuffer *buffer;
10089 GtkTextIter ins, end_iter;
10091 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10093 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10094 mark = gtk_text_buffer_get_insert(buffer);
10095 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10097 if (gtk_text_iter_backward_char(&end_iter)) {
10098 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10102 static void textview_delete_forward_word (GtkTextView *text)
10104 GtkTextBuffer *buffer;
10106 GtkTextIter ins, end_iter;
10108 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10110 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10111 mark = gtk_text_buffer_get_insert(buffer);
10112 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10114 if (gtk_text_iter_forward_word_end(&end_iter)) {
10115 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10119 static void textview_delete_backward_word (GtkTextView *text)
10121 GtkTextBuffer *buffer;
10123 GtkTextIter ins, end_iter;
10125 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10127 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10128 mark = gtk_text_buffer_get_insert(buffer);
10129 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10131 if (gtk_text_iter_backward_word_start(&end_iter)) {
10132 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10136 static void textview_delete_line (GtkTextView *text)
10138 GtkTextBuffer *buffer;
10140 GtkTextIter ins, start_iter, end_iter;
10142 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10144 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10145 mark = gtk_text_buffer_get_insert(buffer);
10146 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10149 gtk_text_iter_set_line_offset(&start_iter, 0);
10152 if (gtk_text_iter_ends_line(&end_iter)){
10153 if (!gtk_text_iter_forward_char(&end_iter))
10154 gtk_text_iter_backward_char(&start_iter);
10157 gtk_text_iter_forward_to_line_end(&end_iter);
10158 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10161 static void textview_delete_to_line_end (GtkTextView *text)
10163 GtkTextBuffer *buffer;
10165 GtkTextIter ins, end_iter;
10167 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10169 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10170 mark = gtk_text_buffer_get_insert(buffer);
10171 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10173 if (gtk_text_iter_ends_line(&end_iter))
10174 gtk_text_iter_forward_char(&end_iter);
10176 gtk_text_iter_forward_to_line_end(&end_iter);
10177 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10180 #define DO_ACTION(name, act) { \
10181 if(!strcmp(name, a_name)) { \
10185 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10187 const gchar *a_name = gtk_action_get_name(action);
10188 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10189 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10190 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10191 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10192 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10193 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10194 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10195 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10196 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10197 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10198 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10199 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10200 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10201 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10205 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10207 Compose *compose = (Compose *)data;
10208 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10209 ComposeCallAdvancedAction action = -1;
10211 action = compose_call_advanced_action_from_path(gaction);
10214 void (*do_action) (GtkTextView *text);
10215 } action_table[] = {
10216 {textview_move_beginning_of_line},
10217 {textview_move_forward_character},
10218 {textview_move_backward_character},
10219 {textview_move_forward_word},
10220 {textview_move_backward_word},
10221 {textview_move_end_of_line},
10222 {textview_move_next_line},
10223 {textview_move_previous_line},
10224 {textview_delete_forward_character},
10225 {textview_delete_backward_character},
10226 {textview_delete_forward_word},
10227 {textview_delete_backward_word},
10228 {textview_delete_line},
10229 {textview_delete_to_line_end}
10232 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10234 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10235 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10236 if (action_table[action].do_action)
10237 action_table[action].do_action(text);
10239 g_warning("Not implemented yet.");
10243 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10247 if (GTK_IS_EDITABLE(widget)) {
10248 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10249 gtk_editable_set_position(GTK_EDITABLE(widget),
10252 if (widget->parent && widget->parent->parent
10253 && widget->parent->parent->parent) {
10254 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10255 gint y = widget->allocation.y;
10256 gint height = widget->allocation.height;
10257 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10258 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10260 if (y < (int)shown->value) {
10261 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10263 if (y + height > (int)shown->value + (int)shown->page_size) {
10264 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10265 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10266 y + height - (int)shown->page_size - 1);
10268 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10269 (int)shown->upper - (int)shown->page_size - 1);
10276 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10277 compose->focused_editable = widget;
10279 #ifdef GENERIC_UMPC
10280 if (GTK_IS_TEXT_VIEW(widget)
10281 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10282 g_object_ref(compose->notebook);
10283 g_object_ref(compose->edit_vbox);
10284 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10285 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10286 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10287 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10288 g_object_unref(compose->notebook);
10289 g_object_unref(compose->edit_vbox);
10290 g_signal_handlers_block_by_func(G_OBJECT(widget),
10291 G_CALLBACK(compose_grab_focus_cb),
10293 gtk_widget_grab_focus(widget);
10294 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10295 G_CALLBACK(compose_grab_focus_cb),
10297 } else if (!GTK_IS_TEXT_VIEW(widget)
10298 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10299 g_object_ref(compose->notebook);
10300 g_object_ref(compose->edit_vbox);
10301 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10302 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10303 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10304 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10305 g_object_unref(compose->notebook);
10306 g_object_unref(compose->edit_vbox);
10307 g_signal_handlers_block_by_func(G_OBJECT(widget),
10308 G_CALLBACK(compose_grab_focus_cb),
10310 gtk_widget_grab_focus(widget);
10311 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10312 G_CALLBACK(compose_grab_focus_cb),
10318 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10320 compose->modified = TRUE;
10321 // compose_beautify_paragraph(compose, NULL, TRUE);
10322 #ifndef GENERIC_UMPC
10323 compose_set_title(compose);
10327 static void compose_wrap_cb(GtkAction *action, gpointer data)
10329 Compose *compose = (Compose *)data;
10330 compose_beautify_paragraph(compose, NULL, TRUE);
10333 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10335 Compose *compose = (Compose *)data;
10336 compose_wrap_all_full(compose, TRUE);
10339 static void compose_find_cb(GtkAction *action, gpointer data)
10341 Compose *compose = (Compose *)data;
10343 message_search_compose(compose);
10346 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10349 Compose *compose = (Compose *)data;
10350 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10351 if (compose->autowrap)
10352 compose_wrap_all_full(compose, TRUE);
10353 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10356 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10359 Compose *compose = (Compose *)data;
10360 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10363 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10365 Compose *compose = (Compose *)data;
10367 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10370 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10372 Compose *compose = (Compose *)data;
10374 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10377 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10379 g_free(compose->privacy_system);
10381 compose->privacy_system = g_strdup(account->default_privacy_system);
10382 compose_update_privacy_system_menu_item(compose, warn);
10385 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10387 Compose *compose = (Compose *)data;
10389 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10390 gtk_widget_show(compose->ruler_hbox);
10391 prefs_common.show_ruler = TRUE;
10393 gtk_widget_hide(compose->ruler_hbox);
10394 gtk_widget_queue_resize(compose->edit_vbox);
10395 prefs_common.show_ruler = FALSE;
10399 static void compose_attach_drag_received_cb (GtkWidget *widget,
10400 GdkDragContext *context,
10403 GtkSelectionData *data,
10406 gpointer user_data)
10408 Compose *compose = (Compose *)user_data;
10411 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10413 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10415 ) && gtk_drag_get_source_widget(context) !=
10416 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10417 list = uri_list_extract_filenames((const gchar *)data->data);
10418 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10419 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10420 compose_attach_append
10421 (compose, (const gchar *)tmp->data,
10422 utf8_filename, NULL);
10423 g_free(utf8_filename);
10425 if (list) compose_changed_cb(NULL, compose);
10426 list_free_strings(list);
10428 } else if (gtk_drag_get_source_widget(context)
10429 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10430 /* comes from our summaryview */
10431 SummaryView * summaryview = NULL;
10432 GSList * list = NULL, *cur = NULL;
10434 if (mainwindow_get_mainwindow())
10435 summaryview = mainwindow_get_mainwindow()->summaryview;
10438 list = summary_get_selected_msg_list(summaryview);
10440 for (cur = list; cur; cur = cur->next) {
10441 MsgInfo *msginfo = (MsgInfo *)cur->data;
10442 gchar *file = NULL;
10444 file = procmsg_get_message_file_full(msginfo,
10447 compose_attach_append(compose, (const gchar *)file,
10448 (const gchar *)file, "message/rfc822");
10452 g_slist_free(list);
10456 static gboolean compose_drag_drop(GtkWidget *widget,
10457 GdkDragContext *drag_context,
10459 guint time, gpointer user_data)
10461 /* not handling this signal makes compose_insert_drag_received_cb
10466 static void compose_insert_drag_received_cb (GtkWidget *widget,
10467 GdkDragContext *drag_context,
10470 GtkSelectionData *data,
10473 gpointer user_data)
10475 Compose *compose = (Compose *)user_data;
10478 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10481 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10483 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10485 AlertValue val = G_ALERTDEFAULT;
10487 list = uri_list_extract_filenames((const gchar *)data->data);
10488 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10489 /* Assume a list of no files, and data has ://, is a remote link */
10490 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10491 gchar *tmpfile = get_tmp_file();
10492 str_write_to_file(tmpdata, tmpfile);
10494 compose_insert_file(compose, tmpfile);
10495 claws_unlink(tmpfile);
10497 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10498 compose_beautify_paragraph(compose, NULL, TRUE);
10501 switch (prefs_common.compose_dnd_mode) {
10502 case COMPOSE_DND_ASK:
10503 val = alertpanel_full(_("Insert or attach?"),
10504 _("Do you want to insert the contents of the file(s) "
10505 "into the message body, or attach it to the email?"),
10506 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10507 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10509 case COMPOSE_DND_INSERT:
10510 val = G_ALERTALTERNATE;
10512 case COMPOSE_DND_ATTACH:
10513 val = G_ALERTOTHER;
10516 /* unexpected case */
10517 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10520 if (val & G_ALERTDISABLE) {
10521 val &= ~G_ALERTDISABLE;
10522 /* remember what action to perform by default, only if we don't click Cancel */
10523 if (val == G_ALERTALTERNATE)
10524 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10525 else if (val == G_ALERTOTHER)
10526 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10529 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10530 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10531 list_free_strings(list);
10534 } else if (val == G_ALERTOTHER) {
10535 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10536 list_free_strings(list);
10541 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10542 compose_insert_file(compose, (const gchar *)tmp->data);
10544 list_free_strings(list);
10546 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10549 #if GTK_CHECK_VERSION(2, 8, 0)
10550 /* do nothing, handled by GTK */
10552 gchar *tmpfile = get_tmp_file();
10553 str_write_to_file((const gchar *)data->data, tmpfile);
10554 compose_insert_file(compose, tmpfile);
10555 claws_unlink(tmpfile);
10557 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10561 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10564 static void compose_header_drag_received_cb (GtkWidget *widget,
10565 GdkDragContext *drag_context,
10568 GtkSelectionData *data,
10571 gpointer user_data)
10573 GtkEditable *entry = (GtkEditable *)user_data;
10574 gchar *email = (gchar *)data->data;
10576 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10579 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10580 gchar *decoded=g_new(gchar, strlen(email));
10583 email += strlen("mailto:");
10584 decode_uri(decoded, email); /* will fit */
10585 gtk_editable_delete_text(entry, 0, -1);
10586 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10587 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10591 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10594 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10596 Compose *compose = (Compose *)data;
10598 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10599 compose->return_receipt = TRUE;
10601 compose->return_receipt = FALSE;
10604 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10606 Compose *compose = (Compose *)data;
10608 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10609 compose->remove_references = TRUE;
10611 compose->remove_references = FALSE;
10614 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10615 ComposeHeaderEntry *headerentry)
10617 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10621 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10622 GdkEventKey *event,
10623 ComposeHeaderEntry *headerentry)
10625 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10626 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10627 !(event->state & GDK_MODIFIER_MASK) &&
10628 (event->keyval == GDK_BackSpace) &&
10629 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10630 gtk_container_remove
10631 (GTK_CONTAINER(headerentry->compose->header_table),
10632 headerentry->combo);
10633 gtk_container_remove
10634 (GTK_CONTAINER(headerentry->compose->header_table),
10635 headerentry->entry);
10636 headerentry->compose->header_list =
10637 g_slist_remove(headerentry->compose->header_list,
10639 g_free(headerentry);
10640 } else if (event->keyval == GDK_Tab) {
10641 if (headerentry->compose->header_last == headerentry) {
10642 /* Override default next focus, and give it to subject_entry
10643 * instead of notebook tabs
10645 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10646 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10653 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10654 ComposeHeaderEntry *headerentry)
10656 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10657 compose_create_header_entry(headerentry->compose);
10658 g_signal_handlers_disconnect_matched
10659 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10660 0, 0, NULL, NULL, headerentry);
10662 /* Automatically scroll down */
10663 GTK_EVENTS_FLUSH();
10664 compose_show_first_last_header(headerentry->compose, FALSE);
10670 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10672 GtkAdjustment *vadj;
10674 cm_return_if_fail(compose);
10675 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10676 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10677 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10678 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10679 gtk_adjustment_changed(vadj);
10682 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10683 const gchar *text, gint len, Compose *compose)
10685 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10686 (G_OBJECT(compose->text), "paste_as_quotation"));
10689 cm_return_if_fail(text != NULL);
10691 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10692 G_CALLBACK(text_inserted),
10694 if (paste_as_quotation) {
10696 const gchar *qmark;
10698 GtkTextIter start_iter;
10701 len = strlen(text);
10703 new_text = g_strndup(text, len);
10705 qmark = compose_quote_char_from_context(compose);
10707 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10708 gtk_text_buffer_place_cursor(buffer, iter);
10710 pos = gtk_text_iter_get_offset(iter);
10712 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10713 _("Quote format error at line %d."));
10714 quote_fmt_reset_vartable();
10716 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10717 GINT_TO_POINTER(paste_as_quotation - 1));
10719 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10720 gtk_text_buffer_place_cursor(buffer, iter);
10721 gtk_text_buffer_delete_mark(buffer, mark);
10723 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10724 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10725 compose_beautify_paragraph(compose, &start_iter, FALSE);
10726 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10727 gtk_text_buffer_delete_mark(buffer, mark);
10729 if (strcmp(text, "\n") || compose->automatic_break
10730 || gtk_text_iter_starts_line(iter)) {
10731 GtkTextIter before_ins;
10732 gtk_text_buffer_insert(buffer, iter, text, len);
10733 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10734 before_ins = *iter;
10735 gtk_text_iter_backward_chars(&before_ins, len);
10736 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10739 /* check if the preceding is just whitespace or quote */
10740 GtkTextIter start_line;
10741 gchar *tmp = NULL, *quote = NULL;
10742 gint quote_len = 0, is_normal = 0;
10743 start_line = *iter;
10744 gtk_text_iter_set_line_offset(&start_line, 0);
10745 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10748 if (*tmp == '\0') {
10751 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10759 gtk_text_buffer_insert(buffer, iter, text, len);
10761 gtk_text_buffer_insert_with_tags_by_name(buffer,
10762 iter, text, len, "no_join", NULL);
10767 if (!paste_as_quotation) {
10768 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10769 compose_beautify_paragraph(compose, iter, FALSE);
10770 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10771 gtk_text_buffer_delete_mark(buffer, mark);
10774 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10775 G_CALLBACK(text_inserted),
10777 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10779 if (prefs_common.autosave &&
10780 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10781 compose->draft_timeout_tag != -2 /* disabled while loading */)
10782 compose->draft_timeout_tag = g_timeout_add
10783 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10785 static gint compose_defer_auto_save_draft(Compose *compose)
10787 compose->draft_timeout_tag = -1;
10788 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10793 static void compose_check_all(GtkAction *action, gpointer data)
10795 Compose *compose = (Compose *)data;
10796 if (!compose->gtkaspell)
10799 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10800 claws_spell_entry_check_all(
10801 CLAWS_SPELL_ENTRY(compose->subject_entry));
10803 gtkaspell_check_all(compose->gtkaspell);
10806 static void compose_highlight_all(GtkAction *action, gpointer data)
10808 Compose *compose = (Compose *)data;
10809 if (compose->gtkaspell) {
10810 claws_spell_entry_recheck_all(
10811 CLAWS_SPELL_ENTRY(compose->subject_entry));
10812 gtkaspell_highlight_all(compose->gtkaspell);
10816 static void compose_check_backwards(GtkAction *action, gpointer data)
10818 Compose *compose = (Compose *)data;
10819 if (!compose->gtkaspell) {
10820 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10824 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10825 claws_spell_entry_check_backwards(
10826 CLAWS_SPELL_ENTRY(compose->subject_entry));
10828 gtkaspell_check_backwards(compose->gtkaspell);
10831 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10833 Compose *compose = (Compose *)data;
10834 if (!compose->gtkaspell) {
10835 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10839 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10840 claws_spell_entry_check_forwards_go(
10841 CLAWS_SPELL_ENTRY(compose->subject_entry));
10843 gtkaspell_check_forwards_go(compose->gtkaspell);
10848 *\brief Guess originating forward account from MsgInfo and several
10849 * "common preference" settings. Return NULL if no guess.
10851 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10853 PrefsAccount *account = NULL;
10855 cm_return_val_if_fail(msginfo, NULL);
10856 cm_return_val_if_fail(msginfo->folder, NULL);
10857 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10859 if (msginfo->folder->prefs->enable_default_account)
10860 account = account_find_from_id(msginfo->folder->prefs->default_account);
10863 account = msginfo->folder->folder->account;
10865 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10867 Xstrdup_a(to, msginfo->to, return NULL);
10868 extract_address(to);
10869 account = account_find_from_address(to, FALSE);
10872 if (!account && prefs_common.forward_account_autosel) {
10873 gchar cc[BUFFSIZE];
10874 if (!procheader_get_header_from_msginfo
10875 (msginfo, cc,sizeof cc , "Cc:")) {
10876 gchar *buf = cc + strlen("Cc:");
10877 extract_address(buf);
10878 account = account_find_from_address(buf, FALSE);
10882 if (!account && prefs_common.forward_account_autosel) {
10883 gchar deliveredto[BUFFSIZE];
10884 if (!procheader_get_header_from_msginfo
10885 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10886 gchar *buf = deliveredto + strlen("Delivered-To:");
10887 extract_address(buf);
10888 account = account_find_from_address(buf, FALSE);
10895 gboolean compose_close(Compose *compose)
10899 if (!g_mutex_trylock(compose->mutex)) {
10900 /* we have to wait for the (possibly deferred by auto-save)
10901 * drafting to be done, before destroying the compose under
10903 debug_print("waiting for drafting to finish...\n");
10904 compose_allow_user_actions(compose, FALSE);
10905 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10908 cm_return_val_if_fail(compose, FALSE);
10909 gtkut_widget_get_uposition(compose->window, &x, &y);
10910 if (!compose->batch) {
10911 prefs_common.compose_x = x;
10912 prefs_common.compose_y = y;
10914 g_mutex_unlock(compose->mutex);
10915 compose_destroy(compose);
10920 * Add entry field for each address in list.
10921 * \param compose E-Mail composition object.
10922 * \param listAddress List of (formatted) E-Mail addresses.
10924 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10927 node = listAddress;
10929 addr = ( gchar * ) node->data;
10930 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
10931 node = g_list_next( node );
10935 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10936 guint action, gboolean opening_multiple)
10938 gchar *body = NULL;
10939 GSList *new_msglist = NULL;
10940 MsgInfo *tmp_msginfo = NULL;
10941 gboolean originally_enc = FALSE;
10942 gboolean originally_sig = FALSE;
10943 Compose *compose = NULL;
10944 gchar *s_system = NULL;
10946 cm_return_if_fail(msgview != NULL);
10948 cm_return_if_fail(msginfo_list != NULL);
10950 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10951 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10952 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10954 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10955 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10956 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10957 orig_msginfo, mimeinfo);
10958 if (tmp_msginfo != NULL) {
10959 new_msglist = g_slist_append(NULL, tmp_msginfo);
10961 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10962 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10963 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10965 tmp_msginfo->folder = orig_msginfo->folder;
10966 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10967 if (orig_msginfo->tags) {
10968 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10969 tmp_msginfo->folder->tags_dirty = TRUE;
10975 if (!opening_multiple)
10976 body = messageview_get_selection(msgview);
10979 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10980 procmsg_msginfo_free(tmp_msginfo);
10981 g_slist_free(new_msglist);
10983 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10985 if (compose && originally_enc) {
10986 compose_force_encryption(compose, compose->account, FALSE, s_system);
10989 if (compose && originally_sig && compose->account->default_sign_reply) {
10990 compose_force_signing(compose, compose->account, s_system);
10996 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10999 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11000 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11001 GSList *cur = msginfo_list;
11002 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11003 "messages. Opening the windows "
11004 "could take some time. Do you "
11005 "want to continue?"),
11006 g_slist_length(msginfo_list));
11007 if (g_slist_length(msginfo_list) > 9
11008 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11009 != G_ALERTALTERNATE) {
11014 /* We'll open multiple compose windows */
11015 /* let the WM place the next windows */
11016 compose_force_window_origin = FALSE;
11017 for (; cur; cur = cur->next) {
11019 tmplist.data = cur->data;
11020 tmplist.next = NULL;
11021 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11023 compose_force_window_origin = TRUE;
11025 /* forwarding multiple mails as attachments is done via a
11026 * single compose window */
11027 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11031 void compose_set_position(Compose *compose, gint pos)
11033 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11035 gtkut_text_view_set_position(text, pos);
11038 gboolean compose_search_string(Compose *compose,
11039 const gchar *str, gboolean case_sens)
11041 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11043 return gtkut_text_view_search_string(text, str, case_sens);
11046 gboolean compose_search_string_backward(Compose *compose,
11047 const gchar *str, gboolean case_sens)
11049 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11051 return gtkut_text_view_search_string_backward(text, str, case_sens);
11054 /* allocate a msginfo structure and populate its data from a compose data structure */
11055 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11057 MsgInfo *newmsginfo;
11059 gchar buf[BUFFSIZE];
11061 cm_return_val_if_fail( compose != NULL, NULL );
11063 newmsginfo = procmsg_msginfo_new();
11066 get_rfc822_date(buf, sizeof(buf));
11067 newmsginfo->date = g_strdup(buf);
11070 if (compose->from_name) {
11071 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11072 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11076 if (compose->subject_entry)
11077 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11079 /* to, cc, reply-to, newsgroups */
11080 for (list = compose->header_list; list; list = list->next) {
11081 gchar *header = gtk_editable_get_chars(
11083 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11084 gchar *entry = gtk_editable_get_chars(
11085 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11087 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11088 if ( newmsginfo->to == NULL ) {
11089 newmsginfo->to = g_strdup(entry);
11090 } else if (entry && *entry) {
11091 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11092 g_free(newmsginfo->to);
11093 newmsginfo->to = tmp;
11096 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11097 if ( newmsginfo->cc == NULL ) {
11098 newmsginfo->cc = g_strdup(entry);
11099 } else if (entry && *entry) {
11100 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11101 g_free(newmsginfo->cc);
11102 newmsginfo->cc = tmp;
11105 if ( strcasecmp(header,
11106 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11107 if ( newmsginfo->newsgroups == NULL ) {
11108 newmsginfo->newsgroups = g_strdup(entry);
11109 } else if (entry && *entry) {
11110 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11111 g_free(newmsginfo->newsgroups);
11112 newmsginfo->newsgroups = tmp;
11120 /* other data is unset */
11126 /* update compose's dictionaries from folder dict settings */
11127 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11128 FolderItem *folder_item)
11130 cm_return_if_fail(compose != NULL);
11132 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11133 FolderItemPrefs *prefs = folder_item->prefs;
11135 if (prefs->enable_default_dictionary)
11136 gtkaspell_change_dict(compose->gtkaspell,
11137 prefs->default_dictionary, FALSE);
11138 if (folder_item->prefs->enable_default_alt_dictionary)
11139 gtkaspell_change_alt_dict(compose->gtkaspell,
11140 prefs->default_alt_dictionary);
11141 if (prefs->enable_default_dictionary
11142 || prefs->enable_default_alt_dictionary)
11143 compose_spell_menu_changed(compose);