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();
1118 mfield = SUBJECT_FIELD_PRESENT;
1122 && *body_format != '\0' )
1125 GtkTextBuffer *buffer;
1126 GtkTextIter start, end;
1130 dummyinfo = compose_msginfo_new_from_compose(compose);
1132 text = GTK_TEXT_VIEW(compose->text);
1133 buffer = gtk_text_view_get_buffer(text);
1134 gtk_text_buffer_get_start_iter(buffer, &start);
1135 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1136 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1138 compose_quote_fmt(compose, dummyinfo,
1140 NULL, tmp, FALSE, TRUE,
1141 _("The body of the \"New message\" template has an error at line %d."));
1142 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1143 quote_fmt_reset_vartable();
1147 if (compose->gtkaspell->check_while_typing)
1148 gtkaspell_highlight_all(compose->gtkaspell);
1150 mfield = BODY_FIELD_PRESENT;
1154 procmsg_msginfo_free( dummyinfo );
1160 for (i = 0; i < attach_files->len; i++) {
1161 file = g_ptr_array_index(attach_files, i);
1162 compose_attach_append(compose, file, file, NULL);
1166 compose_show_first_last_header(compose, TRUE);
1168 /* Set save folder */
1169 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1170 gchar *folderidentifier;
1172 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1173 folderidentifier = folder_item_get_identifier(item);
1174 compose_set_save_to(compose, folderidentifier);
1175 g_free(folderidentifier);
1178 /* Place cursor according to provided input (mfield) */
1180 case NO_FIELD_PRESENT:
1181 if (compose->header_last)
1182 gtk_widget_grab_focus(compose->header_last->entry);
1184 case TO_FIELD_PRESENT:
1185 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1187 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1190 gtk_widget_grab_focus(compose->subject_entry);
1192 case SUBJECT_FIELD_PRESENT:
1193 textview = GTK_TEXT_VIEW(compose->text);
1196 textbuf = gtk_text_view_get_buffer(textview);
1199 mark = gtk_text_buffer_get_insert(textbuf);
1200 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1201 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1203 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1204 * only defers where it comes to the variable body
1205 * is not null. If no body is present compose->text
1206 * will be null in which case you cannot place the
1207 * cursor inside the component so. An empty component
1208 * is therefore created before placing the cursor
1210 case BODY_FIELD_PRESENT:
1211 gtk_widget_grab_focus(compose->text);
1215 undo_unblock(compose->undostruct);
1217 if (prefs_common.auto_exteditor)
1218 compose_exec_ext_editor(compose);
1220 compose->draft_timeout_tag = -1;
1221 SCROLL_TO_CURSOR(compose);
1223 compose->modified = FALSE;
1224 compose_set_title(compose);
1228 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1229 gboolean override_pref, const gchar *system)
1231 const gchar *privacy = NULL;
1233 cm_return_if_fail(compose != NULL);
1234 cm_return_if_fail(account != NULL);
1236 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1241 else if (account->default_privacy_system
1242 && strlen(account->default_privacy_system)) {
1243 privacy = account->default_privacy_system;
1245 GSList *privacy_avail = privacy_get_system_ids();
1246 if (privacy_avail && g_slist_length(privacy_avail)) {
1247 privacy = (gchar *)(privacy_avail->data);
1250 if (privacy != NULL) {
1252 g_free(compose->privacy_system);
1253 compose->privacy_system = NULL;
1255 if (compose->privacy_system == NULL)
1256 compose->privacy_system = g_strdup(privacy);
1257 else if (*(compose->privacy_system) == '\0') {
1258 g_free(compose->privacy_system);
1259 compose->privacy_system = g_strdup(privacy);
1261 compose_update_privacy_system_menu_item(compose, FALSE);
1262 compose_use_encryption(compose, TRUE);
1266 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1268 const gchar *privacy = NULL;
1272 else if (account->default_privacy_system
1273 && strlen(account->default_privacy_system)) {
1274 privacy = account->default_privacy_system;
1276 GSList *privacy_avail = privacy_get_system_ids();
1277 if (privacy_avail && g_slist_length(privacy_avail)) {
1278 privacy = (gchar *)(privacy_avail->data);
1282 if (privacy != NULL) {
1284 g_free(compose->privacy_system);
1285 compose->privacy_system = NULL;
1287 if (compose->privacy_system == NULL)
1288 compose->privacy_system = g_strdup(privacy);
1289 compose_update_privacy_system_menu_item(compose, FALSE);
1290 compose_use_signing(compose, TRUE);
1294 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1298 Compose *compose = NULL;
1300 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1302 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1303 cm_return_val_if_fail(msginfo != NULL, NULL);
1305 list_len = g_slist_length(msginfo_list);
1309 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1310 FALSE, prefs_common.default_reply_list, FALSE, body);
1312 case COMPOSE_REPLY_WITH_QUOTE:
1313 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1314 FALSE, prefs_common.default_reply_list, FALSE, body);
1316 case COMPOSE_REPLY_WITHOUT_QUOTE:
1317 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1318 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1320 case COMPOSE_REPLY_TO_SENDER:
1321 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1322 FALSE, FALSE, TRUE, body);
1324 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1325 compose = compose_followup_and_reply_to(msginfo,
1326 COMPOSE_QUOTE_CHECK,
1327 FALSE, FALSE, body);
1329 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1330 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1331 FALSE, FALSE, TRUE, body);
1333 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1334 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1335 FALSE, FALSE, TRUE, NULL);
1337 case COMPOSE_REPLY_TO_ALL:
1338 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1339 TRUE, FALSE, FALSE, body);
1341 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1342 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1343 TRUE, FALSE, FALSE, body);
1345 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1346 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1347 TRUE, FALSE, FALSE, NULL);
1349 case COMPOSE_REPLY_TO_LIST:
1350 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1351 FALSE, TRUE, FALSE, body);
1353 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1354 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1355 FALSE, TRUE, FALSE, body);
1357 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1358 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1359 FALSE, TRUE, FALSE, NULL);
1361 case COMPOSE_FORWARD:
1362 if (prefs_common.forward_as_attachment) {
1363 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1366 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1370 case COMPOSE_FORWARD_INLINE:
1371 /* check if we reply to more than one Message */
1372 if (list_len == 1) {
1373 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1376 /* more messages FALL THROUGH */
1377 case COMPOSE_FORWARD_AS_ATTACH:
1378 compose = compose_forward_multiple(NULL, msginfo_list);
1380 case COMPOSE_REDIRECT:
1381 compose = compose_redirect(NULL, msginfo, FALSE);
1384 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1387 if (compose == NULL) {
1388 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1392 compose->rmode = mode;
1393 switch (compose->rmode) {
1395 case COMPOSE_REPLY_WITH_QUOTE:
1396 case COMPOSE_REPLY_WITHOUT_QUOTE:
1397 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1398 debug_print("reply mode Normal\n");
1399 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1400 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1402 case COMPOSE_REPLY_TO_SENDER:
1403 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1404 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1405 debug_print("reply mode Sender\n");
1406 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1408 case COMPOSE_REPLY_TO_ALL:
1409 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1410 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1411 debug_print("reply mode All\n");
1412 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1414 case COMPOSE_REPLY_TO_LIST:
1415 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1416 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1417 debug_print("reply mode List\n");
1418 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1426 static Compose *compose_reply(MsgInfo *msginfo,
1427 ComposeQuoteMode quote_mode,
1433 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1434 to_sender, FALSE, body);
1437 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1438 ComposeQuoteMode quote_mode,
1443 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1444 to_sender, TRUE, body);
1447 static void compose_extract_original_charset(Compose *compose)
1449 MsgInfo *info = NULL;
1450 if (compose->replyinfo) {
1451 info = compose->replyinfo;
1452 } else if (compose->fwdinfo) {
1453 info = compose->fwdinfo;
1454 } else if (compose->targetinfo) {
1455 info = compose->targetinfo;
1458 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1459 MimeInfo *partinfo = mimeinfo;
1460 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1461 partinfo = procmime_mimeinfo_next(partinfo);
1463 compose->orig_charset =
1464 g_strdup(procmime_mimeinfo_get_parameter(
1465 partinfo, "charset"));
1467 procmime_mimeinfo_free_all(mimeinfo);
1471 #define SIGNAL_BLOCK(buffer) { \
1472 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1473 G_CALLBACK(compose_changed_cb), \
1475 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1476 G_CALLBACK(text_inserted), \
1480 #define SIGNAL_UNBLOCK(buffer) { \
1481 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1482 G_CALLBACK(compose_changed_cb), \
1484 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1485 G_CALLBACK(text_inserted), \
1489 static Compose *compose_generic_reply(MsgInfo *msginfo,
1490 ComposeQuoteMode quote_mode,
1491 gboolean to_all, gboolean to_ml,
1493 gboolean followup_and_reply_to,
1497 PrefsAccount *account = NULL;
1498 GtkTextView *textview;
1499 GtkTextBuffer *textbuf;
1500 gboolean quote = FALSE;
1501 const gchar *qmark = NULL;
1502 const gchar *body_fmt = NULL;
1503 gchar *s_system = NULL;
1505 cm_return_val_if_fail(msginfo != NULL, NULL);
1506 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1508 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1510 cm_return_val_if_fail(account != NULL, NULL);
1512 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1514 compose->updating = TRUE;
1516 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1517 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1519 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1520 if (!compose->replyinfo)
1521 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1523 compose_extract_original_charset(compose);
1525 if (msginfo->folder && msginfo->folder->ret_rcpt)
1526 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1528 /* Set save folder */
1529 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1530 gchar *folderidentifier;
1532 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1533 folderidentifier = folder_item_get_identifier(msginfo->folder);
1534 compose_set_save_to(compose, folderidentifier);
1535 g_free(folderidentifier);
1538 if (compose_parse_header(compose, msginfo) < 0) {
1539 compose->updating = FALSE;
1540 compose_destroy(compose);
1544 /* override from name according to folder properties */
1545 if (msginfo->folder && msginfo->folder->prefs &&
1546 msginfo->folder->prefs->reply_with_format &&
1547 msginfo->folder->prefs->reply_override_from_format &&
1548 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1553 /* decode \-escape sequences in the internal representation of the quote format */
1554 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1555 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1558 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1559 compose->gtkaspell);
1561 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1563 quote_fmt_scan_string(tmp);
1566 buf = quote_fmt_get_buffer();
1568 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1570 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1571 quote_fmt_reset_vartable();
1576 textview = (GTK_TEXT_VIEW(compose->text));
1577 textbuf = gtk_text_view_get_buffer(textview);
1578 compose_create_tags(textview, compose);
1580 undo_block(compose->undostruct);
1582 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1585 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1586 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1587 /* use the reply format of folder (if enabled), or the account's one
1588 (if enabled) or fallback to the global reply format, which is always
1589 enabled (even if empty), and use the relevant quotemark */
1591 if (msginfo->folder && msginfo->folder->prefs &&
1592 msginfo->folder->prefs->reply_with_format) {
1593 qmark = msginfo->folder->prefs->reply_quotemark;
1594 body_fmt = msginfo->folder->prefs->reply_body_format;
1596 } else if (account->reply_with_format) {
1597 qmark = account->reply_quotemark;
1598 body_fmt = account->reply_body_format;
1601 qmark = prefs_common.quotemark;
1602 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1603 body_fmt = gettext(prefs_common.quotefmt);
1610 /* empty quotemark is not allowed */
1611 if (qmark == NULL || *qmark == '\0')
1613 compose_quote_fmt(compose, compose->replyinfo,
1614 body_fmt, qmark, body, FALSE, TRUE,
1615 _("The body of the \"Reply\" template has an error at line %d."));
1616 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1617 quote_fmt_reset_vartable();
1619 if (compose->gtkaspell->check_while_typing)
1620 gtkaspell_highlight_all(compose->gtkaspell);
1624 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1625 compose_force_encryption(compose, account, FALSE, s_system);
1628 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1629 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1630 compose_force_signing(compose, account, s_system);
1634 SIGNAL_BLOCK(textbuf);
1636 if (account->auto_sig)
1637 compose_insert_sig(compose, FALSE);
1639 compose_wrap_all(compose);
1641 SIGNAL_UNBLOCK(textbuf);
1643 gtk_widget_grab_focus(compose->text);
1645 undo_unblock(compose->undostruct);
1647 if (prefs_common.auto_exteditor)
1648 compose_exec_ext_editor(compose);
1650 compose->modified = FALSE;
1651 compose_set_title(compose);
1653 compose->updating = FALSE;
1654 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1655 SCROLL_TO_CURSOR(compose);
1657 if (compose->deferred_destroy) {
1658 compose_destroy(compose);
1665 #define INSERT_FW_HEADER(var, hdr) \
1666 if (msginfo->var && *msginfo->var) { \
1667 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1668 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1669 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1672 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1673 gboolean as_attach, const gchar *body,
1674 gboolean no_extedit,
1678 GtkTextView *textview;
1679 GtkTextBuffer *textbuf;
1682 cm_return_val_if_fail(msginfo != NULL, NULL);
1683 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1686 !(account = compose_guess_forward_account_from_msginfo
1688 account = cur_account;
1690 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1692 compose->updating = TRUE;
1693 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1694 if (!compose->fwdinfo)
1695 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1697 compose_extract_original_charset(compose);
1699 if (msginfo->subject && *msginfo->subject) {
1700 gchar *buf, *buf2, *p;
1702 buf = p = g_strdup(msginfo->subject);
1703 p += subject_get_prefix_length(p);
1704 memmove(buf, p, strlen(p) + 1);
1706 buf2 = g_strdup_printf("Fw: %s", buf);
1707 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1713 /* override from name according to folder properties */
1714 if (msginfo->folder && msginfo->folder->prefs &&
1715 msginfo->folder->prefs->forward_with_format &&
1716 msginfo->folder->prefs->forward_override_from_format &&
1717 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1721 MsgInfo *full_msginfo = NULL;
1724 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1726 full_msginfo = procmsg_msginfo_copy(msginfo);
1728 /* decode \-escape sequences in the internal representation of the quote format */
1729 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1730 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1733 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1734 compose->gtkaspell);
1736 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1738 quote_fmt_scan_string(tmp);
1741 buf = quote_fmt_get_buffer();
1743 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1745 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1746 quote_fmt_reset_vartable();
1749 procmsg_msginfo_free(full_msginfo);
1752 textview = GTK_TEXT_VIEW(compose->text);
1753 textbuf = gtk_text_view_get_buffer(textview);
1754 compose_create_tags(textview, compose);
1756 undo_block(compose->undostruct);
1760 msgfile = procmsg_get_message_file(msginfo);
1761 if (!is_file_exist(msgfile))
1762 g_warning("%s: file not exist\n", msgfile);
1764 compose_attach_append(compose, msgfile, msgfile,
1769 const gchar *qmark = NULL;
1770 const gchar *body_fmt = NULL;
1771 MsgInfo *full_msginfo;
1773 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1774 body_fmt = gettext(prefs_common.fw_quotefmt);
1778 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1780 full_msginfo = procmsg_msginfo_copy(msginfo);
1782 /* use the forward format of folder (if enabled), or the account's one
1783 (if enabled) or fallback to the global forward format, which is always
1784 enabled (even if empty), and use the relevant quotemark */
1785 if (msginfo->folder && msginfo->folder->prefs &&
1786 msginfo->folder->prefs->forward_with_format) {
1787 qmark = msginfo->folder->prefs->forward_quotemark;
1788 body_fmt = msginfo->folder->prefs->forward_body_format;
1790 } else if (account->forward_with_format) {
1791 qmark = account->forward_quotemark;
1792 body_fmt = account->forward_body_format;
1795 qmark = prefs_common.fw_quotemark;
1796 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1797 body_fmt = gettext(prefs_common.fw_quotefmt);
1802 /* empty quotemark is not allowed */
1803 if (qmark == NULL || *qmark == '\0')
1806 compose_quote_fmt(compose, full_msginfo,
1807 body_fmt, qmark, body, FALSE, TRUE,
1808 _("The body of the \"Forward\" template has an error at line %d."));
1809 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1810 quote_fmt_reset_vartable();
1811 compose_attach_parts(compose, msginfo);
1813 procmsg_msginfo_free(full_msginfo);
1815 if (compose->gtkaspell->check_while_typing)
1816 gtkaspell_highlight_all(compose->gtkaspell);
1820 SIGNAL_BLOCK(textbuf);
1822 if (account->auto_sig)
1823 compose_insert_sig(compose, FALSE);
1825 compose_wrap_all(compose);
1827 SIGNAL_UNBLOCK(textbuf);
1829 gtk_text_buffer_get_start_iter(textbuf, &iter);
1830 gtk_text_buffer_place_cursor(textbuf, &iter);
1832 gtk_widget_grab_focus(compose->header_last->entry);
1834 if (!no_extedit && prefs_common.auto_exteditor)
1835 compose_exec_ext_editor(compose);
1838 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1839 gchar *folderidentifier;
1841 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1842 folderidentifier = folder_item_get_identifier(msginfo->folder);
1843 compose_set_save_to(compose, folderidentifier);
1844 g_free(folderidentifier);
1847 undo_unblock(compose->undostruct);
1849 compose->modified = FALSE;
1850 compose_set_title(compose);
1852 compose->updating = FALSE;
1853 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1854 SCROLL_TO_CURSOR(compose);
1856 if (compose->deferred_destroy) {
1857 compose_destroy(compose);
1864 #undef INSERT_FW_HEADER
1866 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1869 GtkTextView *textview;
1870 GtkTextBuffer *textbuf;
1874 gboolean single_mail = TRUE;
1876 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1878 if (g_slist_length(msginfo_list) > 1)
1879 single_mail = FALSE;
1881 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1882 if (((MsgInfo *)msginfo->data)->folder == NULL)
1885 /* guess account from first selected message */
1887 !(account = compose_guess_forward_account_from_msginfo
1888 (msginfo_list->data)))
1889 account = cur_account;
1891 cm_return_val_if_fail(account != NULL, NULL);
1893 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1894 if (msginfo->data) {
1895 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1896 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1900 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1901 g_warning("no msginfo_list");
1905 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1907 compose->updating = TRUE;
1909 /* override from name according to folder properties */
1910 if (msginfo_list->data) {
1911 MsgInfo *msginfo = msginfo_list->data;
1913 if (msginfo->folder && msginfo->folder->prefs &&
1914 msginfo->folder->prefs->forward_with_format &&
1915 msginfo->folder->prefs->forward_override_from_format &&
1916 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1921 /* decode \-escape sequences in the internal representation of the quote format */
1922 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1923 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1926 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1927 compose->gtkaspell);
1929 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1931 quote_fmt_scan_string(tmp);
1934 buf = quote_fmt_get_buffer();
1936 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1938 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1939 quote_fmt_reset_vartable();
1945 textview = GTK_TEXT_VIEW(compose->text);
1946 textbuf = gtk_text_view_get_buffer(textview);
1947 compose_create_tags(textview, compose);
1949 undo_block(compose->undostruct);
1950 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1951 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1953 if (!is_file_exist(msgfile))
1954 g_warning("%s: file not exist\n", msgfile);
1956 compose_attach_append(compose, msgfile, msgfile,
1962 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1963 if (info->subject && *info->subject) {
1964 gchar *buf, *buf2, *p;
1966 buf = p = g_strdup(info->subject);
1967 p += subject_get_prefix_length(p);
1968 memmove(buf, p, strlen(p) + 1);
1970 buf2 = g_strdup_printf("Fw: %s", buf);
1971 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1977 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1978 _("Fw: multiple emails"));
1981 SIGNAL_BLOCK(textbuf);
1983 if (account->auto_sig)
1984 compose_insert_sig(compose, FALSE);
1986 compose_wrap_all(compose);
1988 SIGNAL_UNBLOCK(textbuf);
1990 gtk_text_buffer_get_start_iter(textbuf, &iter);
1991 gtk_text_buffer_place_cursor(textbuf, &iter);
1993 gtk_widget_grab_focus(compose->header_last->entry);
1994 undo_unblock(compose->undostruct);
1995 compose->modified = FALSE;
1996 compose_set_title(compose);
1998 compose->updating = FALSE;
1999 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2000 SCROLL_TO_CURSOR(compose);
2002 if (compose->deferred_destroy) {
2003 compose_destroy(compose);
2010 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2012 GtkTextIter start = *iter;
2013 GtkTextIter end_iter;
2014 int start_pos = gtk_text_iter_get_offset(&start);
2016 if (!compose->account->sig_sep)
2019 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2020 start_pos+strlen(compose->account->sig_sep));
2022 /* check sig separator */
2023 str = gtk_text_iter_get_text(&start, &end_iter);
2024 if (!strcmp(str, compose->account->sig_sep)) {
2026 /* check end of line (\n) */
2027 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2028 start_pos+strlen(compose->account->sig_sep));
2029 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2030 start_pos+strlen(compose->account->sig_sep)+1);
2031 tmp = gtk_text_iter_get_text(&start, &end_iter);
2032 if (!strcmp(tmp,"\n")) {
2044 static void compose_colorize_signature(Compose *compose)
2046 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2048 GtkTextIter end_iter;
2049 gtk_text_buffer_get_start_iter(buffer, &iter);
2050 while (gtk_text_iter_forward_line(&iter))
2051 if (compose_is_sig_separator(compose, buffer, &iter)) {
2052 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2053 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2057 #define BLOCK_WRAP() { \
2058 prev_autowrap = compose->autowrap; \
2059 buffer = gtk_text_view_get_buffer( \
2060 GTK_TEXT_VIEW(compose->text)); \
2061 compose->autowrap = FALSE; \
2063 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2064 G_CALLBACK(compose_changed_cb), \
2066 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2067 G_CALLBACK(text_inserted), \
2070 #define UNBLOCK_WRAP() { \
2071 compose->autowrap = prev_autowrap; \
2072 if (compose->autowrap) { \
2073 gint old = compose->draft_timeout_tag; \
2074 compose->draft_timeout_tag = -2; \
2075 compose_wrap_all(compose); \
2076 compose->draft_timeout_tag = old; \
2079 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2080 G_CALLBACK(compose_changed_cb), \
2082 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2083 G_CALLBACK(text_inserted), \
2087 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2089 Compose *compose = NULL;
2090 PrefsAccount *account = NULL;
2091 GtkTextView *textview;
2092 GtkTextBuffer *textbuf;
2096 gchar buf[BUFFSIZE];
2097 gboolean use_signing = FALSE;
2098 gboolean use_encryption = FALSE;
2099 gchar *privacy_system = NULL;
2100 int priority = PRIORITY_NORMAL;
2101 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2102 gboolean autowrap = prefs_common.autowrap;
2103 gboolean autoindent = prefs_common.auto_indent;
2105 cm_return_val_if_fail(msginfo != NULL, NULL);
2106 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2108 if (compose_put_existing_to_front(msginfo)) {
2112 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2113 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2114 gchar queueheader_buf[BUFFSIZE];
2117 /* Select Account from queue headers */
2118 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2119 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2120 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2121 account = account_find_from_id(id);
2123 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2124 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2125 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2126 account = account_find_from_id(id);
2128 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2129 sizeof(queueheader_buf), "NAID:")) {
2130 id = atoi(&queueheader_buf[strlen("NAID:")]);
2131 account = account_find_from_id(id);
2133 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2134 sizeof(queueheader_buf), "MAID:")) {
2135 id = atoi(&queueheader_buf[strlen("MAID:")]);
2136 account = account_find_from_id(id);
2138 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2139 sizeof(queueheader_buf), "S:")) {
2140 account = account_find_from_address(queueheader_buf, FALSE);
2142 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2143 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2144 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2145 use_signing = param;
2148 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2149 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2150 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2151 use_signing = param;
2154 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2155 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2156 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2157 use_encryption = param;
2159 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2160 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2161 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2162 use_encryption = param;
2164 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2165 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2166 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2169 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2170 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2171 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2174 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2175 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2176 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2178 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2179 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2180 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2182 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2183 sizeof(queueheader_buf), "X-Priority: ")) {
2184 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2187 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2188 sizeof(queueheader_buf), "RMID:")) {
2189 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2190 if (tokens[0] && tokens[1] && tokens[2]) {
2191 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2192 if (orig_item != NULL) {
2193 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2198 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2199 sizeof(queueheader_buf), "FMID:")) {
2200 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2201 if (tokens[0] && tokens[1] && tokens[2]) {
2202 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2203 if (orig_item != NULL) {
2204 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2210 account = msginfo->folder->folder->account;
2213 if (!account && prefs_common.reedit_account_autosel) {
2214 gchar from[BUFFSIZE];
2215 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2216 extract_address(from);
2217 account = account_find_from_address(from, FALSE);
2221 account = cur_account;
2223 cm_return_val_if_fail(account != NULL, NULL);
2225 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2227 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2228 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2229 compose->autowrap = autowrap;
2230 compose->replyinfo = replyinfo;
2231 compose->fwdinfo = fwdinfo;
2233 compose->updating = TRUE;
2234 compose->priority = priority;
2236 if (privacy_system != NULL) {
2237 compose->privacy_system = privacy_system;
2238 compose_use_signing(compose, use_signing);
2239 compose_use_encryption(compose, use_encryption);
2240 compose_update_privacy_system_menu_item(compose, FALSE);
2242 activate_privacy_system(compose, account, FALSE);
2245 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2247 compose_extract_original_charset(compose);
2249 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2250 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2251 gchar queueheader_buf[BUFFSIZE];
2253 /* Set message save folder */
2254 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2255 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2256 compose_set_save_to(compose, &queueheader_buf[4]);
2258 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2259 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2261 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2266 if (compose_parse_header(compose, msginfo) < 0) {
2267 compose->updating = FALSE;
2268 compose_destroy(compose);
2271 compose_reedit_set_entry(compose, msginfo);
2273 textview = GTK_TEXT_VIEW(compose->text);
2274 textbuf = gtk_text_view_get_buffer(textview);
2275 compose_create_tags(textview, compose);
2277 mark = gtk_text_buffer_get_insert(textbuf);
2278 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2280 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2281 G_CALLBACK(compose_changed_cb),
2284 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2285 fp = procmime_get_first_encrypted_text_content(msginfo);
2287 compose_force_encryption(compose, account, TRUE, NULL);
2290 fp = procmime_get_first_text_content(msginfo);
2293 g_warning("Can't get text part\n");
2297 gboolean prev_autowrap = compose->autowrap;
2298 GtkTextBuffer *buffer = textbuf;
2300 while (fgets(buf, sizeof(buf), fp) != NULL) {
2302 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2308 compose_attach_parts(compose, msginfo);
2310 compose_colorize_signature(compose);
2312 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2313 G_CALLBACK(compose_changed_cb),
2316 gtk_widget_grab_focus(compose->text);
2318 if (prefs_common.auto_exteditor) {
2319 compose_exec_ext_editor(compose);
2321 compose->modified = FALSE;
2322 compose_set_title(compose);
2324 compose->updating = FALSE;
2325 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2326 SCROLL_TO_CURSOR(compose);
2328 if (compose->deferred_destroy) {
2329 compose_destroy(compose);
2333 compose->sig_str = account_get_signature_str(compose->account);
2338 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2345 cm_return_val_if_fail(msginfo != NULL, NULL);
2348 account = account_get_reply_account(msginfo,
2349 prefs_common.reply_account_autosel);
2350 cm_return_val_if_fail(account != NULL, NULL);
2352 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2354 compose->updating = TRUE;
2356 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2357 compose->replyinfo = NULL;
2358 compose->fwdinfo = NULL;
2360 compose_show_first_last_header(compose, TRUE);
2362 gtk_widget_grab_focus(compose->header_last->entry);
2364 filename = procmsg_get_message_file(msginfo);
2366 if (filename == NULL) {
2367 compose->updating = FALSE;
2368 compose_destroy(compose);
2373 compose->redirect_filename = filename;
2375 /* Set save folder */
2376 item = msginfo->folder;
2377 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2378 gchar *folderidentifier;
2380 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2381 folderidentifier = folder_item_get_identifier(item);
2382 compose_set_save_to(compose, folderidentifier);
2383 g_free(folderidentifier);
2386 compose_attach_parts(compose, msginfo);
2388 if (msginfo->subject)
2389 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2391 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2393 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2394 _("The body of the \"Redirect\" template has an error at line %d."));
2395 quote_fmt_reset_vartable();
2396 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2398 compose_colorize_signature(compose);
2401 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2402 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2403 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2405 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2406 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2407 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2408 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2409 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2410 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2411 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2412 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2414 if (compose->toolbar->draft_btn)
2415 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2416 if (compose->toolbar->insert_btn)
2417 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2418 if (compose->toolbar->attach_btn)
2419 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2420 if (compose->toolbar->sig_btn)
2421 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2422 if (compose->toolbar->exteditor_btn)
2423 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2424 if (compose->toolbar->linewrap_current_btn)
2425 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2426 if (compose->toolbar->linewrap_all_btn)
2427 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2429 compose->modified = FALSE;
2430 compose_set_title(compose);
2431 compose->updating = FALSE;
2432 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2433 SCROLL_TO_CURSOR(compose);
2435 if (compose->deferred_destroy) {
2436 compose_destroy(compose);
2443 GList *compose_get_compose_list(void)
2445 return compose_list;
2448 void compose_entry_append(Compose *compose, const gchar *address,
2449 ComposeEntryType type, ComposePrefType pref_type)
2451 const gchar *header;
2453 gboolean in_quote = FALSE;
2454 if (!address || *address == '\0') return;
2461 header = N_("Bcc:");
2463 case COMPOSE_REPLYTO:
2464 header = N_("Reply-To:");
2466 case COMPOSE_NEWSGROUPS:
2467 header = N_("Newsgroups:");
2469 case COMPOSE_FOLLOWUPTO:
2470 header = N_( "Followup-To:");
2477 header = prefs_common_translated_header_name(header);
2479 cur = begin = (gchar *)address;
2481 /* we separate the line by commas, but not if we're inside a quoted
2483 while (*cur != '\0') {
2485 in_quote = !in_quote;
2486 if (*cur == ',' && !in_quote) {
2487 gchar *tmp = g_strdup(begin);
2489 tmp[cur-begin]='\0';
2492 while (*tmp == ' ' || *tmp == '\t')
2494 compose_add_header_entry(compose, header, tmp, pref_type);
2501 gchar *tmp = g_strdup(begin);
2503 tmp[cur-begin]='\0';
2506 while (*tmp == ' ' || *tmp == '\t')
2508 compose_add_header_entry(compose, header, tmp, pref_type);
2513 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2515 static GdkColor yellow;
2516 static GdkColor black;
2517 static gboolean yellow_initialised = FALSE;
2521 if (!yellow_initialised) {
2522 gdk_color_parse("#f5f6be", &yellow);
2523 gdk_color_parse("#000000", &black);
2524 yellow_initialised = gdk_colormap_alloc_color(
2525 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2526 yellow_initialised &= gdk_colormap_alloc_color(
2527 gdk_colormap_get_system(), &black, FALSE, TRUE);
2530 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2531 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2532 if (gtk_entry_get_text(entry) &&
2533 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2534 if (yellow_initialised) {
2535 gtk_widget_modify_base(
2536 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2537 GTK_STATE_NORMAL, &yellow);
2538 gtk_widget_modify_text(
2539 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2540 GTK_STATE_NORMAL, &black);
2546 void compose_toolbar_cb(gint action, gpointer data)
2548 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2549 Compose *compose = (Compose*)toolbar_item->parent;
2551 cm_return_if_fail(compose != NULL);
2555 compose_send_cb(NULL, compose);
2558 compose_send_later_cb(NULL, compose);
2561 compose_draft(compose, COMPOSE_QUIT_EDITING);
2564 compose_insert_file_cb(NULL, compose);
2567 compose_attach_cb(NULL, compose);
2570 compose_insert_sig(compose, FALSE);
2573 compose_ext_editor_cb(NULL, compose);
2575 case A_LINEWRAP_CURRENT:
2576 compose_beautify_paragraph(compose, NULL, TRUE);
2578 case A_LINEWRAP_ALL:
2579 compose_wrap_all_full(compose, TRUE);
2582 compose_address_cb(NULL, compose);
2585 case A_CHECK_SPELLING:
2586 compose_check_all(NULL, compose);
2594 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2599 gchar *subject = NULL;
2603 gchar **attach = NULL;
2604 MailField mfield = NO_FIELD_PRESENT;
2606 /* get mailto parts but skip from */
2607 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2610 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2611 mfield = TO_FIELD_PRESENT;
2614 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2616 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2618 if (!g_utf8_validate (subject, -1, NULL)) {
2619 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2620 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2623 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2625 mfield = SUBJECT_FIELD_PRESENT;
2628 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2629 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2632 gboolean prev_autowrap = compose->autowrap;
2634 compose->autowrap = FALSE;
2636 mark = gtk_text_buffer_get_insert(buffer);
2637 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2639 if (!g_utf8_validate (body, -1, NULL)) {
2640 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2641 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2644 gtk_text_buffer_insert(buffer, &iter, body, -1);
2646 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2648 compose->autowrap = prev_autowrap;
2649 if (compose->autowrap)
2650 compose_wrap_all(compose);
2651 mfield = BODY_FIELD_PRESENT;
2655 gint i = 0, att = 0;
2656 gchar *warn_files = NULL;
2657 while (attach[i] != NULL) {
2658 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2659 if (utf8_filename) {
2660 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2661 gchar *tmp = g_strdup_printf("%s%s\n",
2662 warn_files?warn_files:"",
2668 g_free(utf8_filename);
2670 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2675 alertpanel_notice(ngettext(
2676 "The following file has been attached: \n%s",
2677 "The following files have been attached: \n%s", att), warn_files);
2691 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2693 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2694 {"Cc:", NULL, TRUE},
2695 {"References:", NULL, FALSE},
2696 {"Bcc:", NULL, TRUE},
2697 {"Newsgroups:", NULL, TRUE},
2698 {"Followup-To:", NULL, TRUE},
2699 {"List-Post:", NULL, FALSE},
2700 {"X-Priority:", NULL, FALSE},
2701 {NULL, NULL, FALSE}};
2717 cm_return_val_if_fail(msginfo != NULL, -1);
2719 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2720 procheader_get_header_fields(fp, hentry);
2723 if (hentry[H_REPLY_TO].body != NULL) {
2724 if (hentry[H_REPLY_TO].body[0] != '\0') {
2726 conv_unmime_header(hentry[H_REPLY_TO].body,
2729 g_free(hentry[H_REPLY_TO].body);
2730 hentry[H_REPLY_TO].body = NULL;
2732 if (hentry[H_CC].body != NULL) {
2733 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2734 g_free(hentry[H_CC].body);
2735 hentry[H_CC].body = NULL;
2737 if (hentry[H_REFERENCES].body != NULL) {
2738 if (compose->mode == COMPOSE_REEDIT)
2739 compose->references = hentry[H_REFERENCES].body;
2741 compose->references = compose_parse_references
2742 (hentry[H_REFERENCES].body, msginfo->msgid);
2743 g_free(hentry[H_REFERENCES].body);
2745 hentry[H_REFERENCES].body = NULL;
2747 if (hentry[H_BCC].body != NULL) {
2748 if (compose->mode == COMPOSE_REEDIT)
2750 conv_unmime_header(hentry[H_BCC].body, NULL);
2751 g_free(hentry[H_BCC].body);
2752 hentry[H_BCC].body = NULL;
2754 if (hentry[H_NEWSGROUPS].body != NULL) {
2755 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2756 hentry[H_NEWSGROUPS].body = NULL;
2758 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2759 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2760 compose->followup_to =
2761 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2764 g_free(hentry[H_FOLLOWUP_TO].body);
2765 hentry[H_FOLLOWUP_TO].body = NULL;
2767 if (hentry[H_LIST_POST].body != NULL) {
2768 gchar *to = NULL, *start = NULL;
2770 extract_address(hentry[H_LIST_POST].body);
2771 if (hentry[H_LIST_POST].body[0] != '\0') {
2772 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2774 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2775 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2778 g_free(compose->ml_post);
2779 compose->ml_post = to;
2782 g_free(hentry[H_LIST_POST].body);
2783 hentry[H_LIST_POST].body = NULL;
2786 /* CLAWS - X-Priority */
2787 if (compose->mode == COMPOSE_REEDIT)
2788 if (hentry[H_X_PRIORITY].body != NULL) {
2791 priority = atoi(hentry[H_X_PRIORITY].body);
2792 g_free(hentry[H_X_PRIORITY].body);
2794 hentry[H_X_PRIORITY].body = NULL;
2796 if (priority < PRIORITY_HIGHEST ||
2797 priority > PRIORITY_LOWEST)
2798 priority = PRIORITY_NORMAL;
2800 compose->priority = priority;
2803 if (compose->mode == COMPOSE_REEDIT) {
2804 if (msginfo->inreplyto && *msginfo->inreplyto)
2805 compose->inreplyto = g_strdup(msginfo->inreplyto);
2809 if (msginfo->msgid && *msginfo->msgid)
2810 compose->inreplyto = g_strdup(msginfo->msgid);
2812 if (!compose->references) {
2813 if (msginfo->msgid && *msginfo->msgid) {
2814 if (msginfo->inreplyto && *msginfo->inreplyto)
2815 compose->references =
2816 g_strdup_printf("<%s>\n\t<%s>",
2820 compose->references =
2821 g_strconcat("<", msginfo->msgid, ">",
2823 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2824 compose->references =
2825 g_strconcat("<", msginfo->inreplyto, ">",
2833 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2835 GSList *ref_id_list, *cur;
2839 ref_id_list = references_list_append(NULL, ref);
2840 if (!ref_id_list) return NULL;
2841 if (msgid && *msgid)
2842 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2847 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2848 /* "<" + Message-ID + ">" + CR+LF+TAB */
2849 len += strlen((gchar *)cur->data) + 5;
2851 if (len > MAX_REFERENCES_LEN) {
2852 /* remove second message-ID */
2853 if (ref_id_list && ref_id_list->next &&
2854 ref_id_list->next->next) {
2855 g_free(ref_id_list->next->data);
2856 ref_id_list = g_slist_remove
2857 (ref_id_list, ref_id_list->next->data);
2859 slist_free_strings(ref_id_list);
2860 g_slist_free(ref_id_list);
2867 new_ref = g_string_new("");
2868 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2869 if (new_ref->len > 0)
2870 g_string_append(new_ref, "\n\t");
2871 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2874 slist_free_strings(ref_id_list);
2875 g_slist_free(ref_id_list);
2877 new_ref_str = new_ref->str;
2878 g_string_free(new_ref, FALSE);
2883 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2884 const gchar *fmt, const gchar *qmark,
2885 const gchar *body, gboolean rewrap,
2886 gboolean need_unescape,
2887 const gchar *err_msg)
2889 MsgInfo* dummyinfo = NULL;
2890 gchar *quote_str = NULL;
2892 gboolean prev_autowrap;
2893 const gchar *trimmed_body = body;
2894 gint cursor_pos = -1;
2895 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2896 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2901 SIGNAL_BLOCK(buffer);
2904 dummyinfo = compose_msginfo_new_from_compose(compose);
2905 msginfo = dummyinfo;
2908 if (qmark != NULL) {
2910 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2911 compose->gtkaspell);
2913 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2915 quote_fmt_scan_string(qmark);
2918 buf = quote_fmt_get_buffer();
2920 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2922 Xstrdup_a(quote_str, buf, goto error)
2925 if (fmt && *fmt != '\0') {
2928 while (*trimmed_body == '\n')
2932 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2933 compose->gtkaspell);
2935 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2937 if (need_unescape) {
2940 /* decode \-escape sequences in the internal representation of the quote format */
2941 tmp = malloc(strlen(fmt)+1);
2942 pref_get_unescaped_pref(tmp, fmt);
2943 quote_fmt_scan_string(tmp);
2947 quote_fmt_scan_string(fmt);
2951 buf = quote_fmt_get_buffer();
2953 gint line = quote_fmt_get_line();
2954 alertpanel_error(err_msg, line);
2960 prev_autowrap = compose->autowrap;
2961 compose->autowrap = FALSE;
2963 mark = gtk_text_buffer_get_insert(buffer);
2964 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2965 if (g_utf8_validate(buf, -1, NULL)) {
2966 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2968 gchar *tmpout = NULL;
2969 tmpout = conv_codeset_strdup
2970 (buf, conv_get_locale_charset_str_no_utf8(),
2972 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2974 tmpout = g_malloc(strlen(buf)*2+1);
2975 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2977 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2981 cursor_pos = quote_fmt_get_cursor_pos();
2982 if (cursor_pos == -1)
2983 cursor_pos = gtk_text_iter_get_offset(&iter);
2984 compose->set_cursor_pos = cursor_pos;
2986 gtk_text_buffer_get_start_iter(buffer, &iter);
2987 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2988 gtk_text_buffer_place_cursor(buffer, &iter);
2990 compose->autowrap = prev_autowrap;
2991 if (compose->autowrap && rewrap)
2992 compose_wrap_all(compose);
2999 SIGNAL_UNBLOCK(buffer);
3001 procmsg_msginfo_free( dummyinfo );
3006 /* if ml_post is of type addr@host and from is of type
3007 * addr-anything@host, return TRUE
3009 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3011 gchar *left_ml = NULL;
3012 gchar *right_ml = NULL;
3013 gchar *left_from = NULL;
3014 gchar *right_from = NULL;
3015 gboolean result = FALSE;
3017 if (!ml_post || !from)
3020 left_ml = g_strdup(ml_post);
3021 if (strstr(left_ml, "@")) {
3022 right_ml = strstr(left_ml, "@")+1;
3023 *(strstr(left_ml, "@")) = '\0';
3026 left_from = g_strdup(from);
3027 if (strstr(left_from, "@")) {
3028 right_from = strstr(left_from, "@")+1;
3029 *(strstr(left_from, "@")) = '\0';
3032 if (left_ml && left_from && right_ml && right_from
3033 && !strncmp(left_from, left_ml, strlen(left_ml))
3034 && !strcmp(right_from, right_ml)) {
3043 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3044 gboolean to_all, gboolean to_ml,
3046 gboolean followup_and_reply_to)
3048 GSList *cc_list = NULL;
3051 gchar *replyto = NULL;
3053 gboolean reply_to_ml = FALSE;
3054 gboolean default_reply_to = FALSE;
3056 cm_return_if_fail(compose->account != NULL);
3057 cm_return_if_fail(msginfo != NULL);
3059 reply_to_ml = to_ml && compose->ml_post;
3061 default_reply_to = msginfo->folder &&
3062 msginfo->folder->prefs->enable_default_reply_to;
3064 if (compose->account->protocol != A_NNTP) {
3065 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3066 if (msginfo->folder->prefs->enable_default_replyto) {
3067 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3068 COMPOSE_REPLYTO, PREF_FOLDER);
3070 if (msginfo->folder->prefs->enable_default_bcc) {
3071 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3072 COMPOSE_BCC, PREF_FOLDER);
3074 if (msginfo->folder->prefs->enable_default_cc) {
3075 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3076 COMPOSE_CC, PREF_FOLDER);
3079 if (reply_to_ml && !default_reply_to) {
3081 gboolean is_subscr = is_subscription(compose->ml_post,
3084 /* normal answer to ml post with a reply-to */
3085 compose_entry_append(compose,
3087 COMPOSE_TO, PREF_ML);
3088 if (compose->replyto)
3089 compose_entry_append(compose,
3091 COMPOSE_CC, PREF_ML);
3093 /* answer to subscription confirmation */
3094 if (compose->replyto)
3095 compose_entry_append(compose,
3097 COMPOSE_TO, PREF_ML);
3098 else if (msginfo->from)
3099 compose_entry_append(compose,
3101 COMPOSE_TO, PREF_ML);
3104 else if (!(to_all || to_sender) && default_reply_to) {
3105 compose_entry_append(compose,
3106 msginfo->folder->prefs->default_reply_to,
3107 COMPOSE_TO, PREF_FOLDER);
3108 compose_entry_mark_default_to(compose,
3109 msginfo->folder->prefs->default_reply_to);
3114 Xstrdup_a(tmp1, msginfo->from, return);
3115 extract_address(tmp1);
3116 if (to_all || to_sender ||
3117 !account_find_from_address(tmp1, FALSE))
3118 compose_entry_append(compose,
3119 (compose->replyto && !to_sender)
3120 ? compose->replyto :
3121 msginfo->from ? msginfo->from : "",
3122 COMPOSE_TO, PREF_NONE);
3123 else if (!to_all && !to_sender) {
3124 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3125 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3126 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3127 if (compose->replyto) {
3128 compose_entry_append(compose,
3130 COMPOSE_TO, PREF_NONE);
3132 compose_entry_append(compose,
3133 msginfo->from ? msginfo->from : "",
3134 COMPOSE_TO, PREF_NONE);
3137 /* replying to own mail, use original recp */
3138 compose_entry_append(compose,
3139 msginfo->to ? msginfo->to : "",
3140 COMPOSE_TO, PREF_NONE);
3141 compose_entry_append(compose,
3142 msginfo->cc ? msginfo->cc : "",
3143 COMPOSE_CC, PREF_NONE);
3148 if (to_sender || (compose->followup_to &&
3149 !strncmp(compose->followup_to, "poster", 6)))
3150 compose_entry_append
3152 (compose->replyto ? compose->replyto :
3153 msginfo->from ? msginfo->from : ""),
3154 COMPOSE_TO, PREF_NONE);
3156 else if (followup_and_reply_to || to_all) {
3157 compose_entry_append
3159 (compose->replyto ? compose->replyto :
3160 msginfo->from ? msginfo->from : ""),
3161 COMPOSE_TO, PREF_NONE);
3163 compose_entry_append
3165 compose->followup_to ? compose->followup_to :
3166 compose->newsgroups ? compose->newsgroups : "",
3167 COMPOSE_NEWSGROUPS, PREF_NONE);
3170 compose_entry_append
3172 compose->followup_to ? compose->followup_to :
3173 compose->newsgroups ? compose->newsgroups : "",
3174 COMPOSE_NEWSGROUPS, PREF_NONE);
3177 if (msginfo->subject && *msginfo->subject) {
3181 buf = p = g_strdup(msginfo->subject);
3182 p += subject_get_prefix_length(p);
3183 memmove(buf, p, strlen(p) + 1);
3185 buf2 = g_strdup_printf("Re: %s", buf);
3186 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3191 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3193 if (to_ml && compose->ml_post) return;
3194 if (!to_all || compose->account->protocol == A_NNTP) return;
3196 if (compose->replyto) {
3197 Xstrdup_a(replyto, compose->replyto, return);
3198 extract_address(replyto);
3200 if (msginfo->from) {
3201 Xstrdup_a(from, msginfo->from, return);
3202 extract_address(from);
3205 if (replyto && from)
3206 cc_list = address_list_append_with_comments(cc_list, from);
3207 if (to_all && msginfo->folder &&
3208 msginfo->folder->prefs->enable_default_reply_to)
3209 cc_list = address_list_append_with_comments(cc_list,
3210 msginfo->folder->prefs->default_reply_to);
3211 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3212 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3215 for (cur = cc_list; cur != NULL; cur = cur->next)
3216 compose_entry_append(compose, (gchar *)cur->data,
3217 COMPOSE_CC, PREF_NONE);
3218 slist_free_strings(cc_list);
3219 g_slist_free(cc_list);
3223 #define SET_ENTRY(entry, str) \
3226 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3229 #define SET_ADDRESS(type, str) \
3232 compose_entry_append(compose, str, type, PREF_NONE); \
3235 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3237 cm_return_if_fail(msginfo != NULL);
3239 SET_ENTRY(subject_entry, msginfo->subject);
3240 SET_ENTRY(from_name, msginfo->from);
3241 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3242 SET_ADDRESS(COMPOSE_CC, compose->cc);
3243 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3244 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3245 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3246 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3248 compose_update_priority_menu_item(compose);
3249 compose_update_privacy_system_menu_item(compose, FALSE);
3250 compose_show_first_last_header(compose, TRUE);
3256 static void compose_insert_sig(Compose *compose, gboolean replace)
3258 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3259 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3261 GtkTextIter iter, iter_end;
3262 gint cur_pos, ins_pos;
3263 gboolean prev_autowrap;
3264 gboolean found = FALSE;
3265 gboolean exists = FALSE;
3267 cm_return_if_fail(compose->account != NULL);
3271 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3272 G_CALLBACK(compose_changed_cb),
3275 mark = gtk_text_buffer_get_insert(buffer);
3276 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3277 cur_pos = gtk_text_iter_get_offset (&iter);
3280 gtk_text_buffer_get_end_iter(buffer, &iter);
3282 exists = (compose->sig_str != NULL);
3285 GtkTextIter first_iter, start_iter, end_iter;
3287 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3289 if (!exists || compose->sig_str[0] == '\0')
3292 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3293 compose->signature_tag);
3296 /* include previous \n\n */
3297 gtk_text_iter_backward_chars(&first_iter, 1);
3298 start_iter = first_iter;
3299 end_iter = first_iter;
3301 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3302 compose->signature_tag);
3303 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3304 compose->signature_tag);
3306 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3312 g_free(compose->sig_str);
3313 compose->sig_str = account_get_signature_str(compose->account);
3315 cur_pos = gtk_text_iter_get_offset(&iter);
3317 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3318 g_free(compose->sig_str);
3319 compose->sig_str = NULL;
3321 if (compose->sig_inserted == FALSE)
3322 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3323 compose->sig_inserted = TRUE;
3325 cur_pos = gtk_text_iter_get_offset(&iter);
3326 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3328 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3329 gtk_text_iter_forward_chars(&iter, 1);
3330 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3331 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3333 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3334 cur_pos = gtk_text_buffer_get_char_count (buffer);
3337 /* put the cursor where it should be
3338 * either where the quote_fmt says, either where it was */
3339 if (compose->set_cursor_pos < 0)
3340 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3342 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3343 compose->set_cursor_pos);
3345 compose->set_cursor_pos = -1;
3346 gtk_text_buffer_place_cursor(buffer, &iter);
3347 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3348 G_CALLBACK(compose_changed_cb),
3354 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3357 GtkTextBuffer *buffer;
3360 const gchar *cur_encoding;
3361 gchar buf[BUFFSIZE];
3364 gboolean prev_autowrap;
3365 gboolean badtxt = FALSE;
3366 struct stat file_stat;
3369 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3371 /* get the size of the file we are about to insert */
3372 ret = g_stat(file, &file_stat);
3374 gchar *shortfile = g_path_get_basename(file);
3375 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3377 return COMPOSE_INSERT_NO_FILE;
3378 } else if (prefs_common.warn_large_insert == TRUE) {
3380 /* ask user for confirmation if the file is large */
3381 if (prefs_common.warn_large_insert_size < 0 ||
3382 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3386 msg = g_strdup_printf(_("You are about to insert a file of %s "
3387 "in the message body. Are you sure you want to do that?"),
3388 to_human_readable(file_stat.st_size));
3389 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3390 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3393 /* do we ask for confirmation next time? */
3394 if (aval & G_ALERTDISABLE) {
3395 /* no confirmation next time, disable feature in preferences */
3396 aval &= ~G_ALERTDISABLE;
3397 prefs_common.warn_large_insert = FALSE;
3400 /* abort file insertion if user canceled action */
3401 if (aval != G_ALERTALTERNATE) {
3402 return COMPOSE_INSERT_NO_FILE;
3408 if ((fp = g_fopen(file, "rb")) == NULL) {
3409 FILE_OP_ERROR(file, "fopen");
3410 return COMPOSE_INSERT_READ_ERROR;
3413 prev_autowrap = compose->autowrap;
3414 compose->autowrap = FALSE;
3416 text = GTK_TEXT_VIEW(compose->text);
3417 buffer = gtk_text_view_get_buffer(text);
3418 mark = gtk_text_buffer_get_insert(buffer);
3419 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3421 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3422 G_CALLBACK(text_inserted),
3425 cur_encoding = conv_get_locale_charset_str_no_utf8();
3427 while (fgets(buf, sizeof(buf), fp) != NULL) {
3430 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3431 str = g_strdup(buf);
3433 str = conv_codeset_strdup
3434 (buf, cur_encoding, CS_INTERNAL);
3437 /* strip <CR> if DOS/Windows file,
3438 replace <CR> with <LF> if Macintosh file. */
3441 if (len > 0 && str[len - 1] != '\n') {
3443 if (str[len] == '\r') str[len] = '\n';
3446 gtk_text_buffer_insert(buffer, &iter, str, -1);
3450 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3451 G_CALLBACK(text_inserted),
3453 compose->autowrap = prev_autowrap;
3454 if (compose->autowrap)
3455 compose_wrap_all(compose);
3460 return COMPOSE_INSERT_INVALID_CHARACTER;
3462 return COMPOSE_INSERT_SUCCESS;
3465 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3466 const gchar *filename,
3467 const gchar *content_type)
3475 GtkListStore *store;
3477 gboolean has_binary = FALSE;
3479 if (!is_file_exist(file)) {
3480 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3481 gboolean result = FALSE;
3482 if (file_from_uri && is_file_exist(file_from_uri)) {
3483 result = compose_attach_append(
3484 compose, file_from_uri,
3488 g_free(file_from_uri);
3491 alertpanel_error("File %s doesn't exist\n", filename);
3494 if ((size = get_file_size(file)) < 0) {
3495 alertpanel_error("Can't get file size of %s\n", filename);
3499 alertpanel_error(_("File %s is empty."), filename);
3502 if ((fp = g_fopen(file, "rb")) == NULL) {
3503 alertpanel_error(_("Can't read %s."), filename);
3508 ainfo = g_new0(AttachInfo, 1);
3509 auto_ainfo = g_auto_pointer_new_with_free
3510 (ainfo, (GFreeFunc) compose_attach_info_free);
3511 ainfo->file = g_strdup(file);
3514 ainfo->content_type = g_strdup(content_type);
3515 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3517 MsgFlags flags = {0, 0};
3519 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3520 ainfo->encoding = ENC_7BIT;
3522 ainfo->encoding = ENC_8BIT;
3524 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3525 if (msginfo && msginfo->subject)
3526 name = g_strdup(msginfo->subject);
3528 name = g_path_get_basename(filename ? filename : file);
3530 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3532 procmsg_msginfo_free(msginfo);
3534 if (!g_ascii_strncasecmp(content_type, "text", 4))
3535 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3537 ainfo->encoding = ENC_BASE64;
3538 name = g_path_get_basename(filename ? filename : file);
3539 ainfo->name = g_strdup(name);
3543 ainfo->content_type = procmime_get_mime_type(file);
3544 if (!ainfo->content_type) {
3545 ainfo->content_type =
3546 g_strdup("application/octet-stream");
3547 ainfo->encoding = ENC_BASE64;
3548 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3550 procmime_get_encoding_for_text_file(file, &has_binary);
3552 ainfo->encoding = ENC_BASE64;
3553 name = g_path_get_basename(filename ? filename : file);
3554 ainfo->name = g_strdup(name);
3558 if (ainfo->name != NULL
3559 && !strcmp(ainfo->name, ".")) {
3560 g_free(ainfo->name);
3564 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3565 g_free(ainfo->content_type);
3566 ainfo->content_type = g_strdup("application/octet-stream");
3569 ainfo->size = (goffset)size;
3570 size_text = to_human_readable((goffset)size);
3572 store = GTK_LIST_STORE(gtk_tree_view_get_model
3573 (GTK_TREE_VIEW(compose->attach_clist)));
3575 gtk_list_store_append(store, &iter);
3576 gtk_list_store_set(store, &iter,
3577 COL_MIMETYPE, ainfo->content_type,
3578 COL_SIZE, size_text,
3579 COL_NAME, ainfo->name,
3581 COL_AUTODATA, auto_ainfo,
3584 g_auto_pointer_free(auto_ainfo);
3585 compose_attach_update_label(compose);
3589 static void compose_use_signing(Compose *compose, gboolean use_signing)
3591 compose->use_signing = use_signing;
3592 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3595 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3597 compose->use_encryption = use_encryption;
3598 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3601 #define NEXT_PART_NOT_CHILD(info) \
3603 node = info->node; \
3604 while (node->children) \
3605 node = g_node_last_child(node); \
3606 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3609 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3613 MimeInfo *firsttext = NULL;
3614 MimeInfo *encrypted = NULL;
3617 const gchar *partname = NULL;
3619 mimeinfo = procmime_scan_message(msginfo);
3620 if (!mimeinfo) return;
3622 if (mimeinfo->node->children == NULL) {
3623 procmime_mimeinfo_free_all(mimeinfo);
3627 /* find first content part */
3628 child = (MimeInfo *) mimeinfo->node->children->data;
3629 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3630 child = (MimeInfo *)child->node->children->data;
3633 if (child->type == MIMETYPE_TEXT) {
3635 debug_print("First text part found\n");
3636 } else if (compose->mode == COMPOSE_REEDIT &&
3637 child->type == MIMETYPE_APPLICATION &&
3638 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3639 encrypted = (MimeInfo *)child->node->parent->data;
3642 child = (MimeInfo *) mimeinfo->node->children->data;
3643 while (child != NULL) {
3646 if (child == encrypted) {
3647 /* skip this part of tree */
3648 NEXT_PART_NOT_CHILD(child);
3652 if (child->type == MIMETYPE_MULTIPART) {
3653 /* get the actual content */
3654 child = procmime_mimeinfo_next(child);
3658 if (child == firsttext) {
3659 child = procmime_mimeinfo_next(child);
3663 outfile = procmime_get_tmp_file_name(child);
3664 if ((err = procmime_get_part(outfile, child)) < 0)
3665 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3667 gchar *content_type;
3669 content_type = procmime_get_content_type_str(child->type, child->subtype);
3671 /* if we meet a pgp signature, we don't attach it, but
3672 * we force signing. */
3673 if ((strcmp(content_type, "application/pgp-signature") &&
3674 strcmp(content_type, "application/pkcs7-signature") &&
3675 strcmp(content_type, "application/x-pkcs7-signature"))
3676 || compose->mode == COMPOSE_REDIRECT) {
3677 partname = procmime_mimeinfo_get_parameter(child, "filename");
3678 if (partname == NULL)
3679 partname = procmime_mimeinfo_get_parameter(child, "name");
3680 if (partname == NULL)
3682 compose_attach_append(compose, outfile,
3683 partname, content_type);
3685 compose_force_signing(compose, compose->account, NULL);
3687 g_free(content_type);
3690 NEXT_PART_NOT_CHILD(child);
3692 procmime_mimeinfo_free_all(mimeinfo);
3695 #undef NEXT_PART_NOT_CHILD
3700 WAIT_FOR_INDENT_CHAR,
3701 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3704 /* return indent length, we allow:
3705 indent characters followed by indent characters or spaces/tabs,
3706 alphabets and numbers immediately followed by indent characters,
3707 and the repeating sequences of the above
3708 If quote ends with multiple spaces, only the first one is included. */
3709 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3710 const GtkTextIter *start, gint *len)
3712 GtkTextIter iter = *start;
3716 IndentState state = WAIT_FOR_INDENT_CHAR;
3719 gint alnum_count = 0;
3720 gint space_count = 0;
3723 if (prefs_common.quote_chars == NULL) {
3727 while (!gtk_text_iter_ends_line(&iter)) {
3728 wc = gtk_text_iter_get_char(&iter);
3729 if (g_unichar_iswide(wc))
3731 clen = g_unichar_to_utf8(wc, ch);
3735 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3736 is_space = g_unichar_isspace(wc);
3738 if (state == WAIT_FOR_INDENT_CHAR) {
3739 if (!is_indent && !g_unichar_isalnum(wc))
3742 quote_len += alnum_count + space_count + 1;
3743 alnum_count = space_count = 0;
3744 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3747 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3748 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3752 else if (is_indent) {
3753 quote_len += alnum_count + space_count + 1;
3754 alnum_count = space_count = 0;
3757 state = WAIT_FOR_INDENT_CHAR;
3761 gtk_text_iter_forward_char(&iter);
3764 if (quote_len > 0 && space_count > 0)
3770 if (quote_len > 0) {
3772 gtk_text_iter_forward_chars(&iter, quote_len);
3773 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3779 /* return >0 if the line is itemized */
3780 static int compose_itemized_length(GtkTextBuffer *buffer,
3781 const GtkTextIter *start)
3783 GtkTextIter iter = *start;
3788 if (gtk_text_iter_ends_line(&iter))
3793 wc = gtk_text_iter_get_char(&iter);
3794 if (!g_unichar_isspace(wc))
3796 gtk_text_iter_forward_char(&iter);
3797 if (gtk_text_iter_ends_line(&iter))
3801 clen = g_unichar_to_utf8(wc, ch);
3805 if (!strchr("*-+", ch[0]))
3808 gtk_text_iter_forward_char(&iter);
3809 if (gtk_text_iter_ends_line(&iter))
3811 wc = gtk_text_iter_get_char(&iter);
3812 if (g_unichar_isspace(wc)) {
3818 /* return the string at the start of the itemization */
3819 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3820 const GtkTextIter *start)
3822 GtkTextIter iter = *start;
3825 GString *item_chars = g_string_new("");
3828 if (gtk_text_iter_ends_line(&iter))
3833 wc = gtk_text_iter_get_char(&iter);
3834 if (!g_unichar_isspace(wc))
3836 gtk_text_iter_forward_char(&iter);
3837 if (gtk_text_iter_ends_line(&iter))
3839 g_string_append_unichar(item_chars, wc);
3842 str = item_chars->str;
3843 g_string_free(item_chars, FALSE);
3847 /* return the number of spaces at a line's start */
3848 static int compose_left_offset_length(GtkTextBuffer *buffer,
3849 const GtkTextIter *start)
3851 GtkTextIter iter = *start;
3854 if (gtk_text_iter_ends_line(&iter))
3858 wc = gtk_text_iter_get_char(&iter);
3859 if (!g_unichar_isspace(wc))
3862 gtk_text_iter_forward_char(&iter);
3863 if (gtk_text_iter_ends_line(&iter))
3867 gtk_text_iter_forward_char(&iter);
3868 if (gtk_text_iter_ends_line(&iter))
3873 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3874 const GtkTextIter *start,
3875 GtkTextIter *break_pos,
3879 GtkTextIter iter = *start, line_end = *start;
3880 PangoLogAttr *attrs;
3887 gboolean can_break = FALSE;
3888 gboolean do_break = FALSE;
3889 gboolean was_white = FALSE;
3890 gboolean prev_dont_break = FALSE;
3892 gtk_text_iter_forward_to_line_end(&line_end);
3893 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3894 len = g_utf8_strlen(str, -1);
3898 g_warning("compose_get_line_break_pos: len = 0!\n");
3902 /* g_print("breaking line: %d: %s (len = %d)\n",
3903 gtk_text_iter_get_line(&iter), str, len); */
3905 attrs = g_new(PangoLogAttr, len + 1);
3907 pango_default_break(str, -1, NULL, attrs, len + 1);
3911 /* skip quote and leading spaces */
3912 for (i = 0; *p != '\0' && i < len; i++) {
3915 wc = g_utf8_get_char(p);
3916 if (i >= quote_len && !g_unichar_isspace(wc))
3918 if (g_unichar_iswide(wc))
3920 else if (*p == '\t')
3924 p = g_utf8_next_char(p);
3927 for (; *p != '\0' && i < len; i++) {
3928 PangoLogAttr *attr = attrs + i;
3932 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3935 was_white = attr->is_white;
3937 /* don't wrap URI */
3938 if ((uri_len = get_uri_len(p)) > 0) {
3940 if (pos > 0 && col > max_col) {
3950 wc = g_utf8_get_char(p);
3951 if (g_unichar_iswide(wc)) {
3953 if (prev_dont_break && can_break && attr->is_line_break)
3955 } else if (*p == '\t')
3959 if (pos > 0 && col > max_col) {
3964 if (*p == '-' || *p == '/')
3965 prev_dont_break = TRUE;
3967 prev_dont_break = FALSE;
3969 p = g_utf8_next_char(p);
3973 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3978 *break_pos = *start;
3979 gtk_text_iter_set_line_offset(break_pos, pos);
3984 static gboolean compose_join_next_line(Compose *compose,
3985 GtkTextBuffer *buffer,
3987 const gchar *quote_str)
3989 GtkTextIter iter_ = *iter, cur, prev, next, end;
3990 PangoLogAttr attrs[3];
3992 gchar *next_quote_str;
3995 gboolean keep_cursor = FALSE;
3997 if (!gtk_text_iter_forward_line(&iter_) ||
3998 gtk_text_iter_ends_line(&iter_)) {
4001 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4003 if ((quote_str || next_quote_str) &&
4004 strcmp2(quote_str, next_quote_str) != 0) {
4005 g_free(next_quote_str);
4008 g_free(next_quote_str);
4011 if (quote_len > 0) {
4012 gtk_text_iter_forward_chars(&end, quote_len);
4013 if (gtk_text_iter_ends_line(&end)) {
4018 /* don't join itemized lines */
4019 if (compose_itemized_length(buffer, &end) > 0) {
4023 /* don't join signature separator */
4024 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4027 /* delete quote str */
4029 gtk_text_buffer_delete(buffer, &iter_, &end);
4031 /* don't join line breaks put by the user */
4033 gtk_text_iter_backward_char(&cur);
4034 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4035 gtk_text_iter_forward_char(&cur);
4039 gtk_text_iter_forward_char(&cur);
4040 /* delete linebreak and extra spaces */
4041 while (gtk_text_iter_backward_char(&cur)) {
4042 wc1 = gtk_text_iter_get_char(&cur);
4043 if (!g_unichar_isspace(wc1))
4048 while (!gtk_text_iter_ends_line(&cur)) {
4049 wc1 = gtk_text_iter_get_char(&cur);
4050 if (!g_unichar_isspace(wc1))
4052 gtk_text_iter_forward_char(&cur);
4055 if (!gtk_text_iter_equal(&prev, &next)) {
4058 mark = gtk_text_buffer_get_insert(buffer);
4059 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4060 if (gtk_text_iter_equal(&prev, &cur))
4062 gtk_text_buffer_delete(buffer, &prev, &next);
4066 /* insert space if required */
4067 gtk_text_iter_backward_char(&prev);
4068 wc1 = gtk_text_iter_get_char(&prev);
4069 wc2 = gtk_text_iter_get_char(&next);
4070 gtk_text_iter_forward_char(&next);
4071 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4072 pango_default_break(str, -1, NULL, attrs, 3);
4073 if (!attrs[1].is_line_break ||
4074 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4075 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4077 gtk_text_iter_backward_char(&iter_);
4078 gtk_text_buffer_place_cursor(buffer, &iter_);
4087 #define ADD_TXT_POS(bp_, ep_, pti_) \
4088 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4089 last = last->next; \
4090 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4091 last->next = NULL; \
4093 g_warning("alloc error scanning URIs\n"); \
4096 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4098 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4099 GtkTextBuffer *buffer;
4100 GtkTextIter iter, break_pos, end_of_line;
4101 gchar *quote_str = NULL;
4103 gboolean wrap_quote = prefs_common.linewrap_quote;
4104 gboolean prev_autowrap = compose->autowrap;
4105 gint startq_offset = -1, noq_offset = -1;
4106 gint uri_start = -1, uri_stop = -1;
4107 gint nouri_start = -1, nouri_stop = -1;
4108 gint num_blocks = 0;
4109 gint quotelevel = -1;
4110 gboolean modified = force;
4111 gboolean removed = FALSE;
4112 gboolean modified_before_remove = FALSE;
4114 gboolean start = TRUE;
4115 gint itemized_len = 0, rem_item_len = 0;
4116 gchar *itemized_chars = NULL;
4117 gboolean item_continuation = FALSE;
4122 if (compose->draft_timeout_tag == -2) {
4126 compose->autowrap = FALSE;
4128 buffer = gtk_text_view_get_buffer(text);
4129 undo_wrapping(compose->undostruct, TRUE);
4134 mark = gtk_text_buffer_get_insert(buffer);
4135 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4139 if (compose->draft_timeout_tag == -2) {
4140 if (gtk_text_iter_ends_line(&iter)) {
4141 while (gtk_text_iter_ends_line(&iter) &&
4142 gtk_text_iter_forward_line(&iter))
4145 while (gtk_text_iter_backward_line(&iter)) {
4146 if (gtk_text_iter_ends_line(&iter)) {
4147 gtk_text_iter_forward_line(&iter);
4153 /* move to line start */
4154 gtk_text_iter_set_line_offset(&iter, 0);
4157 itemized_len = compose_itemized_length(buffer, &iter);
4159 if (!itemized_len) {
4160 itemized_len = compose_left_offset_length(buffer, &iter);
4161 item_continuation = TRUE;
4165 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4167 /* go until paragraph end (empty line) */
4168 while (start || !gtk_text_iter_ends_line(&iter)) {
4169 gchar *scanpos = NULL;
4170 /* parse table - in order of priority */
4172 const gchar *needle; /* token */
4174 /* token search function */
4175 gchar *(*search) (const gchar *haystack,
4176 const gchar *needle);
4177 /* part parsing function */
4178 gboolean (*parse) (const gchar *start,
4179 const gchar *scanpos,
4183 /* part to URI function */
4184 gchar *(*build_uri) (const gchar *bp,
4188 static struct table parser[] = {
4189 {"http://", strcasestr, get_uri_part, make_uri_string},
4190 {"https://", strcasestr, get_uri_part, make_uri_string},
4191 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4192 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4193 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4194 {"www.", strcasestr, get_uri_part, make_http_string},
4195 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4196 {"@", strcasestr, get_email_part, make_email_string}
4198 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4199 gint last_index = PARSE_ELEMS;
4201 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4205 if (!prev_autowrap && num_blocks == 0) {
4207 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4208 G_CALLBACK(text_inserted),
4211 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4214 uri_start = uri_stop = -1;
4216 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4219 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4220 if (startq_offset == -1)
4221 startq_offset = gtk_text_iter_get_offset(&iter);
4222 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4223 if (quotelevel > 2) {
4224 /* recycle colors */
4225 if (prefs_common.recycle_quote_colors)
4234 if (startq_offset == -1)
4235 noq_offset = gtk_text_iter_get_offset(&iter);
4239 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4242 if (gtk_text_iter_ends_line(&iter)) {
4244 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4245 prefs_common.linewrap_len,
4247 GtkTextIter prev, next, cur;
4248 if (prev_autowrap != FALSE || force) {
4249 compose->automatic_break = TRUE;
4251 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4252 compose->automatic_break = FALSE;
4253 if (itemized_len && compose->autoindent) {
4254 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4255 if (!item_continuation)
4256 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4258 } else if (quote_str && wrap_quote) {
4259 compose->automatic_break = TRUE;
4261 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4262 compose->automatic_break = FALSE;
4263 if (itemized_len && compose->autoindent) {
4264 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4265 if (!item_continuation)
4266 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4270 /* remove trailing spaces */
4272 rem_item_len = itemized_len;
4273 while (compose->autoindent && rem_item_len-- > 0)
4274 gtk_text_iter_backward_char(&cur);
4275 gtk_text_iter_backward_char(&cur);
4278 while (!gtk_text_iter_starts_line(&cur)) {
4281 gtk_text_iter_backward_char(&cur);
4282 wc = gtk_text_iter_get_char(&cur);
4283 if (!g_unichar_isspace(wc))
4287 if (!gtk_text_iter_equal(&prev, &next)) {
4288 gtk_text_buffer_delete(buffer, &prev, &next);
4290 gtk_text_iter_forward_char(&break_pos);
4294 gtk_text_buffer_insert(buffer, &break_pos,
4298 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4300 /* move iter to current line start */
4301 gtk_text_iter_set_line_offset(&iter, 0);
4308 /* move iter to next line start */
4314 if (!prev_autowrap && num_blocks > 0) {
4316 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4317 G_CALLBACK(text_inserted),
4321 while (!gtk_text_iter_ends_line(&end_of_line)) {
4322 gtk_text_iter_forward_char(&end_of_line);
4324 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4326 nouri_start = gtk_text_iter_get_offset(&iter);
4327 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4329 walk_pos = gtk_text_iter_get_offset(&iter);
4330 /* FIXME: this looks phony. scanning for anything in the parse table */
4331 for (n = 0; n < PARSE_ELEMS; n++) {
4334 tmp = parser[n].search(walk, parser[n].needle);
4336 if (scanpos == NULL || tmp < scanpos) {
4345 /* check if URI can be parsed */
4346 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4347 (const gchar **)&ep, FALSE)
4348 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4352 strlen(parser[last_index].needle);
4355 uri_start = walk_pos + (bp - o_walk);
4356 uri_stop = walk_pos + (ep - o_walk);
4360 gtk_text_iter_forward_line(&iter);
4363 if (startq_offset != -1) {
4364 GtkTextIter startquote, endquote;
4365 gtk_text_buffer_get_iter_at_offset(
4366 buffer, &startquote, startq_offset);
4369 switch (quotelevel) {
4371 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4372 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4373 gtk_text_buffer_apply_tag_by_name(
4374 buffer, "quote0", &startquote, &endquote);
4375 gtk_text_buffer_remove_tag_by_name(
4376 buffer, "quote1", &startquote, &endquote);
4377 gtk_text_buffer_remove_tag_by_name(
4378 buffer, "quote2", &startquote, &endquote);
4383 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4384 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4385 gtk_text_buffer_apply_tag_by_name(
4386 buffer, "quote1", &startquote, &endquote);
4387 gtk_text_buffer_remove_tag_by_name(
4388 buffer, "quote0", &startquote, &endquote);
4389 gtk_text_buffer_remove_tag_by_name(
4390 buffer, "quote2", &startquote, &endquote);
4395 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4396 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4397 gtk_text_buffer_apply_tag_by_name(
4398 buffer, "quote2", &startquote, &endquote);
4399 gtk_text_buffer_remove_tag_by_name(
4400 buffer, "quote0", &startquote, &endquote);
4401 gtk_text_buffer_remove_tag_by_name(
4402 buffer, "quote1", &startquote, &endquote);
4408 } else if (noq_offset != -1) {
4409 GtkTextIter startnoquote, endnoquote;
4410 gtk_text_buffer_get_iter_at_offset(
4411 buffer, &startnoquote, noq_offset);
4414 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4415 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4416 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4417 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4418 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4419 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4420 gtk_text_buffer_remove_tag_by_name(
4421 buffer, "quote0", &startnoquote, &endnoquote);
4422 gtk_text_buffer_remove_tag_by_name(
4423 buffer, "quote1", &startnoquote, &endnoquote);
4424 gtk_text_buffer_remove_tag_by_name(
4425 buffer, "quote2", &startnoquote, &endnoquote);
4431 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4432 GtkTextIter nouri_start_iter, nouri_end_iter;
4433 gtk_text_buffer_get_iter_at_offset(
4434 buffer, &nouri_start_iter, nouri_start);
4435 gtk_text_buffer_get_iter_at_offset(
4436 buffer, &nouri_end_iter, nouri_stop);
4437 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4438 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4439 gtk_text_buffer_remove_tag_by_name(
4440 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4441 modified_before_remove = modified;
4446 if (uri_start >= 0 && uri_stop > 0) {
4447 GtkTextIter uri_start_iter, uri_end_iter, back;
4448 gtk_text_buffer_get_iter_at_offset(
4449 buffer, &uri_start_iter, uri_start);
4450 gtk_text_buffer_get_iter_at_offset(
4451 buffer, &uri_end_iter, uri_stop);
4452 back = uri_end_iter;
4453 gtk_text_iter_backward_char(&back);
4454 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4455 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4456 gtk_text_buffer_apply_tag_by_name(
4457 buffer, "link", &uri_start_iter, &uri_end_iter);
4459 if (removed && !modified_before_remove) {
4465 // debug_print("not modified, out after %d lines\n", lines);
4469 // debug_print("modified, out after %d lines\n", lines);
4471 g_free(itemized_chars);
4474 undo_wrapping(compose->undostruct, FALSE);
4475 compose->autowrap = prev_autowrap;
4480 void compose_action_cb(void *data)
4482 Compose *compose = (Compose *)data;
4483 compose_wrap_all(compose);
4486 static void compose_wrap_all(Compose *compose)
4488 compose_wrap_all_full(compose, FALSE);
4491 static void compose_wrap_all_full(Compose *compose, gboolean force)
4493 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4494 GtkTextBuffer *buffer;
4496 gboolean modified = TRUE;
4498 buffer = gtk_text_view_get_buffer(text);
4500 gtk_text_buffer_get_start_iter(buffer, &iter);
4501 while (!gtk_text_iter_is_end(&iter) && modified)
4502 modified = compose_beautify_paragraph(compose, &iter, force);
4506 static void compose_set_title(Compose *compose)
4512 edited = compose->modified ? _(" [Edited]") : "";
4514 subject = gtk_editable_get_chars(
4515 GTK_EDITABLE(compose->subject_entry), 0, -1);
4517 #ifndef GENERIC_UMPC
4518 if (subject && strlen(subject))
4519 str = g_strdup_printf(_("%s - Compose message%s"),
4522 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4524 str = g_strdup(_("Compose message"));
4527 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4533 * compose_current_mail_account:
4535 * Find a current mail account (the currently selected account, or the
4536 * default account, if a news account is currently selected). If a
4537 * mail account cannot be found, display an error message.
4539 * Return value: Mail account, or NULL if not found.
4541 static PrefsAccount *
4542 compose_current_mail_account(void)
4546 if (cur_account && cur_account->protocol != A_NNTP)
4549 ac = account_get_default();
4550 if (!ac || ac->protocol == A_NNTP) {
4551 alertpanel_error(_("Account for sending mail is not specified.\n"
4552 "Please select a mail account before sending."));
4559 #define QUOTE_IF_REQUIRED(out, str) \
4561 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4565 len = strlen(str) + 3; \
4566 if ((__tmp = alloca(len)) == NULL) { \
4567 g_warning("can't allocate memory\n"); \
4568 g_string_free(header, TRUE); \
4571 g_snprintf(__tmp, len, "\"%s\"", str); \
4576 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4577 g_warning("can't allocate memory\n"); \
4578 g_string_free(header, TRUE); \
4581 strcpy(__tmp, str); \
4587 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4589 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4593 len = strlen(str) + 3; \
4594 if ((__tmp = alloca(len)) == NULL) { \
4595 g_warning("can't allocate memory\n"); \
4598 g_snprintf(__tmp, len, "\"%s\"", str); \
4603 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4604 g_warning("can't allocate memory\n"); \
4607 strcpy(__tmp, str); \
4613 static void compose_select_account(Compose *compose, PrefsAccount *account,
4616 gchar *from = NULL, *email, *header;
4617 ComposeHeaderEntry *header_entry;
4619 cm_return_if_fail(account != NULL);
4621 email = g_utf8_strdown(compose->account->address, -1);
4622 g_hash_table_remove(compose->email_hashtable, email);
4625 compose->account = account;
4626 g_hash_table_insert(compose->email_hashtable,
4627 g_utf8_strdown(account->address, -1),
4628 GUINT_TO_POINTER(1));
4630 if (account->name && *account->name) {
4632 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4633 from = g_strdup_printf("%s <%s>",
4634 buf, account->address);
4635 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4637 from = g_strdup_printf("<%s>",
4639 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4644 compose_set_title(compose);
4646 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4647 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4649 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4650 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4651 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4653 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4655 activate_privacy_system(compose, account, FALSE);
4657 if (!init && compose->mode != COMPOSE_REDIRECT) {
4658 undo_block(compose->undostruct);
4659 compose_insert_sig(compose, TRUE);
4660 undo_unblock(compose->undostruct);
4663 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4664 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4666 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4667 if (account->protocol == A_NNTP) {
4668 if (!strcmp(header, _("To:")))
4669 combobox_select_by_text(
4670 GTK_COMBO_BOX(header_entry->combo),
4673 if (!strcmp(header, _("Newsgroups:")))
4674 combobox_select_by_text(
4675 GTK_COMBO_BOX(header_entry->combo),
4683 /* use account's dict info if set */
4684 if (compose->gtkaspell) {
4685 if (account->enable_default_dictionary)
4686 gtkaspell_change_dict(compose->gtkaspell,
4687 account->default_dictionary, FALSE);
4688 if (account->enable_default_alt_dictionary)
4689 gtkaspell_change_alt_dict(compose->gtkaspell,
4690 account->default_alt_dictionary);
4691 if (account->enable_default_dictionary
4692 || account->enable_default_alt_dictionary)
4693 compose_spell_menu_changed(compose);
4698 gboolean compose_check_for_valid_recipient(Compose *compose) {
4699 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4700 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4701 gboolean recipient_found = FALSE;
4705 /* free to and newsgroup list */
4706 slist_free_strings(compose->to_list);
4707 g_slist_free(compose->to_list);
4708 compose->to_list = NULL;
4710 slist_free_strings(compose->newsgroup_list);
4711 g_slist_free(compose->newsgroup_list);
4712 compose->newsgroup_list = NULL;
4714 /* search header entries for to and newsgroup entries */
4715 for (list = compose->header_list; list; list = list->next) {
4718 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4719 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4722 if (entry[0] != '\0') {
4723 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4724 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4725 compose->to_list = address_list_append(compose->to_list, entry);
4726 recipient_found = TRUE;
4729 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4730 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4731 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4732 recipient_found = TRUE;
4739 return recipient_found;
4742 static gboolean compose_check_for_set_recipients(Compose *compose)
4744 if (compose->account->set_autocc && compose->account->auto_cc) {
4745 gboolean found_other = FALSE;
4747 /* search header entries for to and newsgroup entries */
4748 for (list = compose->header_list; list; list = list->next) {
4751 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4752 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4755 if (strcmp(entry, compose->account->auto_cc)
4756 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4766 if (compose->batch) {
4767 gtk_widget_show_all(compose->window);
4769 aval = alertpanel(_("Send"),
4770 _("The only recipient is the default CC address. Send anyway?"),
4771 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4772 if (aval != G_ALERTALTERNATE)
4776 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4777 gboolean found_other = FALSE;
4779 /* search header entries for to and newsgroup entries */
4780 for (list = compose->header_list; list; list = list->next) {
4783 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4784 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4787 if (strcmp(entry, compose->account->auto_bcc)
4788 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4798 if (compose->batch) {
4799 gtk_widget_show_all(compose->window);
4801 aval = alertpanel(_("Send"),
4802 _("The only recipient is the default BCC address. Send anyway?"),
4803 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4804 if (aval != G_ALERTALTERNATE)
4811 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4815 if (compose_check_for_valid_recipient(compose) == FALSE) {
4816 if (compose->batch) {
4817 gtk_widget_show_all(compose->window);
4819 alertpanel_error(_("Recipient is not specified."));
4823 if (compose_check_for_set_recipients(compose) == FALSE) {
4827 if (!compose->batch) {
4828 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4829 if (*str == '\0' && check_everything == TRUE &&
4830 compose->mode != COMPOSE_REDIRECT) {
4832 gchar *button_label;
4835 if (compose->sending)
4836 button_label = _("+_Send");
4838 button_label = _("+_Queue");
4839 message = g_strdup_printf(_("Subject is empty. %s"),
4840 compose->sending?_("Send it anyway?"):
4841 _("Queue it anyway?"));
4843 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4844 GTK_STOCK_CANCEL, button_label, NULL);
4846 if (aval != G_ALERTALTERNATE)
4851 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4857 gint compose_send(Compose *compose)
4860 FolderItem *folder = NULL;
4862 gchar *msgpath = NULL;
4863 gboolean discard_window = FALSE;
4864 gchar *errstr = NULL;
4865 gchar *tmsgid = NULL;
4866 MainWindow *mainwin = mainwindow_get_mainwindow();
4867 gboolean queued_removed = FALSE;
4869 if (prefs_common.send_dialog_invisible
4870 || compose->batch == TRUE)
4871 discard_window = TRUE;
4873 compose_allow_user_actions (compose, FALSE);
4874 compose->sending = TRUE;
4876 if (compose_check_entries(compose, TRUE) == FALSE) {
4877 if (compose->batch) {
4878 gtk_widget_show_all(compose->window);
4884 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4887 if (compose->batch) {
4888 gtk_widget_show_all(compose->window);
4891 alertpanel_error(_("Could not queue message for sending:\n\n"
4892 "Charset conversion failed."));
4893 } else if (val == -5) {
4894 alertpanel_error(_("Could not queue message for sending:\n\n"
4895 "Couldn't get recipient encryption key."));
4896 } else if (val == -6) {
4898 } else if (val == -3) {
4899 if (privacy_peek_error())
4900 alertpanel_error(_("Could not queue message for sending:\n\n"
4901 "Signature failed: %s"), privacy_get_error());
4902 } else if (val == -2 && errno != 0) {
4903 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4905 alertpanel_error(_("Could not queue message for sending."));
4910 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4911 if (discard_window) {
4912 compose->sending = FALSE;
4913 compose_close(compose);
4914 /* No more compose access in the normal codepath
4915 * after this point! */
4920 alertpanel_error(_("The message was queued but could not be "
4921 "sent.\nUse \"Send queued messages\" from "
4922 "the main window to retry."));
4923 if (!discard_window) {
4930 if (msgpath == NULL) {
4931 msgpath = folder_item_fetch_msg(folder, msgnum);
4932 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4935 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4936 claws_unlink(msgpath);
4939 if (!discard_window) {
4941 if (!queued_removed)
4942 folder_item_remove_msg(folder, msgnum);
4943 folder_item_scan(folder);
4945 /* make sure we delete that */
4946 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4948 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4949 folder_item_remove_msg(folder, tmp->msgnum);
4950 procmsg_msginfo_free(tmp);
4957 if (!queued_removed)
4958 folder_item_remove_msg(folder, msgnum);
4959 folder_item_scan(folder);
4961 /* make sure we delete that */
4962 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4964 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4965 folder_item_remove_msg(folder, tmp->msgnum);
4966 procmsg_msginfo_free(tmp);
4969 if (!discard_window) {
4970 compose->sending = FALSE;
4971 compose_allow_user_actions (compose, TRUE);
4972 compose_close(compose);
4976 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4977 "the main window to retry."), errstr);
4980 alertpanel_error_log(_("The message was queued but could not be "
4981 "sent.\nUse \"Send queued messages\" from "
4982 "the main window to retry."));
4984 if (!discard_window) {
4993 toolbar_main_set_sensitive(mainwin);
4994 main_window_set_menu_sensitive(mainwin);
5000 compose_allow_user_actions (compose, TRUE);
5001 compose->sending = FALSE;
5002 compose->modified = TRUE;
5003 toolbar_main_set_sensitive(mainwin);
5004 main_window_set_menu_sensitive(mainwin);
5009 static gboolean compose_use_attach(Compose *compose)
5011 GtkTreeModel *model = gtk_tree_view_get_model
5012 (GTK_TREE_VIEW(compose->attach_clist));
5013 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5016 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5019 gchar buf[BUFFSIZE];
5021 gboolean first_to_address;
5022 gboolean first_cc_address;
5024 ComposeHeaderEntry *headerentry;
5025 const gchar *headerentryname;
5026 const gchar *cc_hdr;
5027 const gchar *to_hdr;
5028 gboolean err = FALSE;
5030 debug_print("Writing redirect header\n");
5032 cc_hdr = prefs_common_translated_header_name("Cc:");
5033 to_hdr = prefs_common_translated_header_name("To:");
5035 first_to_address = TRUE;
5036 for (list = compose->header_list; list; list = list->next) {
5037 headerentry = ((ComposeHeaderEntry *)list->data);
5038 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5040 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5041 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5042 Xstrdup_a(str, entstr, return -1);
5044 if (str[0] != '\0') {
5045 compose_convert_header
5046 (compose, buf, sizeof(buf), str,
5047 strlen("Resent-To") + 2, TRUE);
5049 if (first_to_address) {
5050 err |= (fprintf(fp, "Resent-To: ") < 0);
5051 first_to_address = FALSE;
5053 err |= (fprintf(fp, ",") < 0);
5055 err |= (fprintf(fp, "%s", buf) < 0);
5059 if (!first_to_address) {
5060 err |= (fprintf(fp, "\n") < 0);
5063 first_cc_address = TRUE;
5064 for (list = compose->header_list; list; list = list->next) {
5065 headerentry = ((ComposeHeaderEntry *)list->data);
5066 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5068 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5069 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5070 Xstrdup_a(str, strg, return -1);
5072 if (str[0] != '\0') {
5073 compose_convert_header
5074 (compose, buf, sizeof(buf), str,
5075 strlen("Resent-Cc") + 2, TRUE);
5077 if (first_cc_address) {
5078 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5079 first_cc_address = FALSE;
5081 err |= (fprintf(fp, ",") < 0);
5083 err |= (fprintf(fp, "%s", buf) < 0);
5087 if (!first_cc_address) {
5088 err |= (fprintf(fp, "\n") < 0);
5091 return (err ? -1:0);
5094 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5096 gchar buf[BUFFSIZE];
5098 const gchar *entstr;
5099 /* struct utsname utsbuf; */
5100 gboolean err = FALSE;
5102 cm_return_val_if_fail(fp != NULL, -1);
5103 cm_return_val_if_fail(compose->account != NULL, -1);
5104 cm_return_val_if_fail(compose->account->address != NULL, -1);
5107 get_rfc822_date(buf, sizeof(buf));
5108 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5111 if (compose->account->name && *compose->account->name) {
5112 compose_convert_header
5113 (compose, buf, sizeof(buf), compose->account->name,
5114 strlen("From: "), TRUE);
5115 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5116 buf, compose->account->address) < 0);
5118 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5121 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5122 if (*entstr != '\0') {
5123 Xstrdup_a(str, entstr, return -1);
5126 compose_convert_header(compose, buf, sizeof(buf), str,
5127 strlen("Subject: "), FALSE);
5128 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5132 /* Resent-Message-ID */
5133 if (compose->account->set_domain && compose->account->domain) {
5134 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5135 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5136 g_snprintf(buf, sizeof(buf), "%s",
5137 strchr(compose->account->address, '@') ?
5138 strchr(compose->account->address, '@')+1 :
5139 compose->account->address);
5141 g_snprintf(buf, sizeof(buf), "%s", "");
5144 if (compose->account->gen_msgid) {
5146 if (compose->account->msgid_with_addr) {
5147 addr = compose->account->address;
5149 generate_msgid(buf, sizeof(buf), addr);
5150 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5151 compose->msgid = g_strdup(buf);
5153 compose->msgid = NULL;
5156 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5159 /* separator between header and body */
5160 err |= (fputs("\n", fp) == EOF);
5162 return (err ? -1:0);
5165 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5169 gchar buf[BUFFSIZE];
5171 gboolean skip = FALSE;
5172 gboolean err = FALSE;
5173 gchar *not_included[]={
5174 "Return-Path:", "Delivered-To:", "Received:",
5175 "Subject:", "X-UIDL:", "AF:",
5176 "NF:", "PS:", "SRH:",
5177 "SFN:", "DSR:", "MID:",
5178 "CFG:", "PT:", "S:",
5179 "RQ:", "SSV:", "NSV:",
5180 "SSH:", "R:", "MAID:",
5181 "NAID:", "RMID:", "FMID:",
5182 "SCF:", "RRCPT:", "NG:",
5183 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5184 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5185 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5186 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5187 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5190 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5191 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5195 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5197 for (i = 0; not_included[i] != NULL; i++) {
5198 if (g_ascii_strncasecmp(buf, not_included[i],
5199 strlen(not_included[i])) == 0) {
5206 if (fputs(buf, fdest) == -1)
5209 if (!prefs_common.redirect_keep_from) {
5210 if (g_ascii_strncasecmp(buf, "From:",
5211 strlen("From:")) == 0) {
5212 err |= (fputs(" (by way of ", fdest) == EOF);
5213 if (compose->account->name
5214 && *compose->account->name) {
5215 compose_convert_header
5216 (compose, buf, sizeof(buf),
5217 compose->account->name,
5220 err |= (fprintf(fdest, "%s <%s>",
5222 compose->account->address) < 0);
5224 err |= (fprintf(fdest, "%s",
5225 compose->account->address) < 0);
5226 err |= (fputs(")", fdest) == EOF);
5230 if (fputs("\n", fdest) == -1)
5237 if (compose_redirect_write_headers(compose, fdest))
5240 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5241 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5254 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5256 GtkTextBuffer *buffer;
5257 GtkTextIter start, end;
5260 const gchar *out_codeset;
5261 EncodingType encoding = ENC_UNKNOWN;
5262 MimeInfo *mimemsg, *mimetext;
5264 const gchar *src_codeset = CS_INTERNAL;
5265 gchar *from_addr = NULL;
5266 gchar *from_name = NULL;
5268 if (action == COMPOSE_WRITE_FOR_SEND)
5269 attach_parts = TRUE;
5271 /* create message MimeInfo */
5272 mimemsg = procmime_mimeinfo_new();
5273 mimemsg->type = MIMETYPE_MESSAGE;
5274 mimemsg->subtype = g_strdup("rfc822");
5275 mimemsg->content = MIMECONTENT_MEM;
5276 mimemsg->tmp = TRUE; /* must free content later */
5277 mimemsg->data.mem = compose_get_header(compose);
5279 /* Create text part MimeInfo */
5280 /* get all composed text */
5281 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5282 gtk_text_buffer_get_start_iter(buffer, &start);
5283 gtk_text_buffer_get_end_iter(buffer, &end);
5284 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5286 out_codeset = conv_get_charset_str(compose->out_encoding);
5288 if (!out_codeset && is_ascii_str(chars)) {
5289 out_codeset = CS_US_ASCII;
5290 } else if (prefs_common.outgoing_fallback_to_ascii &&
5291 is_ascii_str(chars)) {
5292 out_codeset = CS_US_ASCII;
5293 encoding = ENC_7BIT;
5297 gchar *test_conv_global_out = NULL;
5298 gchar *test_conv_reply = NULL;
5300 /* automatic mode. be automatic. */
5301 codeconv_set_strict(TRUE);
5303 out_codeset = conv_get_outgoing_charset_str();
5305 debug_print("trying to convert to %s\n", out_codeset);
5306 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5309 if (!test_conv_global_out && compose->orig_charset
5310 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5311 out_codeset = compose->orig_charset;
5312 debug_print("failure; trying to convert to %s\n", out_codeset);
5313 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5316 if (!test_conv_global_out && !test_conv_reply) {
5318 out_codeset = CS_INTERNAL;
5319 debug_print("failure; finally using %s\n", out_codeset);
5321 g_free(test_conv_global_out);
5322 g_free(test_conv_reply);
5323 codeconv_set_strict(FALSE);
5326 if (encoding == ENC_UNKNOWN) {
5327 if (prefs_common.encoding_method == CTE_BASE64)
5328 encoding = ENC_BASE64;
5329 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5330 encoding = ENC_QUOTED_PRINTABLE;
5331 else if (prefs_common.encoding_method == CTE_8BIT)
5332 encoding = ENC_8BIT;
5334 encoding = procmime_get_encoding_for_charset(out_codeset);
5337 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5338 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5340 if (action == COMPOSE_WRITE_FOR_SEND) {
5341 codeconv_set_strict(TRUE);
5342 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5343 codeconv_set_strict(FALSE);
5349 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5350 "to the specified %s charset.\n"
5351 "Send it as %s?"), out_codeset, src_codeset);
5352 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5353 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5356 if (aval != G_ALERTALTERNATE) {
5361 out_codeset = src_codeset;
5367 out_codeset = src_codeset;
5372 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5373 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5374 strstr(buf, "\nFrom ") != NULL) {
5375 encoding = ENC_QUOTED_PRINTABLE;
5379 mimetext = procmime_mimeinfo_new();
5380 mimetext->content = MIMECONTENT_MEM;
5381 mimetext->tmp = TRUE; /* must free content later */
5382 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5383 * and free the data, which we need later. */
5384 mimetext->data.mem = g_strdup(buf);
5385 mimetext->type = MIMETYPE_TEXT;
5386 mimetext->subtype = g_strdup("plain");
5387 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5388 g_strdup(out_codeset));
5390 /* protect trailing spaces when signing message */
5391 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5392 privacy_system_can_sign(compose->privacy_system)) {
5393 encoding = ENC_QUOTED_PRINTABLE;
5396 debug_print("main text: %zd bytes encoded as %s in %d\n",
5397 strlen(buf), out_codeset, encoding);
5399 /* check for line length limit */
5400 if (action == COMPOSE_WRITE_FOR_SEND &&
5401 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5402 check_line_length(buf, 1000, &line) < 0) {
5406 msg = g_strdup_printf
5407 (_("Line %d exceeds the line length limit (998 bytes).\n"
5408 "The contents of the message might be broken on the way to the delivery.\n"
5410 "Send it anyway?"), line + 1);
5411 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5413 if (aval != G_ALERTALTERNATE) {
5419 if (encoding != ENC_UNKNOWN)
5420 procmime_encode_content(mimetext, encoding);
5422 /* append attachment parts */
5423 if (compose_use_attach(compose) && attach_parts) {
5424 MimeInfo *mimempart;
5425 gchar *boundary = NULL;
5426 mimempart = procmime_mimeinfo_new();
5427 mimempart->content = MIMECONTENT_EMPTY;
5428 mimempart->type = MIMETYPE_MULTIPART;
5429 mimempart->subtype = g_strdup("mixed");
5433 boundary = generate_mime_boundary(NULL);
5434 } while (strstr(buf, boundary) != NULL);
5436 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5439 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5441 g_node_append(mimempart->node, mimetext->node);
5442 g_node_append(mimemsg->node, mimempart->node);
5444 if (compose_add_attachments(compose, mimempart) < 0)
5447 g_node_append(mimemsg->node, mimetext->node);
5451 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5452 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5453 /* extract name and address */
5454 if (strstr(spec, " <") && strstr(spec, ">")) {
5455 from_addr = g_strdup(strrchr(spec, '<')+1);
5456 *(strrchr(from_addr, '>')) = '\0';
5457 from_name = g_strdup(spec);
5458 *(strrchr(from_name, '<')) = '\0';
5465 /* sign message if sending */
5466 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5467 privacy_system_can_sign(compose->privacy_system))
5468 if (!privacy_sign(compose->privacy_system, mimemsg,
5469 compose->account, from_addr)) {
5476 procmime_write_mimeinfo(mimemsg, fp);
5478 procmime_mimeinfo_free_all(mimemsg);
5483 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5485 GtkTextBuffer *buffer;
5486 GtkTextIter start, end;
5491 if ((fp = g_fopen(file, "wb")) == NULL) {
5492 FILE_OP_ERROR(file, "fopen");
5496 /* chmod for security */
5497 if (change_file_mode_rw(fp, file) < 0) {
5498 FILE_OP_ERROR(file, "chmod");
5499 g_warning("can't change file mode\n");
5502 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5503 gtk_text_buffer_get_start_iter(buffer, &start);
5504 gtk_text_buffer_get_end_iter(buffer, &end);
5505 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5507 chars = conv_codeset_strdup
5508 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5511 if (!chars) return -1;
5514 len = strlen(chars);
5515 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5516 FILE_OP_ERROR(file, "fwrite");
5525 if (fclose(fp) == EOF) {
5526 FILE_OP_ERROR(file, "fclose");
5533 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5536 MsgInfo *msginfo = compose->targetinfo;
5538 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5539 if (!msginfo) return -1;
5541 if (!force && MSG_IS_LOCKED(msginfo->flags))
5544 item = msginfo->folder;
5545 cm_return_val_if_fail(item != NULL, -1);
5547 if (procmsg_msg_exist(msginfo) &&
5548 (folder_has_parent_of_type(item, F_QUEUE) ||
5549 folder_has_parent_of_type(item, F_DRAFT)
5550 || msginfo == compose->autosaved_draft)) {
5551 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5552 g_warning("can't remove the old message\n");
5555 debug_print("removed reedit target %d\n", msginfo->msgnum);
5562 static void compose_remove_draft(Compose *compose)
5565 MsgInfo *msginfo = compose->targetinfo;
5566 drafts = account_get_special_folder(compose->account, F_DRAFT);
5568 if (procmsg_msg_exist(msginfo)) {
5569 folder_item_remove_msg(drafts, msginfo->msgnum);
5574 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5575 gboolean remove_reedit_target)
5577 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5580 static gboolean compose_warn_encryption(Compose *compose)
5582 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5583 AlertValue val = G_ALERTALTERNATE;
5585 if (warning == NULL)
5588 val = alertpanel_full(_("Encryption warning"), warning,
5589 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5590 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5591 if (val & G_ALERTDISABLE) {
5592 val &= ~G_ALERTDISABLE;
5593 if (val == G_ALERTALTERNATE)
5594 privacy_inhibit_encrypt_warning(compose->privacy_system,
5598 if (val == G_ALERTALTERNATE) {
5605 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5606 gchar **msgpath, gboolean check_subject,
5607 gboolean remove_reedit_target)
5614 static gboolean lock = FALSE;
5615 PrefsAccount *mailac = NULL, *newsac = NULL;
5616 gboolean err = FALSE;
5618 debug_print("queueing message...\n");
5619 cm_return_val_if_fail(compose->account != NULL, -1);
5623 if (compose_check_entries(compose, check_subject) == FALSE) {
5625 if (compose->batch) {
5626 gtk_widget_show_all(compose->window);
5631 if (!compose->to_list && !compose->newsgroup_list) {
5632 g_warning("can't get recipient list.");
5637 if (compose->to_list) {
5638 if (compose->account->protocol != A_NNTP)
5639 mailac = compose->account;
5640 else if (cur_account && cur_account->protocol != A_NNTP)
5641 mailac = cur_account;
5642 else if (!(mailac = compose_current_mail_account())) {
5644 alertpanel_error(_("No account for sending mails available!"));
5649 if (compose->newsgroup_list) {
5650 if (compose->account->protocol == A_NNTP)
5651 newsac = compose->account;
5654 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5659 /* write queue header */
5660 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5661 G_DIR_SEPARATOR, compose, (guint) rand());
5662 debug_print("queuing to %s\n", tmp);
5663 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5664 FILE_OP_ERROR(tmp, "fopen");
5670 if (change_file_mode_rw(fp, tmp) < 0) {
5671 FILE_OP_ERROR(tmp, "chmod");
5672 g_warning("can't change file mode\n");
5675 /* queueing variables */
5676 err |= (fprintf(fp, "AF:\n") < 0);
5677 err |= (fprintf(fp, "NF:0\n") < 0);
5678 err |= (fprintf(fp, "PS:10\n") < 0);
5679 err |= (fprintf(fp, "SRH:1\n") < 0);
5680 err |= (fprintf(fp, "SFN:\n") < 0);
5681 err |= (fprintf(fp, "DSR:\n") < 0);
5683 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5685 err |= (fprintf(fp, "MID:\n") < 0);
5686 err |= (fprintf(fp, "CFG:\n") < 0);
5687 err |= (fprintf(fp, "PT:0\n") < 0);
5688 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5689 err |= (fprintf(fp, "RQ:\n") < 0);
5691 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5693 err |= (fprintf(fp, "SSV:\n") < 0);
5695 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5697 err |= (fprintf(fp, "NSV:\n") < 0);
5698 err |= (fprintf(fp, "SSH:\n") < 0);
5699 /* write recepient list */
5700 if (compose->to_list) {
5701 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5702 for (cur = compose->to_list->next; cur != NULL;
5704 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5705 err |= (fprintf(fp, "\n") < 0);
5707 /* write newsgroup list */
5708 if (compose->newsgroup_list) {
5709 err |= (fprintf(fp, "NG:") < 0);
5710 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5711 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5712 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5713 err |= (fprintf(fp, "\n") < 0);
5715 /* Sylpheed account IDs */
5717 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5719 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5722 if (compose->privacy_system != NULL) {
5723 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5724 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5725 if (compose->use_encryption) {
5727 if (!compose_warn_encryption(compose)) {
5734 if (mailac && mailac->encrypt_to_self) {
5735 GSList *tmp_list = g_slist_copy(compose->to_list);
5736 tmp_list = g_slist_append(tmp_list, compose->account->address);
5737 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5738 g_slist_free(tmp_list);
5740 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5742 if (encdata != NULL) {
5743 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5744 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5745 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5747 } /* else we finally dont want to encrypt */
5749 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5750 /* and if encdata was null, it means there's been a problem in
5762 /* Save copy folder */
5763 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5764 gchar *savefolderid;
5766 savefolderid = compose_get_save_to(compose);
5767 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5768 g_free(savefolderid);
5770 /* Save copy folder */
5771 if (compose->return_receipt) {
5772 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5774 /* Message-ID of message replying to */
5775 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5778 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5779 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5782 /* Message-ID of message forwarding to */
5783 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5786 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5787 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5791 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5792 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5794 /* end of headers */
5795 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5797 if (compose->redirect_filename != NULL) {
5798 if (compose_redirect_write_to_file(compose, fp) < 0) {
5807 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5812 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5816 g_warning("failed to write queue message\n");
5823 if (fclose(fp) == EOF) {
5824 FILE_OP_ERROR(tmp, "fclose");
5831 if (item && *item) {
5834 queue = account_get_special_folder(compose->account, F_QUEUE);
5837 g_warning("can't find queue folder\n");
5843 folder_item_scan(queue);
5844 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5845 g_warning("can't queue the message\n");
5852 if (msgpath == NULL) {
5858 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5859 compose_remove_reedit_target(compose, FALSE);
5862 if ((msgnum != NULL) && (item != NULL)) {
5870 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5873 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5875 struct stat statbuf;
5876 gchar *type, *subtype;
5877 GtkTreeModel *model;
5880 model = gtk_tree_view_get_model(tree_view);
5882 if (!gtk_tree_model_get_iter_first(model, &iter))
5885 gtk_tree_model_get(model, &iter,
5889 if (!is_file_exist(ainfo->file)) {
5890 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5891 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5892 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5894 if (val == G_ALERTDEFAULT) {
5899 mimepart = procmime_mimeinfo_new();
5900 mimepart->content = MIMECONTENT_FILE;
5901 mimepart->data.filename = g_strdup(ainfo->file);
5902 mimepart->tmp = FALSE; /* or we destroy our attachment */
5903 mimepart->offset = 0;
5905 g_stat(ainfo->file, &statbuf);
5906 mimepart->length = statbuf.st_size;
5908 type = g_strdup(ainfo->content_type);
5910 if (!strchr(type, '/')) {
5912 type = g_strdup("application/octet-stream");
5915 subtype = strchr(type, '/') + 1;
5916 *(subtype - 1) = '\0';
5917 mimepart->type = procmime_get_media_type(type);
5918 mimepart->subtype = g_strdup(subtype);
5921 if (mimepart->type == MIMETYPE_MESSAGE &&
5922 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5923 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5926 if (mimepart->type == MIMETYPE_APPLICATION &&
5927 !strcmp2(mimepart->subtype, "octet-stream"))
5928 g_hash_table_insert(mimepart->typeparameters,
5929 g_strdup("name"), g_strdup(ainfo->name));
5930 g_hash_table_insert(mimepart->dispositionparameters,
5931 g_strdup("filename"), g_strdup(ainfo->name));
5932 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5936 if (compose->use_signing) {
5937 if (ainfo->encoding == ENC_7BIT)
5938 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5939 else if (ainfo->encoding == ENC_8BIT)
5940 ainfo->encoding = ENC_BASE64;
5943 procmime_encode_content(mimepart, ainfo->encoding);
5945 g_node_append(parent->node, mimepart->node);
5946 } while (gtk_tree_model_iter_next(model, &iter));
5951 #define IS_IN_CUSTOM_HEADER(header) \
5952 (compose->account->add_customhdr && \
5953 custom_header_find(compose->account->customhdr_list, header) != NULL)
5955 static void compose_add_headerfield_from_headerlist(Compose *compose,
5957 const gchar *fieldname,
5958 const gchar *seperator)
5960 gchar *str, *fieldname_w_colon;
5961 gboolean add_field = FALSE;
5963 ComposeHeaderEntry *headerentry;
5964 const gchar *headerentryname;
5965 const gchar *trans_fieldname;
5968 if (IS_IN_CUSTOM_HEADER(fieldname))
5971 debug_print("Adding %s-fields\n", fieldname);
5973 fieldstr = g_string_sized_new(64);
5975 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5976 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5978 for (list = compose->header_list; list; list = list->next) {
5979 headerentry = ((ComposeHeaderEntry *)list->data);
5980 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5982 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5983 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5985 if (str[0] != '\0') {
5987 g_string_append(fieldstr, seperator);
5988 g_string_append(fieldstr, str);
5997 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5998 compose_convert_header
5999 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6000 strlen(fieldname) + 2, TRUE);
6001 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6005 g_free(fieldname_w_colon);
6006 g_string_free(fieldstr, TRUE);
6011 static gchar *compose_get_header(Compose *compose)
6013 gchar buf[BUFFSIZE];
6014 const gchar *entry_str;
6018 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6020 gchar *from_name = NULL, *from_address = NULL;
6023 cm_return_val_if_fail(compose->account != NULL, NULL);
6024 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6026 header = g_string_sized_new(64);
6029 get_rfc822_date(buf, sizeof(buf));
6030 g_string_append_printf(header, "Date: %s\n", buf);
6034 if (compose->account->name && *compose->account->name) {
6036 QUOTE_IF_REQUIRED(buf, compose->account->name);
6037 tmp = g_strdup_printf("%s <%s>",
6038 buf, compose->account->address);
6040 tmp = g_strdup_printf("%s",
6041 compose->account->address);
6043 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6044 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6046 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6047 from_address = g_strdup(compose->account->address);
6049 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6050 /* extract name and address */
6051 if (strstr(spec, " <") && strstr(spec, ">")) {
6052 from_address = g_strdup(strrchr(spec, '<')+1);
6053 *(strrchr(from_address, '>')) = '\0';
6054 from_name = g_strdup(spec);
6055 *(strrchr(from_name, '<')) = '\0';
6058 from_address = g_strdup(spec);
6065 if (from_name && *from_name) {
6066 compose_convert_header
6067 (compose, buf, sizeof(buf), from_name,
6068 strlen("From: "), TRUE);
6069 QUOTE_IF_REQUIRED(name, buf);
6071 g_string_append_printf(header, "From: %s <%s>\n",
6072 name, from_address);
6074 g_string_append_printf(header, "From: %s\n", from_address);
6077 g_free(from_address);
6080 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6083 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6086 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6089 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6092 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6094 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6097 compose_convert_header(compose, buf, sizeof(buf), str,
6098 strlen("Subject: "), FALSE);
6099 g_string_append_printf(header, "Subject: %s\n", buf);
6105 if (compose->account->set_domain && compose->account->domain) {
6106 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6107 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6108 g_snprintf(buf, sizeof(buf), "%s",
6109 strchr(compose->account->address, '@') ?
6110 strchr(compose->account->address, '@')+1 :
6111 compose->account->address);
6113 g_snprintf(buf, sizeof(buf), "%s", "");
6116 if (compose->account->gen_msgid) {
6118 if (compose->account->msgid_with_addr) {
6119 addr = compose->account->address;
6121 generate_msgid(buf, sizeof(buf), addr);
6122 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6123 compose->msgid = g_strdup(buf);
6125 compose->msgid = NULL;
6128 if (compose->remove_references == FALSE) {
6130 if (compose->inreplyto && compose->to_list)
6131 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6134 if (compose->references)
6135 g_string_append_printf(header, "References: %s\n", compose->references);
6139 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6142 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6145 if (compose->account->organization &&
6146 strlen(compose->account->organization) &&
6147 !IS_IN_CUSTOM_HEADER("Organization")) {
6148 compose_convert_header(compose, buf, sizeof(buf),
6149 compose->account->organization,
6150 strlen("Organization: "), FALSE);
6151 g_string_append_printf(header, "Organization: %s\n", buf);
6154 /* Program version and system info */
6155 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6156 !compose->newsgroup_list) {
6157 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6159 gtk_major_version, gtk_minor_version, gtk_micro_version,
6162 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6163 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6165 gtk_major_version, gtk_minor_version, gtk_micro_version,
6169 /* custom headers */
6170 if (compose->account->add_customhdr) {
6173 for (cur = compose->account->customhdr_list; cur != NULL;
6175 CustomHeader *chdr = (CustomHeader *)cur->data;
6177 if (custom_header_is_allowed(chdr->name)) {
6178 compose_convert_header
6179 (compose, buf, sizeof(buf),
6180 chdr->value ? chdr->value : "",
6181 strlen(chdr->name) + 2, FALSE);
6182 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6187 /* Automatic Faces and X-Faces */
6188 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6189 g_string_append_printf(header, "X-Face: %s\n", buf);
6191 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6192 g_string_append_printf(header, "X-Face: %s\n", buf);
6194 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6195 g_string_append_printf(header, "Face: %s\n", buf);
6197 else if (get_default_face (buf, sizeof(buf)) == 0) {
6198 g_string_append_printf(header, "Face: %s\n", buf);
6202 switch (compose->priority) {
6203 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6204 "X-Priority: 1 (Highest)\n");
6206 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6207 "X-Priority: 2 (High)\n");
6209 case PRIORITY_NORMAL: break;
6210 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6211 "X-Priority: 4 (Low)\n");
6213 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6214 "X-Priority: 5 (Lowest)\n");
6216 default: debug_print("compose: priority unknown : %d\n",
6220 /* Request Return Receipt */
6221 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6222 if (compose->return_receipt) {
6223 if (compose->account->name
6224 && *compose->account->name) {
6225 compose_convert_header(compose, buf, sizeof(buf),
6226 compose->account->name,
6227 strlen("Disposition-Notification-To: "),
6229 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6231 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6235 /* get special headers */
6236 for (list = compose->header_list; list; list = list->next) {
6237 ComposeHeaderEntry *headerentry;
6240 gchar *headername_wcolon;
6241 const gchar *headername_trans;
6244 gboolean standard_header = FALSE;
6246 headerentry = ((ComposeHeaderEntry *)list->data);
6248 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6250 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6255 if (!strstr(tmp, ":")) {
6256 headername_wcolon = g_strconcat(tmp, ":", NULL);
6257 headername = g_strdup(tmp);
6259 headername_wcolon = g_strdup(tmp);
6260 headername = g_strdup(strtok(tmp, ":"));
6264 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6265 Xstrdup_a(headervalue, entry_str, return NULL);
6266 subst_char(headervalue, '\r', ' ');
6267 subst_char(headervalue, '\n', ' ');
6268 string = std_headers;
6269 while (*string != NULL) {
6270 headername_trans = prefs_common_translated_header_name(*string);
6271 if (!strcmp(headername_trans, headername_wcolon))
6272 standard_header = TRUE;
6275 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6276 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6279 g_free(headername_wcolon);
6283 g_string_free(header, FALSE);
6288 #undef IS_IN_CUSTOM_HEADER
6290 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6291 gint header_len, gboolean addr_field)
6293 gchar *tmpstr = NULL;
6294 const gchar *out_codeset = NULL;
6296 cm_return_if_fail(src != NULL);
6297 cm_return_if_fail(dest != NULL);
6299 if (len < 1) return;
6301 tmpstr = g_strdup(src);
6303 subst_char(tmpstr, '\n', ' ');
6304 subst_char(tmpstr, '\r', ' ');
6307 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6308 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6309 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6314 codeconv_set_strict(TRUE);
6315 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6316 conv_get_charset_str(compose->out_encoding));
6317 codeconv_set_strict(FALSE);
6319 if (!dest || *dest == '\0') {
6320 gchar *test_conv_global_out = NULL;
6321 gchar *test_conv_reply = NULL;
6323 /* automatic mode. be automatic. */
6324 codeconv_set_strict(TRUE);
6326 out_codeset = conv_get_outgoing_charset_str();
6328 debug_print("trying to convert to %s\n", out_codeset);
6329 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6332 if (!test_conv_global_out && compose->orig_charset
6333 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6334 out_codeset = compose->orig_charset;
6335 debug_print("failure; trying to convert to %s\n", out_codeset);
6336 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6339 if (!test_conv_global_out && !test_conv_reply) {
6341 out_codeset = CS_INTERNAL;
6342 debug_print("finally using %s\n", out_codeset);
6344 g_free(test_conv_global_out);
6345 g_free(test_conv_reply);
6346 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6348 codeconv_set_strict(FALSE);
6353 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6357 cm_return_if_fail(user_data != NULL);
6359 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6360 g_strstrip(address);
6361 if (*address != '\0') {
6362 gchar *name = procheader_get_fromname(address);
6363 extract_address(address);
6364 addressbook_add_contact(name, address, NULL, NULL);
6369 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6371 GtkWidget *menuitem;
6374 cm_return_if_fail(menu != NULL);
6375 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6377 menuitem = gtk_separator_menu_item_new();
6378 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6379 gtk_widget_show(menuitem);
6381 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6382 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6384 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6385 g_strstrip(address);
6386 if (*address == '\0') {
6387 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6390 g_signal_connect(G_OBJECT(menuitem), "activate",
6391 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6392 gtk_widget_show(menuitem);
6395 static void compose_create_header_entry(Compose *compose)
6397 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6404 const gchar *header = NULL;
6405 ComposeHeaderEntry *headerentry;
6406 gboolean standard_header = FALSE;
6407 GtkListStore *model;
6409 #if !(GTK_CHECK_VERSION(2,12,0))
6410 GtkTooltips *tips = compose->tooltips;
6413 headerentry = g_new0(ComposeHeaderEntry, 1);
6416 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6417 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6418 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6420 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6422 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6424 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6425 COMPOSE_NEWSGROUPS);
6426 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6428 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6429 COMPOSE_FOLLOWUPTO);
6431 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6432 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6433 G_CALLBACK(compose_grab_focus_cb), compose);
6434 gtk_widget_show(combo);
6435 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6436 compose->header_nextrow, compose->header_nextrow+1,
6437 GTK_SHRINK, GTK_FILL, 0, 0);
6438 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6439 const gchar *last_header_entry = gtk_entry_get_text(
6440 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6442 while (*string != NULL) {
6443 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6444 standard_header = TRUE;
6447 if (standard_header)
6448 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6450 if (!compose->header_last || !standard_header) {
6451 switch(compose->account->protocol) {
6453 header = prefs_common_translated_header_name("Newsgroups:");
6456 header = prefs_common_translated_header_name("To:");
6461 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6463 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6464 G_CALLBACK(compose_grab_focus_cb), compose);
6466 /* Entry field with cleanup button */
6467 #if GTK_CHECK_VERSION(2, 8, 0)
6468 button = gtk_button_new();
6469 gtk_button_set_image(GTK_BUTTON(button),
6470 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6472 button = gtk_button_new_with_label(_("Clear"));
6474 gtk_widget_show(button);
6475 CLAWS_SET_TIP(button,
6476 _("Delete entry contents"));
6477 entry = gtk_entry_new();
6478 gtk_widget_show(entry);
6479 CLAWS_SET_TIP(entry,
6480 _("Use <tab> to autocomplete from addressbook"));
6481 hbox = gtk_hbox_new (FALSE, 0);
6482 gtk_widget_show(hbox);
6483 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6484 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6485 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6486 compose->header_nextrow, compose->header_nextrow+1,
6487 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6489 g_signal_connect(G_OBJECT(entry), "key-press-event",
6490 G_CALLBACK(compose_headerentry_key_press_event_cb),
6492 g_signal_connect(G_OBJECT(entry), "changed",
6493 G_CALLBACK(compose_headerentry_changed_cb),
6495 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6496 G_CALLBACK(compose_grab_focus_cb), compose);
6498 g_signal_connect(G_OBJECT(button), "clicked",
6499 G_CALLBACK(compose_headerentry_button_clicked_cb),
6503 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6504 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6505 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6506 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6507 G_CALLBACK(compose_header_drag_received_cb),
6509 g_signal_connect(G_OBJECT(entry), "drag-drop",
6510 G_CALLBACK(compose_drag_drop),
6512 g_signal_connect(G_OBJECT(entry), "populate-popup",
6513 G_CALLBACK(compose_entry_popup_extend),
6516 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6518 headerentry->compose = compose;
6519 headerentry->combo = combo;
6520 headerentry->entry = entry;
6521 headerentry->button = button;
6522 headerentry->hbox = hbox;
6523 headerentry->headernum = compose->header_nextrow;
6524 headerentry->type = PREF_NONE;
6526 compose->header_nextrow++;
6527 compose->header_last = headerentry;
6528 compose->header_list =
6529 g_slist_append(compose->header_list,
6533 static void compose_add_header_entry(Compose *compose, const gchar *header,
6534 gchar *text, ComposePrefType pref_type)
6536 ComposeHeaderEntry *last_header = compose->header_last;
6537 gchar *tmp = g_strdup(text), *email;
6539 extract_address(tmp);
6540 email = g_utf8_strdown(tmp, -1);
6542 if (!(!strcmp(header, "To:") && !strcasecmp(compose->account->address, email))
6543 && g_hash_table_lookup(compose->email_hashtable, email) != NULL) {
6544 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6545 header, text, (gint) pref_type);
6551 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6552 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6553 last_header->type = pref_type;
6555 g_hash_table_insert(compose->email_hashtable, email,
6556 GUINT_TO_POINTER(1));
6560 static void compose_destroy_headerentry(Compose *compose,
6561 ComposeHeaderEntry *headerentry)
6563 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6566 extract_address(text);
6567 email = g_utf8_strdown(text, -1);
6568 g_hash_table_remove(compose->email_hashtable, email);
6572 gtk_widget_destroy(headerentry->combo);
6573 gtk_widget_destroy(headerentry->entry);
6574 gtk_widget_destroy(headerentry->button);
6575 gtk_widget_destroy(headerentry->hbox);
6576 g_free(headerentry);
6579 static void compose_remove_header_entries(Compose *compose)
6582 for (list = compose->header_list; list; list = list->next)
6583 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6585 compose->header_last = NULL;
6586 g_slist_free(compose->header_list);
6587 compose->header_list = NULL;
6588 compose->header_nextrow = 1;
6589 compose_create_header_entry(compose);
6592 static GtkWidget *compose_create_header(Compose *compose)
6594 GtkWidget *from_optmenu_hbox;
6595 GtkWidget *header_scrolledwin;
6596 GtkWidget *header_table;
6600 /* header labels and entries */
6601 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6602 gtk_widget_show(header_scrolledwin);
6603 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6605 header_table = gtk_table_new(2, 2, FALSE);
6606 gtk_widget_show(header_table);
6607 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6608 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6609 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6612 /* option menu for selecting accounts */
6613 from_optmenu_hbox = compose_account_option_menu_create(compose);
6614 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6615 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6618 compose->header_table = header_table;
6619 compose->header_list = NULL;
6620 compose->header_nextrow = count;
6622 compose_create_header_entry(compose);
6624 compose->table = NULL;
6626 return header_scrolledwin ;
6629 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6631 Compose *compose = (Compose *)data;
6632 GdkEventButton event;
6635 event.time = gtk_get_current_event_time();
6637 return attach_button_pressed(compose->attach_clist, &event, compose);
6640 static GtkWidget *compose_create_attach(Compose *compose)
6642 GtkWidget *attach_scrwin;
6643 GtkWidget *attach_clist;
6645 GtkListStore *store;
6646 GtkCellRenderer *renderer;
6647 GtkTreeViewColumn *column;
6648 GtkTreeSelection *selection;
6650 /* attachment list */
6651 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6652 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6653 GTK_POLICY_AUTOMATIC,
6654 GTK_POLICY_AUTOMATIC);
6655 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6657 store = gtk_list_store_new(N_ATTACH_COLS,
6662 G_TYPE_AUTO_POINTER,
6664 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6665 (GTK_TREE_MODEL(store)));
6666 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6667 g_object_unref(store);
6669 renderer = gtk_cell_renderer_text_new();
6670 column = gtk_tree_view_column_new_with_attributes
6671 (_("Mime type"), renderer, "text",
6672 COL_MIMETYPE, NULL);
6673 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6675 renderer = gtk_cell_renderer_text_new();
6676 column = gtk_tree_view_column_new_with_attributes
6677 (_("Size"), renderer, "text",
6679 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6681 renderer = gtk_cell_renderer_text_new();
6682 column = gtk_tree_view_column_new_with_attributes
6683 (_("Name"), renderer, "text",
6685 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6687 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6688 prefs_common.use_stripes_everywhere);
6689 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6690 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6692 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6693 G_CALLBACK(attach_selected), compose);
6694 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6695 G_CALLBACK(attach_button_pressed), compose);
6697 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6698 G_CALLBACK(popup_attach_button_pressed), compose);
6700 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6701 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6702 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6703 G_CALLBACK(popup_attach_button_pressed), compose);
6705 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6706 G_CALLBACK(attach_key_pressed), compose);
6709 gtk_drag_dest_set(attach_clist,
6710 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6711 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6712 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6713 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6714 G_CALLBACK(compose_attach_drag_received_cb),
6716 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6717 G_CALLBACK(compose_drag_drop),
6720 compose->attach_scrwin = attach_scrwin;
6721 compose->attach_clist = attach_clist;
6723 return attach_scrwin;
6726 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6727 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6729 static GtkWidget *compose_create_others(Compose *compose)
6732 GtkWidget *savemsg_checkbtn;
6733 GtkWidget *savemsg_combo;
6734 GtkWidget *savemsg_select;
6737 gchar *folderidentifier;
6739 /* Table for settings */
6740 table = gtk_table_new(3, 1, FALSE);
6741 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6742 gtk_widget_show(table);
6743 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6746 /* Save Message to folder */
6747 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6748 gtk_widget_show(savemsg_checkbtn);
6749 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6750 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6751 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6753 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6754 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6756 savemsg_combo = gtk_combo_box_entry_new_text();
6757 compose->savemsg_checkbtn = savemsg_checkbtn;
6758 compose->savemsg_combo = savemsg_combo;
6759 gtk_widget_show(savemsg_combo);
6761 if (prefs_common.compose_save_to_history)
6762 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6763 prefs_common.compose_save_to_history);
6765 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6766 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6767 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6768 G_CALLBACK(compose_grab_focus_cb), compose);
6769 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6770 folderidentifier = folder_item_get_identifier(account_get_special_folder
6771 (compose->account, F_OUTBOX));
6772 compose_set_save_to(compose, folderidentifier);
6773 g_free(folderidentifier);
6776 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6777 gtk_widget_show(savemsg_select);
6778 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6779 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6780 G_CALLBACK(compose_savemsg_select_cb),
6788 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6790 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6791 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6794 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6799 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6802 path = folder_item_get_identifier(dest);
6804 compose_set_save_to(compose, path);
6808 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6809 GdkAtom clip, GtkTextIter *insert_place);
6812 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6816 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6818 if (event->button == 3) {
6820 GtkTextIter sel_start, sel_end;
6821 gboolean stuff_selected;
6823 /* move the cursor to allow GtkAspell to check the word
6824 * under the mouse */
6825 if (event->x && event->y) {
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 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6833 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6836 stuff_selected = gtk_text_buffer_get_selection_bounds(
6838 &sel_start, &sel_end);
6840 gtk_text_buffer_place_cursor (buffer, &iter);
6841 /* reselect stuff */
6843 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6844 gtk_text_buffer_select_range(buffer,
6845 &sel_start, &sel_end);
6847 return FALSE; /* pass the event so that the right-click goes through */
6850 if (event->button == 2) {
6855 /* get the middle-click position to paste at the correct place */
6856 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6857 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6859 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6862 entry_paste_clipboard(compose, text,
6863 prefs_common.linewrap_pastes,
6864 GDK_SELECTION_PRIMARY, &iter);
6872 static void compose_spell_menu_changed(void *data)
6874 Compose *compose = (Compose *)data;
6876 GtkWidget *menuitem;
6877 GtkWidget *parent_item;
6878 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6881 if (compose->gtkaspell == NULL)
6884 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6885 "/Menu/Spelling/Options");
6887 /* setting the submenu removes /Spelling/Options from the factory
6888 * so we need to save it */
6890 if (parent_item == NULL) {
6891 parent_item = compose->aspell_options_menu;
6892 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6894 compose->aspell_options_menu = parent_item;
6896 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6898 spell_menu = g_slist_reverse(spell_menu);
6899 for (items = spell_menu;
6900 items; items = items->next) {
6901 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6902 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6903 gtk_widget_show(GTK_WIDGET(menuitem));
6905 g_slist_free(spell_menu);
6907 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6908 gtk_widget_show(parent_item);
6911 static void compose_dict_changed(void *data)
6913 Compose *compose = (Compose *) data;
6915 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6918 gtkaspell_highlight_all(compose->gtkaspell);
6919 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6923 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6925 Compose *compose = (Compose *)data;
6926 GdkEventButton event;
6929 event.time = gtk_get_current_event_time();
6933 return text_clicked(compose->text, &event, compose);
6936 static gboolean compose_force_window_origin = TRUE;
6937 static Compose *compose_create(PrefsAccount *account,
6946 GtkWidget *handlebox;
6948 GtkWidget *notebook;
6950 GtkWidget *attach_hbox;
6951 GtkWidget *attach_lab1;
6952 GtkWidget *attach_lab2;
6957 GtkWidget *subject_hbox;
6958 GtkWidget *subject_frame;
6959 GtkWidget *subject_entry;
6963 GtkWidget *edit_vbox;
6964 GtkWidget *ruler_hbox;
6966 GtkWidget *scrolledwin;
6968 GtkTextBuffer *buffer;
6969 GtkClipboard *clipboard;
6972 UndoMain *undostruct;
6974 gchar *titles[N_ATTACH_COLS];
6975 GtkWidget *popupmenu;
6976 GtkWidget *tmpl_menu;
6977 GtkActionGroup *action_group = NULL;
6980 GtkAspell * gtkaspell = NULL;
6983 static GdkGeometry geometry;
6985 cm_return_val_if_fail(account != NULL, NULL);
6987 debug_print("Creating compose window...\n");
6988 compose = g_new0(Compose, 1);
6990 titles[COL_MIMETYPE] = _("MIME type");
6991 titles[COL_SIZE] = _("Size");
6992 titles[COL_NAME] = _("Name");
6994 compose->batch = batch;
6995 compose->account = account;
6996 compose->folder = folder;
6998 compose->mutex = g_mutex_new();
6999 compose->set_cursor_pos = -1;
7001 #if !(GTK_CHECK_VERSION(2,12,0))
7002 compose->tooltips = tips;
7005 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7007 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7008 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7010 if (!geometry.max_width) {
7011 geometry.max_width = gdk_screen_width();
7012 geometry.max_height = gdk_screen_height();
7015 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7016 &geometry, GDK_HINT_MAX_SIZE);
7017 if (!geometry.min_width) {
7018 geometry.min_width = 600;
7019 geometry.min_height = 440;
7021 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7022 &geometry, GDK_HINT_MIN_SIZE);
7024 #ifndef GENERIC_UMPC
7025 if (compose_force_window_origin)
7026 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7027 prefs_common.compose_y);
7029 g_signal_connect(G_OBJECT(window), "delete_event",
7030 G_CALLBACK(compose_delete_cb), compose);
7031 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7032 gtk_widget_realize(window);
7034 gtkut_widget_set_composer_icon(window);
7036 vbox = gtk_vbox_new(FALSE, 0);
7037 gtk_container_add(GTK_CONTAINER(window), vbox);
7039 compose->ui_manager = gtk_ui_manager_new();
7040 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7041 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7042 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7043 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7044 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7045 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7046 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7047 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7048 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7049 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7052 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7054 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7057 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7060 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7062 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7063 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7064 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7067 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7068 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7069 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7070 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7072 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7073 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7076 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7079 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7080 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7081 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7083 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7084 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7085 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7087 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7088 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7089 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7092 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7094 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7096 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7097 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7098 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7099 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7100 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7101 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7102 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7103 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7104 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7105 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7107 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7108 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7110 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7112 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7113 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7114 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7115 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7116 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7118 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7120 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7124 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7125 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7127 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7128 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7129 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7133 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7134 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7135 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7136 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7137 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7139 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7140 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7146 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7149 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7155 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7157 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7162 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7169 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)
7170 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)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7173 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7176 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)
7177 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)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7182 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)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7186 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)
7187 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7192 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)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7198 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)
7199 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)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7211 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)
7213 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7215 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7221 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7222 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7223 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7227 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7229 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7230 gtk_widget_show_all(menubar);
7232 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7234 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7236 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7239 if (prefs_common.toolbar_detachable) {
7240 handlebox = gtk_handle_box_new();
7242 handlebox = gtk_hbox_new(FALSE, 0);
7244 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7246 gtk_widget_realize(handlebox);
7248 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7251 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7255 vbox2 = gtk_vbox_new(FALSE, 2);
7256 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7257 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7260 notebook = gtk_notebook_new();
7261 gtk_widget_set_size_request(notebook, -1, 130);
7262 gtk_widget_show(notebook);
7264 /* header labels and entries */
7265 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7266 compose_create_header(compose),
7267 gtk_label_new_with_mnemonic(_("Hea_der")));
7268 /* attachment list */
7269 attach_hbox = gtk_hbox_new(FALSE, 0);
7270 gtk_widget_show(attach_hbox);
7272 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7273 gtk_widget_show(attach_lab1);
7274 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7276 attach_lab2 = gtk_label_new("");
7277 gtk_widget_show(attach_lab2);
7278 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7280 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7281 compose_create_attach(compose),
7284 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7285 compose_create_others(compose),
7286 gtk_label_new_with_mnemonic(_("Othe_rs")));
7289 subject_hbox = gtk_hbox_new(FALSE, 0);
7290 gtk_widget_show(subject_hbox);
7292 subject_frame = gtk_frame_new(NULL);
7293 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7294 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7295 gtk_widget_show(subject_frame);
7297 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7298 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7299 gtk_widget_show(subject);
7301 label = gtk_label_new(_("Subject:"));
7302 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7303 gtk_widget_show(label);
7306 subject_entry = claws_spell_entry_new();
7308 subject_entry = gtk_entry_new();
7310 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7311 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7312 G_CALLBACK(compose_grab_focus_cb), compose);
7313 gtk_widget_show(subject_entry);
7314 compose->subject_entry = subject_entry;
7315 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7317 edit_vbox = gtk_vbox_new(FALSE, 0);
7319 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7322 ruler_hbox = gtk_hbox_new(FALSE, 0);
7323 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7325 ruler = gtk_shruler_new();
7326 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7327 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7331 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7332 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7333 GTK_POLICY_AUTOMATIC,
7334 GTK_POLICY_AUTOMATIC);
7335 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7337 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7338 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7340 text = gtk_text_view_new();
7341 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7342 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7343 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7344 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7345 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7347 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7349 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7350 G_CALLBACK(compose_edit_size_alloc),
7352 g_signal_connect(G_OBJECT(buffer), "changed",
7353 G_CALLBACK(compose_changed_cb), compose);
7354 g_signal_connect(G_OBJECT(text), "grab_focus",
7355 G_CALLBACK(compose_grab_focus_cb), compose);
7356 g_signal_connect(G_OBJECT(buffer), "insert_text",
7357 G_CALLBACK(text_inserted), compose);
7358 g_signal_connect(G_OBJECT(text), "button_press_event",
7359 G_CALLBACK(text_clicked), compose);
7361 g_signal_connect(G_OBJECT(text), "popup-menu",
7362 G_CALLBACK(compose_popup_menu), compose);
7364 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7365 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7366 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7367 G_CALLBACK(compose_popup_menu), compose);
7369 g_signal_connect(G_OBJECT(subject_entry), "changed",
7370 G_CALLBACK(compose_changed_cb), compose);
7373 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7374 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7375 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7376 g_signal_connect(G_OBJECT(text), "drag_data_received",
7377 G_CALLBACK(compose_insert_drag_received_cb),
7379 g_signal_connect(G_OBJECT(text), "drag-drop",
7380 G_CALLBACK(compose_drag_drop),
7382 gtk_widget_show_all(vbox);
7384 /* pane between attach clist and text */
7385 paned = gtk_vpaned_new();
7386 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7388 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7389 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7391 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7393 gtk_paned_add1(GTK_PANED(paned), notebook);
7394 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7395 gtk_widget_show_all(paned);
7398 if (prefs_common.textfont) {
7399 PangoFontDescription *font_desc;
7401 font_desc = pango_font_description_from_string
7402 (prefs_common.textfont);
7404 gtk_widget_modify_font(text, font_desc);
7405 pango_font_description_free(font_desc);
7409 gtk_action_group_add_actions(action_group, compose_popup_entries,
7410 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7412 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7413 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7414 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7415 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7416 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7418 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7420 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7421 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7422 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7424 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7426 undostruct = undo_init(text);
7427 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7430 address_completion_start(window);
7432 compose->window = window;
7433 compose->vbox = vbox;
7434 compose->menubar = menubar;
7435 compose->handlebox = handlebox;
7437 compose->vbox2 = vbox2;
7439 compose->paned = paned;
7441 compose->attach_label = attach_lab2;
7443 compose->notebook = notebook;
7444 compose->edit_vbox = edit_vbox;
7445 compose->ruler_hbox = ruler_hbox;
7446 compose->ruler = ruler;
7447 compose->scrolledwin = scrolledwin;
7448 compose->text = text;
7450 compose->focused_editable = NULL;
7452 compose->popupmenu = popupmenu;
7454 compose->tmpl_menu = tmpl_menu;
7456 compose->mode = mode;
7457 compose->rmode = mode;
7459 compose->targetinfo = NULL;
7460 compose->replyinfo = NULL;
7461 compose->fwdinfo = NULL;
7463 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7464 g_str_equal, (GDestroyNotify) g_free, NULL);
7466 compose->replyto = NULL;
7468 compose->bcc = NULL;
7469 compose->followup_to = NULL;
7471 compose->ml_post = NULL;
7473 compose->inreplyto = NULL;
7474 compose->references = NULL;
7475 compose->msgid = NULL;
7476 compose->boundary = NULL;
7478 compose->autowrap = prefs_common.autowrap;
7479 compose->autoindent = prefs_common.auto_indent;
7480 compose->use_signing = FALSE;
7481 compose->use_encryption = FALSE;
7482 compose->privacy_system = NULL;
7484 compose->modified = FALSE;
7486 compose->return_receipt = FALSE;
7488 compose->to_list = NULL;
7489 compose->newsgroup_list = NULL;
7491 compose->undostruct = undostruct;
7493 compose->sig_str = NULL;
7495 compose->exteditor_file = NULL;
7496 compose->exteditor_pid = -1;
7497 compose->exteditor_tag = -1;
7498 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7501 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7502 if (mode != COMPOSE_REDIRECT) {
7503 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7504 strcmp(prefs_common.dictionary, "")) {
7505 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7506 prefs_common.alt_dictionary,
7507 conv_get_locale_charset_str(),
7508 prefs_common.misspelled_col,
7509 prefs_common.check_while_typing,
7510 prefs_common.recheck_when_changing_dict,
7511 prefs_common.use_alternate,
7512 prefs_common.use_both_dicts,
7513 GTK_TEXT_VIEW(text),
7514 GTK_WINDOW(compose->window),
7515 compose_dict_changed,
7516 compose_spell_menu_changed,
7519 alertpanel_error(_("Spell checker could not "
7521 gtkaspell_checkers_strerror());
7522 gtkaspell_checkers_reset_error();
7524 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7528 compose->gtkaspell = gtkaspell;
7529 compose_spell_menu_changed(compose);
7530 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7533 compose_select_account(compose, account, TRUE);
7535 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7536 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7538 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7539 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7541 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7542 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7544 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7545 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7547 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7548 if (account->protocol != A_NNTP)
7549 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7550 prefs_common_translated_header_name("To:"));
7552 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7553 prefs_common_translated_header_name("Newsgroups:"));
7555 addressbook_set_target_compose(compose);
7557 if (mode != COMPOSE_REDIRECT)
7558 compose_set_template_menu(compose);
7560 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7563 compose_list = g_list_append(compose_list, compose);
7565 if (!prefs_common.show_ruler)
7566 gtk_widget_hide(ruler_hbox);
7568 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7571 compose->priority = PRIORITY_NORMAL;
7572 compose_update_priority_menu_item(compose);
7574 compose_set_out_encoding(compose);
7577 compose_update_actions_menu(compose);
7579 /* Privacy Systems menu */
7580 compose_update_privacy_systems_menu(compose);
7582 activate_privacy_system(compose, account, TRUE);
7583 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7585 gtk_widget_realize(window);
7587 gtk_widget_show(window);
7589 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7590 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7597 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7602 GtkWidget *optmenubox;
7605 GtkWidget *from_name = NULL;
7606 #if !(GTK_CHECK_VERSION(2,12,0))
7607 GtkTooltips *tips = compose->tooltips;
7610 gint num = 0, def_menu = 0;
7612 accounts = account_get_list();
7613 cm_return_val_if_fail(accounts != NULL, NULL);
7615 optmenubox = gtk_event_box_new();
7616 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7617 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7619 hbox = gtk_hbox_new(FALSE, 6);
7620 from_name = gtk_entry_new();
7622 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7623 G_CALLBACK(compose_grab_focus_cb), compose);
7625 for (; accounts != NULL; accounts = accounts->next, num++) {
7626 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7627 gchar *name, *from = NULL;
7629 if (ac == compose->account) def_menu = num;
7631 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7634 if (ac == compose->account) {
7635 if (ac->name && *ac->name) {
7637 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7638 from = g_strdup_printf("%s <%s>",
7640 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7642 from = g_strdup_printf("%s",
7644 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7647 COMBOBOX_ADD(menu, name, ac->account_id);
7652 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7654 g_signal_connect(G_OBJECT(optmenu), "changed",
7655 G_CALLBACK(account_activated),
7657 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7658 G_CALLBACK(compose_entry_popup_extend),
7661 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7662 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7664 CLAWS_SET_TIP(optmenubox,
7665 _("Account to use for this email"));
7666 CLAWS_SET_TIP(from_name,
7667 _("Sender address to be used"));
7669 compose->from_name = from_name;
7674 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7676 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7677 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7678 Compose *compose = (Compose *) data;
7680 compose->priority = value;
7684 static void compose_reply_change_mode(Compose *compose,
7687 gboolean was_modified = compose->modified;
7689 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7691 cm_return_if_fail(compose->replyinfo != NULL);
7693 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7695 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7697 if (action == COMPOSE_REPLY_TO_ALL)
7699 if (action == COMPOSE_REPLY_TO_SENDER)
7701 if (action == COMPOSE_REPLY_TO_LIST)
7704 compose_remove_header_entries(compose);
7705 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7706 if (compose->account->set_autocc && compose->account->auto_cc)
7707 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7709 if (compose->account->set_autobcc && compose->account->auto_bcc)
7710 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7712 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7713 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7714 compose_show_first_last_header(compose, TRUE);
7715 compose->modified = was_modified;
7716 compose_set_title(compose);
7719 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7721 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7722 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7723 Compose *compose = (Compose *) data;
7726 compose_reply_change_mode(compose, value);
7729 static void compose_update_priority_menu_item(Compose * compose)
7731 GtkWidget *menuitem = NULL;
7732 switch (compose->priority) {
7733 case PRIORITY_HIGHEST:
7734 menuitem = gtk_ui_manager_get_widget
7735 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7738 menuitem = gtk_ui_manager_get_widget
7739 (compose->ui_manager, "/Menu/Options/Priority/High");
7741 case PRIORITY_NORMAL:
7742 menuitem = gtk_ui_manager_get_widget
7743 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7746 menuitem = gtk_ui_manager_get_widget
7747 (compose->ui_manager, "/Menu/Options/Priority/Low");
7749 case PRIORITY_LOWEST:
7750 menuitem = gtk_ui_manager_get_widget
7751 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7754 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7757 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7759 Compose *compose = (Compose *) data;
7761 gboolean can_sign = FALSE, can_encrypt = FALSE;
7763 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7765 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7768 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7769 g_free(compose->privacy_system);
7770 compose->privacy_system = NULL;
7771 if (systemid != NULL) {
7772 compose->privacy_system = g_strdup(systemid);
7774 can_sign = privacy_system_can_sign(systemid);
7775 can_encrypt = privacy_system_can_encrypt(systemid);
7778 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7780 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7781 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7784 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7786 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7787 GtkWidget *menuitem = NULL;
7789 gboolean can_sign = FALSE, can_encrypt = FALSE;
7790 gboolean found = FALSE;
7792 if (compose->privacy_system != NULL) {
7794 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7795 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7796 cm_return_if_fail(menuitem != NULL);
7798 amenu = GTK_MENU_SHELL(menuitem)->children;
7800 while (amenu != NULL) {
7801 GList *alist = amenu->next;
7803 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7804 if (systemid != NULL) {
7805 if (strcmp(systemid, compose->privacy_system) == 0 &&
7806 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7807 menuitem = GTK_WIDGET(amenu->data);
7809 can_sign = privacy_system_can_sign(systemid);
7810 can_encrypt = privacy_system_can_encrypt(systemid);
7814 } else if (strlen(compose->privacy_system) == 0 &&
7815 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7816 menuitem = GTK_WIDGET(amenu->data);
7819 can_encrypt = FALSE;
7826 if (menuitem != NULL)
7827 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7829 if (warn && !found && strlen(compose->privacy_system)) {
7830 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7831 "will not be able to sign or encrypt this message."),
7832 compose->privacy_system);
7836 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7837 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7840 static void compose_set_out_encoding(Compose *compose)
7842 CharSet out_encoding;
7843 const gchar *branch = NULL;
7844 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7846 switch(out_encoding) {
7847 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7848 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7849 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7850 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7851 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7852 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7853 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7854 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7855 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7856 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7857 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7858 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7859 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7860 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7861 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7862 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7863 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7864 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7865 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7866 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7867 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7868 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7869 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7870 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7871 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7872 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7873 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7874 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7875 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7876 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7877 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7878 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7880 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7883 static void compose_set_template_menu(Compose *compose)
7885 GSList *tmpl_list, *cur;
7889 tmpl_list = template_get_config();
7891 menu = gtk_menu_new();
7893 gtk_menu_set_accel_group (GTK_MENU (menu),
7894 gtk_ui_manager_get_accel_group(compose->ui_manager));
7895 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7896 Template *tmpl = (Template *)cur->data;
7897 gchar *accel_path = NULL;
7898 item = gtk_menu_item_new_with_label(tmpl->name);
7899 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7900 g_signal_connect(G_OBJECT(item), "activate",
7901 G_CALLBACK(compose_template_activate_cb),
7903 g_object_set_data(G_OBJECT(item), "template", tmpl);
7904 gtk_widget_show(item);
7905 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7906 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7910 gtk_widget_show(menu);
7911 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7914 void compose_update_actions_menu(Compose *compose)
7916 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7919 static void compose_update_privacy_systems_menu(Compose *compose)
7921 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7922 GSList *systems, *cur;
7924 GtkWidget *system_none;
7926 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7927 GtkWidget *privacy_menu = gtk_menu_new();
7929 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7930 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7932 g_signal_connect(G_OBJECT(system_none), "activate",
7933 G_CALLBACK(compose_set_privacy_system_cb), compose);
7935 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7936 gtk_widget_show(system_none);
7938 systems = privacy_get_system_ids();
7939 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7940 gchar *systemid = cur->data;
7942 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7943 widget = gtk_radio_menu_item_new_with_label(group,
7944 privacy_system_get_name(systemid));
7945 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7946 g_strdup(systemid), g_free);
7947 g_signal_connect(G_OBJECT(widget), "activate",
7948 G_CALLBACK(compose_set_privacy_system_cb), compose);
7950 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7951 gtk_widget_show(widget);
7954 g_slist_free(systems);
7955 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7956 gtk_widget_show_all(privacy_menu);
7957 gtk_widget_show_all(privacy_menuitem);
7960 void compose_reflect_prefs_all(void)
7965 for (cur = compose_list; cur != NULL; cur = cur->next) {
7966 compose = (Compose *)cur->data;
7967 compose_set_template_menu(compose);
7971 void compose_reflect_prefs_pixmap_theme(void)
7976 for (cur = compose_list; cur != NULL; cur = cur->next) {
7977 compose = (Compose *)cur->data;
7978 toolbar_update(TOOLBAR_COMPOSE, compose);
7982 static const gchar *compose_quote_char_from_context(Compose *compose)
7984 const gchar *qmark = NULL;
7986 cm_return_val_if_fail(compose != NULL, NULL);
7988 switch (compose->mode) {
7989 /* use forward-specific quote char */
7990 case COMPOSE_FORWARD:
7991 case COMPOSE_FORWARD_AS_ATTACH:
7992 case COMPOSE_FORWARD_INLINE:
7993 if (compose->folder && compose->folder->prefs &&
7994 compose->folder->prefs->forward_with_format)
7995 qmark = compose->folder->prefs->forward_quotemark;
7996 else if (compose->account->forward_with_format)
7997 qmark = compose->account->forward_quotemark;
7999 qmark = prefs_common.fw_quotemark;
8002 /* use reply-specific quote char in all other modes */
8004 if (compose->folder && compose->folder->prefs &&
8005 compose->folder->prefs->reply_with_format)
8006 qmark = compose->folder->prefs->reply_quotemark;
8007 else if (compose->account->reply_with_format)
8008 qmark = compose->account->reply_quotemark;
8010 qmark = prefs_common.quotemark;
8014 if (qmark == NULL || *qmark == '\0')
8020 static void compose_template_apply(Compose *compose, Template *tmpl,
8024 GtkTextBuffer *buffer;
8028 gchar *parsed_str = NULL;
8029 gint cursor_pos = 0;
8030 const gchar *err_msg = _("The body of the template has an error at line %d.");
8033 /* process the body */
8035 text = GTK_TEXT_VIEW(compose->text);
8036 buffer = gtk_text_view_get_buffer(text);
8039 qmark = compose_quote_char_from_context(compose);
8041 if (compose->replyinfo != NULL) {
8044 gtk_text_buffer_set_text(buffer, "", -1);
8045 mark = gtk_text_buffer_get_insert(buffer);
8046 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8048 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8049 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8051 } else if (compose->fwdinfo != NULL) {
8054 gtk_text_buffer_set_text(buffer, "", -1);
8055 mark = gtk_text_buffer_get_insert(buffer);
8056 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8058 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8059 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8062 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8064 GtkTextIter start, end;
8067 gtk_text_buffer_get_start_iter(buffer, &start);
8068 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8069 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8071 /* clear the buffer now */
8073 gtk_text_buffer_set_text(buffer, "", -1);
8075 parsed_str = compose_quote_fmt(compose, dummyinfo,
8076 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8077 procmsg_msginfo_free( dummyinfo );
8083 gtk_text_buffer_set_text(buffer, "", -1);
8084 mark = gtk_text_buffer_get_insert(buffer);
8085 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8088 if (replace && parsed_str && compose->account->auto_sig)
8089 compose_insert_sig(compose, FALSE);
8091 if (replace && parsed_str) {
8092 gtk_text_buffer_get_start_iter(buffer, &iter);
8093 gtk_text_buffer_place_cursor(buffer, &iter);
8097 cursor_pos = quote_fmt_get_cursor_pos();
8098 compose->set_cursor_pos = cursor_pos;
8099 if (cursor_pos == -1)
8101 gtk_text_buffer_get_start_iter(buffer, &iter);
8102 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8103 gtk_text_buffer_place_cursor(buffer, &iter);
8106 /* process the other fields */
8108 compose_template_apply_fields(compose, tmpl);
8109 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8110 quote_fmt_reset_vartable();
8111 compose_changed_cb(NULL, compose);
8114 if (compose->gtkaspell->check_while_typing)
8115 gtkaspell_highlight_all(compose->gtkaspell);
8119 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8121 MsgInfo* dummyinfo = NULL;
8122 MsgInfo *msginfo = NULL;
8125 if (compose->replyinfo != NULL)
8126 msginfo = compose->replyinfo;
8127 else if (compose->fwdinfo != NULL)
8128 msginfo = compose->fwdinfo;
8130 dummyinfo = compose_msginfo_new_from_compose(compose);
8131 msginfo = dummyinfo;
8134 if (tmpl->from && *tmpl->from != '\0') {
8136 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8137 compose->gtkaspell);
8139 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8141 quote_fmt_scan_string(tmpl->from);
8144 buf = quote_fmt_get_buffer();
8146 alertpanel_error(_("Template From format error."));
8148 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8152 if (tmpl->to && *tmpl->to != '\0') {
8154 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8155 compose->gtkaspell);
8157 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8159 quote_fmt_scan_string(tmpl->to);
8162 buf = quote_fmt_get_buffer();
8164 alertpanel_error(_("Template To format error."));
8166 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8170 if (tmpl->cc && *tmpl->cc != '\0') {
8172 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8173 compose->gtkaspell);
8175 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8177 quote_fmt_scan_string(tmpl->cc);
8180 buf = quote_fmt_get_buffer();
8182 alertpanel_error(_("Template Cc format error."));
8184 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8188 if (tmpl->bcc && *tmpl->bcc != '\0') {
8190 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8191 compose->gtkaspell);
8193 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8195 quote_fmt_scan_string(tmpl->bcc);
8198 buf = quote_fmt_get_buffer();
8200 alertpanel_error(_("Template Bcc format error."));
8202 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8206 /* process the subject */
8207 if (tmpl->subject && *tmpl->subject != '\0') {
8209 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8210 compose->gtkaspell);
8212 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8214 quote_fmt_scan_string(tmpl->subject);
8217 buf = quote_fmt_get_buffer();
8219 alertpanel_error(_("Template subject format error."));
8221 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8225 procmsg_msginfo_free( dummyinfo );
8228 static void compose_destroy(Compose *compose)
8230 GtkTextBuffer *buffer;
8231 GtkClipboard *clipboard;
8233 compose_list = g_list_remove(compose_list, compose);
8235 if (compose->updating) {
8236 debug_print("danger, not destroying anything now\n");
8237 compose->deferred_destroy = TRUE;
8240 /* NOTE: address_completion_end() does nothing with the window
8241 * however this may change. */
8242 address_completion_end(compose->window);
8244 slist_free_strings(compose->to_list);
8245 g_slist_free(compose->to_list);
8246 slist_free_strings(compose->newsgroup_list);
8247 g_slist_free(compose->newsgroup_list);
8248 slist_free_strings(compose->header_list);
8249 g_slist_free(compose->header_list);
8251 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8253 g_hash_table_destroy(compose->email_hashtable);
8255 procmsg_msginfo_free(compose->targetinfo);
8256 procmsg_msginfo_free(compose->replyinfo);
8257 procmsg_msginfo_free(compose->fwdinfo);
8259 g_free(compose->replyto);
8260 g_free(compose->cc);
8261 g_free(compose->bcc);
8262 g_free(compose->newsgroups);
8263 g_free(compose->followup_to);
8265 g_free(compose->ml_post);
8267 g_free(compose->inreplyto);
8268 g_free(compose->references);
8269 g_free(compose->msgid);
8270 g_free(compose->boundary);
8272 g_free(compose->redirect_filename);
8273 if (compose->undostruct)
8274 undo_destroy(compose->undostruct);
8276 g_free(compose->sig_str);
8278 g_free(compose->exteditor_file);
8280 g_free(compose->orig_charset);
8282 g_free(compose->privacy_system);
8284 if (addressbook_get_target_compose() == compose)
8285 addressbook_set_target_compose(NULL);
8288 if (compose->gtkaspell) {
8289 gtkaspell_delete(compose->gtkaspell);
8290 compose->gtkaspell = NULL;
8294 if (!compose->batch) {
8295 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8296 prefs_common.compose_height = compose->window->allocation.height;
8299 if (!gtk_widget_get_parent(compose->paned))
8300 gtk_widget_destroy(compose->paned);
8301 gtk_widget_destroy(compose->popupmenu);
8303 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8304 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8305 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8307 gtk_widget_destroy(compose->window);
8308 toolbar_destroy(compose->toolbar);
8309 g_free(compose->toolbar);
8310 g_mutex_free(compose->mutex);
8314 static void compose_attach_info_free(AttachInfo *ainfo)
8316 g_free(ainfo->file);
8317 g_free(ainfo->content_type);
8318 g_free(ainfo->name);
8322 static void compose_attach_update_label(Compose *compose)
8327 GtkTreeModel *model;
8332 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8333 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8334 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8338 while(gtk_tree_model_iter_next(model, &iter))
8341 text = g_strdup_printf("(%d)", i);
8342 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8346 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8348 Compose *compose = (Compose *)data;
8349 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8350 GtkTreeSelection *selection;
8352 GtkTreeModel *model;
8354 selection = gtk_tree_view_get_selection(tree_view);
8355 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8360 for (cur = sel; cur != NULL; cur = cur->next) {
8361 GtkTreePath *path = cur->data;
8362 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8365 gtk_tree_path_free(path);
8368 for (cur = sel; cur != NULL; cur = cur->next) {
8369 GtkTreeRowReference *ref = cur->data;
8370 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8373 if (gtk_tree_model_get_iter(model, &iter, path))
8374 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8376 gtk_tree_path_free(path);
8377 gtk_tree_row_reference_free(ref);
8381 compose_attach_update_label(compose);
8384 static struct _AttachProperty
8387 GtkWidget *mimetype_entry;
8388 GtkWidget *encoding_optmenu;
8389 GtkWidget *path_entry;
8390 GtkWidget *filename_entry;
8392 GtkWidget *cancel_btn;
8395 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8397 gtk_tree_path_free((GtkTreePath *)ptr);
8400 static void compose_attach_property(GtkAction *action, gpointer data)
8402 Compose *compose = (Compose *)data;
8403 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8405 GtkComboBox *optmenu;
8406 GtkTreeSelection *selection;
8408 GtkTreeModel *model;
8411 static gboolean cancelled;
8413 /* only if one selected */
8414 selection = gtk_tree_view_get_selection(tree_view);
8415 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8418 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8422 path = (GtkTreePath *) sel->data;
8423 gtk_tree_model_get_iter(model, &iter, path);
8424 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8427 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8433 if (!attach_prop.window)
8434 compose_attach_property_create(&cancelled);
8435 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8436 gtk_widget_grab_focus(attach_prop.ok_btn);
8437 gtk_widget_show(attach_prop.window);
8438 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8440 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8441 if (ainfo->encoding == ENC_UNKNOWN)
8442 combobox_select_by_data(optmenu, ENC_BASE64);
8444 combobox_select_by_data(optmenu, ainfo->encoding);
8446 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8447 ainfo->content_type ? ainfo->content_type : "");
8448 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8449 ainfo->file ? ainfo->file : "");
8450 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8451 ainfo->name ? ainfo->name : "");
8454 const gchar *entry_text;
8456 gchar *cnttype = NULL;
8463 gtk_widget_hide(attach_prop.window);
8464 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8469 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8470 if (*entry_text != '\0') {
8473 text = g_strstrip(g_strdup(entry_text));
8474 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8475 cnttype = g_strdup(text);
8478 alertpanel_error(_("Invalid MIME type."));
8484 ainfo->encoding = combobox_get_active_data(optmenu);
8486 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8487 if (*entry_text != '\0') {
8488 if (is_file_exist(entry_text) &&
8489 (size = get_file_size(entry_text)) > 0)
8490 file = g_strdup(entry_text);
8493 (_("File doesn't exist or is empty."));
8499 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8500 if (*entry_text != '\0') {
8501 g_free(ainfo->name);
8502 ainfo->name = g_strdup(entry_text);
8506 g_free(ainfo->content_type);
8507 ainfo->content_type = cnttype;
8510 g_free(ainfo->file);
8514 ainfo->size = (goffset)size;
8516 /* update tree store */
8517 text = to_human_readable(ainfo->size);
8518 gtk_tree_model_get_iter(model, &iter, path);
8519 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8520 COL_MIMETYPE, ainfo->content_type,
8522 COL_NAME, ainfo->name,
8528 gtk_tree_path_free(path);
8531 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8533 label = gtk_label_new(str); \
8534 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8535 GTK_FILL, 0, 0, 0); \
8536 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8538 entry = gtk_entry_new(); \
8539 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8540 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8543 static void compose_attach_property_create(gboolean *cancelled)
8549 GtkWidget *mimetype_entry;
8552 GtkListStore *optmenu_menu;
8553 GtkWidget *path_entry;
8554 GtkWidget *filename_entry;
8557 GtkWidget *cancel_btn;
8558 GList *mime_type_list, *strlist;
8561 debug_print("Creating attach_property window...\n");
8563 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8564 gtk_widget_set_size_request(window, 480, -1);
8565 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8566 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8567 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8568 g_signal_connect(G_OBJECT(window), "delete_event",
8569 G_CALLBACK(attach_property_delete_event),
8571 g_signal_connect(G_OBJECT(window), "key_press_event",
8572 G_CALLBACK(attach_property_key_pressed),
8575 vbox = gtk_vbox_new(FALSE, 8);
8576 gtk_container_add(GTK_CONTAINER(window), vbox);
8578 table = gtk_table_new(4, 2, FALSE);
8579 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8580 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8581 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8583 label = gtk_label_new(_("MIME type"));
8584 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8586 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8587 mimetype_entry = gtk_combo_box_entry_new_text();
8588 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8589 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8591 /* stuff with list */
8592 mime_type_list = procmime_get_mime_type_list();
8594 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8595 MimeType *type = (MimeType *) mime_type_list->data;
8598 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8600 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8603 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8604 (GCompareFunc)strcmp2);
8607 for (mime_type_list = strlist; mime_type_list != NULL;
8608 mime_type_list = mime_type_list->next) {
8609 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8610 g_free(mime_type_list->data);
8612 g_list_free(strlist);
8613 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8614 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8616 label = gtk_label_new(_("Encoding"));
8617 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8619 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8621 hbox = gtk_hbox_new(FALSE, 0);
8622 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8623 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8625 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8626 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8628 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8629 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8630 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8631 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8632 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8634 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8636 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8637 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8639 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8640 &ok_btn, GTK_STOCK_OK,
8642 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8643 gtk_widget_grab_default(ok_btn);
8645 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8646 G_CALLBACK(attach_property_ok),
8648 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8649 G_CALLBACK(attach_property_cancel),
8652 gtk_widget_show_all(vbox);
8654 attach_prop.window = window;
8655 attach_prop.mimetype_entry = mimetype_entry;
8656 attach_prop.encoding_optmenu = optmenu;
8657 attach_prop.path_entry = path_entry;
8658 attach_prop.filename_entry = filename_entry;
8659 attach_prop.ok_btn = ok_btn;
8660 attach_prop.cancel_btn = cancel_btn;
8663 #undef SET_LABEL_AND_ENTRY
8665 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8671 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8677 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8678 gboolean *cancelled)
8686 static gboolean attach_property_key_pressed(GtkWidget *widget,
8688 gboolean *cancelled)
8690 if (event && event->keyval == GDK_Escape) {
8694 if (event && event->keyval == GDK_Return) {
8702 static void compose_exec_ext_editor(Compose *compose)
8709 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8710 G_DIR_SEPARATOR, compose);
8712 if (pipe(pipe_fds) < 0) {
8718 if ((pid = fork()) < 0) {
8725 /* close the write side of the pipe */
8728 compose->exteditor_file = g_strdup(tmp);
8729 compose->exteditor_pid = pid;
8731 compose_set_ext_editor_sensitive(compose, FALSE);
8734 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8736 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8738 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8742 } else { /* process-monitoring process */
8748 /* close the read side of the pipe */
8751 if (compose_write_body_to_file(compose, tmp) < 0) {
8752 fd_write_all(pipe_fds[1], "2\n", 2);
8756 pid_ed = compose_exec_ext_editor_real(tmp);
8758 fd_write_all(pipe_fds[1], "1\n", 2);
8762 /* wait until editor is terminated */
8763 waitpid(pid_ed, NULL, 0);
8765 fd_write_all(pipe_fds[1], "0\n", 2);
8772 #endif /* G_OS_UNIX */
8776 static gint compose_exec_ext_editor_real(const gchar *file)
8783 cm_return_val_if_fail(file != NULL, -1);
8785 if ((pid = fork()) < 0) {
8790 if (pid != 0) return pid;
8792 /* grandchild process */
8794 if (setpgid(0, getppid()))
8797 if (prefs_common_get_ext_editor_cmd() &&
8798 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8799 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8800 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8802 if (prefs_common_get_ext_editor_cmd())
8803 g_warning("External editor command-line is invalid: '%s'\n",
8804 prefs_common_get_ext_editor_cmd());
8805 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8808 cmdline = strsplit_with_quote(buf, " ", 1024);
8809 execvp(cmdline[0], cmdline);
8812 g_strfreev(cmdline);
8817 static gboolean compose_ext_editor_kill(Compose *compose)
8819 pid_t pgid = compose->exteditor_pid * -1;
8822 ret = kill(pgid, 0);
8824 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8828 msg = g_strdup_printf
8829 (_("The external editor is still working.\n"
8830 "Force terminating the process?\n"
8831 "process group id: %d"), -pgid);
8832 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8833 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8837 if (val == G_ALERTALTERNATE) {
8838 g_source_remove(compose->exteditor_tag);
8839 g_io_channel_shutdown(compose->exteditor_ch,
8841 g_io_channel_unref(compose->exteditor_ch);
8843 if (kill(pgid, SIGTERM) < 0) perror("kill");
8844 waitpid(compose->exteditor_pid, NULL, 0);
8846 g_warning("Terminated process group id: %d", -pgid);
8847 g_warning("Temporary file: %s",
8848 compose->exteditor_file);
8850 compose_set_ext_editor_sensitive(compose, TRUE);
8852 g_free(compose->exteditor_file);
8853 compose->exteditor_file = NULL;
8854 compose->exteditor_pid = -1;
8855 compose->exteditor_ch = NULL;
8856 compose->exteditor_tag = -1;
8864 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8868 Compose *compose = (Compose *)data;
8871 debug_print(_("Compose: input from monitoring process\n"));
8873 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8875 g_io_channel_shutdown(source, FALSE, NULL);
8876 g_io_channel_unref(source);
8878 waitpid(compose->exteditor_pid, NULL, 0);
8880 if (buf[0] == '0') { /* success */
8881 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8882 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8884 gtk_text_buffer_set_text(buffer, "", -1);
8885 compose_insert_file(compose, compose->exteditor_file);
8886 compose_changed_cb(NULL, compose);
8887 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8889 if (claws_unlink(compose->exteditor_file) < 0)
8890 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8891 } else if (buf[0] == '1') { /* failed */
8892 g_warning("Couldn't exec external editor\n");
8893 if (claws_unlink(compose->exteditor_file) < 0)
8894 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8895 } else if (buf[0] == '2') {
8896 g_warning("Couldn't write to file\n");
8897 } else if (buf[0] == '3') {
8898 g_warning("Pipe read failed\n");
8901 compose_set_ext_editor_sensitive(compose, TRUE);
8903 g_free(compose->exteditor_file);
8904 compose->exteditor_file = NULL;
8905 compose->exteditor_pid = -1;
8906 compose->exteditor_ch = NULL;
8907 compose->exteditor_tag = -1;
8912 static void compose_set_ext_editor_sensitive(Compose *compose,
8915 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8916 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8917 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8918 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8919 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8920 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8921 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8923 gtk_widget_set_sensitive(compose->text, sensitive);
8924 if (compose->toolbar->send_btn)
8925 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8926 if (compose->toolbar->sendl_btn)
8927 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8928 if (compose->toolbar->draft_btn)
8929 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8930 if (compose->toolbar->insert_btn)
8931 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8932 if (compose->toolbar->sig_btn)
8933 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8934 if (compose->toolbar->exteditor_btn)
8935 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8936 if (compose->toolbar->linewrap_current_btn)
8937 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8938 if (compose->toolbar->linewrap_all_btn)
8939 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8941 #endif /* G_OS_UNIX */
8944 * compose_undo_state_changed:
8946 * Change the sensivity of the menuentries undo and redo
8948 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8949 gint redo_state, gpointer data)
8951 Compose *compose = (Compose *)data;
8953 switch (undo_state) {
8954 case UNDO_STATE_TRUE:
8955 if (!undostruct->undo_state) {
8956 undostruct->undo_state = TRUE;
8957 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8960 case UNDO_STATE_FALSE:
8961 if (undostruct->undo_state) {
8962 undostruct->undo_state = FALSE;
8963 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8966 case UNDO_STATE_UNCHANGED:
8968 case UNDO_STATE_REFRESH:
8969 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8972 g_warning("Undo state not recognized");
8976 switch (redo_state) {
8977 case UNDO_STATE_TRUE:
8978 if (!undostruct->redo_state) {
8979 undostruct->redo_state = TRUE;
8980 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8983 case UNDO_STATE_FALSE:
8984 if (undostruct->redo_state) {
8985 undostruct->redo_state = FALSE;
8986 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8989 case UNDO_STATE_UNCHANGED:
8991 case UNDO_STATE_REFRESH:
8992 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8995 g_warning("Redo state not recognized");
9000 /* callback functions */
9002 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9003 * includes "non-client" (windows-izm) in calculation, so this calculation
9004 * may not be accurate.
9006 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9007 GtkAllocation *allocation,
9008 GtkSHRuler *shruler)
9010 if (prefs_common.show_ruler) {
9011 gint char_width = 0, char_height = 0;
9012 gint line_width_in_chars;
9014 gtkut_get_font_size(GTK_WIDGET(widget),
9015 &char_width, &char_height);
9016 line_width_in_chars =
9017 (allocation->width - allocation->x) / char_width;
9019 /* got the maximum */
9020 gtk_ruler_set_range(GTK_RULER(shruler),
9021 0.0, line_width_in_chars, 0,
9022 /*line_width_in_chars*/ char_width);
9029 ComposeEntryType header;
9031 ComposePrefType type;
9032 gboolean entry_marked;
9035 static void account_activated(GtkComboBox *optmenu, gpointer data)
9037 Compose *compose = (Compose *)data;
9040 gchar *folderidentifier;
9041 gint account_id = 0;
9044 GSList *list, *saved_list = NULL;
9045 HeaderEntryState *state;
9046 GtkRcStyle *style = NULL;
9047 static GdkColor yellow;
9048 static gboolean color_set = FALSE;
9050 /* Get ID of active account in the combo box */
9051 menu = gtk_combo_box_get_model(optmenu);
9052 gtk_combo_box_get_active_iter(optmenu, &iter);
9053 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9055 ac = account_find_from_id(account_id);
9056 cm_return_if_fail(ac != NULL);
9058 if (ac != compose->account) {
9059 compose_select_account(compose, ac, FALSE);
9061 for (list = compose->header_list; list; list = list->next) {
9062 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9064 if (hentry->type == PREF_ACCOUNT || !list->next) {
9065 compose_destroy_headerentry(compose, hentry);
9069 state = g_malloc0(sizeof(HeaderEntryState));
9070 state->header = combobox_get_active_data(
9071 GTK_COMBO_BOX(hentry->combo));
9072 state->entry = gtk_editable_get_chars(
9073 GTK_EDITABLE(hentry->entry), 0, -1);
9074 state->type = hentry->type;
9077 gdk_color_parse("#f5f6be", &yellow);
9078 color_set = gdk_colormap_alloc_color(
9079 gdk_colormap_get_system(),
9080 &yellow, FALSE, TRUE);
9083 style = gtk_widget_get_modifier_style(hentry->entry);
9084 state->entry_marked = gdk_color_equal(&yellow,
9085 &style->base[GTK_STATE_NORMAL]);
9087 saved_list = g_slist_append(saved_list, state);
9088 compose_destroy_headerentry(compose, hentry);
9091 compose->header_last = NULL;
9092 g_slist_free(compose->header_list);
9093 compose->header_list = NULL;
9094 compose->header_nextrow = 1;
9095 compose_create_header_entry(compose);
9097 if (ac->set_autocc && ac->auto_cc)
9098 compose_entry_append(compose, ac->auto_cc,
9099 COMPOSE_CC, PREF_ACCOUNT);
9101 if (ac->set_autobcc && ac->auto_bcc)
9102 compose_entry_append(compose, ac->auto_bcc,
9103 COMPOSE_BCC, PREF_ACCOUNT);
9105 if (ac->set_autoreplyto && ac->auto_replyto)
9106 compose_entry_append(compose, ac->auto_replyto,
9107 COMPOSE_REPLYTO, PREF_ACCOUNT);
9109 for (list = saved_list; list; list = list->next) {
9110 state = (HeaderEntryState *) list->data;
9112 compose_entry_append(compose, state->entry,
9113 state->header, state->type);
9114 if (state->entry_marked)
9115 compose_entry_mark_default_to(compose, state->entry);
9117 g_free(state->entry);
9119 g_slist_free(saved_list);
9121 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9122 (ac->protocol == A_NNTP) ?
9123 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9126 /* Set message save folder */
9127 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9128 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9130 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9131 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9133 compose_set_save_to(compose, NULL);
9134 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9135 folderidentifier = folder_item_get_identifier(account_get_special_folder
9136 (compose->account, F_OUTBOX));
9137 compose_set_save_to(compose, folderidentifier);
9138 g_free(folderidentifier);
9142 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9143 GtkTreeViewColumn *column, Compose *compose)
9145 compose_attach_property(NULL, compose);
9148 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9151 Compose *compose = (Compose *)data;
9152 GtkTreeSelection *attach_selection;
9153 gint attach_nr_selected;
9155 if (!event) return FALSE;
9157 if (event->button == 3) {
9158 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9159 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9161 if (attach_nr_selected > 0)
9163 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9164 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9166 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9167 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9170 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9171 NULL, NULL, event->button, event->time);
9178 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9181 Compose *compose = (Compose *)data;
9183 if (!event) return FALSE;
9185 switch (event->keyval) {
9187 compose_attach_remove_selected(NULL, compose);
9193 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9195 toolbar_comp_set_sensitive(compose, allow);
9196 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9197 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9199 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9201 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9202 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9203 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9205 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9209 static void compose_send_cb(GtkAction *action, gpointer data)
9211 Compose *compose = (Compose *)data;
9213 if (prefs_common.work_offline &&
9214 !inc_offline_should_override(TRUE,
9215 _("Claws Mail needs network access in order "
9216 "to send this email.")))
9219 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9220 g_source_remove(compose->draft_timeout_tag);
9221 compose->draft_timeout_tag = -1;
9224 compose_send(compose);
9227 static void compose_send_later_cb(GtkAction *action, gpointer data)
9229 Compose *compose = (Compose *)data;
9233 compose_allow_user_actions(compose, FALSE);
9234 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9235 compose_allow_user_actions(compose, TRUE);
9239 compose_close(compose);
9240 } else if (val == -1) {
9241 alertpanel_error(_("Could not queue message."));
9242 } else if (val == -2) {
9243 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9244 } else if (val == -3) {
9245 if (privacy_peek_error())
9246 alertpanel_error(_("Could not queue message for sending:\n\n"
9247 "Signature failed: %s"), privacy_get_error());
9248 } else if (val == -4) {
9249 alertpanel_error(_("Could not queue message for sending:\n\n"
9250 "Charset conversion failed."));
9251 } else if (val == -5) {
9252 alertpanel_error(_("Could not queue message for sending:\n\n"
9253 "Couldn't get recipient encryption key."));
9254 } else if (val == -6) {
9257 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9260 #define DRAFTED_AT_EXIT "drafted_at_exit"
9261 static void compose_register_draft(MsgInfo *info)
9263 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9264 DRAFTED_AT_EXIT, NULL);
9265 FILE *fp = g_fopen(filepath, "ab");
9268 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9276 gboolean compose_draft (gpointer data, guint action)
9278 Compose *compose = (Compose *)data;
9282 MsgFlags flag = {0, 0};
9283 static gboolean lock = FALSE;
9284 MsgInfo *newmsginfo;
9286 gboolean target_locked = FALSE;
9287 gboolean err = FALSE;
9289 if (lock) return FALSE;
9291 if (compose->sending)
9294 draft = account_get_special_folder(compose->account, F_DRAFT);
9295 cm_return_val_if_fail(draft != NULL, FALSE);
9297 if (!g_mutex_trylock(compose->mutex)) {
9298 /* we don't want to lock the mutex once it's available,
9299 * because as the only other part of compose.c locking
9300 * it is compose_close - which means once unlocked,
9301 * the compose struct will be freed */
9302 debug_print("couldn't lock mutex, probably sending\n");
9308 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9309 G_DIR_SEPARATOR, compose);
9310 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9311 FILE_OP_ERROR(tmp, "fopen");
9315 /* chmod for security */
9316 if (change_file_mode_rw(fp, tmp) < 0) {
9317 FILE_OP_ERROR(tmp, "chmod");
9318 g_warning("can't change file mode\n");
9321 /* Save draft infos */
9322 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9323 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9325 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9326 gchar *savefolderid;
9328 savefolderid = compose_get_save_to(compose);
9329 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9330 g_free(savefolderid);
9332 if (compose->return_receipt) {
9333 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9335 if (compose->privacy_system) {
9336 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9337 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9338 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9341 /* Message-ID of message replying to */
9342 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9345 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9346 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9349 /* Message-ID of message forwarding to */
9350 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9353 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9354 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9358 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9359 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9361 /* end of headers */
9362 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9369 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9373 if (fclose(fp) == EOF) {
9377 if (compose->targetinfo) {
9378 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9379 flag.perm_flags = target_locked?MSG_LOCKED:0;
9381 flag.tmp_flags = MSG_DRAFT;
9383 folder_item_scan(draft);
9384 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9385 MsgInfo *tmpinfo = NULL;
9386 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9387 if (compose->msgid) {
9388 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9391 msgnum = tmpinfo->msgnum;
9392 procmsg_msginfo_free(tmpinfo);
9393 debug_print("got draft msgnum %d from scanning\n", msgnum);
9395 debug_print("didn't get draft msgnum after scanning\n");
9398 debug_print("got draft msgnum %d from adding\n", msgnum);
9404 if (action != COMPOSE_AUTO_SAVE) {
9405 if (action != COMPOSE_DRAFT_FOR_EXIT)
9406 alertpanel_error(_("Could not save draft."));
9409 gtkut_window_popup(compose->window);
9410 val = alertpanel_full(_("Could not save draft"),
9411 _("Could not save draft.\n"
9412 "Do you want to cancel exit or discard this email?"),
9413 _("_Cancel exit"), _("_Discard email"), NULL,
9414 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9415 if (val == G_ALERTALTERNATE) {
9417 g_mutex_unlock(compose->mutex); /* must be done before closing */
9418 compose_close(compose);
9422 g_mutex_unlock(compose->mutex); /* must be done before closing */
9431 if (compose->mode == COMPOSE_REEDIT) {
9432 compose_remove_reedit_target(compose, TRUE);
9435 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9438 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9440 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9442 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9443 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9444 procmsg_msginfo_set_flags(newmsginfo, 0,
9445 MSG_HAS_ATTACHMENT);
9447 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9448 compose_register_draft(newmsginfo);
9450 procmsg_msginfo_free(newmsginfo);
9453 folder_item_scan(draft);
9455 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9457 g_mutex_unlock(compose->mutex); /* must be done before closing */
9458 compose_close(compose);
9464 path = folder_item_fetch_msg(draft, msgnum);
9466 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9469 if (g_stat(path, &s) < 0) {
9470 FILE_OP_ERROR(path, "stat");
9476 procmsg_msginfo_free(compose->targetinfo);
9477 compose->targetinfo = procmsg_msginfo_new();
9478 compose->targetinfo->msgnum = msgnum;
9479 compose->targetinfo->size = (goffset)s.st_size;
9480 compose->targetinfo->mtime = s.st_mtime;
9481 compose->targetinfo->folder = draft;
9483 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9484 compose->mode = COMPOSE_REEDIT;
9486 if (action == COMPOSE_AUTO_SAVE) {
9487 compose->autosaved_draft = compose->targetinfo;
9489 compose->modified = FALSE;
9490 compose_set_title(compose);
9494 g_mutex_unlock(compose->mutex);
9498 void compose_clear_exit_drafts(void)
9500 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9501 DRAFTED_AT_EXIT, NULL);
9502 if (is_file_exist(filepath))
9503 claws_unlink(filepath);
9508 void compose_reopen_exit_drafts(void)
9510 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9511 DRAFTED_AT_EXIT, NULL);
9512 FILE *fp = g_fopen(filepath, "rb");
9516 while (fgets(buf, sizeof(buf), fp)) {
9517 gchar **parts = g_strsplit(buf, "\t", 2);
9518 const gchar *folder = parts[0];
9519 int msgnum = parts[1] ? atoi(parts[1]):-1;
9521 if (folder && *folder && msgnum > -1) {
9522 FolderItem *item = folder_find_item_from_identifier(folder);
9523 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9525 compose_reedit(info, FALSE);
9532 compose_clear_exit_drafts();
9535 static void compose_save_cb(GtkAction *action, gpointer data)
9537 Compose *compose = (Compose *)data;
9538 compose_draft(compose, COMPOSE_KEEP_EDITING);
9539 compose->rmode = COMPOSE_REEDIT;
9542 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9544 if (compose && file_list) {
9547 for ( tmp = file_list; tmp; tmp = tmp->next) {
9548 gchar *file = (gchar *) tmp->data;
9549 gchar *utf8_filename = conv_filename_to_utf8(file);
9550 compose_attach_append(compose, file, utf8_filename, NULL);
9551 compose_changed_cb(NULL, compose);
9556 g_free(utf8_filename);
9561 static void compose_attach_cb(GtkAction *action, gpointer data)
9563 Compose *compose = (Compose *)data;
9566 if (compose->redirect_filename != NULL)
9569 file_list = filesel_select_multiple_files_open(_("Select file"));
9572 compose_attach_from_list(compose, file_list, TRUE);
9573 g_list_free(file_list);
9577 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9579 Compose *compose = (Compose *)data;
9581 gint files_inserted = 0;
9583 file_list = filesel_select_multiple_files_open(_("Select file"));
9588 for ( tmp = file_list; tmp; tmp = tmp->next) {
9589 gchar *file = (gchar *) tmp->data;
9590 gchar *filedup = g_strdup(file);
9591 gchar *shortfile = g_path_get_basename(filedup);
9592 ComposeInsertResult res;
9593 /* insert the file if the file is short or if the user confirmed that
9594 he/she wants to insert the large file */
9595 res = compose_insert_file(compose, file);
9596 if (res == COMPOSE_INSERT_READ_ERROR) {
9597 alertpanel_error(_("File '%s' could not be read."), shortfile);
9598 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9599 alertpanel_error(_("File '%s' contained invalid characters\n"
9600 "for the current encoding, insertion may be incorrect."),
9602 } else if (res == COMPOSE_INSERT_SUCCESS)
9609 g_list_free(file_list);
9613 if (files_inserted > 0 && compose->gtkaspell->check_while_typing)
9614 gtkaspell_highlight_all(compose->gtkaspell);
9618 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9620 Compose *compose = (Compose *)data;
9622 compose_insert_sig(compose, FALSE);
9625 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9629 Compose *compose = (Compose *)data;
9631 gtkut_widget_get_uposition(widget, &x, &y);
9632 if (!compose->batch) {
9633 prefs_common.compose_x = x;
9634 prefs_common.compose_y = y;
9636 if (compose->sending || compose->updating)
9638 compose_close_cb(NULL, compose);
9642 void compose_close_toolbar(Compose *compose)
9644 compose_close_cb(NULL, compose);
9647 static void compose_close_cb(GtkAction *action, gpointer data)
9649 Compose *compose = (Compose *)data;
9653 if (compose->exteditor_tag != -1) {
9654 if (!compose_ext_editor_kill(compose))
9659 if (compose->modified) {
9660 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9661 if (!g_mutex_trylock(compose->mutex)) {
9662 /* we don't want to lock the mutex once it's available,
9663 * because as the only other part of compose.c locking
9664 * it is compose_close - which means once unlocked,
9665 * the compose struct will be freed */
9666 debug_print("couldn't lock mutex, probably sending\n");
9670 val = alertpanel(_("Discard message"),
9671 _("This message has been modified. Discard it?"),
9672 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9674 val = alertpanel(_("Save changes"),
9675 _("This message has been modified. Save the latest changes?"),
9676 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9678 g_mutex_unlock(compose->mutex);
9680 case G_ALERTDEFAULT:
9681 if (prefs_common.autosave && !reedit)
9682 compose_remove_draft(compose);
9684 case G_ALERTALTERNATE:
9685 compose_draft(data, COMPOSE_QUIT_EDITING);
9692 compose_close(compose);
9695 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9697 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9698 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9699 Compose *compose = (Compose *) data;
9702 compose->out_encoding = (CharSet)value;
9705 static void compose_address_cb(GtkAction *action, gpointer data)
9707 Compose *compose = (Compose *)data;
9709 addressbook_open(compose);
9712 static void about_show_cb(GtkAction *action, gpointer data)
9717 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9719 Compose *compose = (Compose *)data;
9724 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9725 cm_return_if_fail(tmpl != NULL);
9727 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9729 val = alertpanel(_("Apply template"), msg,
9730 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9733 if (val == G_ALERTDEFAULT)
9734 compose_template_apply(compose, tmpl, TRUE);
9735 else if (val == G_ALERTALTERNATE)
9736 compose_template_apply(compose, tmpl, FALSE);
9739 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9741 Compose *compose = (Compose *)data;
9743 compose_exec_ext_editor(compose);
9746 static void compose_undo_cb(GtkAction *action, gpointer data)
9748 Compose *compose = (Compose *)data;
9749 gboolean prev_autowrap = compose->autowrap;
9751 compose->autowrap = FALSE;
9752 undo_undo(compose->undostruct);
9753 compose->autowrap = prev_autowrap;
9756 static void compose_redo_cb(GtkAction *action, gpointer data)
9758 Compose *compose = (Compose *)data;
9759 gboolean prev_autowrap = compose->autowrap;
9761 compose->autowrap = FALSE;
9762 undo_redo(compose->undostruct);
9763 compose->autowrap = prev_autowrap;
9766 static void entry_cut_clipboard(GtkWidget *entry)
9768 if (GTK_IS_EDITABLE(entry))
9769 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9770 else if (GTK_IS_TEXT_VIEW(entry))
9771 gtk_text_buffer_cut_clipboard(
9772 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9773 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9777 static void entry_copy_clipboard(GtkWidget *entry)
9779 if (GTK_IS_EDITABLE(entry))
9780 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9781 else if (GTK_IS_TEXT_VIEW(entry))
9782 gtk_text_buffer_copy_clipboard(
9783 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9784 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9787 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9788 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9790 if (GTK_IS_TEXT_VIEW(entry)) {
9791 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9792 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9793 GtkTextIter start_iter, end_iter;
9795 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9797 if (contents == NULL)
9800 /* we shouldn't delete the selection when middle-click-pasting, or we
9801 * can't mid-click-paste our own selection */
9802 if (clip != GDK_SELECTION_PRIMARY) {
9803 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9804 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9807 if (insert_place == NULL) {
9808 /* if insert_place isn't specified, insert at the cursor.
9809 * used for Ctrl-V pasting */
9810 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9811 start = gtk_text_iter_get_offset(&start_iter);
9812 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9814 /* if insert_place is specified, paste here.
9815 * used for mid-click-pasting */
9816 start = gtk_text_iter_get_offset(insert_place);
9817 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9818 if (prefs_common.primary_paste_unselects)
9819 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9823 /* paste unwrapped: mark the paste so it's not wrapped later */
9824 end = start + strlen(contents);
9825 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9826 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9827 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9828 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9829 /* rewrap paragraph now (after a mid-click-paste) */
9830 mark_start = gtk_text_buffer_get_insert(buffer);
9831 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9832 gtk_text_iter_backward_char(&start_iter);
9833 compose_beautify_paragraph(compose, &start_iter, TRUE);
9835 } else if (GTK_IS_EDITABLE(entry))
9836 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9838 compose->modified = TRUE;
9841 static void entry_allsel(GtkWidget *entry)
9843 if (GTK_IS_EDITABLE(entry))
9844 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9845 else if (GTK_IS_TEXT_VIEW(entry)) {
9846 GtkTextIter startiter, enditer;
9847 GtkTextBuffer *textbuf;
9849 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9850 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9851 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9853 gtk_text_buffer_move_mark_by_name(textbuf,
9854 "selection_bound", &startiter);
9855 gtk_text_buffer_move_mark_by_name(textbuf,
9856 "insert", &enditer);
9860 static void compose_cut_cb(GtkAction *action, gpointer data)
9862 Compose *compose = (Compose *)data;
9863 if (compose->focused_editable
9864 #ifndef GENERIC_UMPC
9865 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9868 entry_cut_clipboard(compose->focused_editable);
9871 static void compose_copy_cb(GtkAction *action, gpointer data)
9873 Compose *compose = (Compose *)data;
9874 if (compose->focused_editable
9875 #ifndef GENERIC_UMPC
9876 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9879 entry_copy_clipboard(compose->focused_editable);
9882 static void compose_paste_cb(GtkAction *action, gpointer data)
9884 Compose *compose = (Compose *)data;
9886 GtkTextBuffer *buffer;
9888 if (compose->focused_editable &&
9889 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9890 entry_paste_clipboard(compose, compose->focused_editable,
9891 prefs_common.linewrap_pastes,
9892 GDK_SELECTION_CLIPBOARD, NULL);
9896 if (GTK_WIDGET_HAS_FOCUS(compose->text) &&
9897 compose->gtkaspell->check_while_typing)
9898 gtkaspell_highlight_all(compose->gtkaspell);
9902 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9904 Compose *compose = (Compose *)data;
9905 gint wrap_quote = prefs_common.linewrap_quote;
9906 if (compose->focused_editable
9907 #ifndef GENERIC_UMPC
9908 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9911 /* let text_insert() (called directly or at a later time
9912 * after the gtk_editable_paste_clipboard) know that
9913 * text is to be inserted as a quotation. implemented
9914 * by using a simple refcount... */
9915 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9916 G_OBJECT(compose->focused_editable),
9917 "paste_as_quotation"));
9918 g_object_set_data(G_OBJECT(compose->focused_editable),
9919 "paste_as_quotation",
9920 GINT_TO_POINTER(paste_as_quotation + 1));
9921 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9922 entry_paste_clipboard(compose, compose->focused_editable,
9923 prefs_common.linewrap_pastes,
9924 GDK_SELECTION_CLIPBOARD, NULL);
9925 prefs_common.linewrap_quote = wrap_quote;
9929 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9931 Compose *compose = (Compose *)data;
9933 GtkTextBuffer *buffer;
9935 if (compose->focused_editable
9936 #ifndef GENERIC_UMPC
9937 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9940 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9941 GDK_SELECTION_CLIPBOARD, NULL);
9945 if (GTK_WIDGET_HAS_FOCUS(compose->text) &&
9946 compose->gtkaspell->check_while_typing)
9947 gtkaspell_highlight_all(compose->gtkaspell);
9951 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9953 Compose *compose = (Compose *)data;
9955 GtkTextBuffer *buffer;
9957 if (compose->focused_editable
9958 #ifndef GENERIC_UMPC
9959 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9962 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9963 GDK_SELECTION_CLIPBOARD, NULL);
9967 if (GTK_WIDGET_HAS_FOCUS(compose->text) &&
9968 compose->gtkaspell->check_while_typing)
9969 gtkaspell_highlight_all(compose->gtkaspell);
9973 static void compose_allsel_cb(GtkAction *action, gpointer data)
9975 Compose *compose = (Compose *)data;
9976 if (compose->focused_editable
9977 #ifndef GENERIC_UMPC
9978 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9981 entry_allsel(compose->focused_editable);
9984 static void textview_move_beginning_of_line (GtkTextView *text)
9986 GtkTextBuffer *buffer;
9990 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9992 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9993 mark = gtk_text_buffer_get_insert(buffer);
9994 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9995 gtk_text_iter_set_line_offset(&ins, 0);
9996 gtk_text_buffer_place_cursor(buffer, &ins);
9999 static void textview_move_forward_character (GtkTextView *text)
10001 GtkTextBuffer *buffer;
10005 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10007 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10008 mark = gtk_text_buffer_get_insert(buffer);
10009 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10010 if (gtk_text_iter_forward_cursor_position(&ins))
10011 gtk_text_buffer_place_cursor(buffer, &ins);
10014 static void textview_move_backward_character (GtkTextView *text)
10016 GtkTextBuffer *buffer;
10020 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10022 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10023 mark = gtk_text_buffer_get_insert(buffer);
10024 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10025 if (gtk_text_iter_backward_cursor_position(&ins))
10026 gtk_text_buffer_place_cursor(buffer, &ins);
10029 static void textview_move_forward_word (GtkTextView *text)
10031 GtkTextBuffer *buffer;
10036 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10038 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10039 mark = gtk_text_buffer_get_insert(buffer);
10040 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10041 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10042 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10043 gtk_text_iter_backward_word_start(&ins);
10044 gtk_text_buffer_place_cursor(buffer, &ins);
10048 static void textview_move_backward_word (GtkTextView *text)
10050 GtkTextBuffer *buffer;
10055 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10057 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10058 mark = gtk_text_buffer_get_insert(buffer);
10059 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10060 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10061 if (gtk_text_iter_backward_word_starts(&ins, 1))
10062 gtk_text_buffer_place_cursor(buffer, &ins);
10065 static void textview_move_end_of_line (GtkTextView *text)
10067 GtkTextBuffer *buffer;
10071 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10073 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10074 mark = gtk_text_buffer_get_insert(buffer);
10075 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10076 if (gtk_text_iter_forward_to_line_end(&ins))
10077 gtk_text_buffer_place_cursor(buffer, &ins);
10080 static void textview_move_next_line (GtkTextView *text)
10082 GtkTextBuffer *buffer;
10087 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10089 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10090 mark = gtk_text_buffer_get_insert(buffer);
10091 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10092 offset = gtk_text_iter_get_line_offset(&ins);
10093 if (gtk_text_iter_forward_line(&ins)) {
10094 gtk_text_iter_set_line_offset(&ins, offset);
10095 gtk_text_buffer_place_cursor(buffer, &ins);
10099 static void textview_move_previous_line (GtkTextView *text)
10101 GtkTextBuffer *buffer;
10106 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10108 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10109 mark = gtk_text_buffer_get_insert(buffer);
10110 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10111 offset = gtk_text_iter_get_line_offset(&ins);
10112 if (gtk_text_iter_backward_line(&ins)) {
10113 gtk_text_iter_set_line_offset(&ins, offset);
10114 gtk_text_buffer_place_cursor(buffer, &ins);
10118 static void textview_delete_forward_character (GtkTextView *text)
10120 GtkTextBuffer *buffer;
10122 GtkTextIter ins, end_iter;
10124 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10126 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10127 mark = gtk_text_buffer_get_insert(buffer);
10128 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10130 if (gtk_text_iter_forward_char(&end_iter)) {
10131 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10135 static void textview_delete_backward_character (GtkTextView *text)
10137 GtkTextBuffer *buffer;
10139 GtkTextIter ins, end_iter;
10141 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10143 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10144 mark = gtk_text_buffer_get_insert(buffer);
10145 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10147 if (gtk_text_iter_backward_char(&end_iter)) {
10148 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10152 static void textview_delete_forward_word (GtkTextView *text)
10154 GtkTextBuffer *buffer;
10156 GtkTextIter ins, end_iter;
10158 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10160 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10161 mark = gtk_text_buffer_get_insert(buffer);
10162 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10164 if (gtk_text_iter_forward_word_end(&end_iter)) {
10165 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10169 static void textview_delete_backward_word (GtkTextView *text)
10171 GtkTextBuffer *buffer;
10173 GtkTextIter ins, end_iter;
10175 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10177 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10178 mark = gtk_text_buffer_get_insert(buffer);
10179 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10181 if (gtk_text_iter_backward_word_start(&end_iter)) {
10182 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10186 static void textview_delete_line (GtkTextView *text)
10188 GtkTextBuffer *buffer;
10190 GtkTextIter ins, start_iter, end_iter;
10192 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10194 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10195 mark = gtk_text_buffer_get_insert(buffer);
10196 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10199 gtk_text_iter_set_line_offset(&start_iter, 0);
10202 if (gtk_text_iter_ends_line(&end_iter)){
10203 if (!gtk_text_iter_forward_char(&end_iter))
10204 gtk_text_iter_backward_char(&start_iter);
10207 gtk_text_iter_forward_to_line_end(&end_iter);
10208 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10211 static void textview_delete_to_line_end (GtkTextView *text)
10213 GtkTextBuffer *buffer;
10215 GtkTextIter ins, end_iter;
10217 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10219 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10220 mark = gtk_text_buffer_get_insert(buffer);
10221 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10223 if (gtk_text_iter_ends_line(&end_iter))
10224 gtk_text_iter_forward_char(&end_iter);
10226 gtk_text_iter_forward_to_line_end(&end_iter);
10227 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10230 #define DO_ACTION(name, act) { \
10231 if(!strcmp(name, a_name)) { \
10235 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10237 const gchar *a_name = gtk_action_get_name(action);
10238 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10239 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10240 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10241 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10242 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10243 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10244 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10245 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10246 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10247 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10248 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10249 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10250 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10251 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10255 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10257 Compose *compose = (Compose *)data;
10258 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10259 ComposeCallAdvancedAction action = -1;
10261 action = compose_call_advanced_action_from_path(gaction);
10264 void (*do_action) (GtkTextView *text);
10265 } action_table[] = {
10266 {textview_move_beginning_of_line},
10267 {textview_move_forward_character},
10268 {textview_move_backward_character},
10269 {textview_move_forward_word},
10270 {textview_move_backward_word},
10271 {textview_move_end_of_line},
10272 {textview_move_next_line},
10273 {textview_move_previous_line},
10274 {textview_delete_forward_character},
10275 {textview_delete_backward_character},
10276 {textview_delete_forward_word},
10277 {textview_delete_backward_word},
10278 {textview_delete_line},
10279 {textview_delete_to_line_end}
10282 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10284 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10285 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10286 if (action_table[action].do_action)
10287 action_table[action].do_action(text);
10289 g_warning("Not implemented yet.");
10293 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10297 if (GTK_IS_EDITABLE(widget)) {
10298 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10299 gtk_editable_set_position(GTK_EDITABLE(widget),
10302 if (widget->parent && widget->parent->parent
10303 && widget->parent->parent->parent) {
10304 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10305 gint y = widget->allocation.y;
10306 gint height = widget->allocation.height;
10307 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10308 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10310 if (y < (int)shown->value) {
10311 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10313 if (y + height > (int)shown->value + (int)shown->page_size) {
10314 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10315 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10316 y + height - (int)shown->page_size - 1);
10318 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10319 (int)shown->upper - (int)shown->page_size - 1);
10326 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10327 compose->focused_editable = widget;
10329 #ifdef GENERIC_UMPC
10330 if (GTK_IS_TEXT_VIEW(widget)
10331 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10332 g_object_ref(compose->notebook);
10333 g_object_ref(compose->edit_vbox);
10334 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10335 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10336 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10337 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10338 g_object_unref(compose->notebook);
10339 g_object_unref(compose->edit_vbox);
10340 g_signal_handlers_block_by_func(G_OBJECT(widget),
10341 G_CALLBACK(compose_grab_focus_cb),
10343 gtk_widget_grab_focus(widget);
10344 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10345 G_CALLBACK(compose_grab_focus_cb),
10347 } else if (!GTK_IS_TEXT_VIEW(widget)
10348 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10349 g_object_ref(compose->notebook);
10350 g_object_ref(compose->edit_vbox);
10351 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10352 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10353 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10354 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10355 g_object_unref(compose->notebook);
10356 g_object_unref(compose->edit_vbox);
10357 g_signal_handlers_block_by_func(G_OBJECT(widget),
10358 G_CALLBACK(compose_grab_focus_cb),
10360 gtk_widget_grab_focus(widget);
10361 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10362 G_CALLBACK(compose_grab_focus_cb),
10368 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10370 compose->modified = TRUE;
10371 // compose_beautify_paragraph(compose, NULL, TRUE);
10372 #ifndef GENERIC_UMPC
10373 compose_set_title(compose);
10377 static void compose_wrap_cb(GtkAction *action, gpointer data)
10379 Compose *compose = (Compose *)data;
10380 compose_beautify_paragraph(compose, NULL, TRUE);
10383 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10385 Compose *compose = (Compose *)data;
10386 compose_wrap_all_full(compose, TRUE);
10389 static void compose_find_cb(GtkAction *action, gpointer data)
10391 Compose *compose = (Compose *)data;
10393 message_search_compose(compose);
10396 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10399 Compose *compose = (Compose *)data;
10400 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10401 if (compose->autowrap)
10402 compose_wrap_all_full(compose, TRUE);
10403 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10406 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10409 Compose *compose = (Compose *)data;
10410 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10413 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10415 Compose *compose = (Compose *)data;
10417 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10420 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10422 Compose *compose = (Compose *)data;
10424 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10427 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10429 g_free(compose->privacy_system);
10431 compose->privacy_system = g_strdup(account->default_privacy_system);
10432 compose_update_privacy_system_menu_item(compose, warn);
10435 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10437 Compose *compose = (Compose *)data;
10439 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10440 gtk_widget_show(compose->ruler_hbox);
10441 prefs_common.show_ruler = TRUE;
10443 gtk_widget_hide(compose->ruler_hbox);
10444 gtk_widget_queue_resize(compose->edit_vbox);
10445 prefs_common.show_ruler = FALSE;
10449 static void compose_attach_drag_received_cb (GtkWidget *widget,
10450 GdkDragContext *context,
10453 GtkSelectionData *data,
10456 gpointer user_data)
10458 Compose *compose = (Compose *)user_data;
10461 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10463 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10465 ) && gtk_drag_get_source_widget(context) !=
10466 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10467 list = uri_list_extract_filenames((const gchar *)data->data);
10468 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10469 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10470 compose_attach_append
10471 (compose, (const gchar *)tmp->data,
10472 utf8_filename, NULL);
10473 g_free(utf8_filename);
10475 if (list) compose_changed_cb(NULL, compose);
10476 list_free_strings(list);
10478 } else if (gtk_drag_get_source_widget(context)
10479 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10480 /* comes from our summaryview */
10481 SummaryView * summaryview = NULL;
10482 GSList * list = NULL, *cur = NULL;
10484 if (mainwindow_get_mainwindow())
10485 summaryview = mainwindow_get_mainwindow()->summaryview;
10488 list = summary_get_selected_msg_list(summaryview);
10490 for (cur = list; cur; cur = cur->next) {
10491 MsgInfo *msginfo = (MsgInfo *)cur->data;
10492 gchar *file = NULL;
10494 file = procmsg_get_message_file_full(msginfo,
10497 compose_attach_append(compose, (const gchar *)file,
10498 (const gchar *)file, "message/rfc822");
10502 g_slist_free(list);
10506 static gboolean compose_drag_drop(GtkWidget *widget,
10507 GdkDragContext *drag_context,
10509 guint time, gpointer user_data)
10511 /* not handling this signal makes compose_insert_drag_received_cb
10516 static void compose_insert_drag_received_cb (GtkWidget *widget,
10517 GdkDragContext *drag_context,
10520 GtkSelectionData *data,
10523 gpointer user_data)
10525 Compose *compose = (Compose *)user_data;
10528 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10531 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10533 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10535 AlertValue val = G_ALERTDEFAULT;
10537 list = uri_list_extract_filenames((const gchar *)data->data);
10538 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10539 /* Assume a list of no files, and data has ://, is a remote link */
10540 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10541 gchar *tmpfile = get_tmp_file();
10542 str_write_to_file(tmpdata, tmpfile);
10544 compose_insert_file(compose, tmpfile);
10545 claws_unlink(tmpfile);
10547 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10548 compose_beautify_paragraph(compose, NULL, TRUE);
10551 switch (prefs_common.compose_dnd_mode) {
10552 case COMPOSE_DND_ASK:
10553 val = alertpanel_full(_("Insert or attach?"),
10554 _("Do you want to insert the contents of the file(s) "
10555 "into the message body, or attach it to the email?"),
10556 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10557 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10559 case COMPOSE_DND_INSERT:
10560 val = G_ALERTALTERNATE;
10562 case COMPOSE_DND_ATTACH:
10563 val = G_ALERTOTHER;
10566 /* unexpected case */
10567 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10570 if (val & G_ALERTDISABLE) {
10571 val &= ~G_ALERTDISABLE;
10572 /* remember what action to perform by default, only if we don't click Cancel */
10573 if (val == G_ALERTALTERNATE)
10574 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10575 else if (val == G_ALERTOTHER)
10576 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10579 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10580 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10581 list_free_strings(list);
10584 } else if (val == G_ALERTOTHER) {
10585 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10586 list_free_strings(list);
10591 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10592 compose_insert_file(compose, (const gchar *)tmp->data);
10594 list_free_strings(list);
10596 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10599 #if GTK_CHECK_VERSION(2, 8, 0)
10600 /* do nothing, handled by GTK */
10602 gchar *tmpfile = get_tmp_file();
10603 str_write_to_file((const gchar *)data->data, tmpfile);
10604 compose_insert_file(compose, tmpfile);
10605 claws_unlink(tmpfile);
10607 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10611 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10614 static void compose_header_drag_received_cb (GtkWidget *widget,
10615 GdkDragContext *drag_context,
10618 GtkSelectionData *data,
10621 gpointer user_data)
10623 GtkEditable *entry = (GtkEditable *)user_data;
10624 gchar *email = (gchar *)data->data;
10626 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10629 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10630 gchar *decoded=g_new(gchar, strlen(email));
10633 email += strlen("mailto:");
10634 decode_uri(decoded, email); /* will fit */
10635 gtk_editable_delete_text(entry, 0, -1);
10636 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10637 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10641 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10644 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10646 Compose *compose = (Compose *)data;
10648 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10649 compose->return_receipt = TRUE;
10651 compose->return_receipt = FALSE;
10654 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10656 Compose *compose = (Compose *)data;
10658 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10659 compose->remove_references = TRUE;
10661 compose->remove_references = FALSE;
10664 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10665 ComposeHeaderEntry *headerentry)
10667 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10671 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10672 GdkEventKey *event,
10673 ComposeHeaderEntry *headerentry)
10675 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10676 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10677 !(event->state & GDK_MODIFIER_MASK) &&
10678 (event->keyval == GDK_BackSpace) &&
10679 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10680 gtk_container_remove
10681 (GTK_CONTAINER(headerentry->compose->header_table),
10682 headerentry->combo);
10683 gtk_container_remove
10684 (GTK_CONTAINER(headerentry->compose->header_table),
10685 headerentry->entry);
10686 headerentry->compose->header_list =
10687 g_slist_remove(headerentry->compose->header_list,
10689 g_free(headerentry);
10690 } else if (event->keyval == GDK_Tab) {
10691 if (headerentry->compose->header_last == headerentry) {
10692 /* Override default next focus, and give it to subject_entry
10693 * instead of notebook tabs
10695 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10696 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10703 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10704 ComposeHeaderEntry *headerentry)
10706 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10707 compose_create_header_entry(headerentry->compose);
10708 g_signal_handlers_disconnect_matched
10709 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10710 0, 0, NULL, NULL, headerentry);
10712 /* Automatically scroll down */
10713 GTK_EVENTS_FLUSH();
10714 compose_show_first_last_header(headerentry->compose, FALSE);
10720 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10722 GtkAdjustment *vadj;
10724 cm_return_if_fail(compose);
10725 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10726 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10727 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10728 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10729 gtk_adjustment_changed(vadj);
10732 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10733 const gchar *text, gint len, Compose *compose)
10735 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10736 (G_OBJECT(compose->text), "paste_as_quotation"));
10739 cm_return_if_fail(text != NULL);
10741 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10742 G_CALLBACK(text_inserted),
10744 if (paste_as_quotation) {
10746 const gchar *qmark;
10748 GtkTextIter start_iter;
10751 len = strlen(text);
10753 new_text = g_strndup(text, len);
10755 qmark = compose_quote_char_from_context(compose);
10757 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10758 gtk_text_buffer_place_cursor(buffer, iter);
10760 pos = gtk_text_iter_get_offset(iter);
10762 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10763 _("Quote format error at line %d."));
10764 quote_fmt_reset_vartable();
10766 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10767 GINT_TO_POINTER(paste_as_quotation - 1));
10769 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10770 gtk_text_buffer_place_cursor(buffer, iter);
10771 gtk_text_buffer_delete_mark(buffer, mark);
10773 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10774 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10775 compose_beautify_paragraph(compose, &start_iter, FALSE);
10776 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10777 gtk_text_buffer_delete_mark(buffer, mark);
10779 if (strcmp(text, "\n") || compose->automatic_break
10780 || gtk_text_iter_starts_line(iter)) {
10781 GtkTextIter before_ins;
10782 gtk_text_buffer_insert(buffer, iter, text, len);
10783 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10784 before_ins = *iter;
10785 gtk_text_iter_backward_chars(&before_ins, len);
10786 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10789 /* check if the preceding is just whitespace or quote */
10790 GtkTextIter start_line;
10791 gchar *tmp = NULL, *quote = NULL;
10792 gint quote_len = 0, is_normal = 0;
10793 start_line = *iter;
10794 gtk_text_iter_set_line_offset(&start_line, 0);
10795 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10798 if (*tmp == '\0') {
10801 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10809 gtk_text_buffer_insert(buffer, iter, text, len);
10811 gtk_text_buffer_insert_with_tags_by_name(buffer,
10812 iter, text, len, "no_join", NULL);
10817 if (!paste_as_quotation) {
10818 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10819 compose_beautify_paragraph(compose, iter, FALSE);
10820 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10821 gtk_text_buffer_delete_mark(buffer, mark);
10824 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10825 G_CALLBACK(text_inserted),
10827 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10829 if (prefs_common.autosave &&
10830 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10831 compose->draft_timeout_tag != -2 /* disabled while loading */)
10832 compose->draft_timeout_tag = g_timeout_add
10833 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10835 static gint compose_defer_auto_save_draft(Compose *compose)
10837 compose->draft_timeout_tag = -1;
10838 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10843 static void compose_check_all(GtkAction *action, gpointer data)
10845 Compose *compose = (Compose *)data;
10846 if (!compose->gtkaspell)
10849 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10850 claws_spell_entry_check_all(
10851 CLAWS_SPELL_ENTRY(compose->subject_entry));
10853 gtkaspell_check_all(compose->gtkaspell);
10856 static void compose_highlight_all(GtkAction *action, gpointer data)
10858 Compose *compose = (Compose *)data;
10859 if (compose->gtkaspell) {
10860 claws_spell_entry_recheck_all(
10861 CLAWS_SPELL_ENTRY(compose->subject_entry));
10862 gtkaspell_highlight_all(compose->gtkaspell);
10866 static void compose_check_backwards(GtkAction *action, gpointer data)
10868 Compose *compose = (Compose *)data;
10869 if (!compose->gtkaspell) {
10870 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10874 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10875 claws_spell_entry_check_backwards(
10876 CLAWS_SPELL_ENTRY(compose->subject_entry));
10878 gtkaspell_check_backwards(compose->gtkaspell);
10881 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10883 Compose *compose = (Compose *)data;
10884 if (!compose->gtkaspell) {
10885 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10889 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10890 claws_spell_entry_check_forwards_go(
10891 CLAWS_SPELL_ENTRY(compose->subject_entry));
10893 gtkaspell_check_forwards_go(compose->gtkaspell);
10898 *\brief Guess originating forward account from MsgInfo and several
10899 * "common preference" settings. Return NULL if no guess.
10901 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10903 PrefsAccount *account = NULL;
10905 cm_return_val_if_fail(msginfo, NULL);
10906 cm_return_val_if_fail(msginfo->folder, NULL);
10907 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10909 if (msginfo->folder->prefs->enable_default_account)
10910 account = account_find_from_id(msginfo->folder->prefs->default_account);
10913 account = msginfo->folder->folder->account;
10915 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10917 Xstrdup_a(to, msginfo->to, return NULL);
10918 extract_address(to);
10919 account = account_find_from_address(to, FALSE);
10922 if (!account && prefs_common.forward_account_autosel) {
10923 gchar cc[BUFFSIZE];
10924 if (!procheader_get_header_from_msginfo
10925 (msginfo, cc,sizeof cc , "Cc:")) {
10926 gchar *buf = cc + strlen("Cc:");
10927 extract_address(buf);
10928 account = account_find_from_address(buf, FALSE);
10932 if (!account && prefs_common.forward_account_autosel) {
10933 gchar deliveredto[BUFFSIZE];
10934 if (!procheader_get_header_from_msginfo
10935 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10936 gchar *buf = deliveredto + strlen("Delivered-To:");
10937 extract_address(buf);
10938 account = account_find_from_address(buf, FALSE);
10945 gboolean compose_close(Compose *compose)
10949 if (!g_mutex_trylock(compose->mutex)) {
10950 /* we have to wait for the (possibly deferred by auto-save)
10951 * drafting to be done, before destroying the compose under
10953 debug_print("waiting for drafting to finish...\n");
10954 compose_allow_user_actions(compose, FALSE);
10955 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10958 cm_return_val_if_fail(compose, FALSE);
10959 gtkut_widget_get_uposition(compose->window, &x, &y);
10960 if (!compose->batch) {
10961 prefs_common.compose_x = x;
10962 prefs_common.compose_y = y;
10964 g_mutex_unlock(compose->mutex);
10965 compose_destroy(compose);
10970 * Add entry field for each address in list.
10971 * \param compose E-Mail composition object.
10972 * \param listAddress List of (formatted) E-Mail addresses.
10974 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10977 node = listAddress;
10979 addr = ( gchar * ) node->data;
10980 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
10981 node = g_list_next( node );
10985 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10986 guint action, gboolean opening_multiple)
10988 gchar *body = NULL;
10989 GSList *new_msglist = NULL;
10990 MsgInfo *tmp_msginfo = NULL;
10991 gboolean originally_enc = FALSE;
10992 gboolean originally_sig = FALSE;
10993 Compose *compose = NULL;
10994 gchar *s_system = NULL;
10996 cm_return_if_fail(msgview != NULL);
10998 cm_return_if_fail(msginfo_list != NULL);
11000 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11001 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11002 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11004 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11005 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11006 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11007 orig_msginfo, mimeinfo);
11008 if (tmp_msginfo != NULL) {
11009 new_msglist = g_slist_append(NULL, tmp_msginfo);
11011 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11012 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11013 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11015 tmp_msginfo->folder = orig_msginfo->folder;
11016 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11017 if (orig_msginfo->tags) {
11018 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11019 tmp_msginfo->folder->tags_dirty = TRUE;
11025 if (!opening_multiple)
11026 body = messageview_get_selection(msgview);
11029 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11030 procmsg_msginfo_free(tmp_msginfo);
11031 g_slist_free(new_msglist);
11033 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11035 if (compose && originally_enc) {
11036 compose_force_encryption(compose, compose->account, FALSE, s_system);
11039 if (compose && originally_sig && compose->account->default_sign_reply) {
11040 compose_force_signing(compose, compose->account, s_system);
11046 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11049 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11050 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11051 GSList *cur = msginfo_list;
11052 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11053 "messages. Opening the windows "
11054 "could take some time. Do you "
11055 "want to continue?"),
11056 g_slist_length(msginfo_list));
11057 if (g_slist_length(msginfo_list) > 9
11058 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11059 != G_ALERTALTERNATE) {
11064 /* We'll open multiple compose windows */
11065 /* let the WM place the next windows */
11066 compose_force_window_origin = FALSE;
11067 for (; cur; cur = cur->next) {
11069 tmplist.data = cur->data;
11070 tmplist.next = NULL;
11071 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11073 compose_force_window_origin = TRUE;
11075 /* forwarding multiple mails as attachments is done via a
11076 * single compose window */
11077 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11081 void compose_set_position(Compose *compose, gint pos)
11083 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11085 gtkut_text_view_set_position(text, pos);
11088 gboolean compose_search_string(Compose *compose,
11089 const gchar *str, gboolean case_sens)
11091 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11093 return gtkut_text_view_search_string(text, str, case_sens);
11096 gboolean compose_search_string_backward(Compose *compose,
11097 const gchar *str, gboolean case_sens)
11099 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11101 return gtkut_text_view_search_string_backward(text, str, case_sens);
11104 /* allocate a msginfo structure and populate its data from a compose data structure */
11105 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11107 MsgInfo *newmsginfo;
11109 gchar buf[BUFFSIZE];
11111 cm_return_val_if_fail( compose != NULL, NULL );
11113 newmsginfo = procmsg_msginfo_new();
11116 get_rfc822_date(buf, sizeof(buf));
11117 newmsginfo->date = g_strdup(buf);
11120 if (compose->from_name) {
11121 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11122 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11126 if (compose->subject_entry)
11127 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11129 /* to, cc, reply-to, newsgroups */
11130 for (list = compose->header_list; list; list = list->next) {
11131 gchar *header = gtk_editable_get_chars(
11133 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11134 gchar *entry = gtk_editable_get_chars(
11135 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11137 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11138 if ( newmsginfo->to == NULL ) {
11139 newmsginfo->to = g_strdup(entry);
11140 } else if (entry && *entry) {
11141 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11142 g_free(newmsginfo->to);
11143 newmsginfo->to = tmp;
11146 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11147 if ( newmsginfo->cc == NULL ) {
11148 newmsginfo->cc = g_strdup(entry);
11149 } else if (entry && *entry) {
11150 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11151 g_free(newmsginfo->cc);
11152 newmsginfo->cc = tmp;
11155 if ( strcasecmp(header,
11156 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11157 if ( newmsginfo->newsgroups == NULL ) {
11158 newmsginfo->newsgroups = g_strdup(entry);
11159 } else if (entry && *entry) {
11160 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11161 g_free(newmsginfo->newsgroups);
11162 newmsginfo->newsgroups = tmp;
11170 /* other data is unset */
11176 /* update compose's dictionaries from folder dict settings */
11177 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11178 FolderItem *folder_item)
11180 cm_return_if_fail(compose != NULL);
11182 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11183 FolderItemPrefs *prefs = folder_item->prefs;
11185 if (prefs->enable_default_dictionary)
11186 gtkaspell_change_dict(compose->gtkaspell,
11187 prefs->default_dictionary, FALSE);
11188 if (folder_item->prefs->enable_default_alt_dictionary)
11189 gtkaspell_change_alt_dict(compose->gtkaspell,
11190 prefs->default_alt_dictionary);
11191 if (prefs->enable_default_dictionary
11192 || prefs->enable_default_alt_dictionary)
11193 compose_spell_menu_changed(compose);