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) {
2746 gchar *to = NULL, *start = NULL;
2748 extract_address(hentry[H_LIST_POST].body);
2749 if (hentry[H_LIST_POST].body[0] != '\0') {
2750 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2752 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2753 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2756 g_free(compose->ml_post);
2757 compose->ml_post = to;
2760 g_free(hentry[H_LIST_POST].body);
2761 hentry[H_LIST_POST].body = NULL;
2764 /* CLAWS - X-Priority */
2765 if (compose->mode == COMPOSE_REEDIT)
2766 if (hentry[H_X_PRIORITY].body != NULL) {
2769 priority = atoi(hentry[H_X_PRIORITY].body);
2770 g_free(hentry[H_X_PRIORITY].body);
2772 hentry[H_X_PRIORITY].body = NULL;
2774 if (priority < PRIORITY_HIGHEST ||
2775 priority > PRIORITY_LOWEST)
2776 priority = PRIORITY_NORMAL;
2778 compose->priority = priority;
2781 if (compose->mode == COMPOSE_REEDIT) {
2782 if (msginfo->inreplyto && *msginfo->inreplyto)
2783 compose->inreplyto = g_strdup(msginfo->inreplyto);
2787 if (msginfo->msgid && *msginfo->msgid)
2788 compose->inreplyto = g_strdup(msginfo->msgid);
2790 if (!compose->references) {
2791 if (msginfo->msgid && *msginfo->msgid) {
2792 if (msginfo->inreplyto && *msginfo->inreplyto)
2793 compose->references =
2794 g_strdup_printf("<%s>\n\t<%s>",
2798 compose->references =
2799 g_strconcat("<", msginfo->msgid, ">",
2801 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2802 compose->references =
2803 g_strconcat("<", msginfo->inreplyto, ">",
2811 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2813 GSList *ref_id_list, *cur;
2817 ref_id_list = references_list_append(NULL, ref);
2818 if (!ref_id_list) return NULL;
2819 if (msgid && *msgid)
2820 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2825 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2826 /* "<" + Message-ID + ">" + CR+LF+TAB */
2827 len += strlen((gchar *)cur->data) + 5;
2829 if (len > MAX_REFERENCES_LEN) {
2830 /* remove second message-ID */
2831 if (ref_id_list && ref_id_list->next &&
2832 ref_id_list->next->next) {
2833 g_free(ref_id_list->next->data);
2834 ref_id_list = g_slist_remove
2835 (ref_id_list, ref_id_list->next->data);
2837 slist_free_strings(ref_id_list);
2838 g_slist_free(ref_id_list);
2845 new_ref = g_string_new("");
2846 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2847 if (new_ref->len > 0)
2848 g_string_append(new_ref, "\n\t");
2849 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2852 slist_free_strings(ref_id_list);
2853 g_slist_free(ref_id_list);
2855 new_ref_str = new_ref->str;
2856 g_string_free(new_ref, FALSE);
2861 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2862 const gchar *fmt, const gchar *qmark,
2863 const gchar *body, gboolean rewrap,
2864 gboolean need_unescape,
2865 const gchar *err_msg)
2867 MsgInfo* dummyinfo = NULL;
2868 gchar *quote_str = NULL;
2870 gboolean prev_autowrap;
2871 const gchar *trimmed_body = body;
2872 gint cursor_pos = -1;
2873 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2874 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2879 SIGNAL_BLOCK(buffer);
2882 dummyinfo = compose_msginfo_new_from_compose(compose);
2883 msginfo = dummyinfo;
2886 if (qmark != NULL) {
2888 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2889 compose->gtkaspell);
2891 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2893 quote_fmt_scan_string(qmark);
2896 buf = quote_fmt_get_buffer();
2898 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2900 Xstrdup_a(quote_str, buf, goto error)
2903 if (fmt && *fmt != '\0') {
2906 while (*trimmed_body == '\n')
2910 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2911 compose->gtkaspell);
2913 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2915 if (need_unescape) {
2918 /* decode \-escape sequences in the internal representation of the quote format */
2919 tmp = malloc(strlen(fmt)+1);
2920 pref_get_unescaped_pref(tmp, fmt);
2921 quote_fmt_scan_string(tmp);
2925 quote_fmt_scan_string(fmt);
2929 buf = quote_fmt_get_buffer();
2931 gint line = quote_fmt_get_line();
2932 alertpanel_error(err_msg, line);
2938 prev_autowrap = compose->autowrap;
2939 compose->autowrap = FALSE;
2941 mark = gtk_text_buffer_get_insert(buffer);
2942 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2943 if (g_utf8_validate(buf, -1, NULL)) {
2944 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2946 gchar *tmpout = NULL;
2947 tmpout = conv_codeset_strdup
2948 (buf, conv_get_locale_charset_str_no_utf8(),
2950 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2952 tmpout = g_malloc(strlen(buf)*2+1);
2953 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2955 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2959 cursor_pos = quote_fmt_get_cursor_pos();
2960 if (cursor_pos == -1)
2961 cursor_pos = gtk_text_iter_get_offset(&iter);
2962 compose->set_cursor_pos = cursor_pos;
2964 gtk_text_buffer_get_start_iter(buffer, &iter);
2965 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2966 gtk_text_buffer_place_cursor(buffer, &iter);
2968 compose->autowrap = prev_autowrap;
2969 if (compose->autowrap && rewrap)
2970 compose_wrap_all(compose);
2977 SIGNAL_UNBLOCK(buffer);
2979 procmsg_msginfo_free( dummyinfo );
2984 /* if ml_post is of type addr@host and from is of type
2985 * addr-anything@host, return TRUE
2987 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2989 gchar *left_ml = NULL;
2990 gchar *right_ml = NULL;
2991 gchar *left_from = NULL;
2992 gchar *right_from = NULL;
2993 gboolean result = FALSE;
2995 if (!ml_post || !from)
2998 left_ml = g_strdup(ml_post);
2999 if (strstr(left_ml, "@")) {
3000 right_ml = strstr(left_ml, "@")+1;
3001 *(strstr(left_ml, "@")) = '\0';
3004 left_from = g_strdup(from);
3005 if (strstr(left_from, "@")) {
3006 right_from = strstr(left_from, "@")+1;
3007 *(strstr(left_from, "@")) = '\0';
3010 if (left_ml && left_from && right_ml && right_from
3011 && !strncmp(left_from, left_ml, strlen(left_ml))
3012 && !strcmp(right_from, right_ml)) {
3021 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3022 gboolean to_all, gboolean to_ml,
3024 gboolean followup_and_reply_to)
3026 GSList *cc_list = NULL;
3029 gchar *replyto = NULL;
3031 gboolean reply_to_ml = FALSE;
3032 gboolean default_reply_to = FALSE;
3034 cm_return_if_fail(compose->account != NULL);
3035 cm_return_if_fail(msginfo != NULL);
3037 reply_to_ml = to_ml && compose->ml_post;
3039 default_reply_to = msginfo->folder &&
3040 msginfo->folder->prefs->enable_default_reply_to;
3042 if (compose->account->protocol != A_NNTP) {
3043 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3044 if (msginfo->folder->prefs->enable_default_replyto) {
3045 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3046 COMPOSE_REPLYTO, PREF_FOLDER);
3048 if (msginfo->folder->prefs->enable_default_bcc) {
3049 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3050 COMPOSE_BCC, PREF_FOLDER);
3052 if (msginfo->folder->prefs->enable_default_cc) {
3053 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3054 COMPOSE_CC, PREF_FOLDER);
3057 if (reply_to_ml && !default_reply_to) {
3059 gboolean is_subscr = is_subscription(compose->ml_post,
3062 /* normal answer to ml post with a reply-to */
3063 compose_entry_append(compose,
3065 COMPOSE_TO, PREF_ML);
3066 if (compose->replyto)
3067 compose_entry_append(compose,
3069 COMPOSE_CC, PREF_ML);
3071 /* answer to subscription confirmation */
3072 if (compose->replyto)
3073 compose_entry_append(compose,
3075 COMPOSE_TO, PREF_ML);
3076 else if (msginfo->from)
3077 compose_entry_append(compose,
3079 COMPOSE_TO, PREF_ML);
3082 else if (!(to_all || to_sender) && default_reply_to) {
3083 compose_entry_append(compose,
3084 msginfo->folder->prefs->default_reply_to,
3085 COMPOSE_TO, PREF_FOLDER);
3086 compose_entry_mark_default_to(compose,
3087 msginfo->folder->prefs->default_reply_to);
3092 Xstrdup_a(tmp1, msginfo->from, return);
3093 extract_address(tmp1);
3094 if (to_all || to_sender ||
3095 !account_find_from_address(tmp1, FALSE))
3096 compose_entry_append(compose,
3097 (compose->replyto && !to_sender)
3098 ? compose->replyto :
3099 msginfo->from ? msginfo->from : "",
3100 COMPOSE_TO, PREF_NONE);
3101 else if (!to_all && !to_sender) {
3102 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3103 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3104 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3105 if (compose->replyto) {
3106 compose_entry_append(compose,
3108 COMPOSE_TO, PREF_NONE);
3110 compose_entry_append(compose,
3111 msginfo->from ? msginfo->from : "",
3112 COMPOSE_TO, PREF_NONE);
3115 /* replying to own mail, use original recp */
3116 compose_entry_append(compose,
3117 msginfo->to ? msginfo->to : "",
3118 COMPOSE_TO, PREF_NONE);
3119 compose_entry_append(compose,
3120 msginfo->cc ? msginfo->cc : "",
3121 COMPOSE_CC, PREF_NONE);
3126 if (to_sender || (compose->followup_to &&
3127 !strncmp(compose->followup_to, "poster", 6)))
3128 compose_entry_append
3130 (compose->replyto ? compose->replyto :
3131 msginfo->from ? msginfo->from : ""),
3132 COMPOSE_TO, PREF_NONE);
3134 else if (followup_and_reply_to || to_all) {
3135 compose_entry_append
3137 (compose->replyto ? compose->replyto :
3138 msginfo->from ? msginfo->from : ""),
3139 COMPOSE_TO, PREF_NONE);
3141 compose_entry_append
3143 compose->followup_to ? compose->followup_to :
3144 compose->newsgroups ? compose->newsgroups : "",
3145 COMPOSE_NEWSGROUPS, PREF_NONE);
3148 compose_entry_append
3150 compose->followup_to ? compose->followup_to :
3151 compose->newsgroups ? compose->newsgroups : "",
3152 COMPOSE_NEWSGROUPS, PREF_NONE);
3155 if (msginfo->subject && *msginfo->subject) {
3159 buf = p = g_strdup(msginfo->subject);
3160 p += subject_get_prefix_length(p);
3161 memmove(buf, p, strlen(p) + 1);
3163 buf2 = g_strdup_printf("Re: %s", buf);
3164 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3169 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3171 if (to_ml && compose->ml_post) return;
3172 if (!to_all || compose->account->protocol == A_NNTP) return;
3174 if (compose->replyto) {
3175 Xstrdup_a(replyto, compose->replyto, return);
3176 extract_address(replyto);
3178 if (msginfo->from) {
3179 Xstrdup_a(from, msginfo->from, return);
3180 extract_address(from);
3183 if (replyto && from)
3184 cc_list = address_list_append_with_comments(cc_list, from);
3185 if (to_all && msginfo->folder &&
3186 msginfo->folder->prefs->enable_default_reply_to)
3187 cc_list = address_list_append_with_comments(cc_list,
3188 msginfo->folder->prefs->default_reply_to);
3189 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3190 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3193 for (cur = cc_list; cur != NULL; cur = cur->next)
3194 compose_entry_append(compose, (gchar *)cur->data,
3195 COMPOSE_CC, PREF_NONE);
3196 slist_free_strings(cc_list);
3197 g_slist_free(cc_list);
3201 #define SET_ENTRY(entry, str) \
3204 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3207 #define SET_ADDRESS(type, str) \
3210 compose_entry_append(compose, str, type, PREF_NONE); \
3213 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3215 cm_return_if_fail(msginfo != NULL);
3217 SET_ENTRY(subject_entry, msginfo->subject);
3218 SET_ENTRY(from_name, msginfo->from);
3219 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3220 SET_ADDRESS(COMPOSE_CC, compose->cc);
3221 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3222 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3223 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3224 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3226 compose_update_priority_menu_item(compose);
3227 compose_update_privacy_system_menu_item(compose, FALSE);
3228 compose_show_first_last_header(compose, TRUE);
3234 static void compose_insert_sig(Compose *compose, gboolean replace)
3236 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3237 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3239 GtkTextIter iter, iter_end;
3240 gint cur_pos, ins_pos;
3241 gboolean prev_autowrap;
3242 gboolean found = FALSE;
3243 gboolean exists = FALSE;
3245 cm_return_if_fail(compose->account != NULL);
3249 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3250 G_CALLBACK(compose_changed_cb),
3253 mark = gtk_text_buffer_get_insert(buffer);
3254 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3255 cur_pos = gtk_text_iter_get_offset (&iter);
3258 gtk_text_buffer_get_end_iter(buffer, &iter);
3260 exists = (compose->sig_str != NULL);
3263 GtkTextIter first_iter, start_iter, end_iter;
3265 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3267 if (!exists || compose->sig_str[0] == '\0')
3270 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3271 compose->signature_tag);
3274 /* include previous \n\n */
3275 gtk_text_iter_backward_chars(&first_iter, 1);
3276 start_iter = first_iter;
3277 end_iter = first_iter;
3279 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3280 compose->signature_tag);
3281 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3282 compose->signature_tag);
3284 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3290 g_free(compose->sig_str);
3291 compose->sig_str = account_get_signature_str(compose->account);
3293 cur_pos = gtk_text_iter_get_offset(&iter);
3295 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3296 g_free(compose->sig_str);
3297 compose->sig_str = NULL;
3299 if (compose->sig_inserted == FALSE)
3300 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3301 compose->sig_inserted = TRUE;
3303 cur_pos = gtk_text_iter_get_offset(&iter);
3304 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3306 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3307 gtk_text_iter_forward_chars(&iter, 1);
3308 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3309 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3311 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3312 cur_pos = gtk_text_buffer_get_char_count (buffer);
3315 /* put the cursor where it should be
3316 * either where the quote_fmt says, either where it was */
3317 if (compose->set_cursor_pos < 0)
3318 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3320 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3321 compose->set_cursor_pos);
3323 compose->set_cursor_pos = -1;
3324 gtk_text_buffer_place_cursor(buffer, &iter);
3325 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3326 G_CALLBACK(compose_changed_cb),
3332 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3335 GtkTextBuffer *buffer;
3338 const gchar *cur_encoding;
3339 gchar buf[BUFFSIZE];
3342 gboolean prev_autowrap;
3343 gboolean badtxt = FALSE;
3344 struct stat file_stat;
3347 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3349 /* get the size of the file we are about to insert */
3350 ret = g_stat(file, &file_stat);
3352 gchar *shortfile = g_path_get_basename(file);
3353 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3355 return COMPOSE_INSERT_NO_FILE;
3356 } else if (prefs_common.warn_large_insert == TRUE) {
3358 /* ask user for confirmation if the file is large */
3359 if (prefs_common.warn_large_insert_size < 0 ||
3360 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3364 msg = g_strdup_printf(_("You are about to insert a file of %s "
3365 "in the message body. Are you sure you want to do that?"),
3366 to_human_readable(file_stat.st_size));
3367 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3368 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3371 /* do we ask for confirmation next time? */
3372 if (aval & G_ALERTDISABLE) {
3373 /* no confirmation next time, disable feature in preferences */
3374 aval &= ~G_ALERTDISABLE;
3375 prefs_common.warn_large_insert = FALSE;
3378 /* abort file insertion if user canceled action */
3379 if (aval != G_ALERTALTERNATE) {
3380 return COMPOSE_INSERT_NO_FILE;
3386 if ((fp = g_fopen(file, "rb")) == NULL) {
3387 FILE_OP_ERROR(file, "fopen");
3388 return COMPOSE_INSERT_READ_ERROR;
3391 prev_autowrap = compose->autowrap;
3392 compose->autowrap = FALSE;
3394 text = GTK_TEXT_VIEW(compose->text);
3395 buffer = gtk_text_view_get_buffer(text);
3396 mark = gtk_text_buffer_get_insert(buffer);
3397 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3399 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3400 G_CALLBACK(text_inserted),
3403 cur_encoding = conv_get_locale_charset_str_no_utf8();
3405 while (fgets(buf, sizeof(buf), fp) != NULL) {
3408 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3409 str = g_strdup(buf);
3411 str = conv_codeset_strdup
3412 (buf, cur_encoding, CS_INTERNAL);
3415 /* strip <CR> if DOS/Windows file,
3416 replace <CR> with <LF> if Macintosh file. */
3419 if (len > 0 && str[len - 1] != '\n') {
3421 if (str[len] == '\r') str[len] = '\n';
3424 gtk_text_buffer_insert(buffer, &iter, str, -1);
3428 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3429 G_CALLBACK(text_inserted),
3431 compose->autowrap = prev_autowrap;
3432 if (compose->autowrap)
3433 compose_wrap_all(compose);
3438 return COMPOSE_INSERT_INVALID_CHARACTER;
3440 return COMPOSE_INSERT_SUCCESS;
3443 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3444 const gchar *filename,
3445 const gchar *content_type)
3453 GtkListStore *store;
3455 gboolean has_binary = FALSE;
3457 if (!is_file_exist(file)) {
3458 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3459 gboolean result = FALSE;
3460 if (file_from_uri && is_file_exist(file_from_uri)) {
3461 result = compose_attach_append(
3462 compose, file_from_uri,
3466 g_free(file_from_uri);
3469 alertpanel_error("File %s doesn't exist\n", filename);
3472 if ((size = get_file_size(file)) < 0) {
3473 alertpanel_error("Can't get file size of %s\n", filename);
3477 alertpanel_error(_("File %s is empty."), filename);
3480 if ((fp = g_fopen(file, "rb")) == NULL) {
3481 alertpanel_error(_("Can't read %s."), filename);
3486 ainfo = g_new0(AttachInfo, 1);
3487 auto_ainfo = g_auto_pointer_new_with_free
3488 (ainfo, (GFreeFunc) compose_attach_info_free);
3489 ainfo->file = g_strdup(file);
3492 ainfo->content_type = g_strdup(content_type);
3493 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3495 MsgFlags flags = {0, 0};
3497 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3498 ainfo->encoding = ENC_7BIT;
3500 ainfo->encoding = ENC_8BIT;
3502 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3503 if (msginfo && msginfo->subject)
3504 name = g_strdup(msginfo->subject);
3506 name = g_path_get_basename(filename ? filename : file);
3508 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3510 procmsg_msginfo_free(msginfo);
3512 if (!g_ascii_strncasecmp(content_type, "text", 4))
3513 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3515 ainfo->encoding = ENC_BASE64;
3516 name = g_path_get_basename(filename ? filename : file);
3517 ainfo->name = g_strdup(name);
3521 ainfo->content_type = procmime_get_mime_type(file);
3522 if (!ainfo->content_type) {
3523 ainfo->content_type =
3524 g_strdup("application/octet-stream");
3525 ainfo->encoding = ENC_BASE64;
3526 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3528 procmime_get_encoding_for_text_file(file, &has_binary);
3530 ainfo->encoding = ENC_BASE64;
3531 name = g_path_get_basename(filename ? filename : file);
3532 ainfo->name = g_strdup(name);
3536 if (ainfo->name != NULL
3537 && !strcmp(ainfo->name, ".")) {
3538 g_free(ainfo->name);
3542 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3543 g_free(ainfo->content_type);
3544 ainfo->content_type = g_strdup("application/octet-stream");
3547 ainfo->size = (goffset)size;
3548 size_text = to_human_readable((goffset)size);
3550 store = GTK_LIST_STORE(gtk_tree_view_get_model
3551 (GTK_TREE_VIEW(compose->attach_clist)));
3553 gtk_list_store_append(store, &iter);
3554 gtk_list_store_set(store, &iter,
3555 COL_MIMETYPE, ainfo->content_type,
3556 COL_SIZE, size_text,
3557 COL_NAME, ainfo->name,
3559 COL_AUTODATA, auto_ainfo,
3562 g_auto_pointer_free(auto_ainfo);
3563 compose_attach_update_label(compose);
3567 static void compose_use_signing(Compose *compose, gboolean use_signing)
3569 compose->use_signing = use_signing;
3570 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3573 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3575 compose->use_encryption = use_encryption;
3576 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3579 #define NEXT_PART_NOT_CHILD(info) \
3581 node = info->node; \
3582 while (node->children) \
3583 node = g_node_last_child(node); \
3584 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3587 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3591 MimeInfo *firsttext = NULL;
3592 MimeInfo *encrypted = NULL;
3595 const gchar *partname = NULL;
3597 mimeinfo = procmime_scan_message(msginfo);
3598 if (!mimeinfo) return;
3600 if (mimeinfo->node->children == NULL) {
3601 procmime_mimeinfo_free_all(mimeinfo);
3605 /* find first content part */
3606 child = (MimeInfo *) mimeinfo->node->children->data;
3607 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3608 child = (MimeInfo *)child->node->children->data;
3611 if (child->type == MIMETYPE_TEXT) {
3613 debug_print("First text part found\n");
3614 } else if (compose->mode == COMPOSE_REEDIT &&
3615 child->type == MIMETYPE_APPLICATION &&
3616 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3617 encrypted = (MimeInfo *)child->node->parent->data;
3620 child = (MimeInfo *) mimeinfo->node->children->data;
3621 while (child != NULL) {
3624 if (child == encrypted) {
3625 /* skip this part of tree */
3626 NEXT_PART_NOT_CHILD(child);
3630 if (child->type == MIMETYPE_MULTIPART) {
3631 /* get the actual content */
3632 child = procmime_mimeinfo_next(child);
3636 if (child == firsttext) {
3637 child = procmime_mimeinfo_next(child);
3641 outfile = procmime_get_tmp_file_name(child);
3642 if ((err = procmime_get_part(outfile, child)) < 0)
3643 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3645 gchar *content_type;
3647 content_type = procmime_get_content_type_str(child->type, child->subtype);
3649 /* if we meet a pgp signature, we don't attach it, but
3650 * we force signing. */
3651 if ((strcmp(content_type, "application/pgp-signature") &&
3652 strcmp(content_type, "application/pkcs7-signature") &&
3653 strcmp(content_type, "application/x-pkcs7-signature"))
3654 || compose->mode == COMPOSE_REDIRECT) {
3655 partname = procmime_mimeinfo_get_parameter(child, "filename");
3656 if (partname == NULL)
3657 partname = procmime_mimeinfo_get_parameter(child, "name");
3658 if (partname == NULL)
3660 compose_attach_append(compose, outfile,
3661 partname, content_type);
3663 compose_force_signing(compose, compose->account, NULL);
3665 g_free(content_type);
3668 NEXT_PART_NOT_CHILD(child);
3670 procmime_mimeinfo_free_all(mimeinfo);
3673 #undef NEXT_PART_NOT_CHILD
3678 WAIT_FOR_INDENT_CHAR,
3679 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3682 /* return indent length, we allow:
3683 indent characters followed by indent characters or spaces/tabs,
3684 alphabets and numbers immediately followed by indent characters,
3685 and the repeating sequences of the above
3686 If quote ends with multiple spaces, only the first one is included. */
3687 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3688 const GtkTextIter *start, gint *len)
3690 GtkTextIter iter = *start;
3694 IndentState state = WAIT_FOR_INDENT_CHAR;
3697 gint alnum_count = 0;
3698 gint space_count = 0;
3701 if (prefs_common.quote_chars == NULL) {
3705 while (!gtk_text_iter_ends_line(&iter)) {
3706 wc = gtk_text_iter_get_char(&iter);
3707 if (g_unichar_iswide(wc))
3709 clen = g_unichar_to_utf8(wc, ch);
3713 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3714 is_space = g_unichar_isspace(wc);
3716 if (state == WAIT_FOR_INDENT_CHAR) {
3717 if (!is_indent && !g_unichar_isalnum(wc))
3720 quote_len += alnum_count + space_count + 1;
3721 alnum_count = space_count = 0;
3722 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3725 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3726 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3730 else if (is_indent) {
3731 quote_len += alnum_count + space_count + 1;
3732 alnum_count = space_count = 0;
3735 state = WAIT_FOR_INDENT_CHAR;
3739 gtk_text_iter_forward_char(&iter);
3742 if (quote_len > 0 && space_count > 0)
3748 if (quote_len > 0) {
3750 gtk_text_iter_forward_chars(&iter, quote_len);
3751 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3757 /* return >0 if the line is itemized */
3758 static int compose_itemized_length(GtkTextBuffer *buffer,
3759 const GtkTextIter *start)
3761 GtkTextIter iter = *start;
3766 if (gtk_text_iter_ends_line(&iter))
3771 wc = gtk_text_iter_get_char(&iter);
3772 if (!g_unichar_isspace(wc))
3774 gtk_text_iter_forward_char(&iter);
3775 if (gtk_text_iter_ends_line(&iter))
3779 clen = g_unichar_to_utf8(wc, ch);
3783 if (!strchr("*-+", ch[0]))
3786 gtk_text_iter_forward_char(&iter);
3787 if (gtk_text_iter_ends_line(&iter))
3789 wc = gtk_text_iter_get_char(&iter);
3790 if (g_unichar_isspace(wc)) {
3796 /* return the string at the start of the itemization */
3797 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3798 const GtkTextIter *start)
3800 GtkTextIter iter = *start;
3803 GString *item_chars = g_string_new("");
3806 if (gtk_text_iter_ends_line(&iter))
3811 wc = gtk_text_iter_get_char(&iter);
3812 if (!g_unichar_isspace(wc))
3814 gtk_text_iter_forward_char(&iter);
3815 if (gtk_text_iter_ends_line(&iter))
3817 g_string_append_unichar(item_chars, wc);
3820 str = item_chars->str;
3821 g_string_free(item_chars, FALSE);
3825 /* return the number of spaces at a line's start */
3826 static int compose_left_offset_length(GtkTextBuffer *buffer,
3827 const GtkTextIter *start)
3829 GtkTextIter iter = *start;
3832 if (gtk_text_iter_ends_line(&iter))
3836 wc = gtk_text_iter_get_char(&iter);
3837 if (!g_unichar_isspace(wc))
3840 gtk_text_iter_forward_char(&iter);
3841 if (gtk_text_iter_ends_line(&iter))
3845 gtk_text_iter_forward_char(&iter);
3846 if (gtk_text_iter_ends_line(&iter))
3851 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3852 const GtkTextIter *start,
3853 GtkTextIter *break_pos,
3857 GtkTextIter iter = *start, line_end = *start;
3858 PangoLogAttr *attrs;
3865 gboolean can_break = FALSE;
3866 gboolean do_break = FALSE;
3867 gboolean was_white = FALSE;
3868 gboolean prev_dont_break = FALSE;
3870 gtk_text_iter_forward_to_line_end(&line_end);
3871 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3872 len = g_utf8_strlen(str, -1);
3876 g_warning("compose_get_line_break_pos: len = 0!\n");
3880 /* g_print("breaking line: %d: %s (len = %d)\n",
3881 gtk_text_iter_get_line(&iter), str, len); */
3883 attrs = g_new(PangoLogAttr, len + 1);
3885 pango_default_break(str, -1, NULL, attrs, len + 1);
3889 /* skip quote and leading spaces */
3890 for (i = 0; *p != '\0' && i < len; i++) {
3893 wc = g_utf8_get_char(p);
3894 if (i >= quote_len && !g_unichar_isspace(wc))
3896 if (g_unichar_iswide(wc))
3898 else if (*p == '\t')
3902 p = g_utf8_next_char(p);
3905 for (; *p != '\0' && i < len; i++) {
3906 PangoLogAttr *attr = attrs + i;
3910 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3913 was_white = attr->is_white;
3915 /* don't wrap URI */
3916 if ((uri_len = get_uri_len(p)) > 0) {
3918 if (pos > 0 && col > max_col) {
3928 wc = g_utf8_get_char(p);
3929 if (g_unichar_iswide(wc)) {
3931 if (prev_dont_break && can_break && attr->is_line_break)
3933 } else if (*p == '\t')
3937 if (pos > 0 && col > max_col) {
3942 if (*p == '-' || *p == '/')
3943 prev_dont_break = TRUE;
3945 prev_dont_break = FALSE;
3947 p = g_utf8_next_char(p);
3951 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3956 *break_pos = *start;
3957 gtk_text_iter_set_line_offset(break_pos, pos);
3962 static gboolean compose_join_next_line(Compose *compose,
3963 GtkTextBuffer *buffer,
3965 const gchar *quote_str)
3967 GtkTextIter iter_ = *iter, cur, prev, next, end;
3968 PangoLogAttr attrs[3];
3970 gchar *next_quote_str;
3973 gboolean keep_cursor = FALSE;
3975 if (!gtk_text_iter_forward_line(&iter_) ||
3976 gtk_text_iter_ends_line(&iter_)) {
3979 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3981 if ((quote_str || next_quote_str) &&
3982 strcmp2(quote_str, next_quote_str) != 0) {
3983 g_free(next_quote_str);
3986 g_free(next_quote_str);
3989 if (quote_len > 0) {
3990 gtk_text_iter_forward_chars(&end, quote_len);
3991 if (gtk_text_iter_ends_line(&end)) {
3996 /* don't join itemized lines */
3997 if (compose_itemized_length(buffer, &end) > 0) {
4001 /* don't join signature separator */
4002 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4005 /* delete quote str */
4007 gtk_text_buffer_delete(buffer, &iter_, &end);
4009 /* don't join line breaks put by the user */
4011 gtk_text_iter_backward_char(&cur);
4012 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4013 gtk_text_iter_forward_char(&cur);
4017 gtk_text_iter_forward_char(&cur);
4018 /* delete linebreak and extra spaces */
4019 while (gtk_text_iter_backward_char(&cur)) {
4020 wc1 = gtk_text_iter_get_char(&cur);
4021 if (!g_unichar_isspace(wc1))
4026 while (!gtk_text_iter_ends_line(&cur)) {
4027 wc1 = gtk_text_iter_get_char(&cur);
4028 if (!g_unichar_isspace(wc1))
4030 gtk_text_iter_forward_char(&cur);
4033 if (!gtk_text_iter_equal(&prev, &next)) {
4036 mark = gtk_text_buffer_get_insert(buffer);
4037 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4038 if (gtk_text_iter_equal(&prev, &cur))
4040 gtk_text_buffer_delete(buffer, &prev, &next);
4044 /* insert space if required */
4045 gtk_text_iter_backward_char(&prev);
4046 wc1 = gtk_text_iter_get_char(&prev);
4047 wc2 = gtk_text_iter_get_char(&next);
4048 gtk_text_iter_forward_char(&next);
4049 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4050 pango_default_break(str, -1, NULL, attrs, 3);
4051 if (!attrs[1].is_line_break ||
4052 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4053 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4055 gtk_text_iter_backward_char(&iter_);
4056 gtk_text_buffer_place_cursor(buffer, &iter_);
4065 #define ADD_TXT_POS(bp_, ep_, pti_) \
4066 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4067 last = last->next; \
4068 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4069 last->next = NULL; \
4071 g_warning("alloc error scanning URIs\n"); \
4074 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4076 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4077 GtkTextBuffer *buffer;
4078 GtkTextIter iter, break_pos, end_of_line;
4079 gchar *quote_str = NULL;
4081 gboolean wrap_quote = prefs_common.linewrap_quote;
4082 gboolean prev_autowrap = compose->autowrap;
4083 gint startq_offset = -1, noq_offset = -1;
4084 gint uri_start = -1, uri_stop = -1;
4085 gint nouri_start = -1, nouri_stop = -1;
4086 gint num_blocks = 0;
4087 gint quotelevel = -1;
4088 gboolean modified = force;
4089 gboolean removed = FALSE;
4090 gboolean modified_before_remove = FALSE;
4092 gboolean start = TRUE;
4093 gint itemized_len = 0, rem_item_len = 0;
4094 gchar *itemized_chars = NULL;
4095 gboolean item_continuation = FALSE;
4100 if (compose->draft_timeout_tag == -2) {
4104 compose->autowrap = FALSE;
4106 buffer = gtk_text_view_get_buffer(text);
4107 undo_wrapping(compose->undostruct, TRUE);
4112 mark = gtk_text_buffer_get_insert(buffer);
4113 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4117 if (compose->draft_timeout_tag == -2) {
4118 if (gtk_text_iter_ends_line(&iter)) {
4119 while (gtk_text_iter_ends_line(&iter) &&
4120 gtk_text_iter_forward_line(&iter))
4123 while (gtk_text_iter_backward_line(&iter)) {
4124 if (gtk_text_iter_ends_line(&iter)) {
4125 gtk_text_iter_forward_line(&iter);
4131 /* move to line start */
4132 gtk_text_iter_set_line_offset(&iter, 0);
4135 itemized_len = compose_itemized_length(buffer, &iter);
4137 if (!itemized_len) {
4138 itemized_len = compose_left_offset_length(buffer, &iter);
4139 item_continuation = TRUE;
4143 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4145 /* go until paragraph end (empty line) */
4146 while (start || !gtk_text_iter_ends_line(&iter)) {
4147 gchar *scanpos = NULL;
4148 /* parse table - in order of priority */
4150 const gchar *needle; /* token */
4152 /* token search function */
4153 gchar *(*search) (const gchar *haystack,
4154 const gchar *needle);
4155 /* part parsing function */
4156 gboolean (*parse) (const gchar *start,
4157 const gchar *scanpos,
4161 /* part to URI function */
4162 gchar *(*build_uri) (const gchar *bp,
4166 static struct table parser[] = {
4167 {"http://", strcasestr, get_uri_part, make_uri_string},
4168 {"https://", strcasestr, get_uri_part, make_uri_string},
4169 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4170 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4171 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4172 {"www.", strcasestr, get_uri_part, make_http_string},
4173 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4174 {"@", strcasestr, get_email_part, make_email_string}
4176 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4177 gint last_index = PARSE_ELEMS;
4179 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4183 if (!prev_autowrap && num_blocks == 0) {
4185 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4186 G_CALLBACK(text_inserted),
4189 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4192 uri_start = uri_stop = -1;
4194 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4197 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4198 if (startq_offset == -1)
4199 startq_offset = gtk_text_iter_get_offset(&iter);
4200 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4201 if (quotelevel > 2) {
4202 /* recycle colors */
4203 if (prefs_common.recycle_quote_colors)
4212 if (startq_offset == -1)
4213 noq_offset = gtk_text_iter_get_offset(&iter);
4217 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4220 if (gtk_text_iter_ends_line(&iter)) {
4222 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4223 prefs_common.linewrap_len,
4225 GtkTextIter prev, next, cur;
4226 if (prev_autowrap != FALSE || force) {
4227 compose->automatic_break = TRUE;
4229 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4230 compose->automatic_break = FALSE;
4231 if (itemized_len && compose->autoindent) {
4232 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4233 if (!item_continuation)
4234 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4236 } else if (quote_str && wrap_quote) {
4237 compose->automatic_break = TRUE;
4239 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4240 compose->automatic_break = FALSE;
4241 if (itemized_len && compose->autoindent) {
4242 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4243 if (!item_continuation)
4244 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4248 /* remove trailing spaces */
4250 rem_item_len = itemized_len;
4251 while (compose->autoindent && rem_item_len-- > 0)
4252 gtk_text_iter_backward_char(&cur);
4253 gtk_text_iter_backward_char(&cur);
4256 while (!gtk_text_iter_starts_line(&cur)) {
4259 gtk_text_iter_backward_char(&cur);
4260 wc = gtk_text_iter_get_char(&cur);
4261 if (!g_unichar_isspace(wc))
4265 if (!gtk_text_iter_equal(&prev, &next)) {
4266 gtk_text_buffer_delete(buffer, &prev, &next);
4268 gtk_text_iter_forward_char(&break_pos);
4272 gtk_text_buffer_insert(buffer, &break_pos,
4276 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4278 /* move iter to current line start */
4279 gtk_text_iter_set_line_offset(&iter, 0);
4286 /* move iter to next line start */
4292 if (!prev_autowrap && num_blocks > 0) {
4294 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4295 G_CALLBACK(text_inserted),
4299 while (!gtk_text_iter_ends_line(&end_of_line)) {
4300 gtk_text_iter_forward_char(&end_of_line);
4302 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4304 nouri_start = gtk_text_iter_get_offset(&iter);
4305 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4307 walk_pos = gtk_text_iter_get_offset(&iter);
4308 /* FIXME: this looks phony. scanning for anything in the parse table */
4309 for (n = 0; n < PARSE_ELEMS; n++) {
4312 tmp = parser[n].search(walk, parser[n].needle);
4314 if (scanpos == NULL || tmp < scanpos) {
4323 /* check if URI can be parsed */
4324 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4325 (const gchar **)&ep, FALSE)
4326 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4330 strlen(parser[last_index].needle);
4333 uri_start = walk_pos + (bp - o_walk);
4334 uri_stop = walk_pos + (ep - o_walk);
4338 gtk_text_iter_forward_line(&iter);
4341 if (startq_offset != -1) {
4342 GtkTextIter startquote, endquote;
4343 gtk_text_buffer_get_iter_at_offset(
4344 buffer, &startquote, startq_offset);
4347 switch (quotelevel) {
4349 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4350 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4351 gtk_text_buffer_apply_tag_by_name(
4352 buffer, "quote0", &startquote, &endquote);
4353 gtk_text_buffer_remove_tag_by_name(
4354 buffer, "quote1", &startquote, &endquote);
4355 gtk_text_buffer_remove_tag_by_name(
4356 buffer, "quote2", &startquote, &endquote);
4361 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4362 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4363 gtk_text_buffer_apply_tag_by_name(
4364 buffer, "quote1", &startquote, &endquote);
4365 gtk_text_buffer_remove_tag_by_name(
4366 buffer, "quote0", &startquote, &endquote);
4367 gtk_text_buffer_remove_tag_by_name(
4368 buffer, "quote2", &startquote, &endquote);
4373 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4374 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4375 gtk_text_buffer_apply_tag_by_name(
4376 buffer, "quote2", &startquote, &endquote);
4377 gtk_text_buffer_remove_tag_by_name(
4378 buffer, "quote0", &startquote, &endquote);
4379 gtk_text_buffer_remove_tag_by_name(
4380 buffer, "quote1", &startquote, &endquote);
4386 } else if (noq_offset != -1) {
4387 GtkTextIter startnoquote, endnoquote;
4388 gtk_text_buffer_get_iter_at_offset(
4389 buffer, &startnoquote, noq_offset);
4392 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4393 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4394 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4395 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4396 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4397 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4398 gtk_text_buffer_remove_tag_by_name(
4399 buffer, "quote0", &startnoquote, &endnoquote);
4400 gtk_text_buffer_remove_tag_by_name(
4401 buffer, "quote1", &startnoquote, &endnoquote);
4402 gtk_text_buffer_remove_tag_by_name(
4403 buffer, "quote2", &startnoquote, &endnoquote);
4409 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4410 GtkTextIter nouri_start_iter, nouri_end_iter;
4411 gtk_text_buffer_get_iter_at_offset(
4412 buffer, &nouri_start_iter, nouri_start);
4413 gtk_text_buffer_get_iter_at_offset(
4414 buffer, &nouri_end_iter, nouri_stop);
4415 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4416 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4417 gtk_text_buffer_remove_tag_by_name(
4418 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4419 modified_before_remove = modified;
4424 if (uri_start >= 0 && uri_stop > 0) {
4425 GtkTextIter uri_start_iter, uri_end_iter, back;
4426 gtk_text_buffer_get_iter_at_offset(
4427 buffer, &uri_start_iter, uri_start);
4428 gtk_text_buffer_get_iter_at_offset(
4429 buffer, &uri_end_iter, uri_stop);
4430 back = uri_end_iter;
4431 gtk_text_iter_backward_char(&back);
4432 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4433 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4434 gtk_text_buffer_apply_tag_by_name(
4435 buffer, "link", &uri_start_iter, &uri_end_iter);
4437 if (removed && !modified_before_remove) {
4443 // debug_print("not modified, out after %d lines\n", lines);
4447 // debug_print("modified, out after %d lines\n", lines);
4449 g_free(itemized_chars);
4452 undo_wrapping(compose->undostruct, FALSE);
4453 compose->autowrap = prev_autowrap;
4458 void compose_action_cb(void *data)
4460 Compose *compose = (Compose *)data;
4461 compose_wrap_all(compose);
4464 static void compose_wrap_all(Compose *compose)
4466 compose_wrap_all_full(compose, FALSE);
4469 static void compose_wrap_all_full(Compose *compose, gboolean force)
4471 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4472 GtkTextBuffer *buffer;
4474 gboolean modified = TRUE;
4476 buffer = gtk_text_view_get_buffer(text);
4478 gtk_text_buffer_get_start_iter(buffer, &iter);
4479 while (!gtk_text_iter_is_end(&iter) && modified)
4480 modified = compose_beautify_paragraph(compose, &iter, force);
4484 static void compose_set_title(Compose *compose)
4490 edited = compose->modified ? _(" [Edited]") : "";
4492 subject = gtk_editable_get_chars(
4493 GTK_EDITABLE(compose->subject_entry), 0, -1);
4495 #ifndef GENERIC_UMPC
4496 if (subject && strlen(subject))
4497 str = g_strdup_printf(_("%s - Compose message%s"),
4500 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4502 str = g_strdup(_("Compose message"));
4505 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4511 * compose_current_mail_account:
4513 * Find a current mail account (the currently selected account, or the
4514 * default account, if a news account is currently selected). If a
4515 * mail account cannot be found, display an error message.
4517 * Return value: Mail account, or NULL if not found.
4519 static PrefsAccount *
4520 compose_current_mail_account(void)
4524 if (cur_account && cur_account->protocol != A_NNTP)
4527 ac = account_get_default();
4528 if (!ac || ac->protocol == A_NNTP) {
4529 alertpanel_error(_("Account for sending mail is not specified.\n"
4530 "Please select a mail account before sending."));
4537 #define QUOTE_IF_REQUIRED(out, str) \
4539 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4543 len = strlen(str) + 3; \
4544 if ((__tmp = alloca(len)) == NULL) { \
4545 g_warning("can't allocate memory\n"); \
4546 g_string_free(header, TRUE); \
4549 g_snprintf(__tmp, len, "\"%s\"", str); \
4554 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4555 g_warning("can't allocate memory\n"); \
4556 g_string_free(header, TRUE); \
4559 strcpy(__tmp, str); \
4565 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4567 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4571 len = strlen(str) + 3; \
4572 if ((__tmp = alloca(len)) == NULL) { \
4573 g_warning("can't allocate memory\n"); \
4576 g_snprintf(__tmp, len, "\"%s\"", str); \
4581 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4582 g_warning("can't allocate memory\n"); \
4585 strcpy(__tmp, str); \
4591 static void compose_select_account(Compose *compose, PrefsAccount *account,
4594 gchar *from = NULL, *header;
4595 ComposeHeaderEntry *header_entry;
4597 cm_return_if_fail(account != NULL);
4599 compose->account = account;
4601 if (account->name && *account->name) {
4603 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4604 from = g_strdup_printf("%s <%s>",
4605 buf, account->address);
4606 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4608 from = g_strdup_printf("<%s>",
4610 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4615 compose_set_title(compose);
4617 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4618 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4620 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4621 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4622 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4624 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4626 activate_privacy_system(compose, account, FALSE);
4628 if (!init && compose->mode != COMPOSE_REDIRECT) {
4629 undo_block(compose->undostruct);
4630 compose_insert_sig(compose, TRUE);
4631 undo_unblock(compose->undostruct);
4634 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4635 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4637 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4638 if (account->protocol == A_NNTP) {
4639 if (!strcmp(header, _("To:")))
4640 combobox_select_by_text(
4641 GTK_COMBO_BOX(header_entry->combo),
4644 if (!strcmp(header, _("Newsgroups:")))
4645 combobox_select_by_text(
4646 GTK_COMBO_BOX(header_entry->combo),
4654 /* use account's dict info if set */
4655 if (compose->gtkaspell) {
4656 if (account->enable_default_dictionary)
4657 gtkaspell_change_dict(compose->gtkaspell,
4658 account->default_dictionary, FALSE);
4659 if (account->enable_default_alt_dictionary)
4660 gtkaspell_change_alt_dict(compose->gtkaspell,
4661 account->default_alt_dictionary);
4662 if (account->enable_default_dictionary
4663 || account->enable_default_alt_dictionary)
4664 compose_spell_menu_changed(compose);
4669 gboolean compose_check_for_valid_recipient(Compose *compose) {
4670 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4671 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4672 gboolean recipient_found = FALSE;
4676 /* free to and newsgroup list */
4677 slist_free_strings(compose->to_list);
4678 g_slist_free(compose->to_list);
4679 compose->to_list = NULL;
4681 slist_free_strings(compose->newsgroup_list);
4682 g_slist_free(compose->newsgroup_list);
4683 compose->newsgroup_list = NULL;
4685 /* search header entries for to and newsgroup entries */
4686 for (list = compose->header_list; list; list = list->next) {
4689 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4690 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4693 if (entry[0] != '\0') {
4694 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4695 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4696 compose->to_list = address_list_append(compose->to_list, entry);
4697 recipient_found = TRUE;
4700 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4701 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4702 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4703 recipient_found = TRUE;
4710 return recipient_found;
4713 static gboolean compose_check_for_set_recipients(Compose *compose)
4715 if (compose->account->set_autocc && compose->account->auto_cc) {
4716 gboolean found_other = FALSE;
4718 /* search header entries for to and newsgroup entries */
4719 for (list = compose->header_list; list; list = list->next) {
4722 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4723 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4726 if (strcmp(entry, compose->account->auto_cc)
4727 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4737 if (compose->batch) {
4738 gtk_widget_show_all(compose->window);
4740 aval = alertpanel(_("Send"),
4741 _("The only recipient is the default CC address. Send anyway?"),
4742 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4743 if (aval != G_ALERTALTERNATE)
4747 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4748 gboolean found_other = FALSE;
4750 /* search header entries for to and newsgroup entries */
4751 for (list = compose->header_list; list; list = list->next) {
4754 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4755 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4758 if (strcmp(entry, compose->account->auto_bcc)
4759 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4769 if (compose->batch) {
4770 gtk_widget_show_all(compose->window);
4772 aval = alertpanel(_("Send"),
4773 _("The only recipient is the default BCC address. Send anyway?"),
4774 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4775 if (aval != G_ALERTALTERNATE)
4782 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4786 if (compose_check_for_valid_recipient(compose) == FALSE) {
4787 if (compose->batch) {
4788 gtk_widget_show_all(compose->window);
4790 alertpanel_error(_("Recipient is not specified."));
4794 if (compose_check_for_set_recipients(compose) == FALSE) {
4798 if (!compose->batch) {
4799 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4800 if (*str == '\0' && check_everything == TRUE &&
4801 compose->mode != COMPOSE_REDIRECT) {
4803 gchar *button_label;
4806 if (compose->sending)
4807 button_label = _("+_Send");
4809 button_label = _("+_Queue");
4810 message = g_strdup_printf(_("Subject is empty. %s"),
4811 compose->sending?_("Send it anyway?"):
4812 _("Queue it anyway?"));
4814 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4815 GTK_STOCK_CANCEL, button_label, NULL);
4817 if (aval != G_ALERTALTERNATE)
4822 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4828 gint compose_send(Compose *compose)
4831 FolderItem *folder = NULL;
4833 gchar *msgpath = NULL;
4834 gboolean discard_window = FALSE;
4835 gchar *errstr = NULL;
4836 gchar *tmsgid = NULL;
4837 MainWindow *mainwin = mainwindow_get_mainwindow();
4838 gboolean queued_removed = FALSE;
4840 if (prefs_common.send_dialog_invisible
4841 || compose->batch == TRUE)
4842 discard_window = TRUE;
4844 compose_allow_user_actions (compose, FALSE);
4845 compose->sending = TRUE;
4847 if (compose_check_entries(compose, TRUE) == FALSE) {
4848 if (compose->batch) {
4849 gtk_widget_show_all(compose->window);
4855 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4858 if (compose->batch) {
4859 gtk_widget_show_all(compose->window);
4862 alertpanel_error(_("Could not queue message for sending:\n\n"
4863 "Charset conversion failed."));
4864 } else if (val == -5) {
4865 alertpanel_error(_("Could not queue message for sending:\n\n"
4866 "Couldn't get recipient encryption key."));
4867 } else if (val == -6) {
4869 } else if (val == -3) {
4870 if (privacy_peek_error())
4871 alertpanel_error(_("Could not queue message for sending:\n\n"
4872 "Signature failed: %s"), privacy_get_error());
4873 } else if (val == -2 && errno != 0) {
4874 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4876 alertpanel_error(_("Could not queue message for sending."));
4881 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4882 if (discard_window) {
4883 compose->sending = FALSE;
4884 compose_close(compose);
4885 /* No more compose access in the normal codepath
4886 * after this point! */
4891 alertpanel_error(_("The message was queued but could not be "
4892 "sent.\nUse \"Send queued messages\" from "
4893 "the main window to retry."));
4894 if (!discard_window) {
4901 if (msgpath == NULL) {
4902 msgpath = folder_item_fetch_msg(folder, msgnum);
4903 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4906 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4907 claws_unlink(msgpath);
4910 if (!discard_window) {
4912 if (!queued_removed)
4913 folder_item_remove_msg(folder, msgnum);
4914 folder_item_scan(folder);
4916 /* make sure we delete that */
4917 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4919 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4920 folder_item_remove_msg(folder, tmp->msgnum);
4921 procmsg_msginfo_free(tmp);
4928 if (!queued_removed)
4929 folder_item_remove_msg(folder, msgnum);
4930 folder_item_scan(folder);
4932 /* make sure we delete that */
4933 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4935 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4936 folder_item_remove_msg(folder, tmp->msgnum);
4937 procmsg_msginfo_free(tmp);
4940 if (!discard_window) {
4941 compose->sending = FALSE;
4942 compose_allow_user_actions (compose, TRUE);
4943 compose_close(compose);
4947 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4948 "the main window to retry."), errstr);
4951 alertpanel_error_log(_("The message was queued but could not be "
4952 "sent.\nUse \"Send queued messages\" from "
4953 "the main window to retry."));
4955 if (!discard_window) {
4964 toolbar_main_set_sensitive(mainwin);
4965 main_window_set_menu_sensitive(mainwin);
4971 compose_allow_user_actions (compose, TRUE);
4972 compose->sending = FALSE;
4973 compose->modified = TRUE;
4974 toolbar_main_set_sensitive(mainwin);
4975 main_window_set_menu_sensitive(mainwin);
4980 static gboolean compose_use_attach(Compose *compose)
4982 GtkTreeModel *model = gtk_tree_view_get_model
4983 (GTK_TREE_VIEW(compose->attach_clist));
4984 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4987 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4990 gchar buf[BUFFSIZE];
4992 gboolean first_to_address;
4993 gboolean first_cc_address;
4995 ComposeHeaderEntry *headerentry;
4996 const gchar *headerentryname;
4997 const gchar *cc_hdr;
4998 const gchar *to_hdr;
4999 gboolean err = FALSE;
5001 debug_print("Writing redirect header\n");
5003 cc_hdr = prefs_common_translated_header_name("Cc:");
5004 to_hdr = prefs_common_translated_header_name("To:");
5006 first_to_address = TRUE;
5007 for (list = compose->header_list; list; list = list->next) {
5008 headerentry = ((ComposeHeaderEntry *)list->data);
5009 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5011 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5012 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5013 Xstrdup_a(str, entstr, return -1);
5015 if (str[0] != '\0') {
5016 compose_convert_header
5017 (compose, buf, sizeof(buf), str,
5018 strlen("Resent-To") + 2, TRUE);
5020 if (first_to_address) {
5021 err |= (fprintf(fp, "Resent-To: ") < 0);
5022 first_to_address = FALSE;
5024 err |= (fprintf(fp, ",") < 0);
5026 err |= (fprintf(fp, "%s", buf) < 0);
5030 if (!first_to_address) {
5031 err |= (fprintf(fp, "\n") < 0);
5034 first_cc_address = TRUE;
5035 for (list = compose->header_list; list; list = list->next) {
5036 headerentry = ((ComposeHeaderEntry *)list->data);
5037 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5039 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5040 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5041 Xstrdup_a(str, strg, return -1);
5043 if (str[0] != '\0') {
5044 compose_convert_header
5045 (compose, buf, sizeof(buf), str,
5046 strlen("Resent-Cc") + 2, TRUE);
5048 if (first_cc_address) {
5049 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5050 first_cc_address = FALSE;
5052 err |= (fprintf(fp, ",") < 0);
5054 err |= (fprintf(fp, "%s", buf) < 0);
5058 if (!first_cc_address) {
5059 err |= (fprintf(fp, "\n") < 0);
5062 return (err ? -1:0);
5065 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5067 gchar buf[BUFFSIZE];
5069 const gchar *entstr;
5070 /* struct utsname utsbuf; */
5071 gboolean err = FALSE;
5073 cm_return_val_if_fail(fp != NULL, -1);
5074 cm_return_val_if_fail(compose->account != NULL, -1);
5075 cm_return_val_if_fail(compose->account->address != NULL, -1);
5078 get_rfc822_date(buf, sizeof(buf));
5079 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5082 if (compose->account->name && *compose->account->name) {
5083 compose_convert_header
5084 (compose, buf, sizeof(buf), compose->account->name,
5085 strlen("From: "), TRUE);
5086 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5087 buf, compose->account->address) < 0);
5089 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5092 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5093 if (*entstr != '\0') {
5094 Xstrdup_a(str, entstr, return -1);
5097 compose_convert_header(compose, buf, sizeof(buf), str,
5098 strlen("Subject: "), FALSE);
5099 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5103 /* Resent-Message-ID */
5104 if (compose->account->set_domain && compose->account->domain) {
5105 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5106 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5107 g_snprintf(buf, sizeof(buf), "%s",
5108 strchr(compose->account->address, '@') ?
5109 strchr(compose->account->address, '@')+1 :
5110 compose->account->address);
5112 g_snprintf(buf, sizeof(buf), "%s", "");
5115 if (compose->account->gen_msgid) {
5117 if (compose->account->msgid_with_addr) {
5118 addr = compose->account->address;
5120 generate_msgid(buf, sizeof(buf), addr);
5121 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5122 compose->msgid = g_strdup(buf);
5124 compose->msgid = NULL;
5127 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5130 /* separator between header and body */
5131 err |= (fputs("\n", fp) == EOF);
5133 return (err ? -1:0);
5136 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5140 gchar buf[BUFFSIZE];
5142 gboolean skip = FALSE;
5143 gboolean err = FALSE;
5144 gchar *not_included[]={
5145 "Return-Path:", "Delivered-To:", "Received:",
5146 "Subject:", "X-UIDL:", "AF:",
5147 "NF:", "PS:", "SRH:",
5148 "SFN:", "DSR:", "MID:",
5149 "CFG:", "PT:", "S:",
5150 "RQ:", "SSV:", "NSV:",
5151 "SSH:", "R:", "MAID:",
5152 "NAID:", "RMID:", "FMID:",
5153 "SCF:", "RRCPT:", "NG:",
5154 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5155 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5156 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5157 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5158 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5161 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5162 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5166 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5168 for (i = 0; not_included[i] != NULL; i++) {
5169 if (g_ascii_strncasecmp(buf, not_included[i],
5170 strlen(not_included[i])) == 0) {
5177 if (fputs(buf, fdest) == -1)
5180 if (!prefs_common.redirect_keep_from) {
5181 if (g_ascii_strncasecmp(buf, "From:",
5182 strlen("From:")) == 0) {
5183 err |= (fputs(" (by way of ", fdest) == EOF);
5184 if (compose->account->name
5185 && *compose->account->name) {
5186 compose_convert_header
5187 (compose, buf, sizeof(buf),
5188 compose->account->name,
5191 err |= (fprintf(fdest, "%s <%s>",
5193 compose->account->address) < 0);
5195 err |= (fprintf(fdest, "%s",
5196 compose->account->address) < 0);
5197 err |= (fputs(")", fdest) == EOF);
5201 if (fputs("\n", fdest) == -1)
5208 if (compose_redirect_write_headers(compose, fdest))
5211 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5212 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5225 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5227 GtkTextBuffer *buffer;
5228 GtkTextIter start, end;
5231 const gchar *out_codeset;
5232 EncodingType encoding = ENC_UNKNOWN;
5233 MimeInfo *mimemsg, *mimetext;
5235 const gchar *src_codeset = CS_INTERNAL;
5236 gchar *from_addr = NULL;
5237 gchar *from_name = NULL;
5239 if (action == COMPOSE_WRITE_FOR_SEND)
5240 attach_parts = TRUE;
5242 /* create message MimeInfo */
5243 mimemsg = procmime_mimeinfo_new();
5244 mimemsg->type = MIMETYPE_MESSAGE;
5245 mimemsg->subtype = g_strdup("rfc822");
5246 mimemsg->content = MIMECONTENT_MEM;
5247 mimemsg->tmp = TRUE; /* must free content later */
5248 mimemsg->data.mem = compose_get_header(compose);
5250 /* Create text part MimeInfo */
5251 /* get all composed text */
5252 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5253 gtk_text_buffer_get_start_iter(buffer, &start);
5254 gtk_text_buffer_get_end_iter(buffer, &end);
5255 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5257 out_codeset = conv_get_charset_str(compose->out_encoding);
5259 if (!out_codeset && is_ascii_str(chars)) {
5260 out_codeset = CS_US_ASCII;
5261 } else if (prefs_common.outgoing_fallback_to_ascii &&
5262 is_ascii_str(chars)) {
5263 out_codeset = CS_US_ASCII;
5264 encoding = ENC_7BIT;
5268 gchar *test_conv_global_out = NULL;
5269 gchar *test_conv_reply = NULL;
5271 /* automatic mode. be automatic. */
5272 codeconv_set_strict(TRUE);
5274 out_codeset = conv_get_outgoing_charset_str();
5276 debug_print("trying to convert to %s\n", out_codeset);
5277 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5280 if (!test_conv_global_out && compose->orig_charset
5281 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5282 out_codeset = compose->orig_charset;
5283 debug_print("failure; trying to convert to %s\n", out_codeset);
5284 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5287 if (!test_conv_global_out && !test_conv_reply) {
5289 out_codeset = CS_INTERNAL;
5290 debug_print("failure; finally using %s\n", out_codeset);
5292 g_free(test_conv_global_out);
5293 g_free(test_conv_reply);
5294 codeconv_set_strict(FALSE);
5297 if (encoding == ENC_UNKNOWN) {
5298 if (prefs_common.encoding_method == CTE_BASE64)
5299 encoding = ENC_BASE64;
5300 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5301 encoding = ENC_QUOTED_PRINTABLE;
5302 else if (prefs_common.encoding_method == CTE_8BIT)
5303 encoding = ENC_8BIT;
5305 encoding = procmime_get_encoding_for_charset(out_codeset);
5308 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5309 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5311 if (action == COMPOSE_WRITE_FOR_SEND) {
5312 codeconv_set_strict(TRUE);
5313 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5314 codeconv_set_strict(FALSE);
5320 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5321 "to the specified %s charset.\n"
5322 "Send it as %s?"), out_codeset, src_codeset);
5323 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5324 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5327 if (aval != G_ALERTALTERNATE) {
5332 out_codeset = src_codeset;
5338 out_codeset = src_codeset;
5343 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5344 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5345 strstr(buf, "\nFrom ") != NULL) {
5346 encoding = ENC_QUOTED_PRINTABLE;
5350 mimetext = procmime_mimeinfo_new();
5351 mimetext->content = MIMECONTENT_MEM;
5352 mimetext->tmp = TRUE; /* must free content later */
5353 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5354 * and free the data, which we need later. */
5355 mimetext->data.mem = g_strdup(buf);
5356 mimetext->type = MIMETYPE_TEXT;
5357 mimetext->subtype = g_strdup("plain");
5358 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5359 g_strdup(out_codeset));
5361 /* protect trailing spaces when signing message */
5362 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5363 privacy_system_can_sign(compose->privacy_system)) {
5364 encoding = ENC_QUOTED_PRINTABLE;
5367 debug_print("main text: %zd bytes encoded as %s in %d\n",
5368 strlen(buf), out_codeset, encoding);
5370 /* check for line length limit */
5371 if (action == COMPOSE_WRITE_FOR_SEND &&
5372 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5373 check_line_length(buf, 1000, &line) < 0) {
5377 msg = g_strdup_printf
5378 (_("Line %d exceeds the line length limit (998 bytes).\n"
5379 "The contents of the message might be broken on the way to the delivery.\n"
5381 "Send it anyway?"), line + 1);
5382 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5384 if (aval != G_ALERTALTERNATE) {
5390 if (encoding != ENC_UNKNOWN)
5391 procmime_encode_content(mimetext, encoding);
5393 /* append attachment parts */
5394 if (compose_use_attach(compose) && attach_parts) {
5395 MimeInfo *mimempart;
5396 gchar *boundary = NULL;
5397 mimempart = procmime_mimeinfo_new();
5398 mimempart->content = MIMECONTENT_EMPTY;
5399 mimempart->type = MIMETYPE_MULTIPART;
5400 mimempart->subtype = g_strdup("mixed");
5404 boundary = generate_mime_boundary(NULL);
5405 } while (strstr(buf, boundary) != NULL);
5407 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5410 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5412 g_node_append(mimempart->node, mimetext->node);
5413 g_node_append(mimemsg->node, mimempart->node);
5415 if (compose_add_attachments(compose, mimempart) < 0)
5418 g_node_append(mimemsg->node, mimetext->node);
5422 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5423 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5424 /* extract name and address */
5425 if (strstr(spec, " <") && strstr(spec, ">")) {
5426 from_addr = g_strdup(strrchr(spec, '<')+1);
5427 *(strrchr(from_addr, '>')) = '\0';
5428 from_name = g_strdup(spec);
5429 *(strrchr(from_name, '<')) = '\0';
5436 /* sign message if sending */
5437 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5438 privacy_system_can_sign(compose->privacy_system))
5439 if (!privacy_sign(compose->privacy_system, mimemsg,
5440 compose->account, from_addr)) {
5447 procmime_write_mimeinfo(mimemsg, fp);
5449 procmime_mimeinfo_free_all(mimemsg);
5454 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5456 GtkTextBuffer *buffer;
5457 GtkTextIter start, end;
5462 if ((fp = g_fopen(file, "wb")) == NULL) {
5463 FILE_OP_ERROR(file, "fopen");
5467 /* chmod for security */
5468 if (change_file_mode_rw(fp, file) < 0) {
5469 FILE_OP_ERROR(file, "chmod");
5470 g_warning("can't change file mode\n");
5473 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5474 gtk_text_buffer_get_start_iter(buffer, &start);
5475 gtk_text_buffer_get_end_iter(buffer, &end);
5476 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5478 chars = conv_codeset_strdup
5479 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5482 if (!chars) return -1;
5485 len = strlen(chars);
5486 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5487 FILE_OP_ERROR(file, "fwrite");
5496 if (fclose(fp) == EOF) {
5497 FILE_OP_ERROR(file, "fclose");
5504 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5507 MsgInfo *msginfo = compose->targetinfo;
5509 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5510 if (!msginfo) return -1;
5512 if (!force && MSG_IS_LOCKED(msginfo->flags))
5515 item = msginfo->folder;
5516 cm_return_val_if_fail(item != NULL, -1);
5518 if (procmsg_msg_exist(msginfo) &&
5519 (folder_has_parent_of_type(item, F_QUEUE) ||
5520 folder_has_parent_of_type(item, F_DRAFT)
5521 || msginfo == compose->autosaved_draft)) {
5522 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5523 g_warning("can't remove the old message\n");
5526 debug_print("removed reedit target %d\n", msginfo->msgnum);
5533 static void compose_remove_draft(Compose *compose)
5536 MsgInfo *msginfo = compose->targetinfo;
5537 drafts = account_get_special_folder(compose->account, F_DRAFT);
5539 if (procmsg_msg_exist(msginfo)) {
5540 folder_item_remove_msg(drafts, msginfo->msgnum);
5545 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5546 gboolean remove_reedit_target)
5548 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5551 static gboolean compose_warn_encryption(Compose *compose)
5553 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5554 AlertValue val = G_ALERTALTERNATE;
5556 if (warning == NULL)
5559 val = alertpanel_full(_("Encryption warning"), warning,
5560 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5561 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5562 if (val & G_ALERTDISABLE) {
5563 val &= ~G_ALERTDISABLE;
5564 if (val == G_ALERTALTERNATE)
5565 privacy_inhibit_encrypt_warning(compose->privacy_system,
5569 if (val == G_ALERTALTERNATE) {
5576 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5577 gchar **msgpath, gboolean check_subject,
5578 gboolean remove_reedit_target)
5585 static gboolean lock = FALSE;
5586 PrefsAccount *mailac = NULL, *newsac = NULL;
5587 gboolean err = FALSE;
5589 debug_print("queueing message...\n");
5590 cm_return_val_if_fail(compose->account != NULL, -1);
5594 if (compose_check_entries(compose, check_subject) == FALSE) {
5596 if (compose->batch) {
5597 gtk_widget_show_all(compose->window);
5602 if (!compose->to_list && !compose->newsgroup_list) {
5603 g_warning("can't get recipient list.");
5608 if (compose->to_list) {
5609 if (compose->account->protocol != A_NNTP)
5610 mailac = compose->account;
5611 else if (cur_account && cur_account->protocol != A_NNTP)
5612 mailac = cur_account;
5613 else if (!(mailac = compose_current_mail_account())) {
5615 alertpanel_error(_("No account for sending mails available!"));
5620 if (compose->newsgroup_list) {
5621 if (compose->account->protocol == A_NNTP)
5622 newsac = compose->account;
5625 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5630 /* write queue header */
5631 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5632 G_DIR_SEPARATOR, compose, (guint) rand());
5633 debug_print("queuing to %s\n", tmp);
5634 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5635 FILE_OP_ERROR(tmp, "fopen");
5641 if (change_file_mode_rw(fp, tmp) < 0) {
5642 FILE_OP_ERROR(tmp, "chmod");
5643 g_warning("can't change file mode\n");
5646 /* queueing variables */
5647 err |= (fprintf(fp, "AF:\n") < 0);
5648 err |= (fprintf(fp, "NF:0\n") < 0);
5649 err |= (fprintf(fp, "PS:10\n") < 0);
5650 err |= (fprintf(fp, "SRH:1\n") < 0);
5651 err |= (fprintf(fp, "SFN:\n") < 0);
5652 err |= (fprintf(fp, "DSR:\n") < 0);
5654 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5656 err |= (fprintf(fp, "MID:\n") < 0);
5657 err |= (fprintf(fp, "CFG:\n") < 0);
5658 err |= (fprintf(fp, "PT:0\n") < 0);
5659 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5660 err |= (fprintf(fp, "RQ:\n") < 0);
5662 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5664 err |= (fprintf(fp, "SSV:\n") < 0);
5666 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5668 err |= (fprintf(fp, "NSV:\n") < 0);
5669 err |= (fprintf(fp, "SSH:\n") < 0);
5670 /* write recepient list */
5671 if (compose->to_list) {
5672 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5673 for (cur = compose->to_list->next; cur != NULL;
5675 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5676 err |= (fprintf(fp, "\n") < 0);
5678 /* write newsgroup list */
5679 if (compose->newsgroup_list) {
5680 err |= (fprintf(fp, "NG:") < 0);
5681 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5682 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5683 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5684 err |= (fprintf(fp, "\n") < 0);
5686 /* Sylpheed account IDs */
5688 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5690 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5693 if (compose->privacy_system != NULL) {
5694 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5695 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5696 if (compose->use_encryption) {
5698 if (!compose_warn_encryption(compose)) {
5705 if (mailac && mailac->encrypt_to_self) {
5706 GSList *tmp_list = g_slist_copy(compose->to_list);
5707 tmp_list = g_slist_append(tmp_list, compose->account->address);
5708 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5709 g_slist_free(tmp_list);
5711 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5713 if (encdata != NULL) {
5714 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5715 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5716 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5718 } /* else we finally dont want to encrypt */
5720 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5721 /* and if encdata was null, it means there's been a problem in
5733 /* Save copy folder */
5734 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5735 gchar *savefolderid;
5737 savefolderid = compose_get_save_to(compose);
5738 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5739 g_free(savefolderid);
5741 /* Save copy folder */
5742 if (compose->return_receipt) {
5743 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5745 /* Message-ID of message replying to */
5746 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5749 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5750 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5753 /* Message-ID of message forwarding to */
5754 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5757 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5758 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5762 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5763 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5765 /* end of headers */
5766 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5768 if (compose->redirect_filename != NULL) {
5769 if (compose_redirect_write_to_file(compose, fp) < 0) {
5778 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5783 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5787 g_warning("failed to write queue message\n");
5794 if (fclose(fp) == EOF) {
5795 FILE_OP_ERROR(tmp, "fclose");
5802 if (item && *item) {
5805 queue = account_get_special_folder(compose->account, F_QUEUE);
5808 g_warning("can't find queue folder\n");
5814 folder_item_scan(queue);
5815 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5816 g_warning("can't queue the message\n");
5823 if (msgpath == NULL) {
5829 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5830 compose_remove_reedit_target(compose, FALSE);
5833 if ((msgnum != NULL) && (item != NULL)) {
5841 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5844 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5846 struct stat statbuf;
5847 gchar *type, *subtype;
5848 GtkTreeModel *model;
5851 model = gtk_tree_view_get_model(tree_view);
5853 if (!gtk_tree_model_get_iter_first(model, &iter))
5856 gtk_tree_model_get(model, &iter,
5860 if (!is_file_exist(ainfo->file)) {
5861 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5862 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5863 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5865 if (val == G_ALERTDEFAULT) {
5870 mimepart = procmime_mimeinfo_new();
5871 mimepart->content = MIMECONTENT_FILE;
5872 mimepart->data.filename = g_strdup(ainfo->file);
5873 mimepart->tmp = FALSE; /* or we destroy our attachment */
5874 mimepart->offset = 0;
5876 g_stat(ainfo->file, &statbuf);
5877 mimepart->length = statbuf.st_size;
5879 type = g_strdup(ainfo->content_type);
5881 if (!strchr(type, '/')) {
5883 type = g_strdup("application/octet-stream");
5886 subtype = strchr(type, '/') + 1;
5887 *(subtype - 1) = '\0';
5888 mimepart->type = procmime_get_media_type(type);
5889 mimepart->subtype = g_strdup(subtype);
5892 if (mimepart->type == MIMETYPE_MESSAGE &&
5893 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5894 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5897 if (mimepart->type == MIMETYPE_APPLICATION &&
5898 !strcmp2(mimepart->subtype, "octet-stream"))
5899 g_hash_table_insert(mimepart->typeparameters,
5900 g_strdup("name"), g_strdup(ainfo->name));
5901 g_hash_table_insert(mimepart->dispositionparameters,
5902 g_strdup("filename"), g_strdup(ainfo->name));
5903 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5907 if (compose->use_signing) {
5908 if (ainfo->encoding == ENC_7BIT)
5909 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5910 else if (ainfo->encoding == ENC_8BIT)
5911 ainfo->encoding = ENC_BASE64;
5914 procmime_encode_content(mimepart, ainfo->encoding);
5916 g_node_append(parent->node, mimepart->node);
5917 } while (gtk_tree_model_iter_next(model, &iter));
5922 #define IS_IN_CUSTOM_HEADER(header) \
5923 (compose->account->add_customhdr && \
5924 custom_header_find(compose->account->customhdr_list, header) != NULL)
5926 static void compose_add_headerfield_from_headerlist(Compose *compose,
5928 const gchar *fieldname,
5929 const gchar *seperator)
5931 gchar *str, *fieldname_w_colon;
5932 gboolean add_field = FALSE;
5934 ComposeHeaderEntry *headerentry;
5935 const gchar *headerentryname;
5936 const gchar *trans_fieldname;
5939 if (IS_IN_CUSTOM_HEADER(fieldname))
5942 debug_print("Adding %s-fields\n", fieldname);
5944 fieldstr = g_string_sized_new(64);
5946 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5947 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5949 for (list = compose->header_list; list; list = list->next) {
5950 headerentry = ((ComposeHeaderEntry *)list->data);
5951 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5953 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5954 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5956 if (str[0] != '\0') {
5958 g_string_append(fieldstr, seperator);
5959 g_string_append(fieldstr, str);
5968 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5969 compose_convert_header
5970 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5971 strlen(fieldname) + 2, TRUE);
5972 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5976 g_free(fieldname_w_colon);
5977 g_string_free(fieldstr, TRUE);
5982 static gchar *compose_get_header(Compose *compose)
5984 gchar buf[BUFFSIZE];
5985 const gchar *entry_str;
5989 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5991 gchar *from_name = NULL, *from_address = NULL;
5994 cm_return_val_if_fail(compose->account != NULL, NULL);
5995 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5997 header = g_string_sized_new(64);
6000 get_rfc822_date(buf, sizeof(buf));
6001 g_string_append_printf(header, "Date: %s\n", buf);
6005 if (compose->account->name && *compose->account->name) {
6007 QUOTE_IF_REQUIRED(buf, compose->account->name);
6008 tmp = g_strdup_printf("%s <%s>",
6009 buf, compose->account->address);
6011 tmp = g_strdup_printf("%s",
6012 compose->account->address);
6014 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6015 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6017 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6018 from_address = g_strdup(compose->account->address);
6020 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6021 /* extract name and address */
6022 if (strstr(spec, " <") && strstr(spec, ">")) {
6023 from_address = g_strdup(strrchr(spec, '<')+1);
6024 *(strrchr(from_address, '>')) = '\0';
6025 from_name = g_strdup(spec);
6026 *(strrchr(from_name, '<')) = '\0';
6029 from_address = g_strdup(spec);
6036 if (from_name && *from_name) {
6037 compose_convert_header
6038 (compose, buf, sizeof(buf), from_name,
6039 strlen("From: "), TRUE);
6040 QUOTE_IF_REQUIRED(name, buf);
6042 g_string_append_printf(header, "From: %s <%s>\n",
6043 name, from_address);
6045 g_string_append_printf(header, "From: %s\n", from_address);
6048 g_free(from_address);
6051 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6054 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6057 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6060 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6063 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6065 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6068 compose_convert_header(compose, buf, sizeof(buf), str,
6069 strlen("Subject: "), FALSE);
6070 g_string_append_printf(header, "Subject: %s\n", buf);
6076 if (compose->account->set_domain && compose->account->domain) {
6077 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6078 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6079 g_snprintf(buf, sizeof(buf), "%s",
6080 strchr(compose->account->address, '@') ?
6081 strchr(compose->account->address, '@')+1 :
6082 compose->account->address);
6084 g_snprintf(buf, sizeof(buf), "%s", "");
6087 if (compose->account->gen_msgid) {
6089 if (compose->account->msgid_with_addr) {
6090 addr = compose->account->address;
6092 generate_msgid(buf, sizeof(buf), addr);
6093 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6094 compose->msgid = g_strdup(buf);
6096 compose->msgid = NULL;
6099 if (compose->remove_references == FALSE) {
6101 if (compose->inreplyto && compose->to_list)
6102 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6105 if (compose->references)
6106 g_string_append_printf(header, "References: %s\n", compose->references);
6110 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6113 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6116 if (compose->account->organization &&
6117 strlen(compose->account->organization) &&
6118 !IS_IN_CUSTOM_HEADER("Organization")) {
6119 compose_convert_header(compose, buf, sizeof(buf),
6120 compose->account->organization,
6121 strlen("Organization: "), FALSE);
6122 g_string_append_printf(header, "Organization: %s\n", buf);
6125 /* Program version and system info */
6126 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6127 !compose->newsgroup_list) {
6128 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6130 gtk_major_version, gtk_minor_version, gtk_micro_version,
6133 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6134 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6136 gtk_major_version, gtk_minor_version, gtk_micro_version,
6140 /* custom headers */
6141 if (compose->account->add_customhdr) {
6144 for (cur = compose->account->customhdr_list; cur != NULL;
6146 CustomHeader *chdr = (CustomHeader *)cur->data;
6148 if (custom_header_is_allowed(chdr->name)) {
6149 compose_convert_header
6150 (compose, buf, sizeof(buf),
6151 chdr->value ? chdr->value : "",
6152 strlen(chdr->name) + 2, FALSE);
6153 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6158 /* Automatic Faces and X-Faces */
6159 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6160 g_string_append_printf(header, "X-Face: %s\n", buf);
6162 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6163 g_string_append_printf(header, "X-Face: %s\n", buf);
6165 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6166 g_string_append_printf(header, "Face: %s\n", buf);
6168 else if (get_default_face (buf, sizeof(buf)) == 0) {
6169 g_string_append_printf(header, "Face: %s\n", buf);
6173 switch (compose->priority) {
6174 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6175 "X-Priority: 1 (Highest)\n");
6177 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6178 "X-Priority: 2 (High)\n");
6180 case PRIORITY_NORMAL: break;
6181 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6182 "X-Priority: 4 (Low)\n");
6184 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6185 "X-Priority: 5 (Lowest)\n");
6187 default: debug_print("compose: priority unknown : %d\n",
6191 /* Request Return Receipt */
6192 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6193 if (compose->return_receipt) {
6194 if (compose->account->name
6195 && *compose->account->name) {
6196 compose_convert_header(compose, buf, sizeof(buf),
6197 compose->account->name,
6198 strlen("Disposition-Notification-To: "),
6200 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6202 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6206 /* get special headers */
6207 for (list = compose->header_list; list; list = list->next) {
6208 ComposeHeaderEntry *headerentry;
6211 gchar *headername_wcolon;
6212 const gchar *headername_trans;
6215 gboolean standard_header = FALSE;
6217 headerentry = ((ComposeHeaderEntry *)list->data);
6219 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6221 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6226 if (!strstr(tmp, ":")) {
6227 headername_wcolon = g_strconcat(tmp, ":", NULL);
6228 headername = g_strdup(tmp);
6230 headername_wcolon = g_strdup(tmp);
6231 headername = g_strdup(strtok(tmp, ":"));
6235 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6236 Xstrdup_a(headervalue, entry_str, return NULL);
6237 subst_char(headervalue, '\r', ' ');
6238 subst_char(headervalue, '\n', ' ');
6239 string = std_headers;
6240 while (*string != NULL) {
6241 headername_trans = prefs_common_translated_header_name(*string);
6242 if (!strcmp(headername_trans, headername_wcolon))
6243 standard_header = TRUE;
6246 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6247 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6250 g_free(headername_wcolon);
6254 g_string_free(header, FALSE);
6259 #undef IS_IN_CUSTOM_HEADER
6261 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6262 gint header_len, gboolean addr_field)
6264 gchar *tmpstr = NULL;
6265 const gchar *out_codeset = NULL;
6267 cm_return_if_fail(src != NULL);
6268 cm_return_if_fail(dest != NULL);
6270 if (len < 1) return;
6272 tmpstr = g_strdup(src);
6274 subst_char(tmpstr, '\n', ' ');
6275 subst_char(tmpstr, '\r', ' ');
6278 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6279 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6280 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6285 codeconv_set_strict(TRUE);
6286 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6287 conv_get_charset_str(compose->out_encoding));
6288 codeconv_set_strict(FALSE);
6290 if (!dest || *dest == '\0') {
6291 gchar *test_conv_global_out = NULL;
6292 gchar *test_conv_reply = NULL;
6294 /* automatic mode. be automatic. */
6295 codeconv_set_strict(TRUE);
6297 out_codeset = conv_get_outgoing_charset_str();
6299 debug_print("trying to convert to %s\n", out_codeset);
6300 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6303 if (!test_conv_global_out && compose->orig_charset
6304 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6305 out_codeset = compose->orig_charset;
6306 debug_print("failure; trying to convert to %s\n", out_codeset);
6307 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6310 if (!test_conv_global_out && !test_conv_reply) {
6312 out_codeset = CS_INTERNAL;
6313 debug_print("finally using %s\n", out_codeset);
6315 g_free(test_conv_global_out);
6316 g_free(test_conv_reply);
6317 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6319 codeconv_set_strict(FALSE);
6324 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6328 cm_return_if_fail(user_data != NULL);
6330 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6331 g_strstrip(address);
6332 if (*address != '\0') {
6333 gchar *name = procheader_get_fromname(address);
6334 extract_address(address);
6335 addressbook_add_contact(name, address, NULL, NULL);
6340 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6342 GtkWidget *menuitem;
6345 cm_return_if_fail(menu != NULL);
6346 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6348 menuitem = gtk_separator_menu_item_new();
6349 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6350 gtk_widget_show(menuitem);
6352 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6353 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6355 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6356 g_strstrip(address);
6357 if (*address == '\0') {
6358 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6361 g_signal_connect(G_OBJECT(menuitem), "activate",
6362 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6363 gtk_widget_show(menuitem);
6366 static void compose_create_header_entry(Compose *compose)
6368 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6375 const gchar *header = NULL;
6376 ComposeHeaderEntry *headerentry;
6377 gboolean standard_header = FALSE;
6378 GtkListStore *model;
6380 #if !(GTK_CHECK_VERSION(2,12,0))
6381 GtkTooltips *tips = compose->tooltips;
6384 headerentry = g_new0(ComposeHeaderEntry, 1);
6387 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6388 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6389 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6391 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6393 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6395 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6396 COMPOSE_NEWSGROUPS);
6397 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6399 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6400 COMPOSE_FOLLOWUPTO);
6402 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6403 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6404 G_CALLBACK(compose_grab_focus_cb), compose);
6405 gtk_widget_show(combo);
6406 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6407 compose->header_nextrow, compose->header_nextrow+1,
6408 GTK_SHRINK, GTK_FILL, 0, 0);
6409 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6410 const gchar *last_header_entry = gtk_entry_get_text(
6411 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6413 while (*string != NULL) {
6414 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6415 standard_header = TRUE;
6418 if (standard_header)
6419 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6421 if (!compose->header_last || !standard_header) {
6422 switch(compose->account->protocol) {
6424 header = prefs_common_translated_header_name("Newsgroups:");
6427 header = prefs_common_translated_header_name("To:");
6432 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6434 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6435 G_CALLBACK(compose_grab_focus_cb), compose);
6437 /* Entry field with cleanup button */
6438 #if GTK_CHECK_VERSION(2, 8, 0)
6439 button = gtk_button_new();
6440 gtk_button_set_image(GTK_BUTTON(button),
6441 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6443 button = gtk_button_new_with_label(_("Clear"));
6445 gtk_widget_show(button);
6446 CLAWS_SET_TIP(button,
6447 _("Delete entry contents"));
6448 entry = gtk_entry_new();
6449 gtk_widget_show(entry);
6450 CLAWS_SET_TIP(entry,
6451 _("Use <tab> to autocomplete from addressbook"));
6452 hbox = gtk_hbox_new (FALSE, 0);
6453 gtk_widget_show(hbox);
6454 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6455 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6456 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6457 compose->header_nextrow, compose->header_nextrow+1,
6458 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6460 g_signal_connect(G_OBJECT(entry), "key-press-event",
6461 G_CALLBACK(compose_headerentry_key_press_event_cb),
6463 g_signal_connect(G_OBJECT(entry), "changed",
6464 G_CALLBACK(compose_headerentry_changed_cb),
6466 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6467 G_CALLBACK(compose_grab_focus_cb), compose);
6469 g_signal_connect(G_OBJECT(button), "clicked",
6470 G_CALLBACK(compose_headerentry_button_clicked_cb),
6474 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6475 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6476 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6477 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6478 G_CALLBACK(compose_header_drag_received_cb),
6480 g_signal_connect(G_OBJECT(entry), "drag-drop",
6481 G_CALLBACK(compose_drag_drop),
6483 g_signal_connect(G_OBJECT(entry), "populate-popup",
6484 G_CALLBACK(compose_entry_popup_extend),
6487 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6489 headerentry->compose = compose;
6490 headerentry->combo = combo;
6491 headerentry->entry = entry;
6492 headerentry->button = button;
6493 headerentry->hbox = hbox;
6494 headerentry->headernum = compose->header_nextrow;
6495 headerentry->type = PREF_NONE;
6497 compose->header_nextrow++;
6498 compose->header_last = headerentry;
6499 compose->header_list =
6500 g_slist_append(compose->header_list,
6504 static void compose_add_header_entry(Compose *compose, const gchar *header,
6505 gchar *text, ComposePrefType pref_type)
6507 ComposeHeaderEntry *last_header = compose->header_last;
6508 gchar *tmp = g_strdup(text), *email;
6510 extract_address(tmp);
6511 email = g_utf8_strdown(tmp, -1);
6513 if (g_hash_table_lookup(compose->email_hashtable, email) != NULL) {
6514 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6515 header, text, (gint) pref_type);
6521 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6522 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6523 last_header->type = pref_type;
6525 g_hash_table_insert(compose->email_hashtable, email,
6526 GUINT_TO_POINTER(1));
6530 static void compose_destroy_headerentry(Compose *compose,
6531 ComposeHeaderEntry *headerentry)
6533 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6536 extract_address(text);
6537 email = g_utf8_strdown(text, -1);
6538 g_hash_table_remove(compose->email_hashtable, email);
6542 gtk_widget_destroy(headerentry->combo);
6543 gtk_widget_destroy(headerentry->entry);
6544 gtk_widget_destroy(headerentry->button);
6545 gtk_widget_destroy(headerentry->hbox);
6546 g_free(headerentry);
6549 static void compose_remove_header_entries(Compose *compose)
6552 for (list = compose->header_list; list; list = list->next)
6553 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6555 compose->header_last = NULL;
6556 g_slist_free(compose->header_list);
6557 compose->header_list = NULL;
6558 compose->header_nextrow = 1;
6559 compose_create_header_entry(compose);
6562 static GtkWidget *compose_create_header(Compose *compose)
6564 GtkWidget *from_optmenu_hbox;
6565 GtkWidget *header_scrolledwin;
6566 GtkWidget *header_table;
6570 /* header labels and entries */
6571 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6572 gtk_widget_show(header_scrolledwin);
6573 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6575 header_table = gtk_table_new(2, 2, FALSE);
6576 gtk_widget_show(header_table);
6577 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6578 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6579 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6582 /* option menu for selecting accounts */
6583 from_optmenu_hbox = compose_account_option_menu_create(compose);
6584 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6585 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6588 compose->header_table = header_table;
6589 compose->header_list = NULL;
6590 compose->header_nextrow = count;
6592 compose_create_header_entry(compose);
6594 compose->table = NULL;
6596 return header_scrolledwin ;
6599 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6601 Compose *compose = (Compose *)data;
6602 GdkEventButton event;
6605 event.time = gtk_get_current_event_time();
6607 return attach_button_pressed(compose->attach_clist, &event, compose);
6610 static GtkWidget *compose_create_attach(Compose *compose)
6612 GtkWidget *attach_scrwin;
6613 GtkWidget *attach_clist;
6615 GtkListStore *store;
6616 GtkCellRenderer *renderer;
6617 GtkTreeViewColumn *column;
6618 GtkTreeSelection *selection;
6620 /* attachment list */
6621 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6622 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6623 GTK_POLICY_AUTOMATIC,
6624 GTK_POLICY_AUTOMATIC);
6625 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6627 store = gtk_list_store_new(N_ATTACH_COLS,
6632 G_TYPE_AUTO_POINTER,
6634 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6635 (GTK_TREE_MODEL(store)));
6636 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6637 g_object_unref(store);
6639 renderer = gtk_cell_renderer_text_new();
6640 column = gtk_tree_view_column_new_with_attributes
6641 (_("Mime type"), renderer, "text",
6642 COL_MIMETYPE, NULL);
6643 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6645 renderer = gtk_cell_renderer_text_new();
6646 column = gtk_tree_view_column_new_with_attributes
6647 (_("Size"), renderer, "text",
6649 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6651 renderer = gtk_cell_renderer_text_new();
6652 column = gtk_tree_view_column_new_with_attributes
6653 (_("Name"), renderer, "text",
6655 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6657 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6658 prefs_common.use_stripes_everywhere);
6659 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6660 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6662 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6663 G_CALLBACK(attach_selected), compose);
6664 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6665 G_CALLBACK(attach_button_pressed), compose);
6667 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6668 G_CALLBACK(popup_attach_button_pressed), compose);
6670 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6671 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6672 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6673 G_CALLBACK(popup_attach_button_pressed), compose);
6675 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6676 G_CALLBACK(attach_key_pressed), compose);
6679 gtk_drag_dest_set(attach_clist,
6680 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6681 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6682 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6683 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6684 G_CALLBACK(compose_attach_drag_received_cb),
6686 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6687 G_CALLBACK(compose_drag_drop),
6690 compose->attach_scrwin = attach_scrwin;
6691 compose->attach_clist = attach_clist;
6693 return attach_scrwin;
6696 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6697 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6699 static GtkWidget *compose_create_others(Compose *compose)
6702 GtkWidget *savemsg_checkbtn;
6703 GtkWidget *savemsg_combo;
6704 GtkWidget *savemsg_select;
6707 gchar *folderidentifier;
6709 /* Table for settings */
6710 table = gtk_table_new(3, 1, FALSE);
6711 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6712 gtk_widget_show(table);
6713 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6716 /* Save Message to folder */
6717 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6718 gtk_widget_show(savemsg_checkbtn);
6719 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6720 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6721 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6723 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6724 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6726 savemsg_combo = gtk_combo_box_entry_new_text();
6727 compose->savemsg_checkbtn = savemsg_checkbtn;
6728 compose->savemsg_combo = savemsg_combo;
6729 gtk_widget_show(savemsg_combo);
6731 if (prefs_common.compose_save_to_history)
6732 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6733 prefs_common.compose_save_to_history);
6735 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6736 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6737 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6738 G_CALLBACK(compose_grab_focus_cb), compose);
6739 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6740 folderidentifier = folder_item_get_identifier(account_get_special_folder
6741 (compose->account, F_OUTBOX));
6742 compose_set_save_to(compose, folderidentifier);
6743 g_free(folderidentifier);
6746 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6747 gtk_widget_show(savemsg_select);
6748 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6749 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6750 G_CALLBACK(compose_savemsg_select_cb),
6758 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6760 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6761 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6764 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6769 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6772 path = folder_item_get_identifier(dest);
6774 compose_set_save_to(compose, path);
6778 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6779 GdkAtom clip, GtkTextIter *insert_place);
6782 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6786 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6788 if (event->button == 3) {
6790 GtkTextIter sel_start, sel_end;
6791 gboolean stuff_selected;
6793 /* move the cursor to allow GtkAspell to check the word
6794 * under the mouse */
6795 if (event->x && event->y) {
6796 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6797 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6799 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6802 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6803 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6806 stuff_selected = gtk_text_buffer_get_selection_bounds(
6808 &sel_start, &sel_end);
6810 gtk_text_buffer_place_cursor (buffer, &iter);
6811 /* reselect stuff */
6813 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6814 gtk_text_buffer_select_range(buffer,
6815 &sel_start, &sel_end);
6817 return FALSE; /* pass the event so that the right-click goes through */
6820 if (event->button == 2) {
6825 /* get the middle-click position to paste at the correct place */
6826 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6827 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6829 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6832 entry_paste_clipboard(compose, text,
6833 prefs_common.linewrap_pastes,
6834 GDK_SELECTION_PRIMARY, &iter);
6842 static void compose_spell_menu_changed(void *data)
6844 Compose *compose = (Compose *)data;
6846 GtkWidget *menuitem;
6847 GtkWidget *parent_item;
6848 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6851 if (compose->gtkaspell == NULL)
6854 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6855 "/Menu/Spelling/Options");
6857 /* setting the submenu removes /Spelling/Options from the factory
6858 * so we need to save it */
6860 if (parent_item == NULL) {
6861 parent_item = compose->aspell_options_menu;
6862 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6864 compose->aspell_options_menu = parent_item;
6866 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6868 spell_menu = g_slist_reverse(spell_menu);
6869 for (items = spell_menu;
6870 items; items = items->next) {
6871 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6872 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6873 gtk_widget_show(GTK_WIDGET(menuitem));
6875 g_slist_free(spell_menu);
6877 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6878 gtk_widget_show(parent_item);
6881 static void compose_dict_changed(void *data)
6883 Compose *compose = (Compose *) data;
6885 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6888 gtkaspell_highlight_all(compose->gtkaspell);
6889 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6893 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6895 Compose *compose = (Compose *)data;
6896 GdkEventButton event;
6899 event.time = gtk_get_current_event_time();
6903 return text_clicked(compose->text, &event, compose);
6906 static gboolean compose_force_window_origin = TRUE;
6907 static Compose *compose_create(PrefsAccount *account,
6916 GtkWidget *handlebox;
6918 GtkWidget *notebook;
6920 GtkWidget *attach_hbox;
6921 GtkWidget *attach_lab1;
6922 GtkWidget *attach_lab2;
6927 GtkWidget *subject_hbox;
6928 GtkWidget *subject_frame;
6929 GtkWidget *subject_entry;
6933 GtkWidget *edit_vbox;
6934 GtkWidget *ruler_hbox;
6936 GtkWidget *scrolledwin;
6938 GtkTextBuffer *buffer;
6939 GtkClipboard *clipboard;
6942 UndoMain *undostruct;
6944 gchar *titles[N_ATTACH_COLS];
6945 GtkWidget *popupmenu;
6946 GtkWidget *tmpl_menu;
6947 GtkActionGroup *action_group = NULL;
6950 GtkAspell * gtkaspell = NULL;
6953 static GdkGeometry geometry;
6955 cm_return_val_if_fail(account != NULL, NULL);
6957 debug_print("Creating compose window...\n");
6958 compose = g_new0(Compose, 1);
6960 titles[COL_MIMETYPE] = _("MIME type");
6961 titles[COL_SIZE] = _("Size");
6962 titles[COL_NAME] = _("Name");
6964 compose->batch = batch;
6965 compose->account = account;
6966 compose->folder = folder;
6968 compose->mutex = g_mutex_new();
6969 compose->set_cursor_pos = -1;
6971 #if !(GTK_CHECK_VERSION(2,12,0))
6972 compose->tooltips = tips;
6975 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6977 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6978 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6980 if (!geometry.max_width) {
6981 geometry.max_width = gdk_screen_width();
6982 geometry.max_height = gdk_screen_height();
6985 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6986 &geometry, GDK_HINT_MAX_SIZE);
6987 if (!geometry.min_width) {
6988 geometry.min_width = 600;
6989 geometry.min_height = 440;
6991 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6992 &geometry, GDK_HINT_MIN_SIZE);
6994 #ifndef GENERIC_UMPC
6995 if (compose_force_window_origin)
6996 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6997 prefs_common.compose_y);
6999 g_signal_connect(G_OBJECT(window), "delete_event",
7000 G_CALLBACK(compose_delete_cb), compose);
7001 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7002 gtk_widget_realize(window);
7004 gtkut_widget_set_composer_icon(window);
7006 vbox = gtk_vbox_new(FALSE, 0);
7007 gtk_container_add(GTK_CONTAINER(window), vbox);
7009 compose->ui_manager = gtk_ui_manager_new();
7010 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7011 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7012 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7013 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7014 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7015 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7016 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7017 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7018 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7019 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7022 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7024 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7027 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7028 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7030 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7032 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7033 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7034 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7037 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7038 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7039 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7040 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7041 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7042 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7043 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7045 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7046 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7049 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7050 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7051 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7053 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7054 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7055 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7057 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7059 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7060 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7062 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7064 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7065 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7066 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7067 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7068 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7069 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7070 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7072 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7073 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7076 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7077 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7078 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7080 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7082 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7083 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7084 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7085 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7086 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7088 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7094 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7096 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7097 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7098 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7099 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7103 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7104 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7105 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7107 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7109 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7110 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7111 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7112 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7113 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7116 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7117 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7118 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7119 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7120 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7121 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7122 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7124 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7125 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7127 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7128 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7130 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7132 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7133 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7134 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7135 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7136 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7139 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)
7140 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)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7145 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7146 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)
7147 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)
7149 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7152 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)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7155 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7156 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)
7157 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7162 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)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7168 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)
7169 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)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7173 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7181 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)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7199 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7200 gtk_widget_show_all(menubar);
7202 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7204 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7206 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7209 if (prefs_common.toolbar_detachable) {
7210 handlebox = gtk_handle_box_new();
7212 handlebox = gtk_hbox_new(FALSE, 0);
7214 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7216 gtk_widget_realize(handlebox);
7218 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7221 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7225 vbox2 = gtk_vbox_new(FALSE, 2);
7226 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7227 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7230 notebook = gtk_notebook_new();
7231 gtk_widget_set_size_request(notebook, -1, 130);
7232 gtk_widget_show(notebook);
7234 /* header labels and entries */
7235 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7236 compose_create_header(compose),
7237 gtk_label_new_with_mnemonic(_("Hea_der")));
7238 /* attachment list */
7239 attach_hbox = gtk_hbox_new(FALSE, 0);
7240 gtk_widget_show(attach_hbox);
7242 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7243 gtk_widget_show(attach_lab1);
7244 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7246 attach_lab2 = gtk_label_new("");
7247 gtk_widget_show(attach_lab2);
7248 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7250 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7251 compose_create_attach(compose),
7254 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7255 compose_create_others(compose),
7256 gtk_label_new_with_mnemonic(_("Othe_rs")));
7259 subject_hbox = gtk_hbox_new(FALSE, 0);
7260 gtk_widget_show(subject_hbox);
7262 subject_frame = gtk_frame_new(NULL);
7263 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7264 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7265 gtk_widget_show(subject_frame);
7267 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7268 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7269 gtk_widget_show(subject);
7271 label = gtk_label_new(_("Subject:"));
7272 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7273 gtk_widget_show(label);
7276 subject_entry = claws_spell_entry_new();
7278 subject_entry = gtk_entry_new();
7280 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7281 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7282 G_CALLBACK(compose_grab_focus_cb), compose);
7283 gtk_widget_show(subject_entry);
7284 compose->subject_entry = subject_entry;
7285 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7287 edit_vbox = gtk_vbox_new(FALSE, 0);
7289 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7292 ruler_hbox = gtk_hbox_new(FALSE, 0);
7293 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7295 ruler = gtk_shruler_new();
7296 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7297 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7301 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7302 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7303 GTK_POLICY_AUTOMATIC,
7304 GTK_POLICY_AUTOMATIC);
7305 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7307 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7308 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7310 text = gtk_text_view_new();
7311 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7312 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7313 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7314 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7315 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7317 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7319 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7320 G_CALLBACK(compose_edit_size_alloc),
7322 g_signal_connect(G_OBJECT(buffer), "changed",
7323 G_CALLBACK(compose_changed_cb), compose);
7324 g_signal_connect(G_OBJECT(text), "grab_focus",
7325 G_CALLBACK(compose_grab_focus_cb), compose);
7326 g_signal_connect(G_OBJECT(buffer), "insert_text",
7327 G_CALLBACK(text_inserted), compose);
7328 g_signal_connect(G_OBJECT(text), "button_press_event",
7329 G_CALLBACK(text_clicked), compose);
7331 g_signal_connect(G_OBJECT(text), "popup-menu",
7332 G_CALLBACK(compose_popup_menu), compose);
7334 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7335 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7336 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7337 G_CALLBACK(compose_popup_menu), compose);
7339 g_signal_connect(G_OBJECT(subject_entry), "changed",
7340 G_CALLBACK(compose_changed_cb), compose);
7343 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7344 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7345 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7346 g_signal_connect(G_OBJECT(text), "drag_data_received",
7347 G_CALLBACK(compose_insert_drag_received_cb),
7349 g_signal_connect(G_OBJECT(text), "drag-drop",
7350 G_CALLBACK(compose_drag_drop),
7352 gtk_widget_show_all(vbox);
7354 /* pane between attach clist and text */
7355 paned = gtk_vpaned_new();
7356 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7358 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7359 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7361 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7363 gtk_paned_add1(GTK_PANED(paned), notebook);
7364 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7365 gtk_widget_show_all(paned);
7368 if (prefs_common.textfont) {
7369 PangoFontDescription *font_desc;
7371 font_desc = pango_font_description_from_string
7372 (prefs_common.textfont);
7374 gtk_widget_modify_font(text, font_desc);
7375 pango_font_description_free(font_desc);
7379 gtk_action_group_add_actions(action_group, compose_popup_entries,
7380 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7381 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7382 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7383 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7384 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7385 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7386 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7388 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7390 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7391 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7392 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7394 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7396 undostruct = undo_init(text);
7397 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7400 address_completion_start(window);
7402 compose->window = window;
7403 compose->vbox = vbox;
7404 compose->menubar = menubar;
7405 compose->handlebox = handlebox;
7407 compose->vbox2 = vbox2;
7409 compose->paned = paned;
7411 compose->attach_label = attach_lab2;
7413 compose->notebook = notebook;
7414 compose->edit_vbox = edit_vbox;
7415 compose->ruler_hbox = ruler_hbox;
7416 compose->ruler = ruler;
7417 compose->scrolledwin = scrolledwin;
7418 compose->text = text;
7420 compose->focused_editable = NULL;
7422 compose->popupmenu = popupmenu;
7424 compose->tmpl_menu = tmpl_menu;
7426 compose->mode = mode;
7427 compose->rmode = mode;
7429 compose->targetinfo = NULL;
7430 compose->replyinfo = NULL;
7431 compose->fwdinfo = NULL;
7433 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7434 g_str_equal, (GDestroyNotify) g_free, NULL);
7436 compose->replyto = NULL;
7438 compose->bcc = NULL;
7439 compose->followup_to = NULL;
7441 compose->ml_post = NULL;
7443 compose->inreplyto = NULL;
7444 compose->references = NULL;
7445 compose->msgid = NULL;
7446 compose->boundary = NULL;
7448 compose->autowrap = prefs_common.autowrap;
7449 compose->autoindent = prefs_common.auto_indent;
7450 compose->use_signing = FALSE;
7451 compose->use_encryption = FALSE;
7452 compose->privacy_system = NULL;
7454 compose->modified = FALSE;
7456 compose->return_receipt = FALSE;
7458 compose->to_list = NULL;
7459 compose->newsgroup_list = NULL;
7461 compose->undostruct = undostruct;
7463 compose->sig_str = NULL;
7465 compose->exteditor_file = NULL;
7466 compose->exteditor_pid = -1;
7467 compose->exteditor_tag = -1;
7468 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7472 if (mode != COMPOSE_REDIRECT) {
7473 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7474 strcmp(prefs_common.dictionary, "")) {
7475 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7476 prefs_common.alt_dictionary,
7477 conv_get_locale_charset_str(),
7478 prefs_common.misspelled_col,
7479 prefs_common.check_while_typing,
7480 prefs_common.recheck_when_changing_dict,
7481 prefs_common.use_alternate,
7482 prefs_common.use_both_dicts,
7483 GTK_TEXT_VIEW(text),
7484 GTK_WINDOW(compose->window),
7485 compose_dict_changed,
7486 compose_spell_menu_changed,
7489 alertpanel_error(_("Spell checker could not "
7491 gtkaspell_checkers_strerror());
7492 gtkaspell_checkers_reset_error();
7494 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7498 compose->gtkaspell = gtkaspell;
7499 compose_spell_menu_changed(compose);
7500 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7503 compose_select_account(compose, account, TRUE);
7505 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7506 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7508 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7509 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7511 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7512 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7514 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7515 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7517 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7518 if (account->protocol != A_NNTP)
7519 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7520 prefs_common_translated_header_name("To:"));
7522 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7523 prefs_common_translated_header_name("Newsgroups:"));
7525 addressbook_set_target_compose(compose);
7527 if (mode != COMPOSE_REDIRECT)
7528 compose_set_template_menu(compose);
7530 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7533 compose_list = g_list_append(compose_list, compose);
7535 if (!prefs_common.show_ruler)
7536 gtk_widget_hide(ruler_hbox);
7538 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7541 compose->priority = PRIORITY_NORMAL;
7542 compose_update_priority_menu_item(compose);
7544 compose_set_out_encoding(compose);
7547 compose_update_actions_menu(compose);
7549 /* Privacy Systems menu */
7550 compose_update_privacy_systems_menu(compose);
7552 activate_privacy_system(compose, account, TRUE);
7553 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7555 gtk_widget_realize(window);
7557 gtk_widget_show(window);
7559 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7560 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7567 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7572 GtkWidget *optmenubox;
7575 GtkWidget *from_name = NULL;
7576 #if !(GTK_CHECK_VERSION(2,12,0))
7577 GtkTooltips *tips = compose->tooltips;
7580 gint num = 0, def_menu = 0;
7582 accounts = account_get_list();
7583 cm_return_val_if_fail(accounts != NULL, NULL);
7585 optmenubox = gtk_event_box_new();
7586 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7587 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7589 hbox = gtk_hbox_new(FALSE, 6);
7590 from_name = gtk_entry_new();
7592 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7593 G_CALLBACK(compose_grab_focus_cb), compose);
7595 for (; accounts != NULL; accounts = accounts->next, num++) {
7596 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7597 gchar *name, *from = NULL;
7599 if (ac == compose->account) def_menu = num;
7601 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7604 if (ac == compose->account) {
7605 if (ac->name && *ac->name) {
7607 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7608 from = g_strdup_printf("%s <%s>",
7610 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7612 from = g_strdup_printf("%s",
7614 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7617 COMBOBOX_ADD(menu, name, ac->account_id);
7622 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7624 g_signal_connect(G_OBJECT(optmenu), "changed",
7625 G_CALLBACK(account_activated),
7627 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7628 G_CALLBACK(compose_entry_popup_extend),
7631 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7632 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7634 CLAWS_SET_TIP(optmenubox,
7635 _("Account to use for this email"));
7636 CLAWS_SET_TIP(from_name,
7637 _("Sender address to be used"));
7639 compose->from_name = from_name;
7644 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7646 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7647 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7648 Compose *compose = (Compose *) data;
7650 compose->priority = value;
7654 static void compose_reply_change_mode(Compose *compose,
7657 gboolean was_modified = compose->modified;
7659 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7661 cm_return_if_fail(compose->replyinfo != NULL);
7663 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7665 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7667 if (action == COMPOSE_REPLY_TO_ALL)
7669 if (action == COMPOSE_REPLY_TO_SENDER)
7671 if (action == COMPOSE_REPLY_TO_LIST)
7674 compose_remove_header_entries(compose);
7675 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7676 if (compose->account->set_autocc && compose->account->auto_cc)
7677 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7679 if (compose->account->set_autobcc && compose->account->auto_bcc)
7680 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7682 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7683 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7684 compose_show_first_last_header(compose, TRUE);
7685 compose->modified = was_modified;
7686 compose_set_title(compose);
7689 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7691 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7692 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7693 Compose *compose = (Compose *) data;
7696 compose_reply_change_mode(compose, value);
7699 static void compose_update_priority_menu_item(Compose * compose)
7701 GtkWidget *menuitem = NULL;
7702 switch (compose->priority) {
7703 case PRIORITY_HIGHEST:
7704 menuitem = gtk_ui_manager_get_widget
7705 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7708 menuitem = gtk_ui_manager_get_widget
7709 (compose->ui_manager, "/Menu/Options/Priority/High");
7711 case PRIORITY_NORMAL:
7712 menuitem = gtk_ui_manager_get_widget
7713 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7716 menuitem = gtk_ui_manager_get_widget
7717 (compose->ui_manager, "/Menu/Options/Priority/Low");
7719 case PRIORITY_LOWEST:
7720 menuitem = gtk_ui_manager_get_widget
7721 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7724 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7727 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7729 Compose *compose = (Compose *) data;
7731 gboolean can_sign = FALSE, can_encrypt = FALSE;
7733 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7735 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7738 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7739 g_free(compose->privacy_system);
7740 compose->privacy_system = NULL;
7741 if (systemid != NULL) {
7742 compose->privacy_system = g_strdup(systemid);
7744 can_sign = privacy_system_can_sign(systemid);
7745 can_encrypt = privacy_system_can_encrypt(systemid);
7748 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7750 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7751 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7754 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7756 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7757 GtkWidget *menuitem = NULL;
7759 gboolean can_sign = FALSE, can_encrypt = FALSE;
7760 gboolean found = FALSE;
7762 if (compose->privacy_system != NULL) {
7764 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7765 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7766 cm_return_if_fail(menuitem != NULL);
7768 amenu = GTK_MENU_SHELL(menuitem)->children;
7770 while (amenu != NULL) {
7771 GList *alist = amenu->next;
7773 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7774 if (systemid != NULL) {
7775 if (strcmp(systemid, compose->privacy_system) == 0 &&
7776 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7777 menuitem = GTK_WIDGET(amenu->data);
7779 can_sign = privacy_system_can_sign(systemid);
7780 can_encrypt = privacy_system_can_encrypt(systemid);
7784 } else if (strlen(compose->privacy_system) == 0 &&
7785 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7786 menuitem = GTK_WIDGET(amenu->data);
7789 can_encrypt = FALSE;
7796 if (menuitem != NULL)
7797 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7799 if (warn && !found && strlen(compose->privacy_system)) {
7800 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7801 "will not be able to sign or encrypt this message."),
7802 compose->privacy_system);
7806 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7807 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7810 static void compose_set_out_encoding(Compose *compose)
7812 CharSet out_encoding;
7813 const gchar *branch = NULL;
7814 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7816 switch(out_encoding) {
7817 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7818 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7819 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7820 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7821 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7822 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7823 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7824 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7825 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7826 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7827 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7828 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7829 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7830 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7831 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7832 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7833 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7834 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7835 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7836 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7837 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7838 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7839 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7840 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7841 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7842 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7843 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7844 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7845 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7846 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7847 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7848 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7850 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7853 static void compose_set_template_menu(Compose *compose)
7855 GSList *tmpl_list, *cur;
7859 tmpl_list = template_get_config();
7861 menu = gtk_menu_new();
7863 gtk_menu_set_accel_group (GTK_MENU (menu),
7864 gtk_ui_manager_get_accel_group(compose->ui_manager));
7865 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7866 Template *tmpl = (Template *)cur->data;
7867 gchar *accel_path = NULL;
7868 item = gtk_menu_item_new_with_label(tmpl->name);
7869 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7870 g_signal_connect(G_OBJECT(item), "activate",
7871 G_CALLBACK(compose_template_activate_cb),
7873 g_object_set_data(G_OBJECT(item), "template", tmpl);
7874 gtk_widget_show(item);
7875 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7876 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7880 gtk_widget_show(menu);
7881 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7884 void compose_update_actions_menu(Compose *compose)
7886 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7889 static void compose_update_privacy_systems_menu(Compose *compose)
7891 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7892 GSList *systems, *cur;
7894 GtkWidget *system_none;
7896 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7897 GtkWidget *privacy_menu = gtk_menu_new();
7899 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7900 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7902 g_signal_connect(G_OBJECT(system_none), "activate",
7903 G_CALLBACK(compose_set_privacy_system_cb), compose);
7905 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7906 gtk_widget_show(system_none);
7908 systems = privacy_get_system_ids();
7909 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7910 gchar *systemid = cur->data;
7912 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7913 widget = gtk_radio_menu_item_new_with_label(group,
7914 privacy_system_get_name(systemid));
7915 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7916 g_strdup(systemid), g_free);
7917 g_signal_connect(G_OBJECT(widget), "activate",
7918 G_CALLBACK(compose_set_privacy_system_cb), compose);
7920 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7921 gtk_widget_show(widget);
7924 g_slist_free(systems);
7925 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7926 gtk_widget_show_all(privacy_menu);
7927 gtk_widget_show_all(privacy_menuitem);
7930 void compose_reflect_prefs_all(void)
7935 for (cur = compose_list; cur != NULL; cur = cur->next) {
7936 compose = (Compose *)cur->data;
7937 compose_set_template_menu(compose);
7941 void compose_reflect_prefs_pixmap_theme(void)
7946 for (cur = compose_list; cur != NULL; cur = cur->next) {
7947 compose = (Compose *)cur->data;
7948 toolbar_update(TOOLBAR_COMPOSE, compose);
7952 static const gchar *compose_quote_char_from_context(Compose *compose)
7954 const gchar *qmark = NULL;
7956 cm_return_val_if_fail(compose != NULL, NULL);
7958 switch (compose->mode) {
7959 /* use forward-specific quote char */
7960 case COMPOSE_FORWARD:
7961 case COMPOSE_FORWARD_AS_ATTACH:
7962 case COMPOSE_FORWARD_INLINE:
7963 if (compose->folder && compose->folder->prefs &&
7964 compose->folder->prefs->forward_with_format)
7965 qmark = compose->folder->prefs->forward_quotemark;
7966 else if (compose->account->forward_with_format)
7967 qmark = compose->account->forward_quotemark;
7969 qmark = prefs_common.fw_quotemark;
7972 /* use reply-specific quote char in all other modes */
7974 if (compose->folder && compose->folder->prefs &&
7975 compose->folder->prefs->reply_with_format)
7976 qmark = compose->folder->prefs->reply_quotemark;
7977 else if (compose->account->reply_with_format)
7978 qmark = compose->account->reply_quotemark;
7980 qmark = prefs_common.quotemark;
7984 if (qmark == NULL || *qmark == '\0')
7990 static void compose_template_apply(Compose *compose, Template *tmpl,
7994 GtkTextBuffer *buffer;
7998 gchar *parsed_str = NULL;
7999 gint cursor_pos = 0;
8000 const gchar *err_msg = _("The body of the template has an error at line %d.");
8003 /* process the body */
8005 text = GTK_TEXT_VIEW(compose->text);
8006 buffer = gtk_text_view_get_buffer(text);
8009 qmark = compose_quote_char_from_context(compose);
8011 if (compose->replyinfo != NULL) {
8014 gtk_text_buffer_set_text(buffer, "", -1);
8015 mark = gtk_text_buffer_get_insert(buffer);
8016 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8018 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8019 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8021 } else if (compose->fwdinfo != NULL) {
8024 gtk_text_buffer_set_text(buffer, "", -1);
8025 mark = gtk_text_buffer_get_insert(buffer);
8026 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8028 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8029 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8032 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8034 GtkTextIter start, end;
8037 gtk_text_buffer_get_start_iter(buffer, &start);
8038 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8039 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8041 /* clear the buffer now */
8043 gtk_text_buffer_set_text(buffer, "", -1);
8045 parsed_str = compose_quote_fmt(compose, dummyinfo,
8046 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8047 procmsg_msginfo_free( dummyinfo );
8053 gtk_text_buffer_set_text(buffer, "", -1);
8054 mark = gtk_text_buffer_get_insert(buffer);
8055 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8058 if (replace && parsed_str && compose->account->auto_sig)
8059 compose_insert_sig(compose, FALSE);
8061 if (replace && parsed_str) {
8062 gtk_text_buffer_get_start_iter(buffer, &iter);
8063 gtk_text_buffer_place_cursor(buffer, &iter);
8067 cursor_pos = quote_fmt_get_cursor_pos();
8068 compose->set_cursor_pos = cursor_pos;
8069 if (cursor_pos == -1)
8071 gtk_text_buffer_get_start_iter(buffer, &iter);
8072 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8073 gtk_text_buffer_place_cursor(buffer, &iter);
8076 /* process the other fields */
8078 compose_template_apply_fields(compose, tmpl);
8079 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8080 quote_fmt_reset_vartable();
8081 compose_changed_cb(NULL, compose);
8084 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8086 MsgInfo* dummyinfo = NULL;
8087 MsgInfo *msginfo = NULL;
8090 if (compose->replyinfo != NULL)
8091 msginfo = compose->replyinfo;
8092 else if (compose->fwdinfo != NULL)
8093 msginfo = compose->fwdinfo;
8095 dummyinfo = compose_msginfo_new_from_compose(compose);
8096 msginfo = dummyinfo;
8099 if (tmpl->from && *tmpl->from != '\0') {
8101 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8102 compose->gtkaspell);
8104 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8106 quote_fmt_scan_string(tmpl->from);
8109 buf = quote_fmt_get_buffer();
8111 alertpanel_error(_("Template From format error."));
8113 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8117 if (tmpl->to && *tmpl->to != '\0') {
8119 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8120 compose->gtkaspell);
8122 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8124 quote_fmt_scan_string(tmpl->to);
8127 buf = quote_fmt_get_buffer();
8129 alertpanel_error(_("Template To format error."));
8131 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8135 if (tmpl->cc && *tmpl->cc != '\0') {
8137 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8138 compose->gtkaspell);
8140 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8142 quote_fmt_scan_string(tmpl->cc);
8145 buf = quote_fmt_get_buffer();
8147 alertpanel_error(_("Template Cc format error."));
8149 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8153 if (tmpl->bcc && *tmpl->bcc != '\0') {
8155 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8156 compose->gtkaspell);
8158 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8160 quote_fmt_scan_string(tmpl->bcc);
8163 buf = quote_fmt_get_buffer();
8165 alertpanel_error(_("Template Bcc format error."));
8167 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8171 /* process the subject */
8172 if (tmpl->subject && *tmpl->subject != '\0') {
8174 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8175 compose->gtkaspell);
8177 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8179 quote_fmt_scan_string(tmpl->subject);
8182 buf = quote_fmt_get_buffer();
8184 alertpanel_error(_("Template subject format error."));
8186 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8190 procmsg_msginfo_free( dummyinfo );
8193 static void compose_destroy(Compose *compose)
8195 GtkTextBuffer *buffer;
8196 GtkClipboard *clipboard;
8198 compose_list = g_list_remove(compose_list, compose);
8200 if (compose->updating) {
8201 debug_print("danger, not destroying anything now\n");
8202 compose->deferred_destroy = TRUE;
8205 /* NOTE: address_completion_end() does nothing with the window
8206 * however this may change. */
8207 address_completion_end(compose->window);
8209 slist_free_strings(compose->to_list);
8210 g_slist_free(compose->to_list);
8211 slist_free_strings(compose->newsgroup_list);
8212 g_slist_free(compose->newsgroup_list);
8213 slist_free_strings(compose->header_list);
8214 g_slist_free(compose->header_list);
8216 g_hash_table_destroy(compose->email_hashtable);
8218 procmsg_msginfo_free(compose->targetinfo);
8219 procmsg_msginfo_free(compose->replyinfo);
8220 procmsg_msginfo_free(compose->fwdinfo);
8222 g_free(compose->replyto);
8223 g_free(compose->cc);
8224 g_free(compose->bcc);
8225 g_free(compose->newsgroups);
8226 g_free(compose->followup_to);
8228 g_free(compose->ml_post);
8230 g_free(compose->inreplyto);
8231 g_free(compose->references);
8232 g_free(compose->msgid);
8233 g_free(compose->boundary);
8235 g_free(compose->redirect_filename);
8236 if (compose->undostruct)
8237 undo_destroy(compose->undostruct);
8239 g_free(compose->sig_str);
8241 g_free(compose->exteditor_file);
8243 g_free(compose->orig_charset);
8245 g_free(compose->privacy_system);
8247 if (addressbook_get_target_compose() == compose)
8248 addressbook_set_target_compose(NULL);
8251 if (compose->gtkaspell) {
8252 gtkaspell_delete(compose->gtkaspell);
8253 compose->gtkaspell = NULL;
8257 if (!compose->batch) {
8258 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8259 prefs_common.compose_height = compose->window->allocation.height;
8262 if (!gtk_widget_get_parent(compose->paned))
8263 gtk_widget_destroy(compose->paned);
8264 gtk_widget_destroy(compose->popupmenu);
8266 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8267 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8268 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8270 gtk_widget_destroy(compose->window);
8271 toolbar_destroy(compose->toolbar);
8272 g_free(compose->toolbar);
8273 g_mutex_free(compose->mutex);
8277 static void compose_attach_info_free(AttachInfo *ainfo)
8279 g_free(ainfo->file);
8280 g_free(ainfo->content_type);
8281 g_free(ainfo->name);
8285 static void compose_attach_update_label(Compose *compose)
8290 GtkTreeModel *model;
8295 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8296 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8297 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8301 while(gtk_tree_model_iter_next(model, &iter))
8304 text = g_strdup_printf("(%d)", i);
8305 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8309 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8311 Compose *compose = (Compose *)data;
8312 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8313 GtkTreeSelection *selection;
8315 GtkTreeModel *model;
8317 selection = gtk_tree_view_get_selection(tree_view);
8318 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8323 for (cur = sel; cur != NULL; cur = cur->next) {
8324 GtkTreePath *path = cur->data;
8325 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8328 gtk_tree_path_free(path);
8331 for (cur = sel; cur != NULL; cur = cur->next) {
8332 GtkTreeRowReference *ref = cur->data;
8333 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8336 if (gtk_tree_model_get_iter(model, &iter, path))
8337 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8339 gtk_tree_path_free(path);
8340 gtk_tree_row_reference_free(ref);
8344 compose_attach_update_label(compose);
8347 static struct _AttachProperty
8350 GtkWidget *mimetype_entry;
8351 GtkWidget *encoding_optmenu;
8352 GtkWidget *path_entry;
8353 GtkWidget *filename_entry;
8355 GtkWidget *cancel_btn;
8358 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8360 gtk_tree_path_free((GtkTreePath *)ptr);
8363 static void compose_attach_property(GtkAction *action, gpointer data)
8365 Compose *compose = (Compose *)data;
8366 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8368 GtkComboBox *optmenu;
8369 GtkTreeSelection *selection;
8371 GtkTreeModel *model;
8374 static gboolean cancelled;
8376 /* only if one selected */
8377 selection = gtk_tree_view_get_selection(tree_view);
8378 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8381 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8385 path = (GtkTreePath *) sel->data;
8386 gtk_tree_model_get_iter(model, &iter, path);
8387 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8390 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8396 if (!attach_prop.window)
8397 compose_attach_property_create(&cancelled);
8398 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8399 gtk_widget_grab_focus(attach_prop.ok_btn);
8400 gtk_widget_show(attach_prop.window);
8401 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8403 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8404 if (ainfo->encoding == ENC_UNKNOWN)
8405 combobox_select_by_data(optmenu, ENC_BASE64);
8407 combobox_select_by_data(optmenu, ainfo->encoding);
8409 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8410 ainfo->content_type ? ainfo->content_type : "");
8411 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8412 ainfo->file ? ainfo->file : "");
8413 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8414 ainfo->name ? ainfo->name : "");
8417 const gchar *entry_text;
8419 gchar *cnttype = NULL;
8426 gtk_widget_hide(attach_prop.window);
8427 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8432 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8433 if (*entry_text != '\0') {
8436 text = g_strstrip(g_strdup(entry_text));
8437 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8438 cnttype = g_strdup(text);
8441 alertpanel_error(_("Invalid MIME type."));
8447 ainfo->encoding = combobox_get_active_data(optmenu);
8449 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8450 if (*entry_text != '\0') {
8451 if (is_file_exist(entry_text) &&
8452 (size = get_file_size(entry_text)) > 0)
8453 file = g_strdup(entry_text);
8456 (_("File doesn't exist or is empty."));
8462 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8463 if (*entry_text != '\0') {
8464 g_free(ainfo->name);
8465 ainfo->name = g_strdup(entry_text);
8469 g_free(ainfo->content_type);
8470 ainfo->content_type = cnttype;
8473 g_free(ainfo->file);
8477 ainfo->size = (goffset)size;
8479 /* update tree store */
8480 text = to_human_readable(ainfo->size);
8481 gtk_tree_model_get_iter(model, &iter, path);
8482 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8483 COL_MIMETYPE, ainfo->content_type,
8485 COL_NAME, ainfo->name,
8491 gtk_tree_path_free(path);
8494 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8496 label = gtk_label_new(str); \
8497 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8498 GTK_FILL, 0, 0, 0); \
8499 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8501 entry = gtk_entry_new(); \
8502 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8503 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8506 static void compose_attach_property_create(gboolean *cancelled)
8512 GtkWidget *mimetype_entry;
8515 GtkListStore *optmenu_menu;
8516 GtkWidget *path_entry;
8517 GtkWidget *filename_entry;
8520 GtkWidget *cancel_btn;
8521 GList *mime_type_list, *strlist;
8524 debug_print("Creating attach_property window...\n");
8526 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8527 gtk_widget_set_size_request(window, 480, -1);
8528 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8529 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8530 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8531 g_signal_connect(G_OBJECT(window), "delete_event",
8532 G_CALLBACK(attach_property_delete_event),
8534 g_signal_connect(G_OBJECT(window), "key_press_event",
8535 G_CALLBACK(attach_property_key_pressed),
8538 vbox = gtk_vbox_new(FALSE, 8);
8539 gtk_container_add(GTK_CONTAINER(window), vbox);
8541 table = gtk_table_new(4, 2, FALSE);
8542 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8543 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8544 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8546 label = gtk_label_new(_("MIME type"));
8547 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8549 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8550 mimetype_entry = gtk_combo_box_entry_new_text();
8551 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8552 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8554 /* stuff with list */
8555 mime_type_list = procmime_get_mime_type_list();
8557 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8558 MimeType *type = (MimeType *) mime_type_list->data;
8561 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8563 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8566 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8567 (GCompareFunc)strcmp2);
8570 for (mime_type_list = strlist; mime_type_list != NULL;
8571 mime_type_list = mime_type_list->next) {
8572 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8573 g_free(mime_type_list->data);
8575 g_list_free(strlist);
8576 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8577 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8579 label = gtk_label_new(_("Encoding"));
8580 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8582 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8584 hbox = gtk_hbox_new(FALSE, 0);
8585 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8586 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8588 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8589 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8591 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8592 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8593 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8594 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8595 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8597 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8599 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8600 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8602 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8603 &ok_btn, GTK_STOCK_OK,
8605 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8606 gtk_widget_grab_default(ok_btn);
8608 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8609 G_CALLBACK(attach_property_ok),
8611 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8612 G_CALLBACK(attach_property_cancel),
8615 gtk_widget_show_all(vbox);
8617 attach_prop.window = window;
8618 attach_prop.mimetype_entry = mimetype_entry;
8619 attach_prop.encoding_optmenu = optmenu;
8620 attach_prop.path_entry = path_entry;
8621 attach_prop.filename_entry = filename_entry;
8622 attach_prop.ok_btn = ok_btn;
8623 attach_prop.cancel_btn = cancel_btn;
8626 #undef SET_LABEL_AND_ENTRY
8628 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8634 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8640 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8641 gboolean *cancelled)
8649 static gboolean attach_property_key_pressed(GtkWidget *widget,
8651 gboolean *cancelled)
8653 if (event && event->keyval == GDK_Escape) {
8657 if (event && event->keyval == GDK_Return) {
8665 static void compose_exec_ext_editor(Compose *compose)
8672 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8673 G_DIR_SEPARATOR, compose);
8675 if (pipe(pipe_fds) < 0) {
8681 if ((pid = fork()) < 0) {
8688 /* close the write side of the pipe */
8691 compose->exteditor_file = g_strdup(tmp);
8692 compose->exteditor_pid = pid;
8694 compose_set_ext_editor_sensitive(compose, FALSE);
8697 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8699 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8701 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8705 } else { /* process-monitoring process */
8711 /* close the read side of the pipe */
8714 if (compose_write_body_to_file(compose, tmp) < 0) {
8715 fd_write_all(pipe_fds[1], "2\n", 2);
8719 pid_ed = compose_exec_ext_editor_real(tmp);
8721 fd_write_all(pipe_fds[1], "1\n", 2);
8725 /* wait until editor is terminated */
8726 waitpid(pid_ed, NULL, 0);
8728 fd_write_all(pipe_fds[1], "0\n", 2);
8735 #endif /* G_OS_UNIX */
8739 static gint compose_exec_ext_editor_real(const gchar *file)
8746 cm_return_val_if_fail(file != NULL, -1);
8748 if ((pid = fork()) < 0) {
8753 if (pid != 0) return pid;
8755 /* grandchild process */
8757 if (setpgid(0, getppid()))
8760 if (prefs_common_get_ext_editor_cmd() &&
8761 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8762 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8763 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8765 if (prefs_common_get_ext_editor_cmd())
8766 g_warning("External editor command-line is invalid: '%s'\n",
8767 prefs_common_get_ext_editor_cmd());
8768 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8771 cmdline = strsplit_with_quote(buf, " ", 1024);
8772 execvp(cmdline[0], cmdline);
8775 g_strfreev(cmdline);
8780 static gboolean compose_ext_editor_kill(Compose *compose)
8782 pid_t pgid = compose->exteditor_pid * -1;
8785 ret = kill(pgid, 0);
8787 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8791 msg = g_strdup_printf
8792 (_("The external editor is still working.\n"
8793 "Force terminating the process?\n"
8794 "process group id: %d"), -pgid);
8795 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8796 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8800 if (val == G_ALERTALTERNATE) {
8801 g_source_remove(compose->exteditor_tag);
8802 g_io_channel_shutdown(compose->exteditor_ch,
8804 g_io_channel_unref(compose->exteditor_ch);
8806 if (kill(pgid, SIGTERM) < 0) perror("kill");
8807 waitpid(compose->exteditor_pid, NULL, 0);
8809 g_warning("Terminated process group id: %d", -pgid);
8810 g_warning("Temporary file: %s",
8811 compose->exteditor_file);
8813 compose_set_ext_editor_sensitive(compose, TRUE);
8815 g_free(compose->exteditor_file);
8816 compose->exteditor_file = NULL;
8817 compose->exteditor_pid = -1;
8818 compose->exteditor_ch = NULL;
8819 compose->exteditor_tag = -1;
8827 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8831 Compose *compose = (Compose *)data;
8834 debug_print(_("Compose: input from monitoring process\n"));
8836 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8838 g_io_channel_shutdown(source, FALSE, NULL);
8839 g_io_channel_unref(source);
8841 waitpid(compose->exteditor_pid, NULL, 0);
8843 if (buf[0] == '0') { /* success */
8844 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8845 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8847 gtk_text_buffer_set_text(buffer, "", -1);
8848 compose_insert_file(compose, compose->exteditor_file);
8849 compose_changed_cb(NULL, compose);
8850 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8852 if (claws_unlink(compose->exteditor_file) < 0)
8853 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8854 } else if (buf[0] == '1') { /* failed */
8855 g_warning("Couldn't exec external editor\n");
8856 if (claws_unlink(compose->exteditor_file) < 0)
8857 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8858 } else if (buf[0] == '2') {
8859 g_warning("Couldn't write to file\n");
8860 } else if (buf[0] == '3') {
8861 g_warning("Pipe read failed\n");
8864 compose_set_ext_editor_sensitive(compose, TRUE);
8866 g_free(compose->exteditor_file);
8867 compose->exteditor_file = NULL;
8868 compose->exteditor_pid = -1;
8869 compose->exteditor_ch = NULL;
8870 compose->exteditor_tag = -1;
8875 static void compose_set_ext_editor_sensitive(Compose *compose,
8878 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8879 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8880 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8881 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8882 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8883 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8884 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8886 gtk_widget_set_sensitive(compose->text, sensitive);
8887 if (compose->toolbar->send_btn)
8888 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8889 if (compose->toolbar->sendl_btn)
8890 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8891 if (compose->toolbar->draft_btn)
8892 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8893 if (compose->toolbar->insert_btn)
8894 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8895 if (compose->toolbar->sig_btn)
8896 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8897 if (compose->toolbar->exteditor_btn)
8898 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8899 if (compose->toolbar->linewrap_current_btn)
8900 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8901 if (compose->toolbar->linewrap_all_btn)
8902 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8904 #endif /* G_OS_UNIX */
8907 * compose_undo_state_changed:
8909 * Change the sensivity of the menuentries undo and redo
8911 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8912 gint redo_state, gpointer data)
8914 Compose *compose = (Compose *)data;
8916 switch (undo_state) {
8917 case UNDO_STATE_TRUE:
8918 if (!undostruct->undo_state) {
8919 undostruct->undo_state = TRUE;
8920 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8923 case UNDO_STATE_FALSE:
8924 if (undostruct->undo_state) {
8925 undostruct->undo_state = FALSE;
8926 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8929 case UNDO_STATE_UNCHANGED:
8931 case UNDO_STATE_REFRESH:
8932 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8935 g_warning("Undo state not recognized");
8939 switch (redo_state) {
8940 case UNDO_STATE_TRUE:
8941 if (!undostruct->redo_state) {
8942 undostruct->redo_state = TRUE;
8943 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8946 case UNDO_STATE_FALSE:
8947 if (undostruct->redo_state) {
8948 undostruct->redo_state = FALSE;
8949 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8952 case UNDO_STATE_UNCHANGED:
8954 case UNDO_STATE_REFRESH:
8955 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8958 g_warning("Redo state not recognized");
8963 /* callback functions */
8965 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8966 * includes "non-client" (windows-izm) in calculation, so this calculation
8967 * may not be accurate.
8969 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8970 GtkAllocation *allocation,
8971 GtkSHRuler *shruler)
8973 if (prefs_common.show_ruler) {
8974 gint char_width = 0, char_height = 0;
8975 gint line_width_in_chars;
8977 gtkut_get_font_size(GTK_WIDGET(widget),
8978 &char_width, &char_height);
8979 line_width_in_chars =
8980 (allocation->width - allocation->x) / char_width;
8982 /* got the maximum */
8983 gtk_ruler_set_range(GTK_RULER(shruler),
8984 0.0, line_width_in_chars, 0,
8985 /*line_width_in_chars*/ char_width);
8992 ComposeEntryType header;
8994 ComposePrefType type;
8995 gboolean entry_marked;
8998 static void account_activated(GtkComboBox *optmenu, gpointer data)
9000 Compose *compose = (Compose *)data;
9003 gchar *folderidentifier;
9004 gint account_id = 0;
9007 GSList *list, *saved_list = NULL;
9008 HeaderEntryState *state;
9009 GtkRcStyle *style = NULL;
9010 static GdkColor yellow;
9011 static gboolean color_set = FALSE;
9013 /* Get ID of active account in the combo box */
9014 menu = gtk_combo_box_get_model(optmenu);
9015 gtk_combo_box_get_active_iter(optmenu, &iter);
9016 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9018 ac = account_find_from_id(account_id);
9019 cm_return_if_fail(ac != NULL);
9021 if (ac != compose->account) {
9022 compose_select_account(compose, ac, FALSE);
9024 for (list = compose->header_list; list; list = list->next) {
9025 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9027 if (hentry->type == PREF_ACCOUNT || !list->next) {
9028 compose_destroy_headerentry(compose, hentry);
9032 state = g_malloc0(sizeof(HeaderEntryState));
9033 state->header = combobox_get_active_data(
9034 GTK_COMBO_BOX(hentry->combo));
9035 state->entry = gtk_editable_get_chars(
9036 GTK_EDITABLE(hentry->entry), 0, -1);
9037 state->type = hentry->type;
9040 gdk_color_parse("#f5f6be", &yellow);
9041 color_set = gdk_colormap_alloc_color(
9042 gdk_colormap_get_system(),
9043 &yellow, FALSE, TRUE);
9046 style = gtk_widget_get_modifier_style(hentry->entry);
9047 state->entry_marked = gdk_color_equal(&yellow,
9048 &style->base[GTK_STATE_NORMAL]);
9050 saved_list = g_slist_append(saved_list, state);
9051 compose_destroy_headerentry(compose, hentry);
9054 compose->header_last = NULL;
9055 g_slist_free(compose->header_list);
9056 compose->header_list = NULL;
9057 compose->header_nextrow = 1;
9058 compose_create_header_entry(compose);
9060 if (ac->set_autocc && ac->auto_cc)
9061 compose_entry_append(compose, ac->auto_cc,
9062 COMPOSE_CC, PREF_ACCOUNT);
9064 if (ac->set_autobcc && ac->auto_bcc)
9065 compose_entry_append(compose, ac->auto_bcc,
9066 COMPOSE_BCC, PREF_ACCOUNT);
9068 if (ac->set_autoreplyto && ac->auto_replyto)
9069 compose_entry_append(compose, ac->auto_replyto,
9070 COMPOSE_REPLYTO, PREF_ACCOUNT);
9072 for (list = saved_list; list; list = list->next) {
9073 state = (HeaderEntryState *) list->data;
9075 compose_entry_append(compose, state->entry,
9076 state->header, state->type);
9077 if (state->entry_marked)
9078 compose_entry_mark_default_to(compose, state->entry);
9080 g_free(state->entry);
9082 g_slist_free(saved_list);
9084 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9085 (ac->protocol == A_NNTP) ?
9086 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9089 /* Set message save folder */
9090 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9091 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9093 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9094 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9096 compose_set_save_to(compose, NULL);
9097 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9098 folderidentifier = folder_item_get_identifier(account_get_special_folder
9099 (compose->account, F_OUTBOX));
9100 compose_set_save_to(compose, folderidentifier);
9101 g_free(folderidentifier);
9105 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9106 GtkTreeViewColumn *column, Compose *compose)
9108 compose_attach_property(NULL, compose);
9111 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9114 Compose *compose = (Compose *)data;
9115 GtkTreeSelection *attach_selection;
9116 gint attach_nr_selected;
9118 if (!event) return FALSE;
9120 if (event->button == 3) {
9121 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9122 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9124 if (attach_nr_selected > 0)
9126 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9127 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9129 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9130 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9133 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9134 NULL, NULL, event->button, event->time);
9141 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9144 Compose *compose = (Compose *)data;
9146 if (!event) return FALSE;
9148 switch (event->keyval) {
9150 compose_attach_remove_selected(NULL, compose);
9156 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9158 toolbar_comp_set_sensitive(compose, allow);
9159 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9160 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9162 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9164 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9165 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9166 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9168 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9172 static void compose_send_cb(GtkAction *action, gpointer data)
9174 Compose *compose = (Compose *)data;
9176 if (prefs_common.work_offline &&
9177 !inc_offline_should_override(TRUE,
9178 _("Claws Mail needs network access in order "
9179 "to send this email.")))
9182 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9183 g_source_remove(compose->draft_timeout_tag);
9184 compose->draft_timeout_tag = -1;
9187 compose_send(compose);
9190 static void compose_send_later_cb(GtkAction *action, gpointer data)
9192 Compose *compose = (Compose *)data;
9196 compose_allow_user_actions(compose, FALSE);
9197 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9198 compose_allow_user_actions(compose, TRUE);
9202 compose_close(compose);
9203 } else if (val == -1) {
9204 alertpanel_error(_("Could not queue message."));
9205 } else if (val == -2) {
9206 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9207 } else if (val == -3) {
9208 if (privacy_peek_error())
9209 alertpanel_error(_("Could not queue message for sending:\n\n"
9210 "Signature failed: %s"), privacy_get_error());
9211 } else if (val == -4) {
9212 alertpanel_error(_("Could not queue message for sending:\n\n"
9213 "Charset conversion failed."));
9214 } else if (val == -5) {
9215 alertpanel_error(_("Could not queue message for sending:\n\n"
9216 "Couldn't get recipient encryption key."));
9217 } else if (val == -6) {
9220 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9223 #define DRAFTED_AT_EXIT "drafted_at_exit"
9224 static void compose_register_draft(MsgInfo *info)
9226 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9227 DRAFTED_AT_EXIT, NULL);
9228 FILE *fp = g_fopen(filepath, "ab");
9231 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9239 gboolean compose_draft (gpointer data, guint action)
9241 Compose *compose = (Compose *)data;
9245 MsgFlags flag = {0, 0};
9246 static gboolean lock = FALSE;
9247 MsgInfo *newmsginfo;
9249 gboolean target_locked = FALSE;
9250 gboolean err = FALSE;
9252 if (lock) return FALSE;
9254 if (compose->sending)
9257 draft = account_get_special_folder(compose->account, F_DRAFT);
9258 cm_return_val_if_fail(draft != NULL, FALSE);
9260 if (!g_mutex_trylock(compose->mutex)) {
9261 /* we don't want to lock the mutex once it's available,
9262 * because as the only other part of compose.c locking
9263 * it is compose_close - which means once unlocked,
9264 * the compose struct will be freed */
9265 debug_print("couldn't lock mutex, probably sending\n");
9271 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9272 G_DIR_SEPARATOR, compose);
9273 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9274 FILE_OP_ERROR(tmp, "fopen");
9278 /* chmod for security */
9279 if (change_file_mode_rw(fp, tmp) < 0) {
9280 FILE_OP_ERROR(tmp, "chmod");
9281 g_warning("can't change file mode\n");
9284 /* Save draft infos */
9285 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9286 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9288 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9289 gchar *savefolderid;
9291 savefolderid = compose_get_save_to(compose);
9292 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9293 g_free(savefolderid);
9295 if (compose->return_receipt) {
9296 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9298 if (compose->privacy_system) {
9299 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9300 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9301 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9304 /* Message-ID of message replying to */
9305 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9308 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9309 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9312 /* Message-ID of message forwarding to */
9313 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9316 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9317 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9321 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9322 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9324 /* end of headers */
9325 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9332 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9336 if (fclose(fp) == EOF) {
9340 if (compose->targetinfo) {
9341 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9342 flag.perm_flags = target_locked?MSG_LOCKED:0;
9344 flag.tmp_flags = MSG_DRAFT;
9346 folder_item_scan(draft);
9347 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9348 MsgInfo *tmpinfo = NULL;
9349 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9350 if (compose->msgid) {
9351 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9354 msgnum = tmpinfo->msgnum;
9355 procmsg_msginfo_free(tmpinfo);
9356 debug_print("got draft msgnum %d from scanning\n", msgnum);
9358 debug_print("didn't get draft msgnum after scanning\n");
9361 debug_print("got draft msgnum %d from adding\n", msgnum);
9367 if (action != COMPOSE_AUTO_SAVE) {
9368 if (action != COMPOSE_DRAFT_FOR_EXIT)
9369 alertpanel_error(_("Could not save draft."));
9372 gtkut_window_popup(compose->window);
9373 val = alertpanel_full(_("Could not save draft"),
9374 _("Could not save draft.\n"
9375 "Do you want to cancel exit or discard this email?"),
9376 _("_Cancel exit"), _("_Discard email"), NULL,
9377 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9378 if (val == G_ALERTALTERNATE) {
9380 g_mutex_unlock(compose->mutex); /* must be done before closing */
9381 compose_close(compose);
9385 g_mutex_unlock(compose->mutex); /* must be done before closing */
9394 if (compose->mode == COMPOSE_REEDIT) {
9395 compose_remove_reedit_target(compose, TRUE);
9398 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9401 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9403 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9405 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9406 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9407 procmsg_msginfo_set_flags(newmsginfo, 0,
9408 MSG_HAS_ATTACHMENT);
9410 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9411 compose_register_draft(newmsginfo);
9413 procmsg_msginfo_free(newmsginfo);
9416 folder_item_scan(draft);
9418 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9420 g_mutex_unlock(compose->mutex); /* must be done before closing */
9421 compose_close(compose);
9427 path = folder_item_fetch_msg(draft, msgnum);
9429 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9432 if (g_stat(path, &s) < 0) {
9433 FILE_OP_ERROR(path, "stat");
9439 procmsg_msginfo_free(compose->targetinfo);
9440 compose->targetinfo = procmsg_msginfo_new();
9441 compose->targetinfo->msgnum = msgnum;
9442 compose->targetinfo->size = (goffset)s.st_size;
9443 compose->targetinfo->mtime = s.st_mtime;
9444 compose->targetinfo->folder = draft;
9446 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9447 compose->mode = COMPOSE_REEDIT;
9449 if (action == COMPOSE_AUTO_SAVE) {
9450 compose->autosaved_draft = compose->targetinfo;
9452 compose->modified = FALSE;
9453 compose_set_title(compose);
9457 g_mutex_unlock(compose->mutex);
9461 void compose_clear_exit_drafts(void)
9463 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9464 DRAFTED_AT_EXIT, NULL);
9465 if (is_file_exist(filepath))
9466 claws_unlink(filepath);
9471 void compose_reopen_exit_drafts(void)
9473 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9474 DRAFTED_AT_EXIT, NULL);
9475 FILE *fp = g_fopen(filepath, "rb");
9479 while (fgets(buf, sizeof(buf), fp)) {
9480 gchar **parts = g_strsplit(buf, "\t", 2);
9481 const gchar *folder = parts[0];
9482 int msgnum = parts[1] ? atoi(parts[1]):-1;
9484 if (folder && *folder && msgnum > -1) {
9485 FolderItem *item = folder_find_item_from_identifier(folder);
9486 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9488 compose_reedit(info, FALSE);
9495 compose_clear_exit_drafts();
9498 static void compose_save_cb(GtkAction *action, gpointer data)
9500 Compose *compose = (Compose *)data;
9501 compose_draft(compose, COMPOSE_KEEP_EDITING);
9502 compose->rmode = COMPOSE_REEDIT;
9505 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9507 if (compose && file_list) {
9510 for ( tmp = file_list; tmp; tmp = tmp->next) {
9511 gchar *file = (gchar *) tmp->data;
9512 gchar *utf8_filename = conv_filename_to_utf8(file);
9513 compose_attach_append(compose, file, utf8_filename, NULL);
9514 compose_changed_cb(NULL, compose);
9519 g_free(utf8_filename);
9524 static void compose_attach_cb(GtkAction *action, gpointer data)
9526 Compose *compose = (Compose *)data;
9529 if (compose->redirect_filename != NULL)
9532 file_list = filesel_select_multiple_files_open(_("Select file"));
9535 compose_attach_from_list(compose, file_list, TRUE);
9536 g_list_free(file_list);
9540 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9542 Compose *compose = (Compose *)data;
9545 file_list = filesel_select_multiple_files_open(_("Select file"));
9550 for ( tmp = file_list; tmp; tmp = tmp->next) {
9551 gchar *file = (gchar *) tmp->data;
9552 gchar *filedup = g_strdup(file);
9553 gchar *shortfile = g_path_get_basename(filedup);
9554 ComposeInsertResult res;
9555 /* insert the file if the file is short or if the user confirmed that
9556 he/she wants to insert the large file */
9557 res = compose_insert_file(compose, file);
9558 if (res == COMPOSE_INSERT_READ_ERROR) {
9559 alertpanel_error(_("File '%s' could not be read."), shortfile);
9560 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9561 alertpanel_error(_("File '%s' contained invalid characters\n"
9562 "for the current encoding, insertion may be incorrect."),
9570 g_list_free(file_list);
9574 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9576 Compose *compose = (Compose *)data;
9578 compose_insert_sig(compose, FALSE);
9581 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9585 Compose *compose = (Compose *)data;
9587 gtkut_widget_get_uposition(widget, &x, &y);
9588 if (!compose->batch) {
9589 prefs_common.compose_x = x;
9590 prefs_common.compose_y = y;
9592 if (compose->sending || compose->updating)
9594 compose_close_cb(NULL, compose);
9598 void compose_close_toolbar(Compose *compose)
9600 compose_close_cb(NULL, compose);
9603 static void compose_close_cb(GtkAction *action, gpointer data)
9605 Compose *compose = (Compose *)data;
9609 if (compose->exteditor_tag != -1) {
9610 if (!compose_ext_editor_kill(compose))
9615 if (compose->modified) {
9616 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9617 if (!g_mutex_trylock(compose->mutex)) {
9618 /* we don't want to lock the mutex once it's available,
9619 * because as the only other part of compose.c locking
9620 * it is compose_close - which means once unlocked,
9621 * the compose struct will be freed */
9622 debug_print("couldn't lock mutex, probably sending\n");
9626 val = alertpanel(_("Discard message"),
9627 _("This message has been modified. Discard it?"),
9628 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9630 val = alertpanel(_("Save changes"),
9631 _("This message has been modified. Save the latest changes?"),
9632 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9634 g_mutex_unlock(compose->mutex);
9636 case G_ALERTDEFAULT:
9637 if (prefs_common.autosave && !reedit)
9638 compose_remove_draft(compose);
9640 case G_ALERTALTERNATE:
9641 compose_draft(data, COMPOSE_QUIT_EDITING);
9648 compose_close(compose);
9651 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9653 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9654 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9655 Compose *compose = (Compose *) data;
9658 compose->out_encoding = (CharSet)value;
9661 static void compose_address_cb(GtkAction *action, gpointer data)
9663 Compose *compose = (Compose *)data;
9665 addressbook_open(compose);
9668 static void about_show_cb(GtkAction *action, gpointer data)
9673 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9675 Compose *compose = (Compose *)data;
9680 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9681 cm_return_if_fail(tmpl != NULL);
9683 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9685 val = alertpanel(_("Apply template"), msg,
9686 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9689 if (val == G_ALERTDEFAULT)
9690 compose_template_apply(compose, tmpl, TRUE);
9691 else if (val == G_ALERTALTERNATE)
9692 compose_template_apply(compose, tmpl, FALSE);
9695 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9697 Compose *compose = (Compose *)data;
9699 compose_exec_ext_editor(compose);
9702 static void compose_undo_cb(GtkAction *action, gpointer data)
9704 Compose *compose = (Compose *)data;
9705 gboolean prev_autowrap = compose->autowrap;
9707 compose->autowrap = FALSE;
9708 undo_undo(compose->undostruct);
9709 compose->autowrap = prev_autowrap;
9712 static void compose_redo_cb(GtkAction *action, gpointer data)
9714 Compose *compose = (Compose *)data;
9715 gboolean prev_autowrap = compose->autowrap;
9717 compose->autowrap = FALSE;
9718 undo_redo(compose->undostruct);
9719 compose->autowrap = prev_autowrap;
9722 static void entry_cut_clipboard(GtkWidget *entry)
9724 if (GTK_IS_EDITABLE(entry))
9725 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9726 else if (GTK_IS_TEXT_VIEW(entry))
9727 gtk_text_buffer_cut_clipboard(
9728 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9729 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9733 static void entry_copy_clipboard(GtkWidget *entry)
9735 if (GTK_IS_EDITABLE(entry))
9736 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9737 else if (GTK_IS_TEXT_VIEW(entry))
9738 gtk_text_buffer_copy_clipboard(
9739 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9740 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9743 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9744 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9746 if (GTK_IS_TEXT_VIEW(entry)) {
9747 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9748 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9749 GtkTextIter start_iter, end_iter;
9751 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9753 if (contents == NULL)
9756 /* we shouldn't delete the selection when middle-click-pasting, or we
9757 * can't mid-click-paste our own selection */
9758 if (clip != GDK_SELECTION_PRIMARY) {
9759 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9760 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9763 if (insert_place == NULL) {
9764 /* if insert_place isn't specified, insert at the cursor.
9765 * used for Ctrl-V pasting */
9766 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9767 start = gtk_text_iter_get_offset(&start_iter);
9768 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9770 /* if insert_place is specified, paste here.
9771 * used for mid-click-pasting */
9772 start = gtk_text_iter_get_offset(insert_place);
9773 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9774 if (prefs_common.primary_paste_unselects)
9775 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9779 /* paste unwrapped: mark the paste so it's not wrapped later */
9780 end = start + strlen(contents);
9781 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9782 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9783 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9784 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9785 /* rewrap paragraph now (after a mid-click-paste) */
9786 mark_start = gtk_text_buffer_get_insert(buffer);
9787 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9788 gtk_text_iter_backward_char(&start_iter);
9789 compose_beautify_paragraph(compose, &start_iter, TRUE);
9791 } else if (GTK_IS_EDITABLE(entry))
9792 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9794 compose->modified = TRUE;
9797 static void entry_allsel(GtkWidget *entry)
9799 if (GTK_IS_EDITABLE(entry))
9800 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9801 else if (GTK_IS_TEXT_VIEW(entry)) {
9802 GtkTextIter startiter, enditer;
9803 GtkTextBuffer *textbuf;
9805 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9806 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9807 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9809 gtk_text_buffer_move_mark_by_name(textbuf,
9810 "selection_bound", &startiter);
9811 gtk_text_buffer_move_mark_by_name(textbuf,
9812 "insert", &enditer);
9816 static void compose_cut_cb(GtkAction *action, gpointer data)
9818 Compose *compose = (Compose *)data;
9819 if (compose->focused_editable
9820 #ifndef GENERIC_UMPC
9821 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9824 entry_cut_clipboard(compose->focused_editable);
9827 static void compose_copy_cb(GtkAction *action, gpointer data)
9829 Compose *compose = (Compose *)data;
9830 if (compose->focused_editable
9831 #ifndef GENERIC_UMPC
9832 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9835 entry_copy_clipboard(compose->focused_editable);
9838 static void compose_paste_cb(GtkAction *action, gpointer data)
9840 Compose *compose = (Compose *)data;
9842 GtkTextBuffer *buffer;
9844 if (compose->focused_editable &&
9845 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9846 entry_paste_clipboard(compose, compose->focused_editable,
9847 prefs_common.linewrap_pastes,
9848 GDK_SELECTION_CLIPBOARD, NULL);
9852 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9854 Compose *compose = (Compose *)data;
9855 gint wrap_quote = prefs_common.linewrap_quote;
9856 if (compose->focused_editable
9857 #ifndef GENERIC_UMPC
9858 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9861 /* let text_insert() (called directly or at a later time
9862 * after the gtk_editable_paste_clipboard) know that
9863 * text is to be inserted as a quotation. implemented
9864 * by using a simple refcount... */
9865 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9866 G_OBJECT(compose->focused_editable),
9867 "paste_as_quotation"));
9868 g_object_set_data(G_OBJECT(compose->focused_editable),
9869 "paste_as_quotation",
9870 GINT_TO_POINTER(paste_as_quotation + 1));
9871 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9872 entry_paste_clipboard(compose, compose->focused_editable,
9873 prefs_common.linewrap_pastes,
9874 GDK_SELECTION_CLIPBOARD, NULL);
9875 prefs_common.linewrap_quote = wrap_quote;
9879 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9881 Compose *compose = (Compose *)data;
9883 GtkTextBuffer *buffer;
9885 if (compose->focused_editable
9886 #ifndef GENERIC_UMPC
9887 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9890 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9891 GDK_SELECTION_CLIPBOARD, NULL);
9895 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9897 Compose *compose = (Compose *)data;
9899 GtkTextBuffer *buffer;
9901 if (compose->focused_editable
9902 #ifndef GENERIC_UMPC
9903 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9906 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9907 GDK_SELECTION_CLIPBOARD, NULL);
9911 static void compose_allsel_cb(GtkAction *action, gpointer data)
9913 Compose *compose = (Compose *)data;
9914 if (compose->focused_editable
9915 #ifndef GENERIC_UMPC
9916 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9919 entry_allsel(compose->focused_editable);
9922 static void textview_move_beginning_of_line (GtkTextView *text)
9924 GtkTextBuffer *buffer;
9928 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9930 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9931 mark = gtk_text_buffer_get_insert(buffer);
9932 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9933 gtk_text_iter_set_line_offset(&ins, 0);
9934 gtk_text_buffer_place_cursor(buffer, &ins);
9937 static void textview_move_forward_character (GtkTextView *text)
9939 GtkTextBuffer *buffer;
9943 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9945 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9946 mark = gtk_text_buffer_get_insert(buffer);
9947 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9948 if (gtk_text_iter_forward_cursor_position(&ins))
9949 gtk_text_buffer_place_cursor(buffer, &ins);
9952 static void textview_move_backward_character (GtkTextView *text)
9954 GtkTextBuffer *buffer;
9958 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9960 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9961 mark = gtk_text_buffer_get_insert(buffer);
9962 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9963 if (gtk_text_iter_backward_cursor_position(&ins))
9964 gtk_text_buffer_place_cursor(buffer, &ins);
9967 static void textview_move_forward_word (GtkTextView *text)
9969 GtkTextBuffer *buffer;
9974 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9976 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9977 mark = gtk_text_buffer_get_insert(buffer);
9978 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9979 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9980 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9981 gtk_text_iter_backward_word_start(&ins);
9982 gtk_text_buffer_place_cursor(buffer, &ins);
9986 static void textview_move_backward_word (GtkTextView *text)
9988 GtkTextBuffer *buffer;
9993 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9995 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9996 mark = gtk_text_buffer_get_insert(buffer);
9997 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9998 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9999 if (gtk_text_iter_backward_word_starts(&ins, 1))
10000 gtk_text_buffer_place_cursor(buffer, &ins);
10003 static void textview_move_end_of_line (GtkTextView *text)
10005 GtkTextBuffer *buffer;
10009 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10011 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10012 mark = gtk_text_buffer_get_insert(buffer);
10013 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10014 if (gtk_text_iter_forward_to_line_end(&ins))
10015 gtk_text_buffer_place_cursor(buffer, &ins);
10018 static void textview_move_next_line (GtkTextView *text)
10020 GtkTextBuffer *buffer;
10025 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10027 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10028 mark = gtk_text_buffer_get_insert(buffer);
10029 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10030 offset = gtk_text_iter_get_line_offset(&ins);
10031 if (gtk_text_iter_forward_line(&ins)) {
10032 gtk_text_iter_set_line_offset(&ins, offset);
10033 gtk_text_buffer_place_cursor(buffer, &ins);
10037 static void textview_move_previous_line (GtkTextView *text)
10039 GtkTextBuffer *buffer;
10044 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10046 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10047 mark = gtk_text_buffer_get_insert(buffer);
10048 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10049 offset = gtk_text_iter_get_line_offset(&ins);
10050 if (gtk_text_iter_backward_line(&ins)) {
10051 gtk_text_iter_set_line_offset(&ins, offset);
10052 gtk_text_buffer_place_cursor(buffer, &ins);
10056 static void textview_delete_forward_character (GtkTextView *text)
10058 GtkTextBuffer *buffer;
10060 GtkTextIter ins, end_iter;
10062 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10064 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10065 mark = gtk_text_buffer_get_insert(buffer);
10066 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10068 if (gtk_text_iter_forward_char(&end_iter)) {
10069 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10073 static void textview_delete_backward_character (GtkTextView *text)
10075 GtkTextBuffer *buffer;
10077 GtkTextIter ins, end_iter;
10079 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10081 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10082 mark = gtk_text_buffer_get_insert(buffer);
10083 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10085 if (gtk_text_iter_backward_char(&end_iter)) {
10086 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10090 static void textview_delete_forward_word (GtkTextView *text)
10092 GtkTextBuffer *buffer;
10094 GtkTextIter ins, end_iter;
10096 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10098 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10099 mark = gtk_text_buffer_get_insert(buffer);
10100 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10102 if (gtk_text_iter_forward_word_end(&end_iter)) {
10103 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10107 static void textview_delete_backward_word (GtkTextView *text)
10109 GtkTextBuffer *buffer;
10111 GtkTextIter ins, end_iter;
10113 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10115 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10116 mark = gtk_text_buffer_get_insert(buffer);
10117 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10119 if (gtk_text_iter_backward_word_start(&end_iter)) {
10120 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10124 static void textview_delete_line (GtkTextView *text)
10126 GtkTextBuffer *buffer;
10128 GtkTextIter ins, start_iter, end_iter;
10130 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10132 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10133 mark = gtk_text_buffer_get_insert(buffer);
10134 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10137 gtk_text_iter_set_line_offset(&start_iter, 0);
10140 if (gtk_text_iter_ends_line(&end_iter)){
10141 if (!gtk_text_iter_forward_char(&end_iter))
10142 gtk_text_iter_backward_char(&start_iter);
10145 gtk_text_iter_forward_to_line_end(&end_iter);
10146 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10149 static void textview_delete_to_line_end (GtkTextView *text)
10151 GtkTextBuffer *buffer;
10153 GtkTextIter ins, end_iter;
10155 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10157 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10158 mark = gtk_text_buffer_get_insert(buffer);
10159 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10161 if (gtk_text_iter_ends_line(&end_iter))
10162 gtk_text_iter_forward_char(&end_iter);
10164 gtk_text_iter_forward_to_line_end(&end_iter);
10165 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10168 #define DO_ACTION(name, act) { \
10169 if(!strcmp(name, a_name)) { \
10173 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10175 const gchar *a_name = gtk_action_get_name(action);
10176 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10177 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10178 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10179 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10180 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10181 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10182 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10183 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10184 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10185 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10186 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10187 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10188 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10189 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10193 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10195 Compose *compose = (Compose *)data;
10196 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10197 ComposeCallAdvancedAction action = -1;
10199 action = compose_call_advanced_action_from_path(gaction);
10202 void (*do_action) (GtkTextView *text);
10203 } action_table[] = {
10204 {textview_move_beginning_of_line},
10205 {textview_move_forward_character},
10206 {textview_move_backward_character},
10207 {textview_move_forward_word},
10208 {textview_move_backward_word},
10209 {textview_move_end_of_line},
10210 {textview_move_next_line},
10211 {textview_move_previous_line},
10212 {textview_delete_forward_character},
10213 {textview_delete_backward_character},
10214 {textview_delete_forward_word},
10215 {textview_delete_backward_word},
10216 {textview_delete_line},
10217 {textview_delete_to_line_end}
10220 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10222 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10223 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10224 if (action_table[action].do_action)
10225 action_table[action].do_action(text);
10227 g_warning("Not implemented yet.");
10231 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10235 if (GTK_IS_EDITABLE(widget)) {
10236 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10237 gtk_editable_set_position(GTK_EDITABLE(widget),
10240 if (widget->parent && widget->parent->parent
10241 && widget->parent->parent->parent) {
10242 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10243 gint y = widget->allocation.y;
10244 gint height = widget->allocation.height;
10245 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10246 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10248 if (y < (int)shown->value) {
10249 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10251 if (y + height > (int)shown->value + (int)shown->page_size) {
10252 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10253 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10254 y + height - (int)shown->page_size - 1);
10256 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10257 (int)shown->upper - (int)shown->page_size - 1);
10264 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10265 compose->focused_editable = widget;
10267 #ifdef GENERIC_UMPC
10268 if (GTK_IS_TEXT_VIEW(widget)
10269 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10270 g_object_ref(compose->notebook);
10271 g_object_ref(compose->edit_vbox);
10272 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10273 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10274 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10275 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10276 g_object_unref(compose->notebook);
10277 g_object_unref(compose->edit_vbox);
10278 g_signal_handlers_block_by_func(G_OBJECT(widget),
10279 G_CALLBACK(compose_grab_focus_cb),
10281 gtk_widget_grab_focus(widget);
10282 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10283 G_CALLBACK(compose_grab_focus_cb),
10285 } else if (!GTK_IS_TEXT_VIEW(widget)
10286 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10287 g_object_ref(compose->notebook);
10288 g_object_ref(compose->edit_vbox);
10289 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10290 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10291 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10292 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10293 g_object_unref(compose->notebook);
10294 g_object_unref(compose->edit_vbox);
10295 g_signal_handlers_block_by_func(G_OBJECT(widget),
10296 G_CALLBACK(compose_grab_focus_cb),
10298 gtk_widget_grab_focus(widget);
10299 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10300 G_CALLBACK(compose_grab_focus_cb),
10306 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10308 compose->modified = TRUE;
10309 // compose_beautify_paragraph(compose, NULL, TRUE);
10310 #ifndef GENERIC_UMPC
10311 compose_set_title(compose);
10315 static void compose_wrap_cb(GtkAction *action, gpointer data)
10317 Compose *compose = (Compose *)data;
10318 compose_beautify_paragraph(compose, NULL, TRUE);
10321 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10323 Compose *compose = (Compose *)data;
10324 compose_wrap_all_full(compose, TRUE);
10327 static void compose_find_cb(GtkAction *action, gpointer data)
10329 Compose *compose = (Compose *)data;
10331 message_search_compose(compose);
10334 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10337 Compose *compose = (Compose *)data;
10338 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10339 if (compose->autowrap)
10340 compose_wrap_all_full(compose, TRUE);
10341 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10344 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10347 Compose *compose = (Compose *)data;
10348 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10351 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10353 Compose *compose = (Compose *)data;
10355 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10358 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10360 Compose *compose = (Compose *)data;
10362 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10365 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10367 g_free(compose->privacy_system);
10369 compose->privacy_system = g_strdup(account->default_privacy_system);
10370 compose_update_privacy_system_menu_item(compose, warn);
10373 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10375 Compose *compose = (Compose *)data;
10377 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10378 gtk_widget_show(compose->ruler_hbox);
10379 prefs_common.show_ruler = TRUE;
10381 gtk_widget_hide(compose->ruler_hbox);
10382 gtk_widget_queue_resize(compose->edit_vbox);
10383 prefs_common.show_ruler = FALSE;
10387 static void compose_attach_drag_received_cb (GtkWidget *widget,
10388 GdkDragContext *context,
10391 GtkSelectionData *data,
10394 gpointer user_data)
10396 Compose *compose = (Compose *)user_data;
10399 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10401 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10403 ) && gtk_drag_get_source_widget(context) !=
10404 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10405 list = uri_list_extract_filenames((const gchar *)data->data);
10406 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10407 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10408 compose_attach_append
10409 (compose, (const gchar *)tmp->data,
10410 utf8_filename, NULL);
10411 g_free(utf8_filename);
10413 if (list) compose_changed_cb(NULL, compose);
10414 list_free_strings(list);
10416 } else if (gtk_drag_get_source_widget(context)
10417 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10418 /* comes from our summaryview */
10419 SummaryView * summaryview = NULL;
10420 GSList * list = NULL, *cur = NULL;
10422 if (mainwindow_get_mainwindow())
10423 summaryview = mainwindow_get_mainwindow()->summaryview;
10426 list = summary_get_selected_msg_list(summaryview);
10428 for (cur = list; cur; cur = cur->next) {
10429 MsgInfo *msginfo = (MsgInfo *)cur->data;
10430 gchar *file = NULL;
10432 file = procmsg_get_message_file_full(msginfo,
10435 compose_attach_append(compose, (const gchar *)file,
10436 (const gchar *)file, "message/rfc822");
10440 g_slist_free(list);
10444 static gboolean compose_drag_drop(GtkWidget *widget,
10445 GdkDragContext *drag_context,
10447 guint time, gpointer user_data)
10449 /* not handling this signal makes compose_insert_drag_received_cb
10454 static void compose_insert_drag_received_cb (GtkWidget *widget,
10455 GdkDragContext *drag_context,
10458 GtkSelectionData *data,
10461 gpointer user_data)
10463 Compose *compose = (Compose *)user_data;
10466 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10469 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10471 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10473 AlertValue val = G_ALERTDEFAULT;
10475 list = uri_list_extract_filenames((const gchar *)data->data);
10476 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10477 /* Assume a list of no files, and data has ://, is a remote link */
10478 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10479 gchar *tmpfile = get_tmp_file();
10480 str_write_to_file(tmpdata, tmpfile);
10482 compose_insert_file(compose, tmpfile);
10483 claws_unlink(tmpfile);
10485 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10486 compose_beautify_paragraph(compose, NULL, TRUE);
10489 switch (prefs_common.compose_dnd_mode) {
10490 case COMPOSE_DND_ASK:
10491 val = alertpanel_full(_("Insert or attach?"),
10492 _("Do you want to insert the contents of the file(s) "
10493 "into the message body, or attach it to the email?"),
10494 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10495 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10497 case COMPOSE_DND_INSERT:
10498 val = G_ALERTALTERNATE;
10500 case COMPOSE_DND_ATTACH:
10501 val = G_ALERTOTHER;
10504 /* unexpected case */
10505 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10508 if (val & G_ALERTDISABLE) {
10509 val &= ~G_ALERTDISABLE;
10510 /* remember what action to perform by default, only if we don't click Cancel */
10511 if (val == G_ALERTALTERNATE)
10512 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10513 else if (val == G_ALERTOTHER)
10514 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10517 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10518 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10519 list_free_strings(list);
10522 } else if (val == G_ALERTOTHER) {
10523 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10524 list_free_strings(list);
10529 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10530 compose_insert_file(compose, (const gchar *)tmp->data);
10532 list_free_strings(list);
10534 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10537 #if GTK_CHECK_VERSION(2, 8, 0)
10538 /* do nothing, handled by GTK */
10540 gchar *tmpfile = get_tmp_file();
10541 str_write_to_file((const gchar *)data->data, tmpfile);
10542 compose_insert_file(compose, tmpfile);
10543 claws_unlink(tmpfile);
10545 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10549 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10552 static void compose_header_drag_received_cb (GtkWidget *widget,
10553 GdkDragContext *drag_context,
10556 GtkSelectionData *data,
10559 gpointer user_data)
10561 GtkEditable *entry = (GtkEditable *)user_data;
10562 gchar *email = (gchar *)data->data;
10564 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10567 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10568 gchar *decoded=g_new(gchar, strlen(email));
10571 email += strlen("mailto:");
10572 decode_uri(decoded, email); /* will fit */
10573 gtk_editable_delete_text(entry, 0, -1);
10574 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10575 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10579 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10582 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10584 Compose *compose = (Compose *)data;
10586 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10587 compose->return_receipt = TRUE;
10589 compose->return_receipt = FALSE;
10592 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10594 Compose *compose = (Compose *)data;
10596 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10597 compose->remove_references = TRUE;
10599 compose->remove_references = FALSE;
10602 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10603 ComposeHeaderEntry *headerentry)
10605 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10609 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10610 GdkEventKey *event,
10611 ComposeHeaderEntry *headerentry)
10613 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10614 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10615 !(event->state & GDK_MODIFIER_MASK) &&
10616 (event->keyval == GDK_BackSpace) &&
10617 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10618 gtk_container_remove
10619 (GTK_CONTAINER(headerentry->compose->header_table),
10620 headerentry->combo);
10621 gtk_container_remove
10622 (GTK_CONTAINER(headerentry->compose->header_table),
10623 headerentry->entry);
10624 headerentry->compose->header_list =
10625 g_slist_remove(headerentry->compose->header_list,
10627 g_free(headerentry);
10628 } else if (event->keyval == GDK_Tab) {
10629 if (headerentry->compose->header_last == headerentry) {
10630 /* Override default next focus, and give it to subject_entry
10631 * instead of notebook tabs
10633 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10634 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10641 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10642 ComposeHeaderEntry *headerentry)
10644 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10645 compose_create_header_entry(headerentry->compose);
10646 g_signal_handlers_disconnect_matched
10647 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10648 0, 0, NULL, NULL, headerentry);
10650 /* Automatically scroll down */
10651 GTK_EVENTS_FLUSH();
10652 compose_show_first_last_header(headerentry->compose, FALSE);
10658 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10660 GtkAdjustment *vadj;
10662 cm_return_if_fail(compose);
10663 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10664 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10665 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10666 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10667 gtk_adjustment_changed(vadj);
10670 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10671 const gchar *text, gint len, Compose *compose)
10673 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10674 (G_OBJECT(compose->text), "paste_as_quotation"));
10677 cm_return_if_fail(text != NULL);
10679 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10680 G_CALLBACK(text_inserted),
10682 if (paste_as_quotation) {
10684 const gchar *qmark;
10686 GtkTextIter start_iter;
10689 len = strlen(text);
10691 new_text = g_strndup(text, len);
10693 qmark = compose_quote_char_from_context(compose);
10695 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10696 gtk_text_buffer_place_cursor(buffer, iter);
10698 pos = gtk_text_iter_get_offset(iter);
10700 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10701 _("Quote format error at line %d."));
10702 quote_fmt_reset_vartable();
10704 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10705 GINT_TO_POINTER(paste_as_quotation - 1));
10707 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10708 gtk_text_buffer_place_cursor(buffer, iter);
10709 gtk_text_buffer_delete_mark(buffer, mark);
10711 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10712 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10713 compose_beautify_paragraph(compose, &start_iter, FALSE);
10714 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10715 gtk_text_buffer_delete_mark(buffer, mark);
10717 if (strcmp(text, "\n") || compose->automatic_break
10718 || gtk_text_iter_starts_line(iter)) {
10719 GtkTextIter before_ins;
10720 gtk_text_buffer_insert(buffer, iter, text, len);
10721 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10722 before_ins = *iter;
10723 gtk_text_iter_backward_chars(&before_ins, len);
10724 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10727 /* check if the preceding is just whitespace or quote */
10728 GtkTextIter start_line;
10729 gchar *tmp = NULL, *quote = NULL;
10730 gint quote_len = 0, is_normal = 0;
10731 start_line = *iter;
10732 gtk_text_iter_set_line_offset(&start_line, 0);
10733 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10736 if (*tmp == '\0') {
10739 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10747 gtk_text_buffer_insert(buffer, iter, text, len);
10749 gtk_text_buffer_insert_with_tags_by_name(buffer,
10750 iter, text, len, "no_join", NULL);
10755 if (!paste_as_quotation) {
10756 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10757 compose_beautify_paragraph(compose, iter, FALSE);
10758 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10759 gtk_text_buffer_delete_mark(buffer, mark);
10762 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10763 G_CALLBACK(text_inserted),
10765 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10767 if (prefs_common.autosave &&
10768 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10769 compose->draft_timeout_tag != -2 /* disabled while loading */)
10770 compose->draft_timeout_tag = g_timeout_add
10771 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10773 static gint compose_defer_auto_save_draft(Compose *compose)
10775 compose->draft_timeout_tag = -1;
10776 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10781 static void compose_check_all(GtkAction *action, gpointer data)
10783 Compose *compose = (Compose *)data;
10784 if (!compose->gtkaspell)
10787 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10788 claws_spell_entry_check_all(
10789 CLAWS_SPELL_ENTRY(compose->subject_entry));
10791 gtkaspell_check_all(compose->gtkaspell);
10794 static void compose_highlight_all(GtkAction *action, gpointer data)
10796 Compose *compose = (Compose *)data;
10797 if (compose->gtkaspell) {
10798 claws_spell_entry_recheck_all(
10799 CLAWS_SPELL_ENTRY(compose->subject_entry));
10800 gtkaspell_highlight_all(compose->gtkaspell);
10804 static void compose_check_backwards(GtkAction *action, gpointer data)
10806 Compose *compose = (Compose *)data;
10807 if (!compose->gtkaspell) {
10808 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10812 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10813 claws_spell_entry_check_backwards(
10814 CLAWS_SPELL_ENTRY(compose->subject_entry));
10816 gtkaspell_check_backwards(compose->gtkaspell);
10819 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10821 Compose *compose = (Compose *)data;
10822 if (!compose->gtkaspell) {
10823 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10827 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10828 claws_spell_entry_check_forwards_go(
10829 CLAWS_SPELL_ENTRY(compose->subject_entry));
10831 gtkaspell_check_forwards_go(compose->gtkaspell);
10836 *\brief Guess originating forward account from MsgInfo and several
10837 * "common preference" settings. Return NULL if no guess.
10839 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10841 PrefsAccount *account = NULL;
10843 cm_return_val_if_fail(msginfo, NULL);
10844 cm_return_val_if_fail(msginfo->folder, NULL);
10845 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10847 if (msginfo->folder->prefs->enable_default_account)
10848 account = account_find_from_id(msginfo->folder->prefs->default_account);
10851 account = msginfo->folder->folder->account;
10853 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10855 Xstrdup_a(to, msginfo->to, return NULL);
10856 extract_address(to);
10857 account = account_find_from_address(to, FALSE);
10860 if (!account && prefs_common.forward_account_autosel) {
10861 gchar cc[BUFFSIZE];
10862 if (!procheader_get_header_from_msginfo
10863 (msginfo, cc,sizeof cc , "Cc:")) {
10864 gchar *buf = cc + strlen("Cc:");
10865 extract_address(buf);
10866 account = account_find_from_address(buf, FALSE);
10870 if (!account && prefs_common.forward_account_autosel) {
10871 gchar deliveredto[BUFFSIZE];
10872 if (!procheader_get_header_from_msginfo
10873 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10874 gchar *buf = deliveredto + strlen("Delivered-To:");
10875 extract_address(buf);
10876 account = account_find_from_address(buf, FALSE);
10883 gboolean compose_close(Compose *compose)
10887 if (!g_mutex_trylock(compose->mutex)) {
10888 /* we have to wait for the (possibly deferred by auto-save)
10889 * drafting to be done, before destroying the compose under
10891 debug_print("waiting for drafting to finish...\n");
10892 compose_allow_user_actions(compose, FALSE);
10893 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10896 cm_return_val_if_fail(compose, FALSE);
10897 gtkut_widget_get_uposition(compose->window, &x, &y);
10898 if (!compose->batch) {
10899 prefs_common.compose_x = x;
10900 prefs_common.compose_y = y;
10902 g_mutex_unlock(compose->mutex);
10903 compose_destroy(compose);
10908 * Add entry field for each address in list.
10909 * \param compose E-Mail composition object.
10910 * \param listAddress List of (formatted) E-Mail addresses.
10912 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10915 node = listAddress;
10917 addr = ( gchar * ) node->data;
10918 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
10919 node = g_list_next( node );
10923 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10924 guint action, gboolean opening_multiple)
10926 gchar *body = NULL;
10927 GSList *new_msglist = NULL;
10928 MsgInfo *tmp_msginfo = NULL;
10929 gboolean originally_enc = FALSE;
10930 gboolean originally_sig = FALSE;
10931 Compose *compose = NULL;
10932 gchar *s_system = NULL;
10934 cm_return_if_fail(msgview != NULL);
10936 cm_return_if_fail(msginfo_list != NULL);
10938 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10939 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10940 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10942 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10943 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10944 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10945 orig_msginfo, mimeinfo);
10946 if (tmp_msginfo != NULL) {
10947 new_msglist = g_slist_append(NULL, tmp_msginfo);
10949 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10950 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10951 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10953 tmp_msginfo->folder = orig_msginfo->folder;
10954 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10955 if (orig_msginfo->tags) {
10956 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10957 tmp_msginfo->folder->tags_dirty = TRUE;
10963 if (!opening_multiple)
10964 body = messageview_get_selection(msgview);
10967 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10968 procmsg_msginfo_free(tmp_msginfo);
10969 g_slist_free(new_msglist);
10971 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10973 if (compose && originally_enc) {
10974 compose_force_encryption(compose, compose->account, FALSE, s_system);
10977 if (compose && originally_sig && compose->account->default_sign_reply) {
10978 compose_force_signing(compose, compose->account, s_system);
10984 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10987 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10988 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10989 GSList *cur = msginfo_list;
10990 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10991 "messages. Opening the windows "
10992 "could take some time. Do you "
10993 "want to continue?"),
10994 g_slist_length(msginfo_list));
10995 if (g_slist_length(msginfo_list) > 9
10996 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10997 != G_ALERTALTERNATE) {
11002 /* We'll open multiple compose windows */
11003 /* let the WM place the next windows */
11004 compose_force_window_origin = FALSE;
11005 for (; cur; cur = cur->next) {
11007 tmplist.data = cur->data;
11008 tmplist.next = NULL;
11009 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11011 compose_force_window_origin = TRUE;
11013 /* forwarding multiple mails as attachments is done via a
11014 * single compose window */
11015 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11019 void compose_set_position(Compose *compose, gint pos)
11021 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11023 gtkut_text_view_set_position(text, pos);
11026 gboolean compose_search_string(Compose *compose,
11027 const gchar *str, gboolean case_sens)
11029 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11031 return gtkut_text_view_search_string(text, str, case_sens);
11034 gboolean compose_search_string_backward(Compose *compose,
11035 const gchar *str, gboolean case_sens)
11037 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11039 return gtkut_text_view_search_string_backward(text, str, case_sens);
11042 /* allocate a msginfo structure and populate its data from a compose data structure */
11043 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11045 MsgInfo *newmsginfo;
11047 gchar buf[BUFFSIZE];
11049 cm_return_val_if_fail( compose != NULL, NULL );
11051 newmsginfo = procmsg_msginfo_new();
11054 get_rfc822_date(buf, sizeof(buf));
11055 newmsginfo->date = g_strdup(buf);
11058 if (compose->from_name) {
11059 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11060 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11064 if (compose->subject_entry)
11065 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11067 /* to, cc, reply-to, newsgroups */
11068 for (list = compose->header_list; list; list = list->next) {
11069 gchar *header = gtk_editable_get_chars(
11071 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11072 gchar *entry = gtk_editable_get_chars(
11073 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11075 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11076 if ( newmsginfo->to == NULL ) {
11077 newmsginfo->to = g_strdup(entry);
11078 } else if (entry && *entry) {
11079 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11080 g_free(newmsginfo->to);
11081 newmsginfo->to = tmp;
11084 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11085 if ( newmsginfo->cc == NULL ) {
11086 newmsginfo->cc = g_strdup(entry);
11087 } else if (entry && *entry) {
11088 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11089 g_free(newmsginfo->cc);
11090 newmsginfo->cc = tmp;
11093 if ( strcasecmp(header,
11094 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11095 if ( newmsginfo->newsgroups == NULL ) {
11096 newmsginfo->newsgroups = g_strdup(entry);
11097 } else if (entry && *entry) {
11098 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11099 g_free(newmsginfo->newsgroups);
11100 newmsginfo->newsgroups = tmp;
11108 /* other data is unset */
11114 /* update compose's dictionaries from folder dict settings */
11115 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11116 FolderItem *folder_item)
11118 cm_return_if_fail(compose != NULL);
11120 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11121 FolderItemPrefs *prefs = folder_item->prefs;
11123 if (prefs->enable_default_dictionary)
11124 gtkaspell_change_dict(compose->gtkaspell,
11125 prefs->default_dictionary, FALSE);
11126 if (folder_item->prefs->enable_default_alt_dictionary)
11127 gtkaspell_change_alt_dict(compose->gtkaspell,
11128 prefs->default_alt_dictionary);
11129 if (prefs->enable_default_dictionary
11130 || prefs->enable_default_alt_dictionary)
11131 compose_spell_menu_changed(compose);