2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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 #if !GTK_CHECK_VERSION(2, 24, 0)
90 #include "gtkshruler.h"
93 #include "addr_compl.h"
94 #include "quote_fmt.h"
96 #include "foldersel.h"
99 #include "message_search.h"
100 #include "combobox.h"
104 #include "autofaces.h"
105 #include "spell_entry.h"
118 #define N_ATTACH_COLS (N_COL_COLUMNS)
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
136 } ComposeCallAdvancedAction
;
140 PRIORITY_HIGHEST
= 1,
149 COMPOSE_INSERT_SUCCESS
,
150 COMPOSE_INSERT_READ_ERROR
,
151 COMPOSE_INSERT_INVALID_CHARACTER
,
152 COMPOSE_INSERT_NO_FILE
153 } ComposeInsertResult
;
157 COMPOSE_WRITE_FOR_SEND
,
158 COMPOSE_WRITE_FOR_STORE
163 COMPOSE_QUOTE_FORCED
,
170 SUBJECT_FIELD_PRESENT
,
175 #define B64_LINE_SIZE 57
176 #define B64_BUFFSIZE 77
178 #define MAX_REFERENCES_LEN 999
180 static GList
*compose_list
= NULL
;
182 static Compose
*compose_generic_new (PrefsAccount
*account
,
185 GPtrArray
*attach_files
,
186 GList
*listAddress
);
188 static Compose
*compose_create (PrefsAccount
*account
,
193 static void compose_entry_mark_default_to (Compose
*compose
,
194 const gchar
*address
);
195 static Compose
*compose_followup_and_reply_to (MsgInfo
*msginfo
,
196 ComposeQuoteMode quote_mode
,
200 static Compose
*compose_forward_multiple (PrefsAccount
*account
,
201 GSList
*msginfo_list
);
202 static Compose
*compose_reply (MsgInfo
*msginfo
,
203 ComposeQuoteMode quote_mode
,
208 static Compose
*compose_reply_mode (ComposeMode mode
,
209 GSList
*msginfo_list
,
211 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
);
212 static void compose_update_privacy_systems_menu(Compose
*compose
);
214 static GtkWidget
*compose_account_option_menu_create
216 static void compose_set_out_encoding (Compose
*compose
);
217 static void compose_set_template_menu (Compose
*compose
);
218 static void compose_destroy (Compose
*compose
);
220 static MailField
compose_entries_set (Compose
*compose
,
222 ComposeEntryType to_type
);
223 static gint
compose_parse_header (Compose
*compose
,
225 static gchar
*compose_parse_references (const gchar
*ref
,
228 static gchar
*compose_quote_fmt (Compose
*compose
,
234 gboolean need_unescape
,
235 const gchar
*err_msg
);
237 static void compose_reply_set_entry (Compose
*compose
,
243 followup_and_reply_to
);
244 static void compose_reedit_set_entry (Compose
*compose
,
247 static void compose_insert_sig (Compose
*compose
,
249 static ComposeInsertResult
compose_insert_file (Compose
*compose
,
252 static gboolean
compose_attach_append (Compose
*compose
,
255 const gchar
*content_type
,
256 const gchar
*charset
);
257 static void compose_attach_parts (Compose
*compose
,
260 static gboolean
compose_beautify_paragraph (Compose
*compose
,
261 GtkTextIter
*par_iter
,
263 static void compose_wrap_all (Compose
*compose
);
264 static void compose_wrap_all_full (Compose
*compose
,
267 static void compose_set_title (Compose
*compose
);
268 static void compose_select_account (Compose
*compose
,
269 PrefsAccount
*account
,
272 static PrefsAccount
*compose_current_mail_account(void);
273 /* static gint compose_send (Compose *compose); */
274 static gboolean compose_check_for_valid_recipient
276 static gboolean
compose_check_entries (Compose
*compose
,
277 gboolean check_everything
);
278 static gint
compose_write_to_file (Compose
*compose
,
281 gboolean attach_parts
);
282 static gint
compose_write_body_to_file (Compose
*compose
,
284 static gint
compose_remove_reedit_target (Compose
*compose
,
286 static void compose_remove_draft (Compose
*compose
);
287 static gint
compose_queue_sub (Compose
*compose
,
291 gboolean check_subject
,
292 gboolean remove_reedit_target
);
293 static int compose_add_attachments (Compose
*compose
,
295 static gchar
*compose_get_header (Compose
*compose
);
297 static void compose_convert_header (Compose
*compose
,
302 gboolean addr_field
);
304 static void compose_attach_info_free (AttachInfo
*ainfo
);
305 static void compose_attach_remove_selected (GtkAction
*action
,
308 static void compose_template_apply (Compose
*compose
,
311 static void compose_attach_property (GtkAction
*action
,
313 static void compose_attach_property_create (gboolean
*cancelled
);
314 static void attach_property_ok (GtkWidget
*widget
,
315 gboolean
*cancelled
);
316 static void attach_property_cancel (GtkWidget
*widget
,
317 gboolean
*cancelled
);
318 static gint
attach_property_delete_event (GtkWidget
*widget
,
320 gboolean
*cancelled
);
321 static gboolean
attach_property_key_pressed (GtkWidget
*widget
,
323 gboolean
*cancelled
);
325 static void compose_exec_ext_editor (Compose
*compose
);
327 static gint
compose_exec_ext_editor_real (const gchar
*file
);
328 static gboolean
compose_ext_editor_kill (Compose
*compose
);
329 static gboolean
compose_input_cb (GIOChannel
*source
,
330 GIOCondition condition
,
332 static void compose_set_ext_editor_sensitive (Compose
*compose
,
334 #endif /* G_OS_UNIX */
336 static void compose_undo_state_changed (UndoMain
*undostruct
,
341 static void compose_create_header_entry (Compose
*compose
);
342 static void compose_add_header_entry (Compose
*compose
, const gchar
*header
,
343 gchar
*text
, ComposePrefType pref_type
);
344 static void compose_remove_header_entries(Compose
*compose
);
346 static void compose_update_priority_menu_item(Compose
* compose
);
348 static void compose_spell_menu_changed (void *data
);
349 static void compose_dict_changed (void *data
);
351 static void compose_add_field_list ( Compose
*compose
,
352 GList
*listAddress
);
354 /* callback functions */
356 #if !GTK_CHECK_VERSION(2, 24, 0)
357 static gboolean
compose_edit_size_alloc (GtkEditable
*widget
,
358 GtkAllocation
*allocation
,
359 GtkSHRuler
*shruler
);
361 static void account_activated (GtkComboBox
*optmenu
,
363 static void attach_selected (GtkTreeView
*tree_view
,
364 GtkTreePath
*tree_path
,
365 GtkTreeViewColumn
*column
,
367 static gboolean
attach_button_pressed (GtkWidget
*widget
,
368 GdkEventButton
*event
,
370 static gboolean
attach_key_pressed (GtkWidget
*widget
,
373 static void compose_send_cb (GtkAction
*action
, gpointer data
);
374 static void compose_send_later_cb (GtkAction
*action
, gpointer data
);
376 static void compose_save_cb (GtkAction
*action
,
379 static void compose_attach_cb (GtkAction
*action
,
381 static void compose_insert_file_cb (GtkAction
*action
,
383 static void compose_insert_sig_cb (GtkAction
*action
,
386 static void compose_close_cb (GtkAction
*action
,
389 static void compose_set_encoding_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
391 static void compose_address_cb (GtkAction
*action
,
393 static void about_show_cb (GtkAction
*action
,
395 static void compose_template_activate_cb(GtkWidget
*widget
,
398 static void compose_ext_editor_cb (GtkAction
*action
,
401 static gint
compose_delete_cb (GtkWidget
*widget
,
405 static void compose_undo_cb (GtkAction
*action
,
407 static void compose_redo_cb (GtkAction
*action
,
409 static void compose_cut_cb (GtkAction
*action
,
411 static void compose_copy_cb (GtkAction
*action
,
413 static void compose_paste_cb (GtkAction
*action
,
415 static void compose_paste_as_quote_cb (GtkAction
*action
,
417 static void compose_paste_no_wrap_cb (GtkAction
*action
,
419 static void compose_paste_wrap_cb (GtkAction
*action
,
421 static void compose_allsel_cb (GtkAction
*action
,
424 static void compose_advanced_action_cb (GtkAction
*action
,
427 static void compose_grab_focus_cb (GtkWidget
*widget
,
430 static void compose_changed_cb (GtkTextBuffer
*textbuf
,
433 static void compose_wrap_cb (GtkAction
*action
,
435 static void compose_wrap_all_cb (GtkAction
*action
,
437 static void compose_find_cb (GtkAction
*action
,
439 static void compose_toggle_autowrap_cb (GtkToggleAction
*action
,
441 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
444 #if !GTK_CHECK_VERSION(2, 24, 0)
445 static void compose_toggle_ruler_cb (GtkToggleAction
*action
,
448 static void compose_toggle_sign_cb (GtkToggleAction
*action
,
450 static void compose_toggle_encrypt_cb (GtkToggleAction
*action
,
452 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
);
453 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
);
454 static void activate_privacy_system (Compose
*compose
,
455 PrefsAccount
*account
,
457 static void compose_use_signing(Compose
*compose
, gboolean use_signing
);
458 static void compose_use_encryption(Compose
*compose
, gboolean use_encryption
);
459 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
,
461 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
,
463 static void compose_set_priority_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
464 static void compose_reply_change_mode (Compose
*compose
, ComposeMode action
);
465 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
467 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
468 GdkDragContext
*drag_context
,
471 GtkSelectionData
*data
,
475 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
476 GdkDragContext
*drag_context
,
479 GtkSelectionData
*data
,
483 static void compose_header_drag_received_cb (GtkWidget
*widget
,
484 GdkDragContext
*drag_context
,
487 GtkSelectionData
*data
,
492 static gboolean
compose_drag_drop (GtkWidget
*widget
,
493 GdkDragContext
*drag_context
,
495 guint time
, gpointer user_data
);
497 static void text_inserted (GtkTextBuffer
*buffer
,
502 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
503 ComposeQuoteMode quote_mode
,
507 gboolean followup_and_reply_to
,
510 static void compose_headerentry_changed_cb (GtkWidget
*entry
,
511 ComposeHeaderEntry
*headerentry
);
512 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
514 ComposeHeaderEntry
*headerentry
);
515 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
516 ComposeHeaderEntry
*headerentry
);
518 static void compose_show_first_last_header (Compose
*compose
, gboolean show_first
);
520 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
);
522 static void compose_nothing_cb (GtkAction
*action
, gpointer data
)
528 static void compose_check_all (GtkAction
*action
, gpointer data
);
529 static void compose_highlight_all (GtkAction
*action
, gpointer data
);
530 static void compose_check_backwards (GtkAction
*action
, gpointer data
);
531 static void compose_check_forwards_go (GtkAction
*action
, gpointer data
);
534 static PrefsAccount
*compose_guess_forward_account_from_msginfo (MsgInfo
*msginfo
);
536 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
);
539 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
540 FolderItem
*folder_item
);
542 static void compose_attach_update_label(Compose
*compose
);
543 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
544 gboolean respect_default_to
);
546 static GtkActionEntry compose_popup_entries
[] =
548 {"Compose", NULL
, "Compose" },
549 {"Compose/Add", NULL
, N_("_Add..."), NULL
, NULL
, G_CALLBACK(compose_attach_cb
) },
550 {"Compose/Remove", NULL
, N_("_Remove"), NULL
, NULL
, G_CALLBACK(compose_attach_remove_selected
) },
551 {"Compose/---", NULL
, "---", NULL
, NULL
, NULL
},
552 {"Compose/Properties", NULL
, N_("_Properties..."), NULL
, NULL
, G_CALLBACK(compose_attach_property
) },
555 static GtkActionEntry compose_entries
[] =
557 {"Menu", NULL
, "Menu" },
559 {"Message", NULL
, N_("_Message") },
560 {"Edit", NULL
, N_("_Edit") },
562 {"Spelling", NULL
, N_("_Spelling") },
564 {"Options", NULL
, N_("_Options") },
565 {"Tools", NULL
, N_("_Tools") },
566 {"Help", NULL
, N_("_Help") },
568 {"Message/Send", NULL
, N_("S_end"), "<control>Return", NULL
, G_CALLBACK(compose_send_cb
) },
569 {"Message/SendLater", NULL
, N_("Send _later"), "<shift><control>S", NULL
, G_CALLBACK(compose_send_later_cb
) },
570 {"Message/---", NULL
, "---" },
572 {"Message/AttachFile", NULL
, N_("_Attach file"), "<control>M", NULL
, G_CALLBACK(compose_attach_cb
) },
573 {"Message/InsertFile", NULL
, N_("_Insert file"), "<control>I", NULL
, G_CALLBACK(compose_insert_file_cb
) },
574 {"Message/InsertSig", NULL
, N_("Insert si_gnature"), "<control>G", NULL
, G_CALLBACK(compose_insert_sig_cb
) },
575 /* {"Message/---", NULL, "---" }, */
576 {"Message/Save", NULL
, N_("_Save"), "<control>S", NULL
, G_CALLBACK(compose_save_cb
) }, /*COMPOSE_KEEP_EDITING*/
577 /* {"Message/---", NULL, "---" }, */
578 {"Message/Close", NULL
, N_("_Close"), "<control>W", NULL
, G_CALLBACK(compose_close_cb
) },
581 {"Edit/Undo", NULL
, N_("_Undo"), "<control>Z", NULL
, G_CALLBACK(compose_undo_cb
) },
582 {"Edit/Redo", NULL
, N_("_Redo"), "<control>Y", NULL
, G_CALLBACK(compose_redo_cb
) },
583 {"Edit/---", NULL
, "---" },
585 {"Edit/Cut", NULL
, N_("Cu_t"), "<control>X", NULL
, G_CALLBACK(compose_cut_cb
) },
586 {"Edit/Copy", NULL
, N_("_Copy"), "<control>C", NULL
, G_CALLBACK(compose_copy_cb
) },
587 {"Edit/Paste", NULL
, N_("_Paste"), "<control>V", NULL
, G_CALLBACK(compose_paste_cb
) },
589 {"Edit/SpecialPaste", NULL
, N_("Special paste") },
590 {"Edit/SpecialPaste/AsQuotation", NULL
, N_("as _quotation"), NULL
, NULL
, G_CALLBACK(compose_paste_as_quote_cb
) },
591 {"Edit/SpecialPaste/Wrapped", NULL
, N_("_wrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_wrap_cb
) },
592 {"Edit/SpecialPaste/Unwrapped", NULL
, N_("_unwrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_no_wrap_cb
) },
594 {"Edit/SelectAll", NULL
, N_("Select _all"), "<control>A", NULL
, G_CALLBACK(compose_allsel_cb
) },
596 {"Edit/Advanced", NULL
, N_("A_dvanced") },
597 {"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*/
598 {"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*/
599 {"Edit/Advanced/BackWord", NULL
, N_("Move a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
600 {"Edit/Advanced/ForwWord", NULL
, N_("Move a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
601 {"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*/
602 {"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*/
603 {"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*/
604 {"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*/
605 {"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*/
606 {"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*/
607 {"Edit/Advanced/DelBackWord", NULL
, N_("Delete a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
608 {"Edit/Advanced/DelForwWord", NULL
, N_("Delete a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
609 {"Edit/Advanced/DelLine", NULL
, N_("Delete line"), "<control>U", NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
610 {"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*/
612 /* {"Edit/---", NULL, "---" }, */
613 {"Edit/Find", NULL
, N_("_Find"), "<control>F", NULL
, G_CALLBACK(compose_find_cb
) },
615 /* {"Edit/---", NULL, "---" }, */
616 {"Edit/WrapPara", NULL
, N_("_Wrap current paragraph"), "<control>L", NULL
, G_CALLBACK(compose_wrap_cb
) }, /* 0 */
617 {"Edit/WrapAllLines", NULL
, N_("Wrap all long _lines"), "<control><alt>L", NULL
, G_CALLBACK(compose_wrap_all_cb
) }, /* 1 */
618 /* {"Edit/---", NULL, "---" }, */
619 {"Edit/ExtEditor", NULL
, N_("Edit with e_xternal editor"), "<shift><control>X", NULL
, G_CALLBACK(compose_ext_editor_cb
) },
622 {"Spelling/CheckAllSel", NULL
, N_("_Check all or check selection"), NULL
, NULL
, G_CALLBACK(compose_check_all
) },
623 {"Spelling/HighlightAll", NULL
, N_("_Highlight all misspelled words"), NULL
, NULL
, G_CALLBACK(compose_highlight_all
) },
624 {"Spelling/CheckBackwards", NULL
, N_("Check _backwards misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_backwards
) },
625 {"Spelling/ForwardNext", NULL
, N_("_Forward to next misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_forwards_go
) },
627 {"Spelling/---", NULL
, "---" },
628 {"Spelling/Options", NULL
, N_("_Options") },
633 {"Options/ReplyMode", NULL
, N_("Reply _mode") },
634 {"Options/---", NULL
, "---" },
635 {"Options/PrivacySystem", NULL
, N_("Privacy _System") },
636 {"Options/PrivacySystem/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
638 /* {"Options/---", NULL, "---" }, */
640 {"Options/Priority", NULL
, N_("_Priority") },
642 {"Options/Encoding", NULL
, N_("Character _encoding") },
643 {"Options/Encoding/---", NULL
, "---" },
644 #define ENC_ACTION(cs_char,c_char,string) \
645 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
647 {"Options/Encoding/Western", NULL
, N_("Western European") },
648 {"Options/Encoding/Baltic", NULL
, N_("Baltic") },
649 {"Options/Encoding/Hebrew", NULL
, N_("Hebrew") },
650 {"Options/Encoding/Arabic", NULL
, N_("Arabic") },
651 {"Options/Encoding/Cyrillic", NULL
, N_("Cyrillic") },
652 {"Options/Encoding/Japanese", NULL
, N_("Japanese") },
653 {"Options/Encoding/Chinese", NULL
, N_("Chinese") },
654 {"Options/Encoding/Korean", NULL
, N_("Korean") },
655 {"Options/Encoding/Thai", NULL
, N_("Thai") },
658 {"Tools/AddressBook", NULL
, N_("_Address book"), NULL
, NULL
, G_CALLBACK(compose_address_cb
) },
660 {"Tools/Template", NULL
, N_("_Template") },
661 {"Tools/Template/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
662 {"Tools/Actions", NULL
, N_("Actio_ns") },
663 {"Tools/Actions/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
666 {"Help/About", NULL
, N_("_About"), NULL
, NULL
, G_CALLBACK(about_show_cb
) },
669 static GtkToggleActionEntry compose_toggle_entries
[] =
671 {"Edit/AutoWrap", NULL
, N_("Aut_o wrapping"), "<shift><control>L", NULL
, G_CALLBACK(compose_toggle_autowrap_cb
) }, /* TOGGLE */
672 {"Edit/AutoIndent", NULL
, N_("Auto _indent"), NULL
, NULL
, G_CALLBACK(compose_toggle_autoindent_cb
) }, /* TOGGLE */
673 {"Options/Sign", NULL
, N_("Si_gn"), NULL
, NULL
, G_CALLBACK(compose_toggle_sign_cb
) }, /* Toggle */
674 {"Options/Encrypt", NULL
, N_("_Encrypt"), NULL
, NULL
, G_CALLBACK(compose_toggle_encrypt_cb
) }, /* Toggle */
675 {"Options/RequestRetRcpt", NULL
, N_("_Request Return Receipt"), NULL
, NULL
, G_CALLBACK(compose_toggle_return_receipt_cb
) }, /* TOGGLE */
676 {"Options/RemoveReferences", NULL
, N_("Remo_ve references"), NULL
, NULL
, G_CALLBACK(compose_toggle_remove_refs_cb
) }, /* TOGGLE */
677 #if !GTK_CHECK_VERSION(2, 24, 0)
678 {"Tools/ShowRuler", NULL
, N_("Show _ruler"), NULL
, NULL
, G_CALLBACK(compose_toggle_ruler_cb
) }, /* Toggle */
682 static GtkRadioActionEntry compose_radio_rm_entries
[] =
684 {"Options/ReplyMode/Normal", NULL
, N_("_Normal"), NULL
, NULL
, COMPOSE_REPLY
}, /* RADIO compose_reply_change_mode_cb */
685 {"Options/ReplyMode/All", NULL
, N_("_All"), NULL
, NULL
, COMPOSE_REPLY_TO_ALL
}, /* RADIO compose_reply_change_mode_cb */
686 {"Options/ReplyMode/Sender", NULL
, N_("_Sender"), NULL
, NULL
, COMPOSE_REPLY_TO_SENDER
}, /* RADIO compose_reply_change_mode_cb */
687 {"Options/ReplyMode/List", NULL
, N_("_Mailing-list"), NULL
, NULL
, COMPOSE_REPLY_TO_LIST
}, /* RADIO compose_reply_change_mode_cb */
690 static GtkRadioActionEntry compose_radio_prio_entries
[] =
692 {"Options/Priority/Highest", NULL
, N_("_Highest"), NULL
, NULL
, PRIORITY_HIGHEST
}, /* RADIO compose_set_priority_cb */
693 {"Options/Priority/High", NULL
, N_("Hi_gh"), NULL
, NULL
, PRIORITY_HIGH
}, /* RADIO compose_set_priority_cb */
694 {"Options/Priority/Normal", NULL
, N_("_Normal"), NULL
, NULL
, PRIORITY_NORMAL
}, /* RADIO compose_set_priority_cb */
695 {"Options/Priority/Low", NULL
, N_("Lo_w"), NULL
, NULL
, PRIORITY_LOW
}, /* RADIO compose_set_priority_cb */
696 {"Options/Priority/Lowest", NULL
, N_("_Lowest"), NULL
, NULL
, PRIORITY_LOWEST
}, /* RADIO compose_set_priority_cb */
699 static GtkRadioActionEntry compose_radio_enc_entries
[] =
701 ENC_ACTION(CS_AUTO
, C_AUTO
, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION(CS_US_ASCII
, C_US_ASCII
, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION(CS_UTF_8
, C_UTF_8
, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Western/"CS_ISO_8859_1
, C_ISO_8859_1
, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Western/"CS_ISO_8859_15
, C_ISO_8859_15
, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Western/"CS_WINDOWS_1252
, C_WINDOWS_1252
, "Windows-1252"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION(CS_ISO_8859_2
, C_ISO_8859_2
, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Baltic/"CS_ISO_8859_13
, C_ISO_8859_13
, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Baltic/"CS_ISO_8859_4
, C_ISO_8859_14
, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION(CS_ISO_8859_7
, C_ISO_8859_7
, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Hebrew/"CS_ISO_8859_8
, C_ISO_8859_8
, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Hebrew/"CS_WINDOWS_1255
, C_WINDOWS_1255
, "Windows-1255"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Arabic/"CS_ISO_8859_6
, C_ISO_8859_6
, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Arabic/"CS_WINDOWS_1256
, C_WINDOWS_1256
, "Windows-1256"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION(CS_ISO_8859_9
, C_ISO_8859_9
, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Cyrillic/"CS_ISO_8859_5
, C_ISO_8859_5
, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Cyrillic/"CS_KOI8_R
, C_KOI8_R
, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Cyrillic/"CS_KOI8_U
, C_KOI8_U
, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251
, C_WINDOWS_1251
, "Windows-1251"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Japanese/"CS_ISO_2022_JP
, C_ISO_2022_JP
, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2
, C_ISO_2022_JP_2
, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Japanese/"CS_EUC_JP
, C_EUC_JP
, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Japanese/"CS_SHIFT_JIS
, C_SHIFT_JIS
, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Chinese/"CS_GB18030
, C_GB18030
, "_GB18030"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Chinese/"CS_GB2312
, C_GB2312
, "_GB2312"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Chinese/"CS_GBK
, C_GBK
, "GB_K"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Chinese/"CS_BIG5
, C_BIG5
, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Chinese/"CS_EUC_TW
, C_EUC_TW
, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Korean/"CS_EUC_KR
, C_EUC_KR
, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Korean/"CS_ISO_2022_KR
, C_ISO_2022_KR
, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Thai/"CS_TIS_620
, C_TIS_620
, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Thai/"CS_WINDOWS_874
, C_WINDOWS_874
, "_Windows-874"), /* RADIO compose_set_encoding_cb */
735 static GtkTargetEntry compose_mime_types
[] =
737 {"text/uri-list", 0, 0},
738 {"UTF8_STRING", 0, 0},
742 static gboolean
compose_put_existing_to_front(MsgInfo
*info
)
744 GList
*compose_list
= compose_get_compose_list();
748 for (elem
= compose_list
; elem
!= NULL
&& elem
->data
!= NULL
;
750 Compose
*c
= (Compose
*)elem
->data
;
752 if (!c
->targetinfo
|| !c
->targetinfo
->msgid
||
756 if (!strcmp(c
->targetinfo
->msgid
, info
->msgid
)) {
757 gtkut_window_popup(c
->window
);
765 static GdkColor quote_color1
=
766 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
767 static GdkColor quote_color2
=
768 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
769 static GdkColor quote_color3
=
770 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
772 static GdkColor quote_bgcolor1
=
773 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
774 static GdkColor quote_bgcolor2
=
775 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
776 static GdkColor quote_bgcolor3
=
777 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
779 static GdkColor signature_color
= {
786 static GdkColor uri_color
= {
793 static void compose_create_tags(GtkTextView
*text
, Compose
*compose
)
795 GtkTextBuffer
*buffer
;
796 GdkColor black
= {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
797 #if !GTK_CHECK_VERSION(2, 24, 0)
804 buffer
= gtk_text_view_get_buffer(text
);
806 if (prefs_common
.enable_color
) {
807 /* grab the quote colors, converting from an int to a GdkColor */
808 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level1_col
,
810 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level2_col
,
812 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level3_col
,
814 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level1_bgcol
,
816 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level2_bgcol
,
818 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level3_bgcol
,
820 gtkut_convert_int_to_gdk_color(prefs_common
.signature_col
,
822 gtkut_convert_int_to_gdk_color(prefs_common
.uri_col
,
825 signature_color
= quote_color1
= quote_color2
= quote_color3
=
826 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
= uri_color
= black
;
829 if (prefs_common
.enable_color
&& prefs_common
.enable_bgcolor
) {
830 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
831 "foreground-gdk", "e_color1
,
832 "paragraph-background-gdk", "e_bgcolor1
,
834 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
835 "foreground-gdk", "e_color2
,
836 "paragraph-background-gdk", "e_bgcolor2
,
838 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
839 "foreground-gdk", "e_color3
,
840 "paragraph-background-gdk", "e_bgcolor3
,
843 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
844 "foreground-gdk", "e_color1
,
846 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
847 "foreground-gdk", "e_color2
,
849 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
850 "foreground-gdk", "e_color3
,
854 compose
->signature_tag
= gtk_text_buffer_create_tag(buffer
, "signature",
855 "foreground-gdk", &signature_color
,
858 compose
->uri_tag
= gtk_text_buffer_create_tag(buffer
, "link",
859 "foreground-gdk", &uri_color
,
861 compose
->no_wrap_tag
= gtk_text_buffer_create_tag(buffer
, "no_wrap", NULL
);
862 compose
->no_join_tag
= gtk_text_buffer_create_tag(buffer
, "no_join", NULL
);
864 color
[0] = quote_color1
;
865 color
[1] = quote_color2
;
866 color
[2] = quote_color3
;
867 color
[3] = quote_bgcolor1
;
868 color
[4] = quote_bgcolor2
;
869 color
[5] = quote_bgcolor3
;
870 color
[6] = signature_color
;
871 color
[7] = uri_color
;
872 #if !GTK_CHECK_VERSION(2, 24, 0)
873 cmap
= gdk_drawable_get_colormap(gtk_widget_get_window(compose
->window
));
874 gdk_colormap_alloc_colors(cmap
, color
, 8, FALSE
, TRUE
, success
);
876 for (i
= 0; i
< 8; i
++) {
877 if (success
[i
] == FALSE
) {
880 g_warning("Compose: color allocation failed.\n");
881 style
= gtk_widget_get_style(GTK_WIDGET(text
));
882 quote_color1
= quote_color2
= quote_color3
=
883 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
=
884 signature_color
= uri_color
= black
;
890 Compose
*compose_new(PrefsAccount
*account
, const gchar
*mailto
,
891 GPtrArray
*attach_files
)
893 return compose_generic_new(account
, mailto
, NULL
, attach_files
, NULL
);
896 Compose
*compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
, const gchar
*mailto
)
898 return compose_generic_new(account
, mailto
, item
, NULL
, NULL
);
901 Compose
*compose_new_with_list( PrefsAccount
*account
, GList
*listAddress
)
903 return compose_generic_new( account
, NULL
, NULL
, NULL
, listAddress
);
906 #define SCROLL_TO_CURSOR(compose) { \
907 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
908 gtk_text_view_get_buffer( \
909 GTK_TEXT_VIEW(compose->text))); \
910 gtk_text_view_scroll_mark_onscreen( \
911 GTK_TEXT_VIEW(compose->text), \
915 static void compose_set_save_to(Compose
*compose
, const gchar
*folderidentifier
)
918 if (folderidentifier
) {
919 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
));
920 prefs_common
.compose_save_to_history
= add_history(
921 prefs_common
.compose_save_to_history
, folderidentifier
);
922 combobox_set_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
),
923 prefs_common
.compose_save_to_history
);
926 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
927 if (folderidentifier
)
928 gtk_entry_set_text(GTK_ENTRY(entry
), folderidentifier
);
930 gtk_entry_set_text(GTK_ENTRY(entry
), "");
933 static gchar
*compose_get_save_to(Compose
*compose
)
936 gchar
*result
= NULL
;
937 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
938 result
= gtk_editable_get_chars(entry
, 0, -1);
941 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
));
942 prefs_common
.compose_save_to_history
= add_history(
943 prefs_common
.compose_save_to_history
, result
);
944 combobox_set_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
),
945 prefs_common
.compose_save_to_history
);
950 Compose
*compose_generic_new(PrefsAccount
*account
, const gchar
*mailto
, FolderItem
*item
,
951 GPtrArray
*attach_files
, GList
*listAddress
)
954 GtkTextView
*textview
;
955 GtkTextBuffer
*textbuf
;
957 const gchar
*subject_format
= NULL
;
958 const gchar
*body_format
= NULL
;
959 gchar
*mailto_from
= NULL
;
960 PrefsAccount
*mailto_account
= NULL
;
961 MsgInfo
* dummyinfo
= NULL
;
962 gint cursor_pos
= -1;
963 MailField mfield
= NO_FIELD_PRESENT
;
967 /* check if mailto defines a from */
968 if (mailto
&& *mailto
!= '\0') {
969 scan_mailto_url(mailto
, &mailto_from
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
970 /* mailto defines a from, check if we can get account prefs from it,
971 if not, the account prefs will be guessed using other ways, but we'll keep
974 mailto_account
= account_find_from_address(mailto_from
, TRUE
);
976 account
= mailto_account
;
979 /* if no account prefs set from mailto, set if from folder prefs (if any) */
980 if (!mailto_account
&& item
&& item
->prefs
&& item
->prefs
->enable_default_account
)
981 account
= account_find_from_id(item
->prefs
->default_account
);
983 /* if no account prefs set, fallback to the current one */
984 if (!account
) account
= cur_account
;
985 cm_return_val_if_fail(account
!= NULL
, NULL
);
987 compose
= compose_create(account
, item
, COMPOSE_NEW
, FALSE
);
989 /* override from name if mailto asked for it */
991 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), mailto_from
);
994 /* override from name according to folder properties */
995 if (item
&& item
->prefs
&&
996 item
->prefs
->compose_with_format
&&
997 item
->prefs
->compose_override_from_format
&&
998 *item
->prefs
->compose_override_from_format
!= '\0') {
1003 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1005 /* decode \-escape sequences in the internal representation of the quote format */
1006 tmp
= g_malloc(strlen(item
->prefs
->compose_override_from_format
)+1);
1007 pref_get_unescaped_pref(tmp
, item
->prefs
->compose_override_from_format
);
1010 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1011 compose
->gtkaspell
);
1013 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1015 quote_fmt_scan_string(tmp
);
1018 buf
= quote_fmt_get_buffer();
1020 alertpanel_error(_("New message From format error."));
1022 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1023 quote_fmt_reset_vartable();
1028 compose
->replyinfo
= NULL
;
1029 compose
->fwdinfo
= NULL
;
1031 textview
= GTK_TEXT_VIEW(compose
->text
);
1032 textbuf
= gtk_text_view_get_buffer(textview
);
1033 compose_create_tags(textview
, compose
);
1035 undo_block(compose
->undostruct
);
1037 compose_set_dictionaries_from_folder_prefs(compose
, item
);
1040 if (account
->auto_sig
)
1041 compose_insert_sig(compose
, FALSE
);
1042 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
1043 gtk_text_buffer_place_cursor(textbuf
, &iter
);
1045 if (account
->protocol
!= A_NNTP
) {
1046 if (mailto
&& *mailto
!= '\0') {
1047 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1050 compose_set_folder_prefs(compose
, item
, TRUE
);
1052 if (item
&& item
->ret_rcpt
) {
1053 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1056 if (mailto
&& *mailto
!= '\0') {
1057 if (!strchr(mailto
, '@'))
1058 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_NEWSGROUPS
);
1060 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1061 } else if (item
&& FOLDER_CLASS(item
->folder
) == news_get_class()) {
1062 compose_entry_append(compose
, item
->path
, COMPOSE_NEWSGROUPS
, PREF_FOLDER
);
1063 mfield
= TO_FIELD_PRESENT
;
1066 * CLAWS: just don't allow return receipt request, even if the user
1067 * may want to send an email. simple but foolproof.
1069 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", FALSE
);
1071 compose_add_field_list( compose
, listAddress
);
1073 if (item
&& item
->prefs
&& item
->prefs
->compose_with_format
) {
1074 subject_format
= item
->prefs
->compose_subject_format
;
1075 body_format
= item
->prefs
->compose_body_format
;
1076 } else if (account
->compose_with_format
) {
1077 subject_format
= account
->compose_subject_format
;
1078 body_format
= account
->compose_body_format
;
1079 } else if (prefs_common
.compose_with_format
) {
1080 subject_format
= prefs_common
.compose_subject_format
;
1081 body_format
= prefs_common
.compose_body_format
;
1084 if (subject_format
|| body_format
) {
1087 && *subject_format
!= '\0' )
1089 gchar
*subject
= NULL
;
1094 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1096 /* decode \-escape sequences in the internal representation of the quote format */
1097 tmp
= g_malloc(strlen(subject_format
)+1);
1098 pref_get_unescaped_pref(tmp
, subject_format
);
1100 subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1102 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
,
1103 compose
->gtkaspell
);
1105 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
);
1107 quote_fmt_scan_string(tmp
);
1110 buf
= quote_fmt_get_buffer();
1112 alertpanel_error(_("New message subject format error."));
1114 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1115 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1116 quote_fmt_reset_vartable();
1120 mfield
= SUBJECT_FIELD_PRESENT
;
1124 && *body_format
!= '\0' )
1127 GtkTextBuffer
*buffer
;
1128 GtkTextIter start
, end
;
1132 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1134 text
= GTK_TEXT_VIEW(compose
->text
);
1135 buffer
= gtk_text_view_get_buffer(text
);
1136 gtk_text_buffer_get_start_iter(buffer
, &start
);
1137 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
1138 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
1140 compose_quote_fmt(compose
, dummyinfo
,
1142 NULL
, tmp
, FALSE
, TRUE
,
1143 _("The body of the \"New message\" template has an error at line %d."));
1144 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1145 quote_fmt_reset_vartable();
1149 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1150 gtkaspell_highlight_all(compose
->gtkaspell
);
1152 mfield
= BODY_FIELD_PRESENT
;
1156 procmsg_msginfo_free( dummyinfo
);
1162 for (i
= 0; i
< attach_files
->len
; i
++) {
1163 file
= g_ptr_array_index(attach_files
, i
);
1164 compose_attach_append(compose
, file
, file
, NULL
, NULL
);
1168 compose_show_first_last_header(compose
, TRUE
);
1170 /* Set save folder */
1171 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
1172 gchar
*folderidentifier
;
1174 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
1175 folderidentifier
= folder_item_get_identifier(item
);
1176 compose_set_save_to(compose
, folderidentifier
);
1177 g_free(folderidentifier
);
1180 /* Place cursor according to provided input (mfield) */
1182 case NO_FIELD_PRESENT
:
1183 if (compose
->header_last
)
1184 gtk_widget_grab_focus(compose
->header_last
->entry
);
1186 case TO_FIELD_PRESENT
:
1187 buf
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1189 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1192 gtk_widget_grab_focus(compose
->subject_entry
);
1194 case SUBJECT_FIELD_PRESENT
:
1195 textview
= GTK_TEXT_VIEW(compose
->text
);
1198 textbuf
= gtk_text_view_get_buffer(textview
);
1201 mark
= gtk_text_buffer_get_insert(textbuf
);
1202 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
1203 gtk_text_buffer_insert(textbuf
, &iter
, "", -1);
1205 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1206 * only defers where it comes to the variable body
1207 * is not null. If no body is present compose->text
1208 * will be null in which case you cannot place the
1209 * cursor inside the component so. An empty component
1210 * is therefore created before placing the cursor
1212 case BODY_FIELD_PRESENT
:
1213 cursor_pos
= quote_fmt_get_cursor_pos();
1214 if (cursor_pos
== -1)
1215 gtk_widget_grab_focus(compose
->header_last
->entry
);
1217 gtk_widget_grab_focus(compose
->text
);
1221 undo_unblock(compose
->undostruct
);
1223 if (prefs_common
.auto_exteditor
)
1224 compose_exec_ext_editor(compose
);
1226 compose
->draft_timeout_tag
= -1;
1227 SCROLL_TO_CURSOR(compose
);
1229 compose
->modified
= FALSE
;
1230 compose_set_title(compose
);
1232 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1237 static void compose_force_encryption(Compose
*compose
, PrefsAccount
*account
,
1238 gboolean override_pref
, const gchar
*system
)
1240 const gchar
*privacy
= NULL
;
1242 cm_return_if_fail(compose
!= NULL
);
1243 cm_return_if_fail(account
!= NULL
);
1245 if (override_pref
== FALSE
&& account
->default_encrypt_reply
== FALSE
)
1250 else if (account
->default_privacy_system
1251 && strlen(account
->default_privacy_system
)) {
1252 privacy
= account
->default_privacy_system
;
1254 GSList
*privacy_avail
= privacy_get_system_ids();
1255 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1256 privacy
= (gchar
*)(privacy_avail
->data
);
1259 if (privacy
!= NULL
) {
1261 g_free(compose
->privacy_system
);
1262 compose
->privacy_system
= NULL
;
1264 if (compose
->privacy_system
== NULL
)
1265 compose
->privacy_system
= g_strdup(privacy
);
1266 else if (*(compose
->privacy_system
) == '\0') {
1267 g_free(compose
->privacy_system
);
1268 compose
->privacy_system
= g_strdup(privacy
);
1270 compose_update_privacy_system_menu_item(compose
, FALSE
);
1271 compose_use_encryption(compose
, TRUE
);
1275 static void compose_force_signing(Compose
*compose
, PrefsAccount
*account
, const gchar
*system
)
1277 const gchar
*privacy
= NULL
;
1281 else if (account
->default_privacy_system
1282 && strlen(account
->default_privacy_system
)) {
1283 privacy
= account
->default_privacy_system
;
1285 GSList
*privacy_avail
= privacy_get_system_ids();
1286 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1287 privacy
= (gchar
*)(privacy_avail
->data
);
1291 if (privacy
!= NULL
) {
1293 g_free(compose
->privacy_system
);
1294 compose
->privacy_system
= NULL
;
1296 if (compose
->privacy_system
== NULL
)
1297 compose
->privacy_system
= g_strdup(privacy
);
1298 compose_update_privacy_system_menu_item(compose
, FALSE
);
1299 compose_use_signing(compose
, TRUE
);
1303 static Compose
*compose_reply_mode(ComposeMode mode
, GSList
*msginfo_list
, gchar
*body
)
1307 Compose
*compose
= NULL
;
1309 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1311 msginfo
= (MsgInfo
*)g_slist_nth_data(msginfo_list
, 0);
1312 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1314 list_len
= g_slist_length(msginfo_list
);
1318 case COMPOSE_REPLY_TO_ADDRESS
:
1319 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1320 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1322 case COMPOSE_REPLY_WITH_QUOTE
:
1323 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1324 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1326 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1327 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1328 FALSE
, prefs_common
.default_reply_list
, FALSE
, NULL
);
1330 case COMPOSE_REPLY_TO_SENDER
:
1331 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1332 FALSE
, FALSE
, TRUE
, body
);
1334 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1335 compose
= compose_followup_and_reply_to(msginfo
,
1336 COMPOSE_QUOTE_CHECK
,
1337 FALSE
, FALSE
, body
);
1339 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1340 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1341 FALSE
, FALSE
, TRUE
, body
);
1343 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1344 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1345 FALSE
, FALSE
, TRUE
, NULL
);
1347 case COMPOSE_REPLY_TO_ALL
:
1348 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1349 TRUE
, FALSE
, FALSE
, body
);
1351 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1352 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1353 TRUE
, FALSE
, FALSE
, body
);
1355 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1356 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1357 TRUE
, FALSE
, FALSE
, NULL
);
1359 case COMPOSE_REPLY_TO_LIST
:
1360 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1361 FALSE
, TRUE
, FALSE
, body
);
1363 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1364 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1365 FALSE
, TRUE
, FALSE
, body
);
1367 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1368 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1369 FALSE
, TRUE
, FALSE
, NULL
);
1371 case COMPOSE_FORWARD
:
1372 if (prefs_common
.forward_as_attachment
) {
1373 compose
= compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH
, msginfo_list
, body
);
1376 compose
= compose_reply_mode(COMPOSE_FORWARD_INLINE
, msginfo_list
, body
);
1380 case COMPOSE_FORWARD_INLINE
:
1381 /* check if we reply to more than one Message */
1382 if (list_len
== 1) {
1383 compose
= compose_forward(NULL
, msginfo
, FALSE
, body
, FALSE
, FALSE
);
1386 /* more messages FALL THROUGH */
1387 case COMPOSE_FORWARD_AS_ATTACH
:
1388 compose
= compose_forward_multiple(NULL
, msginfo_list
);
1390 case COMPOSE_REDIRECT
:
1391 compose
= compose_redirect(NULL
, msginfo
, FALSE
);
1394 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode
);
1397 if (compose
== NULL
) {
1398 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1402 compose
->rmode
= mode
;
1403 switch (compose
->rmode
) {
1405 case COMPOSE_REPLY_WITH_QUOTE
:
1406 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1407 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1408 debug_print("reply mode Normal\n");
1409 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Normal", TRUE
);
1410 compose_reply_change_mode(compose
, COMPOSE_REPLY
); /* force update */
1412 case COMPOSE_REPLY_TO_SENDER
:
1413 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1414 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1415 debug_print("reply mode Sender\n");
1416 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Sender", TRUE
);
1418 case COMPOSE_REPLY_TO_ALL
:
1419 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1420 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1421 debug_print("reply mode All\n");
1422 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/All", TRUE
);
1424 case COMPOSE_REPLY_TO_LIST
:
1425 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1426 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1427 debug_print("reply mode List\n");
1428 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/List", TRUE
);
1430 case COMPOSE_REPLY_TO_ADDRESS
:
1431 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", FALSE
);
1439 static Compose
*compose_reply(MsgInfo
*msginfo
,
1440 ComposeQuoteMode quote_mode
,
1446 return compose_generic_reply(msginfo
, quote_mode
, to_all
, to_ml
,
1447 to_sender
, FALSE
, body
);
1450 static Compose
*compose_followup_and_reply_to(MsgInfo
*msginfo
,
1451 ComposeQuoteMode quote_mode
,
1456 return compose_generic_reply(msginfo
, quote_mode
, to_all
, FALSE
,
1457 to_sender
, TRUE
, body
);
1460 static void compose_extract_original_charset(Compose
*compose
)
1462 MsgInfo
*info
= NULL
;
1463 if (compose
->replyinfo
) {
1464 info
= compose
->replyinfo
;
1465 } else if (compose
->fwdinfo
) {
1466 info
= compose
->fwdinfo
;
1467 } else if (compose
->targetinfo
) {
1468 info
= compose
->targetinfo
;
1471 MimeInfo
*mimeinfo
= procmime_scan_message_short(info
);
1472 MimeInfo
*partinfo
= mimeinfo
;
1473 while (partinfo
&& partinfo
->type
!= MIMETYPE_TEXT
)
1474 partinfo
= procmime_mimeinfo_next(partinfo
);
1476 compose
->orig_charset
=
1477 g_strdup(procmime_mimeinfo_get_parameter(
1478 partinfo
, "charset"));
1480 procmime_mimeinfo_free_all(mimeinfo
);
1484 #define SIGNAL_BLOCK(buffer) { \
1485 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1486 G_CALLBACK(compose_changed_cb), \
1488 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1489 G_CALLBACK(text_inserted), \
1493 #define SIGNAL_UNBLOCK(buffer) { \
1494 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1495 G_CALLBACK(compose_changed_cb), \
1497 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1498 G_CALLBACK(text_inserted), \
1502 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
1503 ComposeQuoteMode quote_mode
,
1504 gboolean to_all
, gboolean to_ml
,
1506 gboolean followup_and_reply_to
,
1510 PrefsAccount
*account
= NULL
;
1511 GtkTextView
*textview
;
1512 GtkTextBuffer
*textbuf
;
1513 gboolean quote
= FALSE
;
1514 const gchar
*qmark
= NULL
;
1515 const gchar
*body_fmt
= NULL
;
1516 gchar
*s_system
= NULL
;
1518 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1519 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1521 account
= account_get_reply_account(msginfo
, prefs_common
.reply_account_autosel
);
1523 cm_return_val_if_fail(account
!= NULL
, NULL
);
1525 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REPLY
, FALSE
);
1527 compose
->updating
= TRUE
;
1529 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
1530 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
1532 compose
->replyinfo
= procmsg_msginfo_get_full_info(msginfo
);
1533 if (!compose
->replyinfo
)
1534 compose
->replyinfo
= procmsg_msginfo_copy(msginfo
);
1536 compose_extract_original_charset(compose
);
1538 if (msginfo
->folder
&& msginfo
->folder
->ret_rcpt
)
1539 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1541 /* Set save folder */
1542 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1543 gchar
*folderidentifier
;
1545 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1546 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1547 compose_set_save_to(compose
, folderidentifier
);
1548 g_free(folderidentifier
);
1551 if (compose_parse_header(compose
, msginfo
) < 0) {
1552 compose
->updating
= FALSE
;
1553 compose_destroy(compose
);
1557 /* override from name according to folder properties */
1558 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1559 msginfo
->folder
->prefs
->reply_with_format
&&
1560 msginfo
->folder
->prefs
->reply_override_from_format
&&
1561 *msginfo
->folder
->prefs
->reply_override_from_format
!= '\0') {
1566 /* decode \-escape sequences in the internal representation of the quote format */
1567 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->reply_override_from_format
)+1);
1568 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->reply_override_from_format
);
1571 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1572 compose
->gtkaspell
);
1574 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1576 quote_fmt_scan_string(tmp
);
1579 buf
= quote_fmt_get_buffer();
1581 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1583 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1584 quote_fmt_reset_vartable();
1589 textview
= (GTK_TEXT_VIEW(compose
->text
));
1590 textbuf
= gtk_text_view_get_buffer(textview
);
1591 compose_create_tags(textview
, compose
);
1593 undo_block(compose
->undostruct
);
1595 compose_set_dictionaries_from_folder_prefs(compose
, msginfo
->folder
);
1598 if (quote_mode
== COMPOSE_QUOTE_FORCED
||
1599 (quote_mode
== COMPOSE_QUOTE_CHECK
&& prefs_common
.reply_with_quote
)) {
1600 /* use the reply format of folder (if enabled), or the account's one
1601 (if enabled) or fallback to the global reply format, which is always
1602 enabled (even if empty), and use the relevant quotemark */
1604 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1605 msginfo
->folder
->prefs
->reply_with_format
) {
1606 qmark
= msginfo
->folder
->prefs
->reply_quotemark
;
1607 body_fmt
= msginfo
->folder
->prefs
->reply_body_format
;
1609 } else if (account
->reply_with_format
) {
1610 qmark
= account
->reply_quotemark
;
1611 body_fmt
= account
->reply_body_format
;
1614 qmark
= prefs_common
.quotemark
;
1615 if (prefs_common
.quotefmt
&& *prefs_common
.quotefmt
)
1616 body_fmt
= gettext(prefs_common
.quotefmt
);
1623 /* empty quotemark is not allowed */
1624 if (qmark
== NULL
|| *qmark
== '\0')
1626 compose_quote_fmt(compose
, compose
->replyinfo
,
1627 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1628 _("The body of the \"Reply\" template has an error at line %d."));
1629 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1630 quote_fmt_reset_vartable();
1632 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1633 gtkaspell_highlight_all(compose
->gtkaspell
);
1637 if (MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) {
1638 compose_force_encryption(compose
, account
, FALSE
, s_system
);
1641 privacy_msginfo_get_signed_state(compose
->replyinfo
, &s_system
);
1642 if (MSG_IS_SIGNED(compose
->replyinfo
->flags
) && account
->default_sign_reply
) {
1643 compose_force_signing(compose
, account
, s_system
);
1647 SIGNAL_BLOCK(textbuf
);
1649 if (account
->auto_sig
)
1650 compose_insert_sig(compose
, FALSE
);
1652 compose_wrap_all(compose
);
1654 SIGNAL_UNBLOCK(textbuf
);
1656 gtk_widget_grab_focus(compose
->text
);
1658 undo_unblock(compose
->undostruct
);
1660 if (prefs_common
.auto_exteditor
)
1661 compose_exec_ext_editor(compose
);
1663 compose
->modified
= FALSE
;
1664 compose_set_title(compose
);
1666 compose
->updating
= FALSE
;
1667 compose
->draft_timeout_tag
= -1; /* desinhibit auto-drafting after loading */
1668 SCROLL_TO_CURSOR(compose
);
1670 if (compose
->deferred_destroy
) {
1671 compose_destroy(compose
);
1679 #define INSERT_FW_HEADER(var, hdr) \
1680 if (msginfo->var && *msginfo->var) { \
1681 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1682 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1683 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1686 Compose
*compose_forward(PrefsAccount
*account
, MsgInfo
*msginfo
,
1687 gboolean as_attach
, const gchar
*body
,
1688 gboolean no_extedit
,
1692 GtkTextView
*textview
;
1693 GtkTextBuffer
*textbuf
;
1694 gint cursor_pos
= -1;
1697 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1698 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1701 !(account
= compose_guess_forward_account_from_msginfo
1703 account
= cur_account
;
1705 if (!prefs_common
.forward_as_attachment
)
1706 mode
= COMPOSE_FORWARD_INLINE
;
1708 mode
= COMPOSE_FORWARD
;
1709 compose
= compose_create(account
, msginfo
->folder
, mode
, batch
);
1711 compose
->updating
= TRUE
;
1712 compose
->fwdinfo
= procmsg_msginfo_get_full_info(msginfo
);
1713 if (!compose
->fwdinfo
)
1714 compose
->fwdinfo
= procmsg_msginfo_copy(msginfo
);
1716 compose_extract_original_charset(compose
);
1718 if (msginfo
->subject
&& *msginfo
->subject
) {
1719 gchar
*buf
, *buf2
, *p
;
1721 buf
= p
= g_strdup(msginfo
->subject
);
1722 p
+= subject_get_prefix_length(p
);
1723 memmove(buf
, p
, strlen(p
) + 1);
1725 buf2
= g_strdup_printf("Fw: %s", buf
);
1726 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1732 /* override from name according to folder properties */
1733 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1734 msginfo
->folder
->prefs
->forward_with_format
&&
1735 msginfo
->folder
->prefs
->forward_override_from_format
&&
1736 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1740 MsgInfo
*full_msginfo
= NULL
;
1743 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1745 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1747 /* decode \-escape sequences in the internal representation of the quote format */
1748 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1749 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
1752 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1753 compose
->gtkaspell
);
1755 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1757 quote_fmt_scan_string(tmp
);
1760 buf
= quote_fmt_get_buffer();
1762 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1764 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1765 quote_fmt_reset_vartable();
1768 procmsg_msginfo_free(full_msginfo
);
1771 textview
= GTK_TEXT_VIEW(compose
->text
);
1772 textbuf
= gtk_text_view_get_buffer(textview
);
1773 compose_create_tags(textview
, compose
);
1775 undo_block(compose
->undostruct
);
1779 msgfile
= procmsg_get_message_file(msginfo
);
1780 if (!is_file_exist(msgfile
))
1781 g_warning("%s: file not exist\n", msgfile
);
1783 compose_attach_append(compose
, msgfile
, msgfile
,
1784 "message/rfc822", NULL
);
1788 const gchar
*qmark
= NULL
;
1789 const gchar
*body_fmt
= NULL
;
1790 MsgInfo
*full_msginfo
;
1792 if (prefs_common
.fw_quotefmt
&& *prefs_common
.fw_quotefmt
)
1793 body_fmt
= gettext(prefs_common
.fw_quotefmt
);
1797 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1799 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1801 /* use the forward format of folder (if enabled), or the account's one
1802 (if enabled) or fallback to the global forward format, which is always
1803 enabled (even if empty), and use the relevant quotemark */
1804 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1805 msginfo
->folder
->prefs
->forward_with_format
) {
1806 qmark
= msginfo
->folder
->prefs
->forward_quotemark
;
1807 body_fmt
= msginfo
->folder
->prefs
->forward_body_format
;
1809 } else if (account
->forward_with_format
) {
1810 qmark
= account
->forward_quotemark
;
1811 body_fmt
= account
->forward_body_format
;
1814 qmark
= prefs_common
.fw_quotemark
;
1815 if (prefs_common
.fw_quotefmt
&& *prefs_common
.fw_quotefmt
)
1816 body_fmt
= gettext(prefs_common
.fw_quotefmt
);
1821 /* empty quotemark is not allowed */
1822 if (qmark
== NULL
|| *qmark
== '\0')
1825 compose_quote_fmt(compose
, full_msginfo
,
1826 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1827 _("The body of the \"Forward\" template has an error at line %d."));
1828 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1829 quote_fmt_reset_vartable();
1830 compose_attach_parts(compose
, msginfo
);
1832 procmsg_msginfo_free(full_msginfo
);
1834 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1835 gtkaspell_highlight_all(compose
->gtkaspell
);
1839 SIGNAL_BLOCK(textbuf
);
1841 if (account
->auto_sig
)
1842 compose_insert_sig(compose
, FALSE
);
1844 compose_wrap_all(compose
);
1846 SIGNAL_UNBLOCK(textbuf
);
1848 cursor_pos
= quote_fmt_get_cursor_pos();
1849 if (cursor_pos
== -1)
1850 gtk_widget_grab_focus(compose
->header_last
->entry
);
1852 gtk_widget_grab_focus(compose
->text
);
1854 if (!no_extedit
&& prefs_common
.auto_exteditor
)
1855 compose_exec_ext_editor(compose
);
1858 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1859 gchar
*folderidentifier
;
1861 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1862 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1863 compose_set_save_to(compose
, folderidentifier
);
1864 g_free(folderidentifier
);
1867 undo_unblock(compose
->undostruct
);
1869 compose
->modified
= FALSE
;
1870 compose_set_title(compose
);
1872 compose
->updating
= FALSE
;
1873 compose
->draft_timeout_tag
= -1; /* desinhibit auto-drafting after loading */
1874 SCROLL_TO_CURSOR(compose
);
1876 if (compose
->deferred_destroy
) {
1877 compose_destroy(compose
);
1881 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1886 #undef INSERT_FW_HEADER
1888 static Compose
*compose_forward_multiple(PrefsAccount
*account
, GSList
*msginfo_list
)
1891 GtkTextView
*textview
;
1892 GtkTextBuffer
*textbuf
;
1896 gboolean single_mail
= TRUE
;
1898 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1900 if (g_slist_length(msginfo_list
) > 1)
1901 single_mail
= FALSE
;
1903 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
)
1904 if (((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1907 /* guess account from first selected message */
1909 !(account
= compose_guess_forward_account_from_msginfo
1910 (msginfo_list
->data
)))
1911 account
= cur_account
;
1913 cm_return_val_if_fail(account
!= NULL
, NULL
);
1915 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1916 if (msginfo
->data
) {
1917 MSG_UNSET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_REPLIED
);
1918 MSG_SET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_FORWARDED
);
1922 if (msginfo_list
== NULL
|| msginfo_list
->data
== NULL
) {
1923 g_warning("no msginfo_list");
1927 compose
= compose_create(account
, ((MsgInfo
*)msginfo_list
->data
)->folder
, COMPOSE_FORWARD
, FALSE
);
1929 compose
->updating
= TRUE
;
1931 /* override from name according to folder properties */
1932 if (msginfo_list
->data
) {
1933 MsgInfo
*msginfo
= msginfo_list
->data
;
1935 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1936 msginfo
->folder
->prefs
->forward_with_format
&&
1937 msginfo
->folder
->prefs
->forward_override_from_format
&&
1938 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1943 /* decode \-escape sequences in the internal representation of the quote format */
1944 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1945 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
1948 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1949 compose
->gtkaspell
);
1951 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1953 quote_fmt_scan_string(tmp
);
1956 buf
= quote_fmt_get_buffer();
1958 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1960 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1961 quote_fmt_reset_vartable();
1967 textview
= GTK_TEXT_VIEW(compose
->text
);
1968 textbuf
= gtk_text_view_get_buffer(textview
);
1969 compose_create_tags(textview
, compose
);
1971 undo_block(compose
->undostruct
);
1972 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1973 msgfile
= procmsg_get_message_file((MsgInfo
*)msginfo
->data
);
1975 if (!is_file_exist(msgfile
))
1976 g_warning("%s: file not exist\n", msgfile
);
1978 compose_attach_append(compose
, msgfile
, msgfile
,
1979 "message/rfc822", NULL
);
1984 MsgInfo
*info
= (MsgInfo
*)msginfo_list
->data
;
1985 if (info
->subject
&& *info
->subject
) {
1986 gchar
*buf
, *buf2
, *p
;
1988 buf
= p
= g_strdup(info
->subject
);
1989 p
+= subject_get_prefix_length(p
);
1990 memmove(buf
, p
, strlen(p
) + 1);
1992 buf2
= g_strdup_printf("Fw: %s", buf
);
1993 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1999 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2000 _("Fw: multiple emails"));
2003 SIGNAL_BLOCK(textbuf
);
2005 if (account
->auto_sig
)
2006 compose_insert_sig(compose
, FALSE
);
2008 compose_wrap_all(compose
);
2010 SIGNAL_UNBLOCK(textbuf
);
2012 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
2013 gtk_text_buffer_place_cursor(textbuf
, &iter
);
2015 gtk_widget_grab_focus(compose
->header_last
->entry
);
2016 undo_unblock(compose
->undostruct
);
2017 compose
->modified
= FALSE
;
2018 compose_set_title(compose
);
2020 compose
->updating
= FALSE
;
2021 compose
->draft_timeout_tag
= -1; /* desinhibit auto-drafting after loading */
2022 SCROLL_TO_CURSOR(compose
);
2024 if (compose
->deferred_destroy
) {
2025 compose_destroy(compose
);
2029 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2034 static gboolean
compose_is_sig_separator(Compose
*compose
, GtkTextBuffer
*textbuf
, GtkTextIter
*iter
)
2036 GtkTextIter start
= *iter
;
2037 GtkTextIter end_iter
;
2038 int start_pos
= gtk_text_iter_get_offset(&start
);
2040 if (!compose
->account
->sig_sep
)
2043 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2044 start_pos
+strlen(compose
->account
->sig_sep
));
2046 /* check sig separator */
2047 str
= gtk_text_iter_get_text(&start
, &end_iter
);
2048 if (!strcmp(str
, compose
->account
->sig_sep
)) {
2050 /* check end of line (\n) */
2051 gtk_text_buffer_get_iter_at_offset(textbuf
, &start
,
2052 start_pos
+strlen(compose
->account
->sig_sep
));
2053 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2054 start_pos
+strlen(compose
->account
->sig_sep
)+1);
2055 tmp
= gtk_text_iter_get_text(&start
, &end_iter
);
2056 if (!strcmp(tmp
,"\n")) {
2068 static void compose_colorize_signature(Compose
*compose
)
2070 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
2072 GtkTextIter end_iter
;
2073 gtk_text_buffer_get_start_iter(buffer
, &iter
);
2074 while (gtk_text_iter_forward_line(&iter
))
2075 if (compose_is_sig_separator(compose
, buffer
, &iter
)) {
2076 gtk_text_buffer_get_end_iter(buffer
, &end_iter
);
2077 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &end_iter
);
2081 #define BLOCK_WRAP() { \
2082 prev_autowrap = compose->autowrap; \
2083 buffer = gtk_text_view_get_buffer( \
2084 GTK_TEXT_VIEW(compose->text)); \
2085 compose->autowrap = FALSE; \
2087 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2088 G_CALLBACK(compose_changed_cb), \
2090 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2091 G_CALLBACK(text_inserted), \
2094 #define UNBLOCK_WRAP() { \
2095 compose->autowrap = prev_autowrap; \
2096 if (compose->autowrap) { \
2097 gint old = compose->draft_timeout_tag; \
2098 compose->draft_timeout_tag = -2; \
2099 compose_wrap_all(compose); \
2100 compose->draft_timeout_tag = old; \
2103 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2104 G_CALLBACK(compose_changed_cb), \
2106 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2107 G_CALLBACK(text_inserted), \
2111 Compose
*compose_reedit(MsgInfo
*msginfo
, gboolean batch
)
2113 Compose
*compose
= NULL
;
2114 PrefsAccount
*account
= NULL
;
2115 GtkTextView
*textview
;
2116 GtkTextBuffer
*textbuf
;
2120 gchar buf
[BUFFSIZE
];
2121 gboolean use_signing
= FALSE
;
2122 gboolean use_encryption
= FALSE
;
2123 gchar
*privacy_system
= NULL
;
2124 int priority
= PRIORITY_NORMAL
;
2125 MsgInfo
*replyinfo
= NULL
, *fwdinfo
= NULL
;
2126 gboolean autowrap
= prefs_common
.autowrap
;
2127 gboolean autoindent
= prefs_common
.auto_indent
;
2129 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2130 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
2132 if (compose_put_existing_to_front(msginfo
)) {
2136 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2137 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2138 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2139 gchar queueheader_buf
[BUFFSIZE
];
2142 /* Select Account from queue headers */
2143 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2144 sizeof(queueheader_buf
), "X-Claws-Account-Id:")) {
2145 id
= atoi(&queueheader_buf
[strlen("X-Claws-Account-Id:")]);
2146 account
= account_find_from_id(id
);
2148 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2149 sizeof(queueheader_buf
), "X-Sylpheed-Account-Id:")) {
2150 id
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Account-Id:")]);
2151 account
= account_find_from_id(id
);
2153 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2154 sizeof(queueheader_buf
), "NAID:")) {
2155 id
= atoi(&queueheader_buf
[strlen("NAID:")]);
2156 account
= account_find_from_id(id
);
2158 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2159 sizeof(queueheader_buf
), "MAID:")) {
2160 id
= atoi(&queueheader_buf
[strlen("MAID:")]);
2161 account
= account_find_from_id(id
);
2163 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2164 sizeof(queueheader_buf
), "S:")) {
2165 account
= account_find_from_address(queueheader_buf
, FALSE
);
2167 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2168 sizeof(queueheader_buf
), "X-Claws-Sign:")) {
2169 param
= atoi(&queueheader_buf
[strlen("X-Claws-Sign:")]);
2170 use_signing
= param
;
2173 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2174 sizeof(queueheader_buf
), "X-Sylpheed-Sign:")) {
2175 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Sign:")]);
2176 use_signing
= param
;
2179 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2180 sizeof(queueheader_buf
), "X-Claws-Encrypt:")) {
2181 param
= atoi(&queueheader_buf
[strlen("X-Claws-Encrypt:")]);
2182 use_encryption
= param
;
2184 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2185 sizeof(queueheader_buf
), "X-Sylpheed-Encrypt:")) {
2186 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Encrypt:")]);
2187 use_encryption
= param
;
2189 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2190 sizeof(queueheader_buf
), "X-Claws-Auto-Wrapping:")) {
2191 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Wrapping:")]);
2194 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2195 sizeof(queueheader_buf
), "X-Claws-Auto-Indent:")) {
2196 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Indent:")]);
2199 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2200 sizeof(queueheader_buf
), "X-Claws-Privacy-System:")) {
2201 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Claws-Privacy-System:")]);
2203 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2204 sizeof(queueheader_buf
), "X-Sylpheed-Privacy-System:")) {
2205 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Sylpheed-Privacy-System:")]);
2207 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2208 sizeof(queueheader_buf
), "X-Priority: ")) {
2209 param
= atoi(&queueheader_buf
[strlen("X-Priority: ")]); /* mind the space */
2212 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2213 sizeof(queueheader_buf
), "RMID:")) {
2214 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("RMID:")], "\t", 0);
2215 if (tokens
[0] && tokens
[1] && tokens
[2]) {
2216 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2217 if (orig_item
!= NULL
) {
2218 replyinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2223 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2224 sizeof(queueheader_buf
), "FMID:")) {
2225 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("FMID:")], "\t", 0);
2226 if (tokens
[0] && tokens
[1] && tokens
[2]) {
2227 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2228 if (orig_item
!= NULL
) {
2229 fwdinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2235 account
= msginfo
->folder
->folder
->account
;
2238 if (!account
&& prefs_common
.reedit_account_autosel
) {
2239 gchar from
[BUFFSIZE
];
2240 if (!procheader_get_header_from_msginfo(msginfo
, from
, sizeof(from
), "FROM:")) {
2241 extract_address(from
);
2242 account
= account_find_from_address(from
, FALSE
);
2246 account
= cur_account
;
2248 cm_return_val_if_fail(account
!= NULL
, NULL
);
2250 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REEDIT
, batch
);
2252 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", autowrap
);
2253 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", autoindent
);
2254 compose
->autowrap
= autowrap
;
2255 compose
->replyinfo
= replyinfo
;
2256 compose
->fwdinfo
= fwdinfo
;
2258 compose
->updating
= TRUE
;
2259 compose
->priority
= priority
;
2261 if (privacy_system
!= NULL
) {
2262 compose
->privacy_system
= privacy_system
;
2263 compose_use_signing(compose
, use_signing
);
2264 compose_use_encryption(compose
, use_encryption
);
2265 compose_update_privacy_system_menu_item(compose
, FALSE
);
2267 activate_privacy_system(compose
, account
, FALSE
);
2270 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
2272 compose_extract_original_charset(compose
);
2274 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2275 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2276 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2277 gchar queueheader_buf
[BUFFSIZE
];
2279 /* Set message save folder */
2280 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
, sizeof(queueheader_buf
), "SCF:")) {
2281 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2282 compose_set_save_to(compose
, &queueheader_buf
[4]);
2284 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
, sizeof(queueheader_buf
), "RRCPT:")) {
2285 gint active
= atoi(&queueheader_buf
[strlen("RRCPT:")]);
2287 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
2292 if (compose_parse_header(compose
, msginfo
) < 0) {
2293 compose
->updating
= FALSE
;
2294 compose_destroy(compose
);
2297 compose_reedit_set_entry(compose
, msginfo
);
2299 textview
= GTK_TEXT_VIEW(compose
->text
);
2300 textbuf
= gtk_text_view_get_buffer(textview
);
2301 compose_create_tags(textview
, compose
);
2303 mark
= gtk_text_buffer_get_insert(textbuf
);
2304 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
2306 g_signal_handlers_block_by_func(G_OBJECT(textbuf
),
2307 G_CALLBACK(compose_changed_cb
),
2310 if (MSG_IS_ENCRYPTED(msginfo
->flags
)) {
2311 fp
= procmime_get_first_encrypted_text_content(msginfo
);
2313 compose_force_encryption(compose
, account
, TRUE
, NULL
);
2316 fp
= procmime_get_first_text_content(msginfo
);
2319 g_warning("Can't get text part\n");
2323 gboolean prev_autowrap
= compose
->autowrap
;
2324 GtkTextBuffer
*buffer
= textbuf
;
2326 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2328 gtk_text_buffer_insert(textbuf
, &iter
, buf
, -1);
2334 compose_attach_parts(compose
, msginfo
);
2336 compose_colorize_signature(compose
);
2338 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf
),
2339 G_CALLBACK(compose_changed_cb
),
2342 gtk_widget_grab_focus(compose
->text
);
2344 if (prefs_common
.auto_exteditor
) {
2345 compose_exec_ext_editor(compose
);
2347 compose
->modified
= FALSE
;
2348 compose_set_title(compose
);
2350 compose
->updating
= FALSE
;
2351 compose
->draft_timeout_tag
= -1; /* desinhibit auto-drafting after loading */
2352 SCROLL_TO_CURSOR(compose
);
2354 if (compose
->deferred_destroy
) {
2355 compose_destroy(compose
);
2359 compose
->sig_str
= account_get_signature_str(compose
->account
);
2361 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2366 Compose
*compose_redirect(PrefsAccount
*account
, MsgInfo
*msginfo
,
2373 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2376 account
= account_get_reply_account(msginfo
,
2377 prefs_common
.reply_account_autosel
);
2378 cm_return_val_if_fail(account
!= NULL
, NULL
);
2380 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REDIRECT
, batch
);
2382 compose
->updating
= TRUE
;
2384 compose_create_tags(GTK_TEXT_VIEW(compose
->text
), compose
);
2385 compose
->replyinfo
= NULL
;
2386 compose
->fwdinfo
= NULL
;
2388 compose_show_first_last_header(compose
, TRUE
);
2390 gtk_widget_grab_focus(compose
->header_last
->entry
);
2392 filename
= procmsg_get_message_file(msginfo
);
2394 if (filename
== NULL
) {
2395 compose
->updating
= FALSE
;
2396 compose_destroy(compose
);
2401 compose
->redirect_filename
= filename
;
2403 /* Set save folder */
2404 item
= msginfo
->folder
;
2405 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
2406 gchar
*folderidentifier
;
2408 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
2409 folderidentifier
= folder_item_get_identifier(item
);
2410 compose_set_save_to(compose
, folderidentifier
);
2411 g_free(folderidentifier
);
2414 compose_attach_parts(compose
, msginfo
);
2416 if (msginfo
->subject
)
2417 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2419 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
2421 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
, FALSE
, FALSE
,
2422 _("The body of the \"Redirect\" template has an error at line %d."));
2423 quote_fmt_reset_vartable();
2424 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), FALSE
);
2426 compose_colorize_signature(compose
);
2429 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Add", FALSE
);
2430 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
2431 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
2433 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Save", FALSE
);
2434 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", FALSE
);
2435 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/AttachFile", FALSE
);
2436 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", FALSE
);
2437 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", FALSE
);
2438 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", FALSE
);
2439 #if !GTK_CHECK_VERSION(2, 24, 0)
2440 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", FALSE
);
2442 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Actions", FALSE
);
2444 if (compose
->toolbar
->draft_btn
)
2445 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
2446 if (compose
->toolbar
->insert_btn
)
2447 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
2448 if (compose
->toolbar
->attach_btn
)
2449 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
2450 if (compose
->toolbar
->sig_btn
)
2451 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
2452 if (compose
->toolbar
->exteditor_btn
)
2453 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
2454 if (compose
->toolbar
->linewrap_current_btn
)
2455 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, FALSE
);
2456 if (compose
->toolbar
->linewrap_all_btn
)
2457 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, FALSE
);
2459 compose
->modified
= FALSE
;
2460 compose_set_title(compose
);
2461 compose
->updating
= FALSE
;
2462 compose
->draft_timeout_tag
= -1; /* desinhibit auto-drafting after loading */
2463 SCROLL_TO_CURSOR(compose
);
2465 if (compose
->deferred_destroy
) {
2466 compose_destroy(compose
);
2470 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2475 GList
*compose_get_compose_list(void)
2477 return compose_list
;
2480 void compose_entry_append(Compose
*compose
, const gchar
*address
,
2481 ComposeEntryType type
, ComposePrefType pref_type
)
2483 const gchar
*header
;
2485 gboolean in_quote
= FALSE
;
2486 if (!address
|| *address
== '\0') return;
2493 header
= N_("Bcc:");
2495 case COMPOSE_REPLYTO
:
2496 header
= N_("Reply-To:");
2498 case COMPOSE_NEWSGROUPS
:
2499 header
= N_("Newsgroups:");
2501 case COMPOSE_FOLLOWUPTO
:
2502 header
= N_( "Followup-To:");
2504 case COMPOSE_INREPLYTO
:
2505 header
= N_( "In-Reply-To:");
2512 header
= prefs_common_translated_header_name(header
);
2514 cur
= begin
= (gchar
*)address
;
2516 /* we separate the line by commas, but not if we're inside a quoted
2518 while (*cur
!= '\0') {
2520 in_quote
= !in_quote
;
2521 if (*cur
== ',' && !in_quote
) {
2522 gchar
*tmp
= g_strdup(begin
);
2524 tmp
[cur
-begin
]='\0';
2527 while (*tmp
== ' ' || *tmp
== '\t')
2529 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2536 gchar
*tmp
= g_strdup(begin
);
2538 tmp
[cur
-begin
]='\0';
2541 while (*tmp
== ' ' || *tmp
== '\t')
2543 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2548 static void compose_entry_mark_default_to(Compose
*compose
, const gchar
*mailto
)
2550 #if !GTK_CHECK_VERSION(3, 0, 0)
2551 static GdkColor yellow
;
2552 static GdkColor black
;
2553 static gboolean yellow_initialised
= FALSE
;
2555 static GdkColor yellow
= { (guint32
)0, (guint16
)0xf5, (guint16
)0xf6, (guint16
)0xbe };
2556 static GdkColor black
= { (guint32
)0, (guint16
)0x0, (guint16
)0x0, (guint16
)0x0 };
2561 #if !GTK_CHECK_VERSION(3, 0, 0)
2562 if (!yellow_initialised
) {
2563 gdk_color_parse("#f5f6be", &yellow
);
2564 gdk_color_parse("#000000", &black
);
2565 yellow_initialised
= gdk_colormap_alloc_color(
2566 gdk_colormap_get_system(), &yellow
, FALSE
, TRUE
);
2567 yellow_initialised
&= gdk_colormap_alloc_color(
2568 gdk_colormap_get_system(), &black
, FALSE
, TRUE
);
2572 for (h_list
= compose
->header_list
; h_list
!= NULL
; h_list
= h_list
->next
) {
2573 entry
= GTK_ENTRY(((ComposeHeaderEntry
*)h_list
->data
)->entry
);
2574 if (gtk_entry_get_text(entry
) &&
2575 !g_utf8_collate(gtk_entry_get_text(entry
), mailto
)) {
2576 #if !GTK_CHECK_VERSION(3, 0, 0)
2577 if (yellow_initialised
) {
2579 gtk_widget_modify_base(
2580 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2581 GTK_STATE_NORMAL
, &yellow
);
2582 gtk_widget_modify_text(
2583 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2584 GTK_STATE_NORMAL
, &black
);
2585 #if !GTK_CHECK_VERSION(3, 0, 0)
2592 void compose_toolbar_cb(gint action
, gpointer data
)
2594 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
2595 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
2597 cm_return_if_fail(compose
!= NULL
);
2601 compose_send_cb(NULL
, compose
);
2604 compose_send_later_cb(NULL
, compose
);
2607 compose_draft(compose
, COMPOSE_QUIT_EDITING
);
2610 compose_insert_file_cb(NULL
, compose
);
2613 compose_attach_cb(NULL
, compose
);
2616 compose_insert_sig(compose
, FALSE
);
2619 compose_ext_editor_cb(NULL
, compose
);
2621 case A_LINEWRAP_CURRENT
:
2622 compose_beautify_paragraph(compose
, NULL
, TRUE
);
2624 case A_LINEWRAP_ALL
:
2625 compose_wrap_all_full(compose
, TRUE
);
2628 compose_address_cb(NULL
, compose
);
2631 case A_CHECK_SPELLING
:
2632 compose_check_all(NULL
, compose
);
2640 static MailField
compose_entries_set(Compose
*compose
, const gchar
*mailto
, ComposeEntryType to_type
)
2645 gchar
*subject
= NULL
;
2649 gchar
**attach
= NULL
;
2650 gchar
*inreplyto
= NULL
;
2651 MailField mfield
= NO_FIELD_PRESENT
;
2653 /* get mailto parts but skip from */
2654 scan_mailto_url(mailto
, NULL
, &to
, &cc
, &bcc
, &subject
, &body
, &attach
, &inreplyto
);
2657 compose_entry_append(compose
, to
, to_type
, PREF_MAILTO
);
2658 mfield
= TO_FIELD_PRESENT
;
2661 compose_entry_append(compose
, cc
, COMPOSE_CC
, PREF_MAILTO
);
2663 compose_entry_append(compose
, bcc
, COMPOSE_BCC
, PREF_MAILTO
);
2665 if (!g_utf8_validate (subject
, -1, NULL
)) {
2666 temp
= g_locale_to_utf8 (subject
, -1, NULL
, &len
, NULL
);
2667 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), temp
);
2670 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
2672 mfield
= SUBJECT_FIELD_PRESENT
;
2675 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2676 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2679 gboolean prev_autowrap
= compose
->autowrap
;
2681 compose
->autowrap
= FALSE
;
2683 mark
= gtk_text_buffer_get_insert(buffer
);
2684 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
2686 if (!g_utf8_validate (body
, -1, NULL
)) {
2687 temp
= g_locale_to_utf8 (body
, -1, NULL
, &len
, NULL
);
2688 gtk_text_buffer_insert(buffer
, &iter
, temp
, -1);
2691 gtk_text_buffer_insert(buffer
, &iter
, body
, -1);
2693 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
2695 compose
->autowrap
= prev_autowrap
;
2696 if (compose
->autowrap
)
2697 compose_wrap_all(compose
);
2698 mfield
= BODY_FIELD_PRESENT
;
2702 gint i
= 0, att
= 0;
2703 gchar
*warn_files
= NULL
;
2704 while (attach
[i
] != NULL
) {
2705 gchar
*utf8_filename
= conv_filename_to_utf8(attach
[i
]);
2706 if (utf8_filename
) {
2707 if (compose_attach_append(compose
, attach
[i
], utf8_filename
, NULL
, NULL
)) {
2708 gchar
*tmp
= g_strdup_printf("%s%s\n",
2709 warn_files
?warn_files
:"",
2715 g_free(utf8_filename
);
2717 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2722 alertpanel_notice(ngettext(
2723 "The following file has been attached: \n%s",
2724 "The following files have been attached: \n%s", att
), warn_files
);
2729 compose_entry_append(compose
, inreplyto
, COMPOSE_INREPLYTO
, PREF_MAILTO
);
2742 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
2744 static HeaderEntry hentry
[] = {{"Reply-To:", NULL
, TRUE
},
2745 {"Cc:", NULL
, TRUE
},
2746 {"References:", NULL
, FALSE
},
2747 {"Bcc:", NULL
, TRUE
},
2748 {"Newsgroups:", NULL
, TRUE
},
2749 {"Followup-To:", NULL
, TRUE
},
2750 {"List-Post:", NULL
, FALSE
},
2751 {"X-Priority:", NULL
, FALSE
},
2752 {NULL
, NULL
, FALSE
}};
2768 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2770 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return -1;
2771 procheader_get_header_fields(fp
, hentry
);
2774 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
2775 if (hentry
[H_REPLY_TO
].body
[0] != '\0') {
2777 conv_unmime_header(hentry
[H_REPLY_TO
].body
,
2780 g_free(hentry
[H_REPLY_TO
].body
);
2781 hentry
[H_REPLY_TO
].body
= NULL
;
2783 if (hentry
[H_CC
].body
!= NULL
) {
2784 compose
->cc
= conv_unmime_header(hentry
[H_CC
].body
, NULL
, TRUE
);
2785 g_free(hentry
[H_CC
].body
);
2786 hentry
[H_CC
].body
= NULL
;
2788 if (hentry
[H_REFERENCES
].body
!= NULL
) {
2789 if (compose
->mode
== COMPOSE_REEDIT
)
2790 compose
->references
= hentry
[H_REFERENCES
].body
;
2792 compose
->references
= compose_parse_references
2793 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
2794 g_free(hentry
[H_REFERENCES
].body
);
2796 hentry
[H_REFERENCES
].body
= NULL
;
2798 if (hentry
[H_BCC
].body
!= NULL
) {
2799 if (compose
->mode
== COMPOSE_REEDIT
)
2801 conv_unmime_header(hentry
[H_BCC
].body
, NULL
, TRUE
);
2802 g_free(hentry
[H_BCC
].body
);
2803 hentry
[H_BCC
].body
= NULL
;
2805 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
2806 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
2807 hentry
[H_NEWSGROUPS
].body
= NULL
;
2809 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
2810 if (hentry
[H_FOLLOWUP_TO
].body
[0] != '\0') {
2811 compose
->followup_to
=
2812 conv_unmime_header(hentry
[H_FOLLOWUP_TO
].body
,
2815 g_free(hentry
[H_FOLLOWUP_TO
].body
);
2816 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
2818 if (hentry
[H_LIST_POST
].body
!= NULL
) {
2819 gchar
*to
= NULL
, *start
= NULL
;
2821 extract_address(hentry
[H_LIST_POST
].body
);
2822 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
2823 start
= strstr(hentry
[H_LIST_POST
].body
, "mailto:");
2825 scan_mailto_url(start
? start
: hentry
[H_LIST_POST
].body
,
2826 NULL
, &to
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2829 g_free(compose
->ml_post
);
2830 compose
->ml_post
= to
;
2833 g_free(hentry
[H_LIST_POST
].body
);
2834 hentry
[H_LIST_POST
].body
= NULL
;
2837 /* CLAWS - X-Priority */
2838 if (compose
->mode
== COMPOSE_REEDIT
)
2839 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
2842 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
2843 g_free(hentry
[H_X_PRIORITY
].body
);
2845 hentry
[H_X_PRIORITY
].body
= NULL
;
2847 if (priority
< PRIORITY_HIGHEST
||
2848 priority
> PRIORITY_LOWEST
)
2849 priority
= PRIORITY_NORMAL
;
2851 compose
->priority
= priority
;
2854 if (compose
->mode
== COMPOSE_REEDIT
) {
2855 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2856 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
2860 if (msginfo
->msgid
&& *msginfo
->msgid
)
2861 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
2863 if (!compose
->references
) {
2864 if (msginfo
->msgid
&& *msginfo
->msgid
) {
2865 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2866 compose
->references
=
2867 g_strdup_printf("<%s>\n\t<%s>",
2871 compose
->references
=
2872 g_strconcat("<", msginfo
->msgid
, ">",
2874 } else if (msginfo
->inreplyto
&& *msginfo
->inreplyto
) {
2875 compose
->references
=
2876 g_strconcat("<", msginfo
->inreplyto
, ">",
2884 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
2886 GSList
*ref_id_list
, *cur
;
2890 ref_id_list
= references_list_append(NULL
, ref
);
2891 if (!ref_id_list
) return NULL
;
2892 if (msgid
&& *msgid
)
2893 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
2898 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
2899 /* "<" + Message-ID + ">" + CR+LF+TAB */
2900 len
+= strlen((gchar
*)cur
->data
) + 5;
2902 if (len
> MAX_REFERENCES_LEN
) {
2903 /* remove second message-ID */
2904 if (ref_id_list
&& ref_id_list
->next
&&
2905 ref_id_list
->next
->next
) {
2906 g_free(ref_id_list
->next
->data
);
2907 ref_id_list
= g_slist_remove
2908 (ref_id_list
, ref_id_list
->next
->data
);
2910 slist_free_strings(ref_id_list
);
2911 g_slist_free(ref_id_list
);
2918 new_ref
= g_string_new("");
2919 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
2920 if (new_ref
->len
> 0)
2921 g_string_append(new_ref
, "\n\t");
2922 g_string_append_printf(new_ref
, "<%s>", (gchar
*)cur
->data
);
2925 slist_free_strings(ref_id_list
);
2926 g_slist_free(ref_id_list
);
2928 new_ref_str
= new_ref
->str
;
2929 g_string_free(new_ref
, FALSE
);
2934 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
2935 const gchar
*fmt
, const gchar
*qmark
,
2936 const gchar
*body
, gboolean rewrap
,
2937 gboolean need_unescape
,
2938 const gchar
*err_msg
)
2940 MsgInfo
* dummyinfo
= NULL
;
2941 gchar
*quote_str
= NULL
;
2943 gboolean prev_autowrap
;
2944 const gchar
*trimmed_body
= body
;
2945 gint cursor_pos
= -1;
2946 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2947 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2952 SIGNAL_BLOCK(buffer
);
2955 dummyinfo
= compose_msginfo_new_from_compose(compose
);
2956 msginfo
= dummyinfo
;
2959 if (qmark
!= NULL
) {
2961 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
2962 compose
->gtkaspell
);
2964 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
2966 quote_fmt_scan_string(qmark
);
2969 buf
= quote_fmt_get_buffer();
2971 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2973 Xstrdup_a(quote_str
, buf
, goto error
)
2976 if (fmt
&& *fmt
!= '\0') {
2979 while (*trimmed_body
== '\n')
2983 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
,
2984 compose
->gtkaspell
);
2986 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
);
2988 if (need_unescape
) {
2991 /* decode \-escape sequences in the internal representation of the quote format */
2992 tmp
= g_malloc(strlen(fmt
)+1);
2993 pref_get_unescaped_pref(tmp
, fmt
);
2994 quote_fmt_scan_string(tmp
);
2998 quote_fmt_scan_string(fmt
);
3002 buf
= quote_fmt_get_buffer();
3004 gint line
= quote_fmt_get_line();
3005 alertpanel_error(err_msg
, line
);
3011 prev_autowrap
= compose
->autowrap
;
3012 compose
->autowrap
= FALSE
;
3014 mark
= gtk_text_buffer_get_insert(buffer
);
3015 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3016 if (g_utf8_validate(buf
, -1, NULL
)) {
3017 gtk_text_buffer_insert(buffer
, &iter
, buf
, -1);
3019 gchar
*tmpout
= NULL
;
3020 tmpout
= conv_codeset_strdup
3021 (buf
, conv_get_locale_charset_str_no_utf8(),
3023 if (!tmpout
|| !g_utf8_validate(tmpout
, -1, NULL
)) {
3025 tmpout
= g_malloc(strlen(buf
)*2+1);
3026 conv_localetodisp(tmpout
, strlen(buf
)*2+1, buf
);
3028 gtk_text_buffer_insert(buffer
, &iter
, tmpout
, -1);
3032 cursor_pos
= quote_fmt_get_cursor_pos();
3033 if (cursor_pos
== -1)
3034 cursor_pos
= gtk_text_iter_get_offset(&iter
);
3035 compose
->set_cursor_pos
= cursor_pos
;
3037 gtk_text_buffer_get_start_iter(buffer
, &iter
);
3038 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
3039 gtk_text_buffer_place_cursor(buffer
, &iter
);
3041 compose
->autowrap
= prev_autowrap
;
3042 if (compose
->autowrap
&& rewrap
)
3043 compose_wrap_all(compose
);
3050 SIGNAL_UNBLOCK(buffer
);
3052 procmsg_msginfo_free( dummyinfo
);
3057 /* if ml_post is of type addr@host and from is of type
3058 * addr-anything@host, return TRUE
3060 static gboolean
is_subscription(const gchar
*ml_post
, const gchar
*from
)
3062 gchar
*left_ml
= NULL
;
3063 gchar
*right_ml
= NULL
;
3064 gchar
*left_from
= NULL
;
3065 gchar
*right_from
= NULL
;
3066 gboolean result
= FALSE
;
3068 if (!ml_post
|| !from
)
3071 left_ml
= g_strdup(ml_post
);
3072 if (strstr(left_ml
, "@")) {
3073 right_ml
= strstr(left_ml
, "@")+1;
3074 *(strstr(left_ml
, "@")) = '\0';
3077 left_from
= g_strdup(from
);
3078 if (strstr(left_from
, "@")) {
3079 right_from
= strstr(left_from
, "@")+1;
3080 *(strstr(left_from
, "@")) = '\0';
3083 if (left_ml
&& left_from
&& right_ml
&& right_from
3084 && !strncmp(left_from
, left_ml
, strlen(left_ml
))
3085 && !strcmp(right_from
, right_ml
)) {
3094 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
3095 gboolean respect_default_to
)
3099 if (!folder
|| !folder
->prefs
)
3102 if (respect_default_to
&& folder
->prefs
->enable_default_to
) {
3103 compose_entry_append(compose
, folder
->prefs
->default_to
,
3104 COMPOSE_TO
, PREF_FOLDER
);
3105 compose_entry_mark_default_to(compose
, folder
->prefs
->default_to
);
3107 if (folder
->prefs
->enable_default_cc
)
3108 compose_entry_append(compose
, folder
->prefs
->default_cc
,
3109 COMPOSE_CC
, PREF_FOLDER
);
3110 if (folder
->prefs
->enable_default_bcc
)
3111 compose_entry_append(compose
, folder
->prefs
->default_bcc
,
3112 COMPOSE_BCC
, PREF_FOLDER
);
3113 if (folder
->prefs
->enable_default_replyto
)
3114 compose_entry_append(compose
, folder
->prefs
->default_replyto
,
3115 COMPOSE_REPLYTO
, PREF_FOLDER
);
3118 static void compose_reply_set_subject(Compose
*compose
, MsgInfo
*msginfo
)
3123 if (!compose
|| !msginfo
)
3126 if (msginfo
->subject
&& *msginfo
->subject
) {
3127 buf
= p
= g_strdup(msginfo
->subject
);
3128 p
+= subject_get_prefix_length(p
);
3129 memmove(buf
, p
, strlen(p
) + 1);
3131 buf2
= g_strdup_printf("Re: %s", buf
);
3132 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
3137 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
3140 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
3141 gboolean to_all
, gboolean to_ml
,
3143 gboolean followup_and_reply_to
)
3145 GSList
*cc_list
= NULL
;
3148 gchar
*replyto
= NULL
;
3149 gchar
*ac_email
= NULL
;
3151 gboolean reply_to_ml
= FALSE
;
3152 gboolean default_reply_to
= FALSE
;
3154 cm_return_if_fail(compose
->account
!= NULL
);
3155 cm_return_if_fail(msginfo
!= NULL
);
3157 reply_to_ml
= to_ml
&& compose
->ml_post
;
3159 default_reply_to
= msginfo
->folder
&&
3160 msginfo
->folder
->prefs
->enable_default_reply_to
;
3162 if (compose
->account
->protocol
!= A_NNTP
) {
3163 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
3165 if (reply_to_ml
&& !default_reply_to
) {
3167 gboolean is_subscr
= is_subscription(compose
->ml_post
,
3170 /* normal answer to ml post with a reply-to */
3171 compose_entry_append(compose
,
3173 COMPOSE_TO
, PREF_ML
);
3174 if (compose
->replyto
)
3175 compose_entry_append(compose
,
3177 COMPOSE_CC
, PREF_ML
);
3179 /* answer to subscription confirmation */
3180 if (compose
->replyto
)
3181 compose_entry_append(compose
,
3183 COMPOSE_TO
, PREF_ML
);
3184 else if (msginfo
->from
)
3185 compose_entry_append(compose
,
3187 COMPOSE_TO
, PREF_ML
);
3190 else if (!(to_all
|| to_sender
) && default_reply_to
) {
3191 compose_entry_append(compose
,
3192 msginfo
->folder
->prefs
->default_reply_to
,
3193 COMPOSE_TO
, PREF_FOLDER
);
3194 compose_entry_mark_default_to(compose
,
3195 msginfo
->folder
->prefs
->default_reply_to
);
3200 Xstrdup_a(tmp1
, msginfo
->from
, return);
3201 extract_address(tmp1
);
3202 if (to_all
|| to_sender
||
3203 !account_find_from_address(tmp1
, FALSE
))
3204 compose_entry_append(compose
,
3205 (compose
->replyto
&& !to_sender
)
3206 ? compose
->replyto
:
3207 msginfo
->from
? msginfo
->from
: "",
3208 COMPOSE_TO
, PREF_NONE
);
3209 else if (!to_all
&& !to_sender
) {
3210 if (!folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) &&
3211 !folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
) &&
3212 !folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
)) {
3213 if (compose
->replyto
) {
3214 compose_entry_append(compose
,
3216 COMPOSE_TO
, PREF_NONE
);
3218 compose_entry_append(compose
,
3219 msginfo
->from
? msginfo
->from
: "",
3220 COMPOSE_TO
, PREF_NONE
);
3223 /* replying to own mail, use original recp */
3224 compose_entry_append(compose
,
3225 msginfo
->to
? msginfo
->to
: "",
3226 COMPOSE_TO
, PREF_NONE
);
3227 compose_entry_append(compose
,
3228 msginfo
->cc
? msginfo
->cc
: "",
3229 COMPOSE_CC
, PREF_NONE
);
3234 if (to_sender
|| (compose
->followup_to
&&
3235 !strncmp(compose
->followup_to
, "poster", 6)))
3236 compose_entry_append
3238 (compose
->replyto
? compose
->replyto
:
3239 msginfo
->from
? msginfo
->from
: ""),
3240 COMPOSE_TO
, PREF_NONE
);
3242 else if (followup_and_reply_to
|| to_all
) {
3243 compose_entry_append
3245 (compose
->replyto
? compose
->replyto
:
3246 msginfo
->from
? msginfo
->from
: ""),
3247 COMPOSE_TO
, PREF_NONE
);
3249 compose_entry_append
3251 compose
->followup_to
? compose
->followup_to
:
3252 compose
->newsgroups
? compose
->newsgroups
: "",
3253 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3256 compose_entry_append
3258 compose
->followup_to
? compose
->followup_to
:
3259 compose
->newsgroups
? compose
->newsgroups
: "",
3260 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3262 compose_reply_set_subject(compose
, msginfo
);
3264 if (to_ml
&& compose
->ml_post
) return;
3265 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
3267 if (compose
->replyto
) {
3268 Xstrdup_a(replyto
, compose
->replyto
, return);
3269 extract_address(replyto
);
3271 if (msginfo
->from
) {
3272 Xstrdup_a(from
, msginfo
->from
, return);
3273 extract_address(from
);
3276 if (replyto
&& from
)
3277 cc_list
= address_list_append_with_comments(cc_list
, from
);
3278 if (to_all
&& msginfo
->folder
&&
3279 msginfo
->folder
->prefs
->enable_default_reply_to
)
3280 cc_list
= address_list_append_with_comments(cc_list
,
3281 msginfo
->folder
->prefs
->default_reply_to
);
3282 cc_list
= address_list_append_with_comments(cc_list
, msginfo
->to
);
3283 cc_list
= address_list_append_with_comments(cc_list
, compose
->cc
);
3285 ac_email
= g_utf8_strdown(compose
->account
->address
, -1);
3288 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
) {
3289 gchar
*addr
= g_utf8_strdown(cur
->data
, -1);
3290 extract_address(addr
);
3292 if (strcmp(ac_email
, addr
))
3293 compose_entry_append(compose
, (gchar
*)cur
->data
,
3294 COMPOSE_CC
, PREF_NONE
);
3296 debug_print("Cc address same as compose account's, ignoring\n");
3301 slist_free_strings(cc_list
);
3302 g_slist_free(cc_list
);
3308 #define SET_ENTRY(entry, str) \
3311 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3314 #define SET_ADDRESS(type, str) \
3317 compose_entry_append(compose, str, type, PREF_NONE); \
3320 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
3322 cm_return_if_fail(msginfo
!= NULL
);
3324 SET_ENTRY(subject_entry
, msginfo
->subject
);
3325 SET_ENTRY(from_name
, msginfo
->from
);
3326 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
3327 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
3328 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
3329 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
3330 SET_ADDRESS(COMPOSE_NEWSGROUPS
, compose
->newsgroups
);
3331 SET_ADDRESS(COMPOSE_FOLLOWUPTO
, compose
->followup_to
);
3333 compose_update_priority_menu_item(compose
);
3334 compose_update_privacy_system_menu_item(compose
, FALSE
);
3335 compose_show_first_last_header(compose
, TRUE
);
3341 static void compose_insert_sig(Compose
*compose
, gboolean replace
)
3343 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3344 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3346 GtkTextIter iter
, iter_end
;
3347 gint cur_pos
, ins_pos
;
3348 gboolean prev_autowrap
;
3349 gboolean found
= FALSE
;
3350 gboolean exists
= FALSE
;
3352 cm_return_if_fail(compose
->account
!= NULL
);
3356 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3357 G_CALLBACK(compose_changed_cb
),
3360 mark
= gtk_text_buffer_get_insert(buffer
);
3361 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3362 cur_pos
= gtk_text_iter_get_offset (&iter
);
3365 gtk_text_buffer_get_end_iter(buffer
, &iter
);
3367 exists
= (compose
->sig_str
!= NULL
);
3370 GtkTextIter first_iter
, start_iter
, end_iter
;
3372 gtk_text_buffer_get_start_iter(buffer
, &first_iter
);
3374 if (!exists
|| compose
->sig_str
[0] == '\0')
3377 found
= gtk_text_iter_forward_to_tag_toggle(&first_iter
,
3378 compose
->signature_tag
);
3381 /* include previous \n\n */
3382 gtk_text_iter_backward_chars(&first_iter
, 1);
3383 start_iter
= first_iter
;
3384 end_iter
= first_iter
;
3386 found
= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3387 compose
->signature_tag
);
3388 found
&= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3389 compose
->signature_tag
);
3391 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
3397 g_free(compose
->sig_str
);
3398 compose
->sig_str
= account_get_signature_str(compose
->account
);
3400 cur_pos
= gtk_text_iter_get_offset(&iter
);
3402 if (!compose
->sig_str
|| (replace
&& !compose
->account
->auto_sig
)) {
3403 g_free(compose
->sig_str
);
3404 compose
->sig_str
= NULL
;
3406 if (compose
->sig_inserted
== FALSE
)
3407 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
3408 compose
->sig_inserted
= TRUE
;
3410 cur_pos
= gtk_text_iter_get_offset(&iter
);
3411 gtk_text_buffer_insert(buffer
, &iter
, compose
->sig_str
, -1);
3413 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cur_pos
);
3414 gtk_text_iter_forward_chars(&iter
, 1);
3415 gtk_text_buffer_get_end_iter(buffer
, &iter_end
);
3416 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &iter_end
);
3418 if (cur_pos
> gtk_text_buffer_get_char_count (buffer
))
3419 cur_pos
= gtk_text_buffer_get_char_count (buffer
);
3422 /* put the cursor where it should be
3423 * either where the quote_fmt says, either where it was */
3424 if (compose
->set_cursor_pos
< 0)
3425 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, ins_pos
);
3427 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
,
3428 compose
->set_cursor_pos
);
3430 compose
->set_cursor_pos
= -1;
3431 gtk_text_buffer_place_cursor(buffer
, &iter
);
3432 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3433 G_CALLBACK(compose_changed_cb
),
3439 static ComposeInsertResult
compose_insert_file(Compose
*compose
, const gchar
*file
)
3442 GtkTextBuffer
*buffer
;
3445 const gchar
*cur_encoding
;
3446 gchar buf
[BUFFSIZE
];
3449 gboolean prev_autowrap
;
3450 gboolean badtxt
= FALSE
;
3451 struct stat file_stat
;
3454 cm_return_val_if_fail(file
!= NULL
, COMPOSE_INSERT_NO_FILE
);
3456 /* get the size of the file we are about to insert */
3457 ret
= g_stat(file
, &file_stat
);
3459 gchar
*shortfile
= g_path_get_basename(file
);
3460 alertpanel_error(_("Could not get size of file '%s'."), shortfile
);
3462 return COMPOSE_INSERT_NO_FILE
;
3463 } else if (prefs_common
.warn_large_insert
== TRUE
) {
3465 /* ask user for confirmation if the file is large */
3466 if (prefs_common
.warn_large_insert_size
< 0 ||
3467 file_stat
.st_size
> (prefs_common
.warn_large_insert_size
* 1024)) {
3471 msg
= g_strdup_printf(_("You are about to insert a file of %s "
3472 "in the message body. Are you sure you want to do that?"),
3473 to_human_readable(file_stat
.st_size
));
3474 aval
= alertpanel_full(_("Are you sure?"), msg
, GTK_STOCK_CANCEL
,
3475 _("+_Insert"), NULL
, TRUE
, NULL
, ALERT_QUESTION
, G_ALERTDEFAULT
);
3478 /* do we ask for confirmation next time? */
3479 if (aval
& G_ALERTDISABLE
) {
3480 /* no confirmation next time, disable feature in preferences */
3481 aval
&= ~G_ALERTDISABLE
;
3482 prefs_common
.warn_large_insert
= FALSE
;
3485 /* abort file insertion if user canceled action */
3486 if (aval
!= G_ALERTALTERNATE
) {
3487 return COMPOSE_INSERT_NO_FILE
;
3493 if ((fp
= g_fopen(file
, "rb")) == NULL
) {
3494 FILE_OP_ERROR(file
, "fopen");
3495 return COMPOSE_INSERT_READ_ERROR
;
3498 prev_autowrap
= compose
->autowrap
;
3499 compose
->autowrap
= FALSE
;
3501 text
= GTK_TEXT_VIEW(compose
->text
);
3502 buffer
= gtk_text_view_get_buffer(text
);
3503 mark
= gtk_text_buffer_get_insert(buffer
);
3504 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3506 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3507 G_CALLBACK(text_inserted
),
3510 cur_encoding
= conv_get_locale_charset_str_no_utf8();
3512 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
3515 if (g_utf8_validate(buf
, -1, NULL
) == TRUE
)
3516 str
= g_strdup(buf
);
3518 str
= conv_codeset_strdup
3519 (buf
, cur_encoding
, CS_INTERNAL
);
3522 /* strip <CR> if DOS/Windows file,
3523 replace <CR> with <LF> if Macintosh file. */
3526 if (len
> 0 && str
[len
- 1] != '\n') {
3528 if (str
[len
] == '\r') str
[len
] = '\n';
3531 gtk_text_buffer_insert(buffer
, &iter
, str
, -1);
3535 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3536 G_CALLBACK(text_inserted
),
3538 compose
->autowrap
= prev_autowrap
;
3539 if (compose
->autowrap
)
3540 compose_wrap_all(compose
);
3545 return COMPOSE_INSERT_INVALID_CHARACTER
;
3547 return COMPOSE_INSERT_SUCCESS
;
3550 static gboolean
compose_attach_append(Compose
*compose
, const gchar
*file
,
3551 const gchar
*filename
,
3552 const gchar
*content_type
,
3553 const gchar
*charset
)
3561 GtkListStore
*store
;
3563 gboolean has_binary
= FALSE
;
3565 if (!is_file_exist(file
)) {
3566 gchar
*file_from_uri
= g_filename_from_uri(file
, NULL
, NULL
);
3567 gboolean result
= FALSE
;
3568 if (file_from_uri
&& is_file_exist(file_from_uri
)) {
3569 result
= compose_attach_append(
3570 compose
, file_from_uri
,
3571 filename
, content_type
,
3574 g_free(file_from_uri
);
3577 alertpanel_error("File %s doesn't exist\n", filename
);
3580 if ((size
= get_file_size(file
)) < 0) {
3581 alertpanel_error("Can't get file size of %s\n", filename
);
3585 alertpanel_error(_("File %s is empty."), filename
);
3588 if ((fp
= g_fopen(file
, "rb")) == NULL
) {
3589 alertpanel_error(_("Can't read %s."), filename
);
3594 ainfo
= g_new0(AttachInfo
, 1);
3595 auto_ainfo
= g_auto_pointer_new_with_free
3596 (ainfo
, (GFreeFunc
) compose_attach_info_free
);
3597 ainfo
->file
= g_strdup(file
);
3600 ainfo
->content_type
= g_strdup(content_type
);
3601 if (!g_ascii_strcasecmp(content_type
, "message/rfc822")) {
3603 MsgFlags flags
= {0, 0};
3605 if (procmime_get_encoding_for_text_file(file
, &has_binary
) == ENC_7BIT
)
3606 ainfo
->encoding
= ENC_7BIT
;
3608 ainfo
->encoding
= ENC_8BIT
;
3610 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
3611 if (msginfo
&& msginfo
->subject
)
3612 name
= g_strdup(msginfo
->subject
);
3614 name
= g_path_get_basename(filename
? filename
: file
);
3616 ainfo
->name
= g_strdup_printf(_("Message: %s"), name
);
3618 procmsg_msginfo_free(msginfo
);
3620 if (!g_ascii_strncasecmp(content_type
, "text/", 5)) {
3621 ainfo
->charset
= g_strdup(charset
);
3622 ainfo
->encoding
= procmime_get_encoding_for_text_file(file
, &has_binary
);
3624 ainfo
->encoding
= ENC_BASE64
;
3626 name
= g_path_get_basename(filename
? filename
: file
);
3627 ainfo
->name
= g_strdup(name
);
3631 ainfo
->content_type
= procmime_get_mime_type(file
);
3632 if (!ainfo
->content_type
) {
3633 ainfo
->content_type
=
3634 g_strdup("application/octet-stream");
3635 ainfo
->encoding
= ENC_BASE64
;
3636 } else if (!g_ascii_strncasecmp(ainfo
->content_type
, "text/", 5))
3638 procmime_get_encoding_for_text_file(file
, &has_binary
);
3640 ainfo
->encoding
= ENC_BASE64
;
3641 name
= g_path_get_basename(filename
? filename
: file
);
3642 ainfo
->name
= g_strdup(name
);
3646 if (ainfo
->name
!= NULL
3647 && !strcmp(ainfo
->name
, ".")) {
3648 g_free(ainfo
->name
);
3652 if (!strcmp(ainfo
->content_type
, "unknown") || has_binary
) {
3653 g_free(ainfo
->content_type
);
3654 ainfo
->content_type
= g_strdup("application/octet-stream");
3655 g_free(ainfo
->charset
);
3656 ainfo
->charset
= NULL
;
3659 ainfo
->size
= (goffset
)size
;
3660 size_text
= to_human_readable((goffset
)size
);
3662 store
= GTK_LIST_STORE(gtk_tree_view_get_model
3663 (GTK_TREE_VIEW(compose
->attach_clist
)));
3665 gtk_list_store_append(store
, &iter
);
3666 gtk_list_store_set(store
, &iter
,
3667 COL_MIMETYPE
, ainfo
->content_type
,
3668 COL_SIZE
, size_text
,
3669 COL_NAME
, ainfo
->name
,
3670 COL_CHARSET
, ainfo
->charset
,
3672 COL_AUTODATA
, auto_ainfo
,
3675 g_auto_pointer_free(auto_ainfo
);
3676 compose_attach_update_label(compose
);
3680 static void compose_use_signing(Compose
*compose
, gboolean use_signing
)
3682 compose
->use_signing
= use_signing
;
3683 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", use_signing
);
3686 static void compose_use_encryption(Compose
*compose
, gboolean use_encryption
)
3688 compose
->use_encryption
= use_encryption
;
3689 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", use_encryption
);
3692 #define NEXT_PART_NOT_CHILD(info) \
3694 node = info->node; \
3695 while (node->children) \
3696 node = g_node_last_child(node); \
3697 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3700 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
3704 MimeInfo
*firsttext
= NULL
;
3705 MimeInfo
*encrypted
= NULL
;
3708 const gchar
*partname
= NULL
;
3710 mimeinfo
= procmime_scan_message(msginfo
);
3711 if (!mimeinfo
) return;
3713 if (mimeinfo
->node
->children
== NULL
) {
3714 procmime_mimeinfo_free_all(mimeinfo
);
3718 /* find first content part */
3719 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3720 while (child
&& child
->node
->children
&& (child
->type
== MIMETYPE_MULTIPART
))
3721 child
= (MimeInfo
*)child
->node
->children
->data
;
3724 if (child
->type
== MIMETYPE_TEXT
) {
3726 debug_print("First text part found\n");
3727 } else if (compose
->mode
== COMPOSE_REEDIT
&&
3728 child
->type
== MIMETYPE_APPLICATION
&&
3729 !g_ascii_strcasecmp(child
->subtype
, "pgp-encrypted")) {
3730 encrypted
= (MimeInfo
*)child
->node
->parent
->data
;
3733 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3734 while (child
!= NULL
) {
3737 if (child
== encrypted
) {
3738 /* skip this part of tree */
3739 NEXT_PART_NOT_CHILD(child
);
3743 if (child
->type
== MIMETYPE_MULTIPART
) {
3744 /* get the actual content */
3745 child
= procmime_mimeinfo_next(child
);
3749 if (child
== firsttext
) {
3750 child
= procmime_mimeinfo_next(child
);
3754 outfile
= procmime_get_tmp_file_name(child
);
3755 if ((err
= procmime_get_part(outfile
, child
)) < 0)
3756 g_warning("Can't get the part of multipart message. (%s)", strerror(-err
));
3758 gchar
*content_type
;
3760 content_type
= procmime_get_content_type_str(child
->type
, child
->subtype
);
3762 /* if we meet a pgp signature, we don't attach it, but
3763 * we force signing. */
3764 if ((strcmp(content_type
, "application/pgp-signature") &&
3765 strcmp(content_type
, "application/pkcs7-signature") &&
3766 strcmp(content_type
, "application/x-pkcs7-signature"))
3767 || compose
->mode
== COMPOSE_REDIRECT
) {
3768 partname
= procmime_mimeinfo_get_parameter(child
, "filename");
3769 if (partname
== NULL
)
3770 partname
= procmime_mimeinfo_get_parameter(child
, "name");
3771 if (partname
== NULL
)
3773 compose_attach_append(compose
, outfile
,
3774 partname
, content_type
,
3775 procmime_mimeinfo_get_parameter(child
, "charset"));
3777 compose_force_signing(compose
, compose
->account
, NULL
);
3779 g_free(content_type
);
3782 NEXT_PART_NOT_CHILD(child
);
3784 procmime_mimeinfo_free_all(mimeinfo
);
3787 #undef NEXT_PART_NOT_CHILD
3792 WAIT_FOR_INDENT_CHAR
,
3793 WAIT_FOR_INDENT_CHAR_OR_SPACE
,
3796 /* return indent length, we allow:
3797 indent characters followed by indent characters or spaces/tabs,
3798 alphabets and numbers immediately followed by indent characters,
3799 and the repeating sequences of the above
3800 If quote ends with multiple spaces, only the first one is included. */
3801 static gchar
*compose_get_quote_str(GtkTextBuffer
*buffer
,
3802 const GtkTextIter
*start
, gint
*len
)
3804 GtkTextIter iter
= *start
;
3808 IndentState state
= WAIT_FOR_INDENT_CHAR
;
3811 gint alnum_count
= 0;
3812 gint space_count
= 0;
3815 if (prefs_common
.quote_chars
== NULL
) {
3819 while (!gtk_text_iter_ends_line(&iter
)) {
3820 wc
= gtk_text_iter_get_char(&iter
);
3821 if (g_unichar_iswide(wc
))
3823 clen
= g_unichar_to_utf8(wc
, ch
);
3827 is_indent
= strchr(prefs_common
.quote_chars
, ch
[0]) ? TRUE
: FALSE
;
3828 is_space
= g_unichar_isspace(wc
);
3830 if (state
== WAIT_FOR_INDENT_CHAR
) {
3831 if (!is_indent
&& !g_unichar_isalnum(wc
))
3834 quote_len
+= alnum_count
+ space_count
+ 1;
3835 alnum_count
= space_count
= 0;
3836 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
3839 } else if (state
== WAIT_FOR_INDENT_CHAR_OR_SPACE
) {
3840 if (!is_indent
&& !is_space
&& !g_unichar_isalnum(wc
))
3844 else if (is_indent
) {
3845 quote_len
+= alnum_count
+ space_count
+ 1;
3846 alnum_count
= space_count
= 0;
3849 state
= WAIT_FOR_INDENT_CHAR
;
3853 gtk_text_iter_forward_char(&iter
);
3856 if (quote_len
> 0 && space_count
> 0)
3862 if (quote_len
> 0) {
3864 gtk_text_iter_forward_chars(&iter
, quote_len
);
3865 return gtk_text_buffer_get_text(buffer
, start
, &iter
, FALSE
);
3871 /* return >0 if the line is itemized */
3872 static int compose_itemized_length(GtkTextBuffer
*buffer
,
3873 const GtkTextIter
*start
)
3875 GtkTextIter iter
= *start
;
3880 if (gtk_text_iter_ends_line(&iter
))
3885 wc
= gtk_text_iter_get_char(&iter
);
3886 if (!g_unichar_isspace(wc
))
3888 gtk_text_iter_forward_char(&iter
);
3889 if (gtk_text_iter_ends_line(&iter
))
3893 clen
= g_unichar_to_utf8(wc
, ch
);
3897 if (!strchr("*-+", ch
[0]))
3900 gtk_text_iter_forward_char(&iter
);
3901 if (gtk_text_iter_ends_line(&iter
))
3903 wc
= gtk_text_iter_get_char(&iter
);
3904 if (g_unichar_isspace(wc
)) {
3910 /* return the string at the start of the itemization */
3911 static gchar
* compose_get_itemized_chars(GtkTextBuffer
*buffer
,
3912 const GtkTextIter
*start
)
3914 GtkTextIter iter
= *start
;
3917 GString
*item_chars
= g_string_new("");
3920 if (gtk_text_iter_ends_line(&iter
))
3925 wc
= gtk_text_iter_get_char(&iter
);
3926 if (!g_unichar_isspace(wc
))
3928 gtk_text_iter_forward_char(&iter
);
3929 if (gtk_text_iter_ends_line(&iter
))
3931 g_string_append_unichar(item_chars
, wc
);
3934 str
= item_chars
->str
;
3935 g_string_free(item_chars
, FALSE
);
3939 /* return the number of spaces at a line's start */
3940 static int compose_left_offset_length(GtkTextBuffer
*buffer
,
3941 const GtkTextIter
*start
)
3943 GtkTextIter iter
= *start
;
3946 if (gtk_text_iter_ends_line(&iter
))
3950 wc
= gtk_text_iter_get_char(&iter
);
3951 if (!g_unichar_isspace(wc
))
3954 gtk_text_iter_forward_char(&iter
);
3955 if (gtk_text_iter_ends_line(&iter
))
3959 gtk_text_iter_forward_char(&iter
);
3960 if (gtk_text_iter_ends_line(&iter
))
3965 static gboolean
compose_get_line_break_pos(GtkTextBuffer
*buffer
,
3966 const GtkTextIter
*start
,
3967 GtkTextIter
*break_pos
,
3971 GtkTextIter iter
= *start
, line_end
= *start
;
3972 PangoLogAttr
*attrs
;
3979 gboolean can_break
= FALSE
;
3980 gboolean do_break
= FALSE
;
3981 gboolean was_white
= FALSE
;
3982 gboolean prev_dont_break
= FALSE
;
3984 gtk_text_iter_forward_to_line_end(&line_end
);
3985 str
= gtk_text_buffer_get_text(buffer
, &iter
, &line_end
, FALSE
);
3986 len
= g_utf8_strlen(str
, -1);
3990 g_warning("compose_get_line_break_pos: len = 0!\n");
3994 /* g_print("breaking line: %d: %s (len = %d)\n",
3995 gtk_text_iter_get_line(&iter), str, len); */
3997 attrs
= g_new(PangoLogAttr
, len
+ 1);
3999 pango_default_break(str
, -1, NULL
, attrs
, len
+ 1);
4003 /* skip quote and leading spaces */
4004 for (i
= 0; *p
!= '\0' && i
< len
; i
++) {
4007 wc
= g_utf8_get_char(p
);
4008 if (i
>= quote_len
&& !g_unichar_isspace(wc
))
4010 if (g_unichar_iswide(wc
))
4012 else if (*p
== '\t')
4016 p
= g_utf8_next_char(p
);
4019 for (; *p
!= '\0' && i
< len
; i
++) {
4020 PangoLogAttr
*attr
= attrs
+ i
;
4024 if (attr
->is_line_break
&& can_break
&& was_white
&& !prev_dont_break
)
4027 was_white
= attr
->is_white
;
4029 /* don't wrap URI */
4030 if ((uri_len
= get_uri_len(p
)) > 0) {
4032 if (pos
> 0 && col
> max_col
) {
4042 wc
= g_utf8_get_char(p
);
4043 if (g_unichar_iswide(wc
)) {
4045 if (prev_dont_break
&& can_break
&& attr
->is_line_break
)
4047 } else if (*p
== '\t')
4051 if (pos
> 0 && col
> max_col
) {
4056 if (*p
== '-' || *p
== '/')
4057 prev_dont_break
= TRUE
;
4059 prev_dont_break
= FALSE
;
4061 p
= g_utf8_next_char(p
);
4065 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4070 *break_pos
= *start
;
4071 gtk_text_iter_set_line_offset(break_pos
, pos
);
4076 static gboolean
compose_join_next_line(Compose
*compose
,
4077 GtkTextBuffer
*buffer
,
4079 const gchar
*quote_str
)
4081 GtkTextIter iter_
= *iter
, cur
, prev
, next
, end
;
4082 PangoLogAttr attrs
[3];
4084 gchar
*next_quote_str
;
4087 gboolean keep_cursor
= FALSE
;
4089 if (!gtk_text_iter_forward_line(&iter_
) ||
4090 gtk_text_iter_ends_line(&iter_
)) {
4093 next_quote_str
= compose_get_quote_str(buffer
, &iter_
, "e_len
);
4095 if ((quote_str
|| next_quote_str
) &&
4096 strcmp2(quote_str
, next_quote_str
) != 0) {
4097 g_free(next_quote_str
);
4100 g_free(next_quote_str
);
4103 if (quote_len
> 0) {
4104 gtk_text_iter_forward_chars(&end
, quote_len
);
4105 if (gtk_text_iter_ends_line(&end
)) {
4110 /* don't join itemized lines */
4111 if (compose_itemized_length(buffer
, &end
) > 0) {
4115 /* don't join signature separator */
4116 if (compose_is_sig_separator(compose
, buffer
, &iter_
)) {
4119 /* delete quote str */
4121 gtk_text_buffer_delete(buffer
, &iter_
, &end
);
4123 /* don't join line breaks put by the user */
4125 gtk_text_iter_backward_char(&cur
);
4126 if (gtk_text_iter_has_tag(&cur
, compose
->no_join_tag
)) {
4127 gtk_text_iter_forward_char(&cur
);
4131 gtk_text_iter_forward_char(&cur
);
4132 /* delete linebreak and extra spaces */
4133 while (gtk_text_iter_backward_char(&cur
)) {
4134 wc1
= gtk_text_iter_get_char(&cur
);
4135 if (!g_unichar_isspace(wc1
))
4140 while (!gtk_text_iter_ends_line(&cur
)) {
4141 wc1
= gtk_text_iter_get_char(&cur
);
4142 if (!g_unichar_isspace(wc1
))
4144 gtk_text_iter_forward_char(&cur
);
4147 if (!gtk_text_iter_equal(&prev
, &next
)) {
4150 mark
= gtk_text_buffer_get_insert(buffer
);
4151 gtk_text_buffer_get_iter_at_mark(buffer
, &cur
, mark
);
4152 if (gtk_text_iter_equal(&prev
, &cur
))
4154 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4158 /* insert space if required */
4159 gtk_text_iter_backward_char(&prev
);
4160 wc1
= gtk_text_iter_get_char(&prev
);
4161 wc2
= gtk_text_iter_get_char(&next
);
4162 gtk_text_iter_forward_char(&next
);
4163 str
= gtk_text_buffer_get_text(buffer
, &prev
, &next
, FALSE
);
4164 pango_default_break(str
, -1, NULL
, attrs
, 3);
4165 if (!attrs
[1].is_line_break
||
4166 (!g_unichar_iswide(wc1
) || !g_unichar_iswide(wc2
))) {
4167 gtk_text_buffer_insert(buffer
, &iter_
, " ", 1);
4169 gtk_text_iter_backward_char(&iter_
);
4170 gtk_text_buffer_place_cursor(buffer
, &iter_
);
4179 #define ADD_TXT_POS(bp_, ep_, pti_) \
4180 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4181 last = last->next; \
4182 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4183 last->next = NULL; \
4185 g_warning("alloc error scanning URIs\n"); \
4188 static gboolean
compose_beautify_paragraph(Compose
*compose
, GtkTextIter
*par_iter
, gboolean force
)
4190 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4191 GtkTextBuffer
*buffer
;
4192 GtkTextIter iter
, break_pos
, end_of_line
;
4193 gchar
*quote_str
= NULL
;
4195 gboolean wrap_quote
= prefs_common
.linewrap_quote
;
4196 gboolean prev_autowrap
= compose
->autowrap
;
4197 gint startq_offset
= -1, noq_offset
= -1;
4198 gint uri_start
= -1, uri_stop
= -1;
4199 gint nouri_start
= -1, nouri_stop
= -1;
4200 gint num_blocks
= 0;
4201 gint quotelevel
= -1;
4202 gboolean modified
= force
;
4203 gboolean removed
= FALSE
;
4204 gboolean modified_before_remove
= FALSE
;
4206 gboolean start
= TRUE
;
4207 gint itemized_len
= 0, rem_item_len
= 0;
4208 gchar
*itemized_chars
= NULL
;
4209 gboolean item_continuation
= FALSE
;
4214 if (compose
->draft_timeout_tag
== -2) {
4218 compose
->autowrap
= FALSE
;
4220 buffer
= gtk_text_view_get_buffer(text
);
4221 undo_wrapping(compose
->undostruct
, TRUE
);
4226 mark
= gtk_text_buffer_get_insert(buffer
);
4227 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
4231 if (compose
->draft_timeout_tag
== -2) {
4232 if (gtk_text_iter_ends_line(&iter
)) {
4233 while (gtk_text_iter_ends_line(&iter
) &&
4234 gtk_text_iter_forward_line(&iter
))
4237 while (gtk_text_iter_backward_line(&iter
)) {
4238 if (gtk_text_iter_ends_line(&iter
)) {
4239 gtk_text_iter_forward_line(&iter
);
4245 /* move to line start */
4246 gtk_text_iter_set_line_offset(&iter
, 0);
4249 itemized_len
= compose_itemized_length(buffer
, &iter
);
4251 if (!itemized_len
) {
4252 itemized_len
= compose_left_offset_length(buffer
, &iter
);
4253 item_continuation
= TRUE
;
4257 itemized_chars
= compose_get_itemized_chars(buffer
, &iter
);
4259 /* go until paragraph end (empty line) */
4260 while (start
|| !gtk_text_iter_ends_line(&iter
)) {
4261 gchar
*scanpos
= NULL
;
4262 /* parse table - in order of priority */
4264 const gchar
*needle
; /* token */
4266 /* token search function */
4267 gchar
*(*search
) (const gchar
*haystack
,
4268 const gchar
*needle
);
4269 /* part parsing function */
4270 gboolean (*parse
) (const gchar
*start
,
4271 const gchar
*scanpos
,
4275 /* part to URI function */
4276 gchar
*(*build_uri
) (const gchar
*bp
,
4280 static struct table parser
[] = {
4281 {"http://", strcasestr
, get_uri_part
, make_uri_string
},
4282 {"https://", strcasestr
, get_uri_part
, make_uri_string
},
4283 {"ftp://", strcasestr
, get_uri_part
, make_uri_string
},
4284 {"sftp://", strcasestr
, get_uri_part
, make_uri_string
},
4285 {"gopher://",strcasestr
, get_uri_part
, make_uri_string
},
4286 {"www.", strcasestr
, get_uri_part
, make_http_string
},
4287 {"mailto:", strcasestr
, get_uri_part
, make_uri_string
},
4288 {"@", strcasestr
, get_email_part
, make_email_string
}
4290 const gint PARSE_ELEMS
= sizeof parser
/ sizeof parser
[0];
4291 gint last_index
= PARSE_ELEMS
;
4293 gchar
*o_walk
= NULL
, *walk
= NULL
, *bp
= NULL
, *ep
= NULL
;
4297 if (!prev_autowrap
&& num_blocks
== 0) {
4299 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
4300 G_CALLBACK(text_inserted
),
4303 if (gtk_text_iter_has_tag(&iter
, compose
->no_wrap_tag
) && !force
)
4306 uri_start
= uri_stop
= -1;
4308 quote_str
= compose_get_quote_str(buffer
, &iter
, "e_len
);
4311 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4312 if (startq_offset
== -1)
4313 startq_offset
= gtk_text_iter_get_offset(&iter
);
4314 quotelevel
= get_quote_level(quote_str
, prefs_common
.quote_chars
);
4315 if (quotelevel
> 2) {
4316 /* recycle colors */
4317 if (prefs_common
.recycle_quote_colors
)
4326 if (startq_offset
== -1)
4327 noq_offset
= gtk_text_iter_get_offset(&iter
);
4331 if (prev_autowrap
== FALSE
&& !force
&& !wrap_quote
) {
4334 if (gtk_text_iter_ends_line(&iter
)) {
4336 } else if (compose_get_line_break_pos(buffer
, &iter
, &break_pos
,
4337 prefs_common
.linewrap_len
,
4339 GtkTextIter prev
, next
, cur
;
4340 if (prev_autowrap
!= FALSE
|| force
) {
4341 compose
->automatic_break
= TRUE
;
4343 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4344 compose
->automatic_break
= FALSE
;
4345 if (itemized_len
&& compose
->autoindent
) {
4346 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4347 if (!item_continuation
)
4348 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4350 } else if (quote_str
&& wrap_quote
) {
4351 compose
->automatic_break
= TRUE
;
4353 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4354 compose
->automatic_break
= FALSE
;
4355 if (itemized_len
&& compose
->autoindent
) {
4356 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4357 if (!item_continuation
)
4358 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4362 /* remove trailing spaces */
4364 rem_item_len
= itemized_len
;
4365 while (compose
->autoindent
&& rem_item_len
-- > 0)
4366 gtk_text_iter_backward_char(&cur
);
4367 gtk_text_iter_backward_char(&cur
);
4370 while (!gtk_text_iter_starts_line(&cur
)) {
4373 gtk_text_iter_backward_char(&cur
);
4374 wc
= gtk_text_iter_get_char(&cur
);
4375 if (!g_unichar_isspace(wc
))
4379 if (!gtk_text_iter_equal(&prev
, &next
)) {
4380 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4382 gtk_text_iter_forward_char(&break_pos
);
4386 gtk_text_buffer_insert(buffer
, &break_pos
,
4390 modified
|= compose_join_next_line(compose
, buffer
, &iter
, quote_str
);
4392 /* move iter to current line start */
4393 gtk_text_iter_set_line_offset(&iter
, 0);
4400 /* move iter to next line start */
4406 if (!prev_autowrap
&& num_blocks
> 0) {
4408 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
4409 G_CALLBACK(text_inserted
),
4413 while (!gtk_text_iter_ends_line(&end_of_line
)) {
4414 gtk_text_iter_forward_char(&end_of_line
);
4416 o_walk
= walk
= gtk_text_buffer_get_text(buffer
, &iter
, &end_of_line
, FALSE
);
4418 nouri_start
= gtk_text_iter_get_offset(&iter
);
4419 nouri_stop
= gtk_text_iter_get_offset(&end_of_line
);
4421 walk_pos
= gtk_text_iter_get_offset(&iter
);
4422 /* FIXME: this looks phony. scanning for anything in the parse table */
4423 for (n
= 0; n
< PARSE_ELEMS
; n
++) {
4426 tmp
= parser
[n
].search(walk
, parser
[n
].needle
);
4428 if (scanpos
== NULL
|| tmp
< scanpos
) {
4437 /* check if URI can be parsed */
4438 if (parser
[last_index
].parse(walk
, scanpos
, (const gchar
**)&bp
,
4439 (const gchar
**)&ep
, FALSE
)
4440 && (size_t) (ep
- bp
- 1) > strlen(parser
[last_index
].needle
)) {
4444 strlen(parser
[last_index
].needle
);
4447 uri_start
= walk_pos
+ (bp
- o_walk
);
4448 uri_stop
= walk_pos
+ (ep
- o_walk
);
4452 gtk_text_iter_forward_line(&iter
);
4455 if (startq_offset
!= -1) {
4456 GtkTextIter startquote
, endquote
;
4457 gtk_text_buffer_get_iter_at_offset(
4458 buffer
, &startquote
, startq_offset
);
4461 switch (quotelevel
) {
4463 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote0_tag
) ||
4464 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) {
4465 gtk_text_buffer_apply_tag_by_name(
4466 buffer
, "quote0", &startquote
, &endquote
);
4467 gtk_text_buffer_remove_tag_by_name(
4468 buffer
, "quote1", &startquote
, &endquote
);
4469 gtk_text_buffer_remove_tag_by_name(
4470 buffer
, "quote2", &startquote
, &endquote
);
4475 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote1_tag
) ||
4476 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) {
4477 gtk_text_buffer_apply_tag_by_name(
4478 buffer
, "quote1", &startquote
, &endquote
);
4479 gtk_text_buffer_remove_tag_by_name(
4480 buffer
, "quote0", &startquote
, &endquote
);
4481 gtk_text_buffer_remove_tag_by_name(
4482 buffer
, "quote2", &startquote
, &endquote
);
4487 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote2_tag
) ||
4488 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
)) {
4489 gtk_text_buffer_apply_tag_by_name(
4490 buffer
, "quote2", &startquote
, &endquote
);
4491 gtk_text_buffer_remove_tag_by_name(
4492 buffer
, "quote0", &startquote
, &endquote
);
4493 gtk_text_buffer_remove_tag_by_name(
4494 buffer
, "quote1", &startquote
, &endquote
);
4500 } else if (noq_offset
!= -1) {
4501 GtkTextIter startnoquote
, endnoquote
;
4502 gtk_text_buffer_get_iter_at_offset(
4503 buffer
, &startnoquote
, noq_offset
);
4506 if ((gtk_text_iter_has_tag(&startnoquote
, compose
->quote0_tag
)
4507 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) ||
4508 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote1_tag
)
4509 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) ||
4510 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote2_tag
)
4511 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
))) {
4512 gtk_text_buffer_remove_tag_by_name(
4513 buffer
, "quote0", &startnoquote
, &endnoquote
);
4514 gtk_text_buffer_remove_tag_by_name(
4515 buffer
, "quote1", &startnoquote
, &endnoquote
);
4516 gtk_text_buffer_remove_tag_by_name(
4517 buffer
, "quote2", &startnoquote
, &endnoquote
);
4523 if (uri_start
!= nouri_start
&& uri_stop
!= nouri_stop
) {
4524 GtkTextIter nouri_start_iter
, nouri_end_iter
;
4525 gtk_text_buffer_get_iter_at_offset(
4526 buffer
, &nouri_start_iter
, nouri_start
);
4527 gtk_text_buffer_get_iter_at_offset(
4528 buffer
, &nouri_end_iter
, nouri_stop
);
4529 if (gtk_text_iter_has_tag(&nouri_start_iter
, compose
->uri_tag
) &&
4530 gtk_text_iter_has_tag(&nouri_end_iter
, compose
->uri_tag
)) {
4531 gtk_text_buffer_remove_tag_by_name(
4532 buffer
, "link", &nouri_start_iter
, &nouri_end_iter
);
4533 modified_before_remove
= modified
;
4538 if (uri_start
>= 0 && uri_stop
> 0) {
4539 GtkTextIter uri_start_iter
, uri_end_iter
, back
;
4540 gtk_text_buffer_get_iter_at_offset(
4541 buffer
, &uri_start_iter
, uri_start
);
4542 gtk_text_buffer_get_iter_at_offset(
4543 buffer
, &uri_end_iter
, uri_stop
);
4544 back
= uri_end_iter
;
4545 gtk_text_iter_backward_char(&back
);
4546 if (!gtk_text_iter_has_tag(&uri_start_iter
, compose
->uri_tag
) ||
4547 !gtk_text_iter_has_tag(&back
, compose
->uri_tag
)) {
4548 gtk_text_buffer_apply_tag_by_name(
4549 buffer
, "link", &uri_start_iter
, &uri_end_iter
);
4551 if (removed
&& !modified_before_remove
) {
4557 // debug_print("not modified, out after %d lines\n", lines);
4561 // debug_print("modified, out after %d lines\n", lines);
4563 g_free(itemized_chars
);
4566 undo_wrapping(compose
->undostruct
, FALSE
);
4567 compose
->autowrap
= prev_autowrap
;
4572 void compose_action_cb(void *data
)
4574 Compose
*compose
= (Compose
*)data
;
4575 compose_wrap_all(compose
);
4578 static void compose_wrap_all(Compose
*compose
)
4580 compose_wrap_all_full(compose
, FALSE
);
4583 static void compose_wrap_all_full(Compose
*compose
, gboolean force
)
4585 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4586 GtkTextBuffer
*buffer
;
4588 gboolean modified
= TRUE
;
4590 buffer
= gtk_text_view_get_buffer(text
);
4592 gtk_text_buffer_get_start_iter(buffer
, &iter
);
4593 while (!gtk_text_iter_is_end(&iter
) && modified
)
4594 modified
= compose_beautify_paragraph(compose
, &iter
, force
);
4598 static void compose_set_title(Compose
*compose
)
4604 edited
= compose
->modified
? _(" [Edited]") : "";
4606 subject
= gtk_editable_get_chars(
4607 GTK_EDITABLE(compose
->subject_entry
), 0, -1);
4609 #ifndef GENERIC_UMPC
4610 if (subject
&& strlen(subject
))
4611 str
= g_strdup_printf(_("%s - Compose message%s"),
4614 str
= g_strdup_printf(_("[no subject] - Compose message%s"), edited
);
4616 str
= g_strdup(_("Compose message"));
4619 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
4625 * compose_current_mail_account:
4627 * Find a current mail account (the currently selected account, or the
4628 * default account, if a news account is currently selected). If a
4629 * mail account cannot be found, display an error message.
4631 * Return value: Mail account, or NULL if not found.
4633 static PrefsAccount
*
4634 compose_current_mail_account(void)
4638 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
4641 ac
= account_get_default();
4642 if (!ac
|| ac
->protocol
== A_NNTP
) {
4643 alertpanel_error(_("Account for sending mail is not specified.\n"
4644 "Please select a mail account before sending."));
4651 #define QUOTE_IF_REQUIRED(out, str) \
4653 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4657 len = strlen(str) + 3; \
4658 if ((__tmp = alloca(len)) == NULL) { \
4659 g_warning("can't allocate memory\n"); \
4660 g_string_free(header, TRUE); \
4663 g_snprintf(__tmp, len, "\"%s\"", str); \
4668 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4669 g_warning("can't allocate memory\n"); \
4670 g_string_free(header, TRUE); \
4673 strcpy(__tmp, str); \
4679 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4681 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4685 len = strlen(str) + 3; \
4686 if ((__tmp = alloca(len)) == NULL) { \
4687 g_warning("can't allocate memory\n"); \
4690 g_snprintf(__tmp, len, "\"%s\"", str); \
4695 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4696 g_warning("can't allocate memory\n"); \
4699 strcpy(__tmp, str); \
4705 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
,
4708 gchar
*from
= NULL
, *header
;
4709 ComposeHeaderEntry
*header_entry
;
4711 cm_return_if_fail(account
!= NULL
);
4713 compose
->account
= account
;
4714 if (account
->name
&& *account
->name
) {
4716 QUOTE_IF_REQUIRED_NORMAL(buf
, account
->name
, return);
4717 from
= g_strdup_printf("%s <%s>",
4718 buf
, account
->address
);
4719 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4721 from
= g_strdup_printf("<%s>",
4723 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4728 compose_set_title(compose
);
4730 if (account
->default_sign
&& compose
->mode
!= COMPOSE_REDIRECT
)
4731 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", TRUE
);
4733 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", FALSE
);
4734 if (account
->default_encrypt
&& compose
->mode
!= COMPOSE_REDIRECT
)
4735 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", TRUE
);
4737 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", FALSE
);
4739 activate_privacy_system(compose
, account
, FALSE
);
4741 if (!init
&& compose
->mode
!= COMPOSE_REDIRECT
) {
4742 undo_block(compose
->undostruct
);
4743 compose_insert_sig(compose
, TRUE
);
4744 undo_unblock(compose
->undostruct
);
4747 header_entry
= (ComposeHeaderEntry
*) compose
->header_list
->data
;
4748 header
= gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry
->combo
));
4750 if (header
&& !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry
->entry
)))) {
4751 if (account
->protocol
== A_NNTP
) {
4752 if (!strcmp(header
, _("To:")))
4753 combobox_select_by_text(
4754 GTK_COMBO_BOX(header_entry
->combo
),
4757 if (!strcmp(header
, _("Newsgroups:")))
4758 combobox_select_by_text(
4759 GTK_COMBO_BOX(header_entry
->combo
),
4767 /* use account's dict info if set */
4768 if (compose
->gtkaspell
) {
4769 if (account
->enable_default_dictionary
)
4770 gtkaspell_change_dict(compose
->gtkaspell
,
4771 account
->default_dictionary
, FALSE
);
4772 if (account
->enable_default_alt_dictionary
)
4773 gtkaspell_change_alt_dict(compose
->gtkaspell
,
4774 account
->default_alt_dictionary
);
4775 if (account
->enable_default_dictionary
4776 || account
->enable_default_alt_dictionary
)
4777 compose_spell_menu_changed(compose
);
4782 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
4783 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
4784 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
4785 gboolean recipient_found
= FALSE
;
4789 /* free to and newsgroup list */
4790 slist_free_strings(compose
->to_list
);
4791 g_slist_free(compose
->to_list
);
4792 compose
->to_list
= NULL
;
4794 slist_free_strings(compose
->newsgroup_list
);
4795 g_slist_free(compose
->newsgroup_list
);
4796 compose
->newsgroup_list
= NULL
;
4798 /* search header entries for to and newsgroup entries */
4799 for (list
= compose
->header_list
; list
; list
= list
->next
) {
4802 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
4803 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
4806 if (entry
[0] != '\0') {
4807 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
4808 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
4809 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
4810 recipient_found
= TRUE
;
4813 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
4814 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
4815 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
4816 recipient_found
= TRUE
;
4823 return recipient_found
;
4826 static gboolean
compose_check_for_set_recipients(Compose
*compose
)
4828 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
) {
4829 gboolean found_other
= FALSE
;
4831 /* search header entries for to and newsgroup entries */
4832 for (list
= compose
->header_list
; list
; list
= list
->next
) {
4835 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
4836 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
4839 if (strcmp(entry
, compose
->account
->auto_cc
)
4840 || strcmp(header
, prefs_common_translated_header_name("Cc:"))) {
4850 if (compose
->batch
) {
4851 gtk_widget_show_all(compose
->window
);
4853 aval
= alertpanel(_("Send"),
4854 _("The only recipient is the default CC address. Send anyway?"),
4855 GTK_STOCK_CANCEL
, _("+_Send"), NULL
);
4856 if (aval
!= G_ALERTALTERNATE
)
4860 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
) {
4861 gboolean found_other
= FALSE
;
4863 /* search header entries for to and newsgroup entries */
4864 for (list
= compose
->header_list
; list
; list
= list
->next
) {
4867 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
4868 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
4871 if (strcmp(entry
, compose
->account
->auto_bcc
)
4872 || strcmp(header
, prefs_common_translated_header_name("Bcc:"))) {
4882 if (compose
->batch
) {
4883 gtk_widget_show_all(compose
->window
);
4885 aval
= alertpanel(_("Send"),
4886 _("The only recipient is the default BCC address. Send anyway?"),
4887 GTK_STOCK_CANCEL
, _("+_Send"), NULL
);
4888 if (aval
!= G_ALERTALTERNATE
)
4895 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_everything
)
4899 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
4900 if (compose
->batch
) {
4901 gtk_widget_show_all(compose
->window
);
4903 alertpanel_error(_("Recipient is not specified."));
4907 if (compose_check_for_set_recipients(compose
) == FALSE
) {
4911 if (!compose
->batch
) {
4912 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
4913 if (*str
== '\0' && check_everything
== TRUE
&&
4914 compose
->mode
!= COMPOSE_REDIRECT
) {
4916 gchar
*button_label
;
4919 if (compose
->sending
)
4920 button_label
= _("+_Send");
4922 button_label
= _("+_Queue");
4923 message
= g_strdup_printf(_("Subject is empty. %s"),
4924 compose
->sending
?_("Send it anyway?"):
4925 _("Queue it anyway?"));
4927 aval
= alertpanel(compose
->sending
?_("Send"):_("Send later"), message
,
4928 GTK_STOCK_CANCEL
, button_label
, NULL
);
4930 if (aval
!= G_ALERTALTERNATE
)
4935 if (check_everything
&& hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
, compose
))
4941 gint
compose_send(Compose
*compose
)
4944 FolderItem
*folder
= NULL
;
4946 gchar
*msgpath
= NULL
;
4947 gboolean discard_window
= FALSE
;
4948 gchar
*errstr
= NULL
;
4949 gchar
*tmsgid
= NULL
;
4950 MainWindow
*mainwin
= mainwindow_get_mainwindow();
4951 gboolean queued_removed
= FALSE
;
4953 if (prefs_common
.send_dialog_invisible
4954 || compose
->batch
== TRUE
)
4955 discard_window
= TRUE
;
4957 compose_allow_user_actions (compose
, FALSE
);
4958 compose
->sending
= TRUE
;
4960 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
4961 if (compose
->batch
) {
4962 gtk_widget_show_all(compose
->window
);
4968 val
= compose_queue(compose
, &msgnum
, &folder
, &msgpath
, TRUE
);
4971 if (compose
->batch
) {
4972 gtk_widget_show_all(compose
->window
);
4975 alertpanel_error(_("Could not queue message for sending:\n\n"
4976 "Charset conversion failed."));
4977 } else if (val
== -5) {
4978 alertpanel_error(_("Could not queue message for sending:\n\n"
4979 "Couldn't get recipient encryption key."));
4980 } else if (val
== -6) {
4982 } else if (val
== -3) {
4983 if (privacy_peek_error())
4984 alertpanel_error(_("Could not queue message for sending:\n\n"
4985 "Signature failed: %s"), privacy_get_error());
4986 } else if (val
== -2 && errno
!= 0) {
4987 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno
));
4989 alertpanel_error(_("Could not queue message for sending."));
4994 tmsgid
= compose
->msgid
? g_strdup(compose
->msgid
) : NULL
;
4995 if (discard_window
) {
4996 compose
->sending
= FALSE
;
4997 compose_close(compose
);
4998 /* No more compose access in the normal codepath
4999 * after this point! */
5004 alertpanel_error(_("The message was queued but could not be "
5005 "sent.\nUse \"Send queued messages\" from "
5006 "the main window to retry."));
5007 if (!discard_window
) {
5014 if (msgpath
== NULL
) {
5015 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
5016 val
= procmsg_send_message_queue(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5019 val
= procmsg_send_message_queue(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5020 claws_unlink(msgpath
);
5023 if (!discard_window
) {
5025 if (!queued_removed
)
5026 folder_item_remove_msg(folder
, msgnum
);
5027 folder_item_scan(folder
);
5029 /* make sure we delete that */
5030 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5032 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5033 folder_item_remove_msg(folder
, tmp
->msgnum
);
5034 procmsg_msginfo_free(tmp
);
5041 if (!queued_removed
)
5042 folder_item_remove_msg(folder
, msgnum
);
5043 folder_item_scan(folder
);
5045 /* make sure we delete that */
5046 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5048 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5049 folder_item_remove_msg(folder
, tmp
->msgnum
);
5050 procmsg_msginfo_free(tmp
);
5053 if (!discard_window
) {
5054 compose
->sending
= FALSE
;
5055 compose_allow_user_actions (compose
, TRUE
);
5056 compose_close(compose
);
5060 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5061 "the main window to retry."), errstr
);
5064 alertpanel_error_log(_("The message was queued but could not be "
5065 "sent.\nUse \"Send queued messages\" from "
5066 "the main window to retry."));
5068 if (!discard_window
) {
5077 toolbar_main_set_sensitive(mainwin
);
5078 main_window_set_menu_sensitive(mainwin
);
5084 compose_allow_user_actions (compose
, TRUE
);
5085 compose
->sending
= FALSE
;
5086 compose
->modified
= TRUE
;
5087 toolbar_main_set_sensitive(mainwin
);
5088 main_window_set_menu_sensitive(mainwin
);
5093 static gboolean
compose_use_attach(Compose
*compose
)
5095 GtkTreeModel
*model
= gtk_tree_view_get_model
5096 (GTK_TREE_VIEW(compose
->attach_clist
));
5097 return gtk_tree_model_iter_n_children(model
, NULL
) > 0;
5100 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
5103 gchar buf
[BUFFSIZE
];
5105 gboolean first_to_address
;
5106 gboolean first_cc_address
;
5108 ComposeHeaderEntry
*headerentry
;
5109 const gchar
*headerentryname
;
5110 const gchar
*cc_hdr
;
5111 const gchar
*to_hdr
;
5112 gboolean err
= FALSE
;
5114 debug_print("Writing redirect header\n");
5116 cc_hdr
= prefs_common_translated_header_name("Cc:");
5117 to_hdr
= prefs_common_translated_header_name("To:");
5119 first_to_address
= TRUE
;
5120 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5121 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5122 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5124 if (g_utf8_collate(headerentryname
, to_hdr
) == 0) {
5125 const gchar
*entstr
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5126 Xstrdup_a(str
, entstr
, return -1);
5128 if (str
[0] != '\0') {
5129 compose_convert_header
5130 (compose
, buf
, sizeof(buf
), str
,
5131 strlen("Resent-To") + 2, TRUE
);
5133 if (first_to_address
) {
5134 err
|= (fprintf(fp
, "Resent-To: ") < 0);
5135 first_to_address
= FALSE
;
5137 err
|= (fprintf(fp
, ",") < 0);
5139 err
|= (fprintf(fp
, "%s", buf
) < 0);
5143 if (!first_to_address
) {
5144 err
|= (fprintf(fp
, "\n") < 0);
5147 first_cc_address
= TRUE
;
5148 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5149 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5150 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5152 if (g_utf8_collate(headerentryname
, cc_hdr
) == 0) {
5153 const gchar
*strg
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5154 Xstrdup_a(str
, strg
, return -1);
5156 if (str
[0] != '\0') {
5157 compose_convert_header
5158 (compose
, buf
, sizeof(buf
), str
,
5159 strlen("Resent-Cc") + 2, TRUE
);
5161 if (first_cc_address
) {
5162 err
|= (fprintf(fp
, "Resent-Cc: ") < 0);
5163 first_cc_address
= FALSE
;
5165 err
|= (fprintf(fp
, ",") < 0);
5167 err
|= (fprintf(fp
, "%s", buf
) < 0);
5171 if (!first_cc_address
) {
5172 err
|= (fprintf(fp
, "\n") < 0);
5175 return (err
? -1:0);
5178 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
5180 gchar buf
[BUFFSIZE
];
5182 const gchar
*entstr
;
5183 /* struct utsname utsbuf; */
5184 gboolean err
= FALSE
;
5186 cm_return_val_if_fail(fp
!= NULL
, -1);
5187 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5188 cm_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
5191 get_rfc822_date(buf
, sizeof(buf
));
5192 err
|= (fprintf(fp
, "Resent-Date: %s\n", buf
) < 0);
5195 if (compose
->account
->name
&& *compose
->account
->name
) {
5196 compose_convert_header
5197 (compose
, buf
, sizeof(buf
), compose
->account
->name
,
5198 strlen("From: "), TRUE
);
5199 err
|= (fprintf(fp
, "Resent-From: %s <%s>\n",
5200 buf
, compose
->account
->address
) < 0);
5202 err
|= (fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
) < 0);
5205 entstr
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5206 if (*entstr
!= '\0') {
5207 Xstrdup_a(str
, entstr
, return -1);
5210 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
5211 strlen("Subject: "), FALSE
);
5212 err
|= (fprintf(fp
, "Subject: %s\n", buf
) < 0);
5216 /* Resent-Message-ID */
5217 if (compose
->account
->set_domain
&& compose
->account
->domain
) {
5218 g_snprintf(buf
, sizeof(buf
), "%s", compose
->account
->domain
);
5219 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5220 g_snprintf(buf
, sizeof(buf
), "%s",
5221 strchr(compose
->account
->address
, '@') ?
5222 strchr(compose
->account
->address
, '@')+1 :
5223 compose
->account
->address
);
5225 g_snprintf(buf
, sizeof(buf
), "%s", "");
5228 if (compose
->account
->gen_msgid
) {
5230 if (compose
->account
->msgid_with_addr
) {
5231 addr
= compose
->account
->address
;
5233 generate_msgid(buf
, sizeof(buf
), addr
);
5234 err
|= (fprintf(fp
, "Resent-Message-ID: <%s>\n", buf
) < 0);
5235 compose
->msgid
= g_strdup(buf
);
5237 compose
->msgid
= NULL
;
5240 if (compose_redirect_write_headers_from_headerlist(compose
, fp
))
5243 /* separator between header and body */
5244 err
|= (fputs("\n", fp
) == EOF
);
5246 return (err
? -1:0);
5249 static gint
compose_redirect_write_to_file(Compose
*compose
, FILE *fdest
)
5253 gchar buf
[BUFFSIZE
];
5255 gboolean skip
= FALSE
;
5256 gboolean err
= FALSE
;
5257 gchar
*not_included
[]={
5258 "Return-Path:", "Delivered-To:", "Received:",
5259 "Subject:", "X-UIDL:", "AF:",
5260 "NF:", "PS:", "SRH:",
5261 "SFN:", "DSR:", "MID:",
5262 "CFG:", "PT:", "S:",
5263 "RQ:", "SSV:", "NSV:",
5264 "SSH:", "R:", "MAID:",
5265 "NAID:", "RMID:", "FMID:",
5266 "SCF:", "RRCPT:", "NG:",
5267 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5268 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5269 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5270 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5271 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5274 if ((fp
= g_fopen(compose
->redirect_filename
, "rb")) == NULL
) {
5275 FILE_OP_ERROR(compose
->redirect_filename
, "fopen");
5279 while (procheader_get_one_field_asis(buf
, sizeof(buf
), fp
) != -1) {
5281 for (i
= 0; not_included
[i
] != NULL
; i
++) {
5282 if (g_ascii_strncasecmp(buf
, not_included
[i
],
5283 strlen(not_included
[i
])) == 0) {
5290 if (fputs(buf
, fdest
) == -1)
5293 if (!prefs_common
.redirect_keep_from
) {
5294 if (g_ascii_strncasecmp(buf
, "From:",
5295 strlen("From:")) == 0) {
5296 err
|= (fputs(" (by way of ", fdest
) == EOF
);
5297 if (compose
->account
->name
5298 && *compose
->account
->name
) {
5299 compose_convert_header
5300 (compose
, buf
, sizeof(buf
),
5301 compose
->account
->name
,
5304 err
|= (fprintf(fdest
, "%s <%s>",
5306 compose
->account
->address
) < 0);
5308 err
|= (fprintf(fdest
, "%s",
5309 compose
->account
->address
) < 0);
5310 err
|= (fputs(")", fdest
) == EOF
);
5314 if (fputs("\n", fdest
) == -1)
5321 if (compose_redirect_write_headers(compose
, fdest
))
5324 while ((len
= fread(buf
, sizeof(gchar
), sizeof(buf
), fp
)) > 0) {
5325 if (fwrite(buf
, sizeof(gchar
), len
, fdest
) != len
)
5338 static gint
compose_write_to_file(Compose
*compose
, FILE *fp
, gint action
, gboolean attach_parts
)
5340 GtkTextBuffer
*buffer
;
5341 GtkTextIter start
, end
;
5344 const gchar
*out_codeset
;
5345 EncodingType encoding
= ENC_UNKNOWN
;
5346 MimeInfo
*mimemsg
, *mimetext
;
5348 const gchar
*src_codeset
= CS_INTERNAL
;
5349 gchar
*from_addr
= NULL
;
5350 gchar
*from_name
= NULL
;
5352 if (action
== COMPOSE_WRITE_FOR_SEND
)
5353 attach_parts
= TRUE
;
5355 /* create message MimeInfo */
5356 mimemsg
= procmime_mimeinfo_new();
5357 mimemsg
->type
= MIMETYPE_MESSAGE
;
5358 mimemsg
->subtype
= g_strdup("rfc822");
5359 mimemsg
->content
= MIMECONTENT_MEM
;
5360 mimemsg
->tmp
= TRUE
; /* must free content later */
5361 mimemsg
->data
.mem
= compose_get_header(compose
);
5363 /* Create text part MimeInfo */
5364 /* get all composed text */
5365 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5366 gtk_text_buffer_get_start_iter(buffer
, &start
);
5367 gtk_text_buffer_get_end_iter(buffer
, &end
);
5368 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5370 out_codeset
= conv_get_charset_str(compose
->out_encoding
);
5372 if (!out_codeset
&& is_ascii_str(chars
)) {
5373 out_codeset
= CS_US_ASCII
;
5374 } else if (prefs_common
.outgoing_fallback_to_ascii
&&
5375 is_ascii_str(chars
)) {
5376 out_codeset
= CS_US_ASCII
;
5377 encoding
= ENC_7BIT
;
5381 gchar
*test_conv_global_out
= NULL
;
5382 gchar
*test_conv_reply
= NULL
;
5384 /* automatic mode. be automatic. */
5385 codeconv_set_strict(TRUE
);
5387 out_codeset
= conv_get_outgoing_charset_str();
5389 debug_print("trying to convert to %s\n", out_codeset
);
5390 test_conv_global_out
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5393 if (!test_conv_global_out
&& compose
->orig_charset
5394 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
5395 out_codeset
= compose
->orig_charset
;
5396 debug_print("failure; trying to convert to %s\n", out_codeset
);
5397 test_conv_reply
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5400 if (!test_conv_global_out
&& !test_conv_reply
) {
5402 out_codeset
= CS_INTERNAL
;
5403 debug_print("failure; finally using %s\n", out_codeset
);
5405 g_free(test_conv_global_out
);
5406 g_free(test_conv_reply
);
5407 codeconv_set_strict(FALSE
);
5410 if (encoding
== ENC_UNKNOWN
) {
5411 if (prefs_common
.encoding_method
== CTE_BASE64
)
5412 encoding
= ENC_BASE64
;
5413 else if (prefs_common
.encoding_method
== CTE_QUOTED_PRINTABLE
)
5414 encoding
= ENC_QUOTED_PRINTABLE
;
5415 else if (prefs_common
.encoding_method
== CTE_8BIT
)
5416 encoding
= ENC_8BIT
;
5418 encoding
= procmime_get_encoding_for_charset(out_codeset
);
5421 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5422 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
5424 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5425 codeconv_set_strict(TRUE
);
5426 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5427 codeconv_set_strict(FALSE
);
5433 msg
= g_strdup_printf(_("Can't convert the character encoding of the message \n"
5434 "to the specified %s charset.\n"
5435 "Send it as %s?"), out_codeset
, src_codeset
);
5436 aval
= alertpanel_full(_("Error"), msg
, GTK_STOCK_CANCEL
, _("+_Send"), NULL
, FALSE
,
5437 NULL
, ALERT_ERROR
, G_ALERTDEFAULT
);
5440 if (aval
!= G_ALERTALTERNATE
) {
5445 out_codeset
= src_codeset
;
5451 out_codeset
= src_codeset
;
5456 if (encoding
== ENC_8BIT
|| encoding
== ENC_7BIT
) {
5457 if (!strncmp(buf
, "From ", sizeof("From ")-1) ||
5458 strstr(buf
, "\nFrom ") != NULL
) {
5459 encoding
= ENC_QUOTED_PRINTABLE
;
5463 mimetext
= procmime_mimeinfo_new();
5464 mimetext
->content
= MIMECONTENT_MEM
;
5465 mimetext
->tmp
= TRUE
; /* must free content later */
5466 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5467 * and free the data, which we need later. */
5468 mimetext
->data
.mem
= g_strdup(buf
);
5469 mimetext
->type
= MIMETYPE_TEXT
;
5470 mimetext
->subtype
= g_strdup("plain");
5471 g_hash_table_insert(mimetext
->typeparameters
, g_strdup("charset"),
5472 g_strdup(out_codeset
));
5474 /* protect trailing spaces when signing message */
5475 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5476 privacy_system_can_sign(compose
->privacy_system
)) {
5477 encoding
= ENC_QUOTED_PRINTABLE
;
5480 debug_print("main text: %zd bytes encoded as %s in %d\n",
5481 strlen(buf
), out_codeset
, encoding
);
5483 /* check for line length limit */
5484 if (action
== COMPOSE_WRITE_FOR_SEND
&&
5485 encoding
!= ENC_QUOTED_PRINTABLE
&& encoding
!= ENC_BASE64
&&
5486 check_line_length(buf
, 1000, &line
) < 0) {
5490 msg
= g_strdup_printf
5491 (_("Line %d exceeds the line length limit (998 bytes).\n"
5492 "The contents of the message might be broken on the way to the delivery.\n"
5494 "Send it anyway?"), line
+ 1);
5495 aval
= alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_OK
, NULL
);
5497 if (aval
!= G_ALERTALTERNATE
) {
5503 if (encoding
!= ENC_UNKNOWN
)
5504 procmime_encode_content(mimetext
, encoding
);
5506 /* append attachment parts */
5507 if (compose_use_attach(compose
) && attach_parts
) {
5508 MimeInfo
*mimempart
;
5509 gchar
*boundary
= NULL
;
5510 mimempart
= procmime_mimeinfo_new();
5511 mimempart
->content
= MIMECONTENT_EMPTY
;
5512 mimempart
->type
= MIMETYPE_MULTIPART
;
5513 mimempart
->subtype
= g_strdup("mixed");
5517 boundary
= generate_mime_boundary(NULL
);
5518 } while (strstr(buf
, boundary
) != NULL
);
5520 g_hash_table_insert(mimempart
->typeparameters
, g_strdup("boundary"),
5523 mimetext
->disposition
= DISPOSITIONTYPE_INLINE
;
5525 g_node_append(mimempart
->node
, mimetext
->node
);
5526 g_node_append(mimemsg
->node
, mimempart
->node
);
5528 if (compose_add_attachments(compose
, mimempart
) < 0)
5531 g_node_append(mimemsg
->node
, mimetext
->node
);
5535 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) != 0) {
5536 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
5537 /* extract name and address */
5538 if (strstr(spec
, " <") && strstr(spec
, ">")) {
5539 from_addr
= g_strdup(strrchr(spec
, '<')+1);
5540 *(strrchr(from_addr
, '>')) = '\0';
5541 from_name
= g_strdup(spec
);
5542 *(strrchr(from_name
, '<')) = '\0';
5549 /* sign message if sending */
5550 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5551 privacy_system_can_sign(compose
->privacy_system
))
5552 if (!privacy_sign(compose
->privacy_system
, mimemsg
,
5553 compose
->account
, from_addr
)) {
5560 procmime_write_mimeinfo(mimemsg
, fp
);
5562 procmime_mimeinfo_free_all(mimemsg
);
5567 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
5569 GtkTextBuffer
*buffer
;
5570 GtkTextIter start
, end
;
5575 if ((fp
= g_fopen(file
, "wb")) == NULL
) {
5576 FILE_OP_ERROR(file
, "fopen");
5580 /* chmod for security */
5581 if (change_file_mode_rw(fp
, file
) < 0) {
5582 FILE_OP_ERROR(file
, "chmod");
5583 g_warning("can't change file mode\n");
5586 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5587 gtk_text_buffer_get_start_iter(buffer
, &start
);
5588 gtk_text_buffer_get_end_iter(buffer
, &end
);
5589 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5591 chars
= conv_codeset_strdup
5592 (tmp
, CS_INTERNAL
, conv_get_locale_charset_str());
5595 if (!chars
) return -1;
5598 len
= strlen(chars
);
5599 if (fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
5600 FILE_OP_ERROR(file
, "fwrite");
5609 if (fclose(fp
) == EOF
) {
5610 FILE_OP_ERROR(file
, "fclose");
5617 static gint
compose_remove_reedit_target(Compose
*compose
, gboolean force
)
5620 MsgInfo
*msginfo
= compose
->targetinfo
;
5622 cm_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
5623 if (!msginfo
) return -1;
5625 if (!force
&& MSG_IS_LOCKED(msginfo
->flags
))
5628 item
= msginfo
->folder
;
5629 cm_return_val_if_fail(item
!= NULL
, -1);
5631 if (procmsg_msg_exist(msginfo
) &&
5632 (folder_has_parent_of_type(item
, F_QUEUE
) ||
5633 folder_has_parent_of_type(item
, F_DRAFT
)
5634 || msginfo
== compose
->autosaved_draft
)) {
5635 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
5636 g_warning("can't remove the old message\n");
5639 debug_print("removed reedit target %d\n", msginfo
->msgnum
);
5646 static void compose_remove_draft(Compose
*compose
)
5649 MsgInfo
*msginfo
= compose
->targetinfo
;
5650 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
5652 if (procmsg_msg_exist(msginfo
)) {
5653 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
5658 gint
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gchar
**msgpath
,
5659 gboolean remove_reedit_target
)
5661 return compose_queue_sub (compose
, msgnum
, item
, msgpath
, FALSE
, remove_reedit_target
);
5664 static gboolean
compose_warn_encryption(Compose
*compose
)
5666 const gchar
*warning
= privacy_get_encrypt_warning(compose
->privacy_system
);
5667 AlertValue val
= G_ALERTALTERNATE
;
5669 if (warning
== NULL
)
5672 val
= alertpanel_full(_("Encryption warning"), warning
,
5673 GTK_STOCK_CANCEL
, _("+C_ontinue"), NULL
,
5674 TRUE
, NULL
, ALERT_WARNING
, G_ALERTALTERNATE
);
5675 if (val
& G_ALERTDISABLE
) {
5676 val
&= ~G_ALERTDISABLE
;
5677 if (val
== G_ALERTALTERNATE
)
5678 privacy_inhibit_encrypt_warning(compose
->privacy_system
,
5682 if (val
== G_ALERTALTERNATE
) {
5689 static gint
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
,
5690 gchar
**msgpath
, gboolean check_subject
,
5691 gboolean remove_reedit_target
)
5698 static gboolean lock
= FALSE
;
5699 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
5700 gboolean err
= FALSE
;
5702 debug_print("queueing message...\n");
5703 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5707 if (compose_check_entries(compose
, check_subject
) == FALSE
) {
5709 if (compose
->batch
) {
5710 gtk_widget_show_all(compose
->window
);
5715 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
5716 g_warning("can't get recipient list.");
5721 if (compose
->to_list
) {
5722 if (compose
->account
->protocol
!= A_NNTP
)
5723 mailac
= compose
->account
;
5724 else if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
5725 mailac
= cur_account
;
5726 else if (!(mailac
= compose_current_mail_account())) {
5728 alertpanel_error(_("No account for sending mails available!"));
5733 if (compose
->newsgroup_list
) {
5734 if (compose
->account
->protocol
== A_NNTP
)
5735 newsac
= compose
->account
;
5738 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5743 /* write queue header */
5744 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5745 G_DIR_SEPARATOR
, compose
, (guint
) rand());
5746 debug_print("queuing to %s\n", tmp
);
5747 if ((fp
= g_fopen(tmp
, "wb")) == NULL
) {
5748 FILE_OP_ERROR(tmp
, "fopen");
5754 if (change_file_mode_rw(fp
, tmp
) < 0) {
5755 FILE_OP_ERROR(tmp
, "chmod");
5756 g_warning("can't change file mode\n");
5759 /* queueing variables */
5760 err
|= (fprintf(fp
, "AF:\n") < 0);
5761 err
|= (fprintf(fp
, "NF:0\n") < 0);
5762 err
|= (fprintf(fp
, "PS:10\n") < 0);
5763 err
|= (fprintf(fp
, "SRH:1\n") < 0);
5764 err
|= (fprintf(fp
, "SFN:\n") < 0);
5765 err
|= (fprintf(fp
, "DSR:\n") < 0);
5767 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
5769 err
|= (fprintf(fp
, "MID:\n") < 0);
5770 err
|= (fprintf(fp
, "CFG:\n") < 0);
5771 err
|= (fprintf(fp
, "PT:0\n") < 0);
5772 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
5773 err
|= (fprintf(fp
, "RQ:\n") < 0);
5775 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
5777 err
|= (fprintf(fp
, "SSV:\n") < 0);
5779 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
5781 err
|= (fprintf(fp
, "NSV:\n") < 0);
5782 err
|= (fprintf(fp
, "SSH:\n") < 0);
5783 /* write recepient list */
5784 if (compose
->to_list
) {
5785 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
5786 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
5788 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
5789 err
|= (fprintf(fp
, "\n") < 0);
5791 /* write newsgroup list */
5792 if (compose
->newsgroup_list
) {
5793 err
|= (fprintf(fp
, "NG:") < 0);
5794 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
5795 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
5796 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
5797 err
|= (fprintf(fp
, "\n") < 0);
5799 /* Sylpheed account IDs */
5801 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
5803 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
5806 if (compose
->privacy_system
!= NULL
) {
5807 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
5808 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
5809 if (compose
->use_encryption
) {
5811 if (!compose_warn_encryption(compose
)) {
5818 if (mailac
&& mailac
->encrypt_to_self
) {
5819 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
5820 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
5821 encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
5822 g_slist_free(tmp_list
);
5824 encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
5826 if (encdata
!= NULL
) {
5827 if (strcmp(encdata
, "_DONT_ENCRYPT_")) {
5828 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
5829 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
5831 } /* else we finally dont want to encrypt */
5833 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
5834 /* and if encdata was null, it means there's been a problem in
5846 /* Save copy folder */
5847 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
5848 gchar
*savefolderid
;
5850 savefolderid
= compose_get_save_to(compose
);
5851 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
5852 g_free(savefolderid
);
5854 /* Save copy folder */
5855 if (compose
->return_receipt
) {
5856 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
5858 /* Message-ID of message replying to */
5859 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
5862 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
5863 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
5866 /* Message-ID of message forwarding to */
5867 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
5870 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
5871 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
5875 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
5876 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
5878 /* end of headers */
5879 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
5881 if (compose
->redirect_filename
!= NULL
) {
5882 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
5891 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
5896 return result
- 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5900 g_warning("failed to write queue message\n");
5907 if (fclose(fp
) == EOF
) {
5908 FILE_OP_ERROR(tmp
, "fclose");
5915 if (item
&& *item
) {
5918 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
5921 g_warning("can't find queue folder\n");
5927 folder_item_scan(queue
);
5928 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
5929 g_warning("can't queue the message\n");
5936 if (msgpath
== NULL
) {
5942 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
5943 compose_remove_reedit_target(compose
, FALSE
);
5946 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
5954 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
)
5957 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
5959 struct stat statbuf
;
5960 gchar
*type
, *subtype
;
5961 GtkTreeModel
*model
;
5964 model
= gtk_tree_view_get_model(tree_view
);
5966 if (!gtk_tree_model_get_iter_first(model
, &iter
))
5969 gtk_tree_model_get(model
, &iter
,
5973 if (!is_file_exist(ainfo
->file
)) {
5974 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
5975 AlertValue val
= alertpanel_full(_("Warning"), msg
, _("Cancel sending"), _("Ignore attachment"),
5976 NULL
, FALSE
, NULL
, ALERT_WARNING
, G_ALERTDEFAULT
);
5978 if (val
== G_ALERTDEFAULT
) {
5983 mimepart
= procmime_mimeinfo_new();
5984 mimepart
->content
= MIMECONTENT_FILE
;
5985 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
5986 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
5987 mimepart
->offset
= 0;
5989 g_stat(ainfo
->file
, &statbuf
);
5990 mimepart
->length
= statbuf
.st_size
;
5992 type
= g_strdup(ainfo
->content_type
);
5994 if (!strchr(type
, '/')) {
5996 type
= g_strdup("application/octet-stream");
5999 subtype
= strchr(type
, '/') + 1;
6000 *(subtype
- 1) = '\0';
6001 mimepart
->type
= procmime_get_media_type(type
);
6002 mimepart
->subtype
= g_strdup(subtype
);
6005 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6006 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6007 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6008 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6009 if (!ainfo
->name
&& compose
->mode
== COMPOSE_FORWARD_INLINE
) {
6010 /* Text parts with no name come from multipart/alternative
6011 * forwards. Make sure the recipient won't look at the
6012 * original HTML part by mistake. */
6013 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6014 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6018 g_hash_table_insert(mimepart
->typeparameters
,
6019 g_strdup("charset"), g_strdup(ainfo
->charset
));
6021 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6022 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6023 !strcmp2(mimepart
->subtype
, "octet-stream"))
6024 g_hash_table_insert(mimepart
->typeparameters
,
6025 g_strdup("name"), g_strdup(ainfo
->name
));
6026 g_hash_table_insert(mimepart
->dispositionparameters
,
6027 g_strdup("filename"), g_strdup(ainfo
->name
));
6028 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6031 if (mimepart
->type
== MIMETYPE_MESSAGE
6032 || mimepart
->type
== MIMETYPE_MULTIPART
)
6033 ainfo
->encoding
= ENC_BINARY
;
6034 else if (compose
->use_signing
) {
6035 if (ainfo
->encoding
== ENC_7BIT
)
6036 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6037 else if (ainfo
->encoding
== ENC_8BIT
)
6038 ainfo
->encoding
= ENC_BASE64
;
6043 procmime_encode_content(mimepart
, ainfo
->encoding
);
6045 g_node_append(parent
->node
, mimepart
->node
);
6046 } while (gtk_tree_model_iter_next(model
, &iter
));
6051 #define IS_IN_CUSTOM_HEADER(header) \
6052 (compose->account->add_customhdr && \
6053 custom_header_find(compose->account->customhdr_list, header) != NULL)
6055 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6057 const gchar
*fieldname
,
6058 const gchar
*seperator
)
6060 gchar
*str
, *fieldname_w_colon
;
6061 gboolean add_field
= FALSE
;
6063 ComposeHeaderEntry
*headerentry
;
6064 const gchar
*headerentryname
;
6065 const gchar
*trans_fieldname
;
6068 if (IS_IN_CUSTOM_HEADER(fieldname
))
6071 debug_print("Adding %s-fields\n", fieldname
);
6073 fieldstr
= g_string_sized_new(64);
6075 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6076 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6078 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6079 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6080 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6082 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6083 str
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6085 if (str
[0] != '\0') {
6087 g_string_append(fieldstr
, seperator
);
6088 g_string_append(fieldstr
, str
);
6097 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6098 compose_convert_header
6099 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6100 strlen(fieldname
) + 2, TRUE
);
6101 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6105 g_free(fieldname_w_colon
);
6106 g_string_free(fieldstr
, TRUE
);
6111 static gchar
*compose_get_header(Compose
*compose
)
6113 gchar buf
[BUFFSIZE
];
6114 const gchar
*entry_str
;
6118 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6120 gchar
*from_name
= NULL
, *from_address
= NULL
;
6123 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6124 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6126 header
= g_string_sized_new(64);
6129 get_rfc822_date(buf
, sizeof(buf
));
6130 g_string_append_printf(header
, "Date: %s\n", buf
);
6134 if (compose
->account
->name
&& *compose
->account
->name
) {
6136 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6137 tmp
= g_strdup_printf("%s <%s>",
6138 buf
, compose
->account
->address
);
6140 tmp
= g_strdup_printf("%s",
6141 compose
->account
->address
);
6143 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6144 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6146 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6147 from_address
= g_strdup(compose
->account
->address
);
6149 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6150 /* extract name and address */
6151 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6152 from_address
= g_strdup(strrchr(spec
, '<')+1);
6153 *(strrchr(from_address
, '>')) = '\0';
6154 from_name
= g_strdup(spec
);
6155 *(strrchr(from_name
, '<')) = '\0';
6158 from_address
= g_strdup(spec
);
6165 if (from_name
&& *from_name
) {
6166 compose_convert_header
6167 (compose
, buf
, sizeof(buf
), from_name
,
6168 strlen("From: "), TRUE
);
6169 QUOTE_IF_REQUIRED(name
, buf
);
6171 g_string_append_printf(header
, "From: %s <%s>\n",
6172 name
, from_address
);
6174 g_string_append_printf(header
, "From: %s\n", from_address
);
6177 g_free(from_address
);
6180 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6183 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6186 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6190 * If this account is a NNTP account remove Bcc header from
6191 * message body since it otherwise will be publicly shown
6193 if (compose
->account
->protocol
!= A_NNTP
)
6194 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6197 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6199 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6202 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6203 strlen("Subject: "), FALSE
);
6204 g_string_append_printf(header
, "Subject: %s\n", buf
);
6210 if (compose
->account
->set_domain
&& compose
->account
->domain
) {
6211 g_snprintf(buf
, sizeof(buf
), "%s", compose
->account
->domain
);
6212 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6213 g_snprintf(buf
, sizeof(buf
), "%s",
6214 strchr(compose
->account
->address
, '@') ?
6215 strchr(compose
->account
->address
, '@')+1 :
6216 compose
->account
->address
);
6218 g_snprintf(buf
, sizeof(buf
), "%s", "");
6221 if (compose
->account
->gen_msgid
) {
6223 if (compose
->account
->msgid_with_addr
) {
6224 addr
= compose
->account
->address
;
6226 generate_msgid(buf
, sizeof(buf
), addr
);
6227 g_string_append_printf(header
, "Message-ID: <%s>\n", buf
);
6228 compose
->msgid
= g_strdup(buf
);
6230 compose
->msgid
= NULL
;
6233 if (compose
->remove_references
== FALSE
) {
6235 if (compose
->inreplyto
&& compose
->to_list
)
6236 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6239 if (compose
->references
)
6240 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6244 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6247 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6250 if (compose
->account
->organization
&&
6251 strlen(compose
->account
->organization
) &&
6252 !IS_IN_CUSTOM_HEADER("Organization")) {
6253 compose_convert_header(compose
, buf
, sizeof(buf
),
6254 compose
->account
->organization
,
6255 strlen("Organization: "), FALSE
);
6256 g_string_append_printf(header
, "Organization: %s\n", buf
);
6259 /* Program version and system info */
6260 if (compose
->account
->gen_xmailer
&&
6261 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6262 !compose
->newsgroup_list
) {
6263 g_string_append_printf(header
, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6265 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6268 if (g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6269 g_string_append_printf(header
, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6271 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6275 /* custom headers */
6276 if (compose
->account
->add_customhdr
) {
6279 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6281 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6283 if (custom_header_is_allowed(chdr
->name
)
6284 && chdr
->value
!= NULL
6285 && *(chdr
->value
) != '\0') {
6286 compose_convert_header
6287 (compose
, buf
, sizeof(buf
),
6289 strlen(chdr
->name
) + 2, FALSE
);
6290 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6295 /* Automatic Faces and X-Faces */
6296 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6297 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6299 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6300 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6302 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6303 g_string_append_printf(header
, "Face: %s\n", buf
);
6305 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6306 g_string_append_printf(header
, "Face: %s\n", buf
);
6310 switch (compose
->priority
) {
6311 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6312 "X-Priority: 1 (Highest)\n");
6314 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6315 "X-Priority: 2 (High)\n");
6317 case PRIORITY_NORMAL
: break;
6318 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6319 "X-Priority: 4 (Low)\n");
6321 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6322 "X-Priority: 5 (Lowest)\n");
6324 default: debug_print("compose: priority unknown : %d\n",
6328 /* Request Return Receipt */
6329 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6330 if (compose
->return_receipt
) {
6331 if (compose
->account
->name
6332 && *compose
->account
->name
) {
6333 compose_convert_header(compose
, buf
, sizeof(buf
),
6334 compose
->account
->name
,
6335 strlen("Disposition-Notification-To: "),
6337 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, compose
->account
->address
);
6339 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", compose
->account
->address
);
6343 /* get special headers */
6344 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6345 ComposeHeaderEntry
*headerentry
;
6348 gchar
*headername_wcolon
;
6349 const gchar
*headername_trans
;
6352 gboolean standard_header
= FALSE
;
6354 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6356 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6358 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6363 if (!strstr(tmp
, ":")) {
6364 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6365 headername
= g_strdup(tmp
);
6367 headername_wcolon
= g_strdup(tmp
);
6368 headername
= g_strdup(strtok(tmp
, ":"));
6372 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6373 Xstrdup_a(headervalue
, entry_str
, return NULL
);
6374 subst_char(headervalue
, '\r', ' ');
6375 subst_char(headervalue
, '\n', ' ');
6376 string
= std_headers
;
6377 while (*string
!= NULL
) {
6378 headername_trans
= prefs_common_translated_header_name(*string
);
6379 if (!strcmp(headername_trans
, headername_wcolon
))
6380 standard_header
= TRUE
;
6383 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6384 g_string_append_printf(header
, "%s %s\n", headername_wcolon
, headervalue
);
6387 g_free(headername_wcolon
);
6391 g_string_free(header
, FALSE
);
6396 #undef IS_IN_CUSTOM_HEADER
6398 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6399 gint header_len
, gboolean addr_field
)
6401 gchar
*tmpstr
= NULL
;
6402 const gchar
*out_codeset
= NULL
;
6404 cm_return_if_fail(src
!= NULL
);
6405 cm_return_if_fail(dest
!= NULL
);
6407 if (len
< 1) return;
6409 tmpstr
= g_strdup(src
);
6411 subst_char(tmpstr
, '\n', ' ');
6412 subst_char(tmpstr
, '\r', ' ');
6415 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
6416 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
6417 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
6422 codeconv_set_strict(TRUE
);
6423 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
6424 conv_get_charset_str(compose
->out_encoding
));
6425 codeconv_set_strict(FALSE
);
6427 if (!dest
|| *dest
== '\0') {
6428 gchar
*test_conv_global_out
= NULL
;
6429 gchar
*test_conv_reply
= NULL
;
6431 /* automatic mode. be automatic. */
6432 codeconv_set_strict(TRUE
);
6434 out_codeset
= conv_get_outgoing_charset_str();
6436 debug_print("trying to convert to %s\n", out_codeset
);
6437 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
6440 if (!test_conv_global_out
&& compose
->orig_charset
6441 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
6442 out_codeset
= compose
->orig_charset
;
6443 debug_print("failure; trying to convert to %s\n", out_codeset
);
6444 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
6447 if (!test_conv_global_out
&& !test_conv_reply
) {
6449 out_codeset
= CS_INTERNAL
;
6450 debug_print("finally using %s\n", out_codeset
);
6452 g_free(test_conv_global_out
);
6453 g_free(test_conv_reply
);
6454 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
6456 codeconv_set_strict(FALSE
);
6461 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
6465 cm_return_if_fail(user_data
!= NULL
);
6467 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
6468 g_strstrip(address
);
6469 if (*address
!= '\0') {
6470 gchar
*name
= procheader_get_fromname(address
);
6471 extract_address(address
);
6472 addressbook_add_contact(name
, address
, NULL
, NULL
);
6477 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
6479 GtkWidget
*menuitem
;
6482 cm_return_if_fail(menu
!= NULL
);
6483 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
6485 menuitem
= gtk_separator_menu_item_new();
6486 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
6487 gtk_widget_show(menuitem
);
6489 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6490 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
6492 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
6493 g_strstrip(address
);
6494 if (*address
== '\0') {
6495 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
6498 g_signal_connect(G_OBJECT(menuitem
), "activate",
6499 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
6500 gtk_widget_show(menuitem
);
6503 static void compose_create_header_entry(Compose
*compose
)
6505 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6512 const gchar
*header
= NULL
;
6513 ComposeHeaderEntry
*headerentry
;
6514 gboolean standard_header
= FALSE
;
6515 GtkListStore
*model
;
6517 #if !(GTK_CHECK_VERSION(2,12,0))
6518 GtkTooltips
*tips
= compose
->tooltips
;
6521 headerentry
= g_new0(ComposeHeaderEntry
, 1);
6524 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
6525 combo
= gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model
), 0);
6526 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
6528 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
6530 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
6532 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
6533 COMPOSE_NEWSGROUPS
);
6534 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
6536 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
6537 COMPOSE_FOLLOWUPTO
);
6539 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
6540 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
6541 G_CALLBACK(compose_grab_focus_cb
), compose
);
6542 gtk_widget_show(combo
);
6543 gtk_table_attach(GTK_TABLE(compose
->header_table
), combo
, 0, 1,
6544 compose
->header_nextrow
, compose
->header_nextrow
+1,
6545 GTK_SHRINK
, GTK_FILL
, 0, 0);
6546 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= -2)) {
6547 const gchar
*last_header_entry
= gtk_entry_get_text(
6548 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
6550 while (*string
!= NULL
) {
6551 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
6552 standard_header
= TRUE
;
6555 if (standard_header
)
6556 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
6558 if (!compose
->header_last
|| !standard_header
) {
6559 switch(compose
->account
->protocol
) {
6561 header
= prefs_common_translated_header_name("Newsgroups:");
6564 header
= prefs_common_translated_header_name("To:");
6569 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
6571 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
6572 G_CALLBACK(compose_grab_focus_cb
), compose
);
6574 /* Entry field with cleanup button */
6575 button
= gtk_button_new();
6576 gtk_button_set_image(GTK_BUTTON(button
),
6577 gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
));
6578 gtk_widget_show(button
);
6579 CLAWS_SET_TIP(button
,
6580 _("Delete entry contents"));
6581 entry
= gtk_entry_new();
6582 gtk_widget_show(entry
);
6583 CLAWS_SET_TIP(entry
,
6584 _("Use <tab> to autocomplete from addressbook"));
6585 hbox
= gtk_hbox_new (FALSE
, 0);
6586 gtk_widget_show(hbox
);
6587 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
6588 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
6589 gtk_table_attach(GTK_TABLE(compose
->header_table
), hbox
, 1, 2,
6590 compose
->header_nextrow
, compose
->header_nextrow
+1,
6591 GTK_EXPAND
| GTK_FILL
, GTK_FILL
, 0, 0);
6593 g_signal_connect(G_OBJECT(entry
), "key-press-event",
6594 G_CALLBACK(compose_headerentry_key_press_event_cb
),
6596 g_signal_connect(G_OBJECT(entry
), "changed",
6597 G_CALLBACK(compose_headerentry_changed_cb
),
6599 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
6600 G_CALLBACK(compose_grab_focus_cb
), compose
);
6602 g_signal_connect(G_OBJECT(button
), "clicked",
6603 G_CALLBACK(compose_headerentry_button_clicked_cb
),
6607 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
6608 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
6609 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
6610 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
6611 G_CALLBACK(compose_header_drag_received_cb
),
6613 g_signal_connect(G_OBJECT(entry
), "drag-drop",
6614 G_CALLBACK(compose_drag_drop
),
6616 g_signal_connect(G_OBJECT(entry
), "populate-popup",
6617 G_CALLBACK(compose_entry_popup_extend
),
6620 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
6622 headerentry
->compose
= compose
;
6623 headerentry
->combo
= combo
;
6624 headerentry
->entry
= entry
;
6625 headerentry
->button
= button
;
6626 headerentry
->hbox
= hbox
;
6627 headerentry
->headernum
= compose
->header_nextrow
;
6628 headerentry
->type
= PREF_NONE
;
6630 compose
->header_nextrow
++;
6631 compose
->header_last
= headerentry
;
6632 compose
->header_list
=
6633 g_slist_append(compose
->header_list
,
6637 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
6638 gchar
*text
, ComposePrefType pref_type
)
6640 ComposeHeaderEntry
*last_header
= compose
->header_last
;
6641 gchar
*tmp
= g_strdup(text
), *email
;
6642 gboolean replyto_hdr
;
6644 replyto_hdr
= (!strcasecmp(header
,
6645 prefs_common_translated_header_name("Reply-To:")) ||
6647 prefs_common_translated_header_name("Followup-To:")) ||
6649 prefs_common_translated_header_name("In-Reply-To:")));
6651 extract_address(tmp
);
6652 email
= g_utf8_strdown(tmp
, -1);
6654 if (replyto_hdr
== FALSE
&&
6655 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
6657 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6658 header
, text
, (gint
) pref_type
);
6664 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
6665 gtk_entry_set_text(GTK_ENTRY(
6666 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
6668 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
6669 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
6670 last_header
->type
= pref_type
;
6672 if (replyto_hdr
== FALSE
)
6673 g_hash_table_insert(compose
->email_hashtable
, email
,
6674 GUINT_TO_POINTER(1));
6681 static void compose_destroy_headerentry(Compose
*compose
,
6682 ComposeHeaderEntry
*headerentry
)
6684 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6687 extract_address(text
);
6688 email
= g_utf8_strdown(text
, -1);
6689 g_hash_table_remove(compose
->email_hashtable
, email
);
6693 gtk_widget_destroy(headerentry
->combo
);
6694 gtk_widget_destroy(headerentry
->entry
);
6695 gtk_widget_destroy(headerentry
->button
);
6696 gtk_widget_destroy(headerentry
->hbox
);
6697 g_free(headerentry
);
6700 static void compose_remove_header_entries(Compose
*compose
)
6703 for (list
= compose
->header_list
; list
; list
= list
->next
)
6704 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
6706 compose
->header_last
= NULL
;
6707 g_slist_free(compose
->header_list
);
6708 compose
->header_list
= NULL
;
6709 compose
->header_nextrow
= 1;
6710 compose_create_header_entry(compose
);
6713 static GtkWidget
*compose_create_header(Compose
*compose
)
6715 GtkWidget
*from_optmenu_hbox
;
6716 GtkWidget
*header_scrolledwin
;
6717 GtkWidget
*header_table
;
6721 /* header labels and entries */
6722 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
6723 gtk_widget_show(header_scrolledwin
);
6724 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
6726 header_table
= gtk_table_new(2, 2, FALSE
);
6727 gtk_widget_show(header_table
);
6728 gtk_container_set_border_width(GTK_CONTAINER(header_table
), BORDER_WIDTH
);
6729 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin
), header_table
);
6730 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin
)))), GTK_SHADOW_NONE
);
6733 /* option menu for selecting accounts */
6734 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
6735 gtk_table_attach(GTK_TABLE(header_table
), from_optmenu_hbox
,
6736 0, 2, count
, count
+ 1, GTK_EXPAND
| GTK_FILL
, GTK_SHRINK
, 0, 0);
6739 compose
->header_table
= header_table
;
6740 compose
->header_list
= NULL
;
6741 compose
->header_nextrow
= count
;
6743 compose_create_header_entry(compose
);
6745 compose
->table
= NULL
;
6747 return header_scrolledwin
;
6750 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
6752 Compose
*compose
= (Compose
*)data
;
6753 GdkEventButton event
;
6756 event
.time
= gtk_get_current_event_time();
6758 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
6761 static GtkWidget
*compose_create_attach(Compose
*compose
)
6763 GtkWidget
*attach_scrwin
;
6764 GtkWidget
*attach_clist
;
6766 GtkListStore
*store
;
6767 GtkCellRenderer
*renderer
;
6768 GtkTreeViewColumn
*column
;
6769 GtkTreeSelection
*selection
;
6771 /* attachment list */
6772 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
6773 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
6774 GTK_POLICY_AUTOMATIC
,
6775 GTK_POLICY_AUTOMATIC
);
6776 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
6778 store
= gtk_list_store_new(N_ATTACH_COLS
,
6784 G_TYPE_AUTO_POINTER
,
6786 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
6787 (GTK_TREE_MODEL(store
)));
6788 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
6789 g_object_unref(store
);
6791 renderer
= gtk_cell_renderer_text_new();
6792 column
= gtk_tree_view_column_new_with_attributes
6793 (_("Mime type"), renderer
, "text",
6794 COL_MIMETYPE
, NULL
);
6795 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
6797 renderer
= gtk_cell_renderer_text_new();
6798 column
= gtk_tree_view_column_new_with_attributes
6799 (_("Size"), renderer
, "text",
6801 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
6803 renderer
= gtk_cell_renderer_text_new();
6804 column
= gtk_tree_view_column_new_with_attributes
6805 (_("Name"), renderer
, "text",
6807 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
6809 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
6810 prefs_common
.use_stripes_everywhere
);
6811 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
6812 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
6814 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
6815 G_CALLBACK(attach_selected
), compose
);
6816 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
6817 G_CALLBACK(attach_button_pressed
), compose
);
6819 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
6820 G_CALLBACK(popup_attach_button_pressed
), compose
);
6822 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist
), NULL
, NULL
,
6823 GTK_TAP_AND_HOLD_NONE
| GTK_TAP_AND_HOLD_NO_INTERNALS
);
6824 g_signal_connect(G_OBJECT(attach_clist
), "tap-and-hold",
6825 G_CALLBACK(popup_attach_button_pressed
), compose
);
6827 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
6828 G_CALLBACK(attach_key_pressed
), compose
);
6831 gtk_drag_dest_set(attach_clist
,
6832 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
6833 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
6834 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
6835 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
6836 G_CALLBACK(compose_attach_drag_received_cb
),
6838 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
6839 G_CALLBACK(compose_drag_drop
),
6842 compose
->attach_scrwin
= attach_scrwin
;
6843 compose
->attach_clist
= attach_clist
;
6845 return attach_scrwin
;
6848 static void compose_savemsg_checkbtn_cb(GtkWidget
*widget
, Compose
*compose
);
6849 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
6851 static GtkWidget
*compose_create_others(Compose
*compose
)
6854 GtkWidget
*savemsg_checkbtn
;
6855 GtkWidget
*savemsg_combo
;
6856 GtkWidget
*savemsg_select
;
6859 gchar
*folderidentifier
;
6861 /* Table for settings */
6862 table
= gtk_table_new(3, 1, FALSE
);
6863 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
6864 gtk_widget_show(table
);
6865 gtk_table_set_row_spacings(GTK_TABLE(table
), VSPACING_NARROW
);
6868 /* Save Message to folder */
6869 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
6870 gtk_widget_show(savemsg_checkbtn
);
6871 gtk_table_attach(GTK_TABLE(table
), savemsg_checkbtn
, 0, 1, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
6872 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
6873 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
6875 g_signal_connect(G_OBJECT(savemsg_checkbtn
), "toggled",
6876 G_CALLBACK(compose_savemsg_checkbtn_cb
), compose
);
6878 savemsg_combo
= gtk_combo_box_entry_new_text();
6879 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
6880 compose
->savemsg_combo
= savemsg_combo
;
6881 gtk_widget_show(savemsg_combo
);
6883 if (prefs_common
.compose_save_to_history
)
6884 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo
),
6885 prefs_common
.compose_save_to_history
);
6887 gtk_table_attach(GTK_TABLE(table
), savemsg_combo
, 1, 2, rowcount
, rowcount
+ 1, GTK_FILL
|GTK_EXPAND
, GTK_SHRINK
, 0, 0);
6888 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
6889 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
6890 G_CALLBACK(compose_grab_focus_cb
), compose
);
6891 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
6892 folderidentifier
= folder_item_get_identifier(account_get_special_folder
6893 (compose
->account
, F_OUTBOX
));
6894 compose_set_save_to(compose
, folderidentifier
);
6895 g_free(folderidentifier
);
6898 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
6899 gtk_widget_show(savemsg_select
);
6900 gtk_table_attach(GTK_TABLE(table
), savemsg_select
, 2, 3, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
6901 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
6902 G_CALLBACK(compose_savemsg_select_cb
),
6910 static void compose_savemsg_checkbtn_cb(GtkWidget
*widget
, Compose
*compose
)
6912 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
),
6913 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
)));
6916 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
6921 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
);
6924 path
= folder_item_get_identifier(dest
);
6926 compose_set_save_to(compose
, path
);
6930 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
6931 GdkAtom clip
, GtkTextIter
*insert_place
);
6934 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
6938 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
6940 if (event
->button
== 3) {
6942 GtkTextIter sel_start
, sel_end
;
6943 gboolean stuff_selected
;
6945 /* move the cursor to allow GtkAspell to check the word
6946 * under the mouse */
6947 if (event
->x
&& event
->y
) {
6948 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
6949 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
6951 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
6954 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
6955 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
6958 stuff_selected
= gtk_text_buffer_get_selection_bounds(
6960 &sel_start
, &sel_end
);
6962 gtk_text_buffer_place_cursor (buffer
, &iter
);
6963 /* reselect stuff */
6965 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
6966 gtk_text_buffer_select_range(buffer
,
6967 &sel_start
, &sel_end
);
6969 return FALSE
; /* pass the event so that the right-click goes through */
6972 if (event
->button
== 2) {
6977 /* get the middle-click position to paste at the correct place */
6978 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
6979 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
6981 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
6984 entry_paste_clipboard(compose
, text
,
6985 prefs_common
.linewrap_pastes
,
6986 GDK_SELECTION_PRIMARY
, &iter
);
6994 static void compose_spell_menu_changed(void *data
)
6996 Compose
*compose
= (Compose
*)data
;
6998 GtkWidget
*menuitem
;
6999 GtkWidget
*parent_item
;
7000 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7003 if (compose
->gtkaspell
== NULL
)
7006 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7007 "/Menu/Spelling/Options");
7009 /* setting the submenu removes /Spelling/Options from the factory
7010 * so we need to save it */
7012 if (parent_item
== NULL
) {
7013 parent_item
= compose
->aspell_options_menu
;
7014 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7016 compose
->aspell_options_menu
= parent_item
;
7018 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7020 spell_menu
= g_slist_reverse(spell_menu
);
7021 for (items
= spell_menu
;
7022 items
; items
= items
->next
) {
7023 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7024 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7025 gtk_widget_show(GTK_WIDGET(menuitem
));
7027 g_slist_free(spell_menu
);
7029 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7030 gtk_widget_show(parent_item
);
7033 static void compose_dict_changed(void *data
)
7035 Compose
*compose
= (Compose
*) data
;
7037 if(compose
->gtkaspell
&&
7038 compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7041 gtkaspell_highlight_all(compose
->gtkaspell
);
7042 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7046 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7048 Compose
*compose
= (Compose
*)data
;
7049 GdkEventButton event
;
7052 event
.time
= gtk_get_current_event_time();
7056 return text_clicked(compose
->text
, &event
, compose
);
7059 static gboolean compose_force_window_origin
= TRUE
;
7060 static Compose
*compose_create(PrefsAccount
*account
,
7069 GtkWidget
*handlebox
;
7071 GtkWidget
*notebook
;
7073 GtkWidget
*attach_hbox
;
7074 GtkWidget
*attach_lab1
;
7075 GtkWidget
*attach_lab2
;
7080 GtkWidget
*subject_hbox
;
7081 GtkWidget
*subject_frame
;
7082 GtkWidget
*subject_entry
;
7086 GtkWidget
*edit_vbox
;
7087 #if !GTK_CHECK_VERSION(2, 24, 0)
7088 GtkWidget
*ruler_hbox
;
7091 GtkWidget
*scrolledwin
;
7093 GtkTextBuffer
*buffer
;
7094 GtkClipboard
*clipboard
;
7097 UndoMain
*undostruct
;
7099 gchar
*titles
[N_ATTACH_COLS
];
7100 GtkWidget
*popupmenu
;
7101 GtkWidget
*tmpl_menu
;
7102 GtkActionGroup
*action_group
= NULL
;
7105 GtkAspell
* gtkaspell
= NULL
;
7108 static GdkGeometry geometry
;
7110 cm_return_val_if_fail(account
!= NULL
, NULL
);
7112 debug_print("Creating compose window...\n");
7113 compose
= g_new0(Compose
, 1);
7115 titles
[COL_MIMETYPE
] = _("MIME type");
7116 titles
[COL_SIZE
] = _("Size");
7117 titles
[COL_NAME
] = _("Name");
7118 titles
[COL_CHARSET
] = _("Charset");
7120 compose
->batch
= batch
;
7121 compose
->account
= account
;
7122 compose
->folder
= folder
;
7124 compose
->mutex
= g_mutex_new();
7125 compose
->set_cursor_pos
= -1;
7127 #if !(GTK_CHECK_VERSION(2,12,0))
7128 compose
->tooltips
= tips
;
7131 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7133 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7134 gtk_widget_set_size_request(window
, -1, prefs_common
.compose_height
);
7136 if (!geometry
.max_width
) {
7137 geometry
.max_width
= gdk_screen_width();
7138 geometry
.max_height
= gdk_screen_height();
7141 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7142 &geometry
, GDK_HINT_MAX_SIZE
);
7143 if (!geometry
.min_width
) {
7144 geometry
.min_width
= 600;
7145 geometry
.min_height
= 440;
7147 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7148 &geometry
, GDK_HINT_MIN_SIZE
);
7150 #ifndef GENERIC_UMPC
7151 if (compose_force_window_origin
)
7152 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7153 prefs_common
.compose_y
);
7155 g_signal_connect(G_OBJECT(window
), "delete_event",
7156 G_CALLBACK(compose_delete_cb
), compose
);
7157 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7158 gtk_widget_realize(window
);
7160 gtkut_widget_set_composer_icon(window
);
7162 vbox
= gtk_vbox_new(FALSE
, 0);
7163 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7165 compose
->ui_manager
= gtk_ui_manager_new();
7166 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7167 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7168 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7169 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7170 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7171 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7172 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7173 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7174 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7175 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7178 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7180 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_POPUP
)
7183 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7184 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7186 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7188 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7189 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7190 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7193 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7194 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7195 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7196 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7197 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7198 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7199 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7200 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7201 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7202 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7205 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7206 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7207 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7209 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7210 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7211 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7213 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7214 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7215 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7216 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7218 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7220 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7221 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7222 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7223 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7224 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7225 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7226 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7227 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7228 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7229 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7230 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7231 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7232 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7233 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7234 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7236 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7238 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7239 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7240 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7241 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7242 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7244 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7246 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7250 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7251 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7252 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7253 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7254 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7255 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7259 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7260 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7261 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7262 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7263 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7265 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7266 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7267 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7268 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7269 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7272 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7273 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7274 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7275 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7276 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7277 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7278 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7280 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7281 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7282 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7283 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7284 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7286 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7288 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7289 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7290 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7291 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7292 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7294 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7295 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
)
7296 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
)
7297 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7299 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7301 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7302 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
)
7303 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
)
7305 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7307 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7308 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
)
7309 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7311 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7312 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
)
7313 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7315 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7317 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7318 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
)
7319 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7320 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
7321 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
7323 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
7324 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
)
7325 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
)
7326 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
7327 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
7329 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
7330 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
7331 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
7332 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
7333 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
7334 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
7336 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
7337 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
7338 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
)
7340 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
7341 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
7342 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
7346 #if !GTK_CHECK_VERSION(2, 24, 0)
7347 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
7349 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
7350 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
7351 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7352 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
7353 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7356 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
7358 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
7359 gtk_widget_show_all(menubar
);
7361 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
7363 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
7365 hildon_window_set_menu(HILDON_WINDOW(window
), GTK_MENU(menubar
));
7368 if (prefs_common
.toolbar_detachable
) {
7369 handlebox
= gtk_handle_box_new();
7371 handlebox
= gtk_hbox_new(FALSE
, 0);
7373 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
7375 gtk_widget_realize(handlebox
);
7377 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, window
,
7380 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
7384 vbox2
= gtk_vbox_new(FALSE
, 2);
7385 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
7386 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
7389 notebook
= gtk_notebook_new();
7390 gtk_widget_set_size_request(notebook
, -1, 130);
7391 gtk_widget_show(notebook
);
7393 /* header labels and entries */
7394 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
7395 compose_create_header(compose
),
7396 gtk_label_new_with_mnemonic(_("Hea_der")));
7397 /* attachment list */
7398 attach_hbox
= gtk_hbox_new(FALSE
, 0);
7399 gtk_widget_show(attach_hbox
);
7401 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
7402 gtk_widget_show(attach_lab1
);
7403 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
7405 attach_lab2
= gtk_label_new("");
7406 gtk_widget_show(attach_lab2
);
7407 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
7409 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
7410 compose_create_attach(compose
),
7413 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
7414 compose_create_others(compose
),
7415 gtk_label_new_with_mnemonic(_("Othe_rs")));
7418 subject_hbox
= gtk_hbox_new(FALSE
, 0);
7419 gtk_widget_show(subject_hbox
);
7421 subject_frame
= gtk_frame_new(NULL
);
7422 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
7423 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
7424 gtk_widget_show(subject_frame
);
7426 subject
= gtk_hbox_new(FALSE
, HSPACING_NARROW
);
7427 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
7428 gtk_widget_show(subject
);
7430 label
= gtk_label_new(_("Subject:"));
7431 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
7432 gtk_widget_show(label
);
7435 subject_entry
= claws_spell_entry_new();
7437 subject_entry
= gtk_entry_new();
7439 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
7440 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
7441 G_CALLBACK(compose_grab_focus_cb
), compose
);
7442 gtk_widget_show(subject_entry
);
7443 compose
->subject_entry
= subject_entry
;
7444 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
7446 edit_vbox
= gtk_vbox_new(FALSE
, 0);
7448 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
7450 #if !GTK_CHECK_VERSION(2, 24, 0)
7452 ruler_hbox
= gtk_hbox_new(FALSE
, 0);
7453 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
7455 ruler
= gtk_shruler_new();
7456 gtk_ruler_set_range(GTK_RULER(ruler
), 0.0, 100.0, 1.0, 100.0);
7457 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
7462 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7463 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
7464 GTK_POLICY_AUTOMATIC
,
7465 GTK_POLICY_AUTOMATIC
);
7466 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
7468 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
7469 gtk_widget_set_size_request(scrolledwin
, prefs_common
.compose_width
, -1);
7471 text
= gtk_text_view_new();
7472 if (prefs_common
.show_compose_margin
) {
7473 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
7474 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
7476 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7477 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
7478 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
7479 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
7480 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
7482 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
7483 #if !GTK_CHECK_VERSION(2, 24, 0)
7484 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
7485 G_CALLBACK(compose_edit_size_alloc
),
7488 g_signal_connect(G_OBJECT(buffer
), "changed",
7489 G_CALLBACK(compose_changed_cb
), compose
);
7490 g_signal_connect(G_OBJECT(text
), "grab_focus",
7491 G_CALLBACK(compose_grab_focus_cb
), compose
);
7492 g_signal_connect(G_OBJECT(buffer
), "insert_text",
7493 G_CALLBACK(text_inserted
), compose
);
7494 g_signal_connect(G_OBJECT(text
), "button_press_event",
7495 G_CALLBACK(text_clicked
), compose
);
7497 g_signal_connect(G_OBJECT(text
), "popup-menu",
7498 G_CALLBACK(compose_popup_menu
), compose
);
7500 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text
), NULL
, NULL
,
7501 GTK_TAP_AND_HOLD_NONE
| GTK_TAP_AND_HOLD_NO_INTERNALS
);
7502 g_signal_connect(G_OBJECT(text
), "tap-and-hold",
7503 G_CALLBACK(compose_popup_menu
), compose
);
7505 g_signal_connect(G_OBJECT(subject_entry
), "changed",
7506 G_CALLBACK(compose_changed_cb
), compose
);
7509 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7510 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7511 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7512 g_signal_connect(G_OBJECT(text
), "drag_data_received",
7513 G_CALLBACK(compose_insert_drag_received_cb
),
7515 g_signal_connect(G_OBJECT(text
), "drag-drop",
7516 G_CALLBACK(compose_drag_drop
),
7518 gtk_widget_show_all(vbox
);
7520 /* pane between attach clist and text */
7521 paned
= gtk_vpaned_new();
7522 gtk_container_add(GTK_CONTAINER(vbox2
), paned
);
7524 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window
) )
7525 gtk_widget_set_size_request(edit_vbox
, -1, mode
== COMPOSE_NEW
? 300 : 280);
7527 gtk_widget_set_size_request(edit_vbox
, -1, mode
== COMPOSE_NEW
? 250 : 230);
7529 gtk_paned_add1(GTK_PANED(paned
), notebook
);
7530 gtk_paned_add2(GTK_PANED(paned
), edit_vbox
);
7531 gtk_widget_show_all(paned
);
7534 if (prefs_common
.textfont
) {
7535 PangoFontDescription
*font_desc
;
7537 font_desc
= pango_font_description_from_string
7538 (prefs_common
.textfont
);
7540 gtk_widget_modify_font(text
, font_desc
);
7541 pango_font_description_free(font_desc
);
7545 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
7546 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
7547 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
7548 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
7549 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
7550 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
7551 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
7552 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
7554 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
7556 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
7557 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
7558 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
7560 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
7562 undostruct
= undo_init(text
);
7563 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
7566 address_completion_start(window
);
7568 compose
->window
= window
;
7569 compose
->vbox
= vbox
;
7570 compose
->menubar
= menubar
;
7571 compose
->handlebox
= handlebox
;
7573 compose
->vbox2
= vbox2
;
7575 compose
->paned
= paned
;
7577 compose
->attach_label
= attach_lab2
;
7579 compose
->notebook
= notebook
;
7580 compose
->edit_vbox
= edit_vbox
;
7581 #if !GTK_CHECK_VERSION(2, 24, 0)
7582 compose
->ruler_hbox
= ruler_hbox
;
7583 compose
->ruler
= ruler
;
7585 compose
->scrolledwin
= scrolledwin
;
7586 compose
->text
= text
;
7588 compose
->focused_editable
= NULL
;
7590 compose
->popupmenu
= popupmenu
;
7592 compose
->tmpl_menu
= tmpl_menu
;
7594 compose
->mode
= mode
;
7595 compose
->rmode
= mode
;
7597 compose
->targetinfo
= NULL
;
7598 compose
->replyinfo
= NULL
;
7599 compose
->fwdinfo
= NULL
;
7601 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
7602 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
7604 compose
->replyto
= NULL
;
7606 compose
->bcc
= NULL
;
7607 compose
->followup_to
= NULL
;
7609 compose
->ml_post
= NULL
;
7611 compose
->inreplyto
= NULL
;
7612 compose
->references
= NULL
;
7613 compose
->msgid
= NULL
;
7614 compose
->boundary
= NULL
;
7616 compose
->autowrap
= prefs_common
.autowrap
;
7617 compose
->autoindent
= prefs_common
.auto_indent
;
7618 compose
->use_signing
= FALSE
;
7619 compose
->use_encryption
= FALSE
;
7620 compose
->privacy_system
= NULL
;
7622 compose
->modified
= FALSE
;
7624 compose
->return_receipt
= FALSE
;
7626 compose
->to_list
= NULL
;
7627 compose
->newsgroup_list
= NULL
;
7629 compose
->undostruct
= undostruct
;
7631 compose
->sig_str
= NULL
;
7633 compose
->exteditor_file
= NULL
;
7634 compose
->exteditor_pid
= -1;
7635 compose
->exteditor_tag
= -1;
7636 compose
->draft_timeout_tag
= -2; /* inhibit auto-drafting while loading */
7639 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
7640 if (mode
!= COMPOSE_REDIRECT
) {
7641 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
7642 strcmp(prefs_common
.dictionary
, "")) {
7643 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
7644 prefs_common
.alt_dictionary
,
7645 conv_get_locale_charset_str(),
7646 prefs_common
.misspelled_col
,
7647 prefs_common
.check_while_typing
,
7648 prefs_common
.recheck_when_changing_dict
,
7649 prefs_common
.use_alternate
,
7650 prefs_common
.use_both_dicts
,
7651 GTK_TEXT_VIEW(text
),
7652 GTK_WINDOW(compose
->window
),
7653 compose_dict_changed
,
7654 compose_spell_menu_changed
,
7657 alertpanel_error(_("Spell checker could not "
7659 gtkaspell_checkers_strerror());
7660 gtkaspell_checkers_reset_error();
7662 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
7666 compose
->gtkaspell
= gtkaspell
;
7667 compose_spell_menu_changed(compose
);
7668 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
7671 compose_select_account(compose
, account
, TRUE
);
7673 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
7674 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
7676 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
7677 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
7679 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
7680 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
7682 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
7683 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
7685 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
7686 if (account
->protocol
!= A_NNTP
)
7687 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
7688 prefs_common_translated_header_name("To:"));
7690 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
7691 prefs_common_translated_header_name("Newsgroups:"));
7693 addressbook_set_target_compose(compose
);
7695 if (mode
!= COMPOSE_REDIRECT
)
7696 compose_set_template_menu(compose
);
7698 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
7701 compose_list
= g_list_append(compose_list
, compose
);
7703 #if !GTK_CHECK_VERSION(2, 24, 0)
7704 if (!prefs_common
.show_ruler
)
7705 gtk_widget_hide(ruler_hbox
);
7707 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
7711 compose
->priority
= PRIORITY_NORMAL
;
7712 compose_update_priority_menu_item(compose
);
7714 compose_set_out_encoding(compose
);
7717 compose_update_actions_menu(compose
);
7719 /* Privacy Systems menu */
7720 compose_update_privacy_systems_menu(compose
);
7722 activate_privacy_system(compose
, account
, TRUE
);
7723 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
7725 gtk_widget_realize(window
);
7727 gtk_widget_show(window
);
7729 maemo_window_full_screen_if_needed(GTK_WINDOW(window
));
7730 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window
));
7737 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
7742 GtkWidget
*optmenubox
;
7745 GtkWidget
*from_name
= NULL
;
7746 #if !(GTK_CHECK_VERSION(2,12,0))
7747 GtkTooltips
*tips
= compose
->tooltips
;
7750 gint num
= 0, def_menu
= 0;
7752 accounts
= account_get_list();
7753 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
7755 optmenubox
= gtk_event_box_new();
7756 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
7757 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
7759 hbox
= gtk_hbox_new(FALSE
, 6);
7760 from_name
= gtk_entry_new();
7762 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
7763 G_CALLBACK(compose_grab_focus_cb
), compose
);
7765 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
7766 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
7767 gchar
*name
, *from
= NULL
;
7769 if (ac
== compose
->account
) def_menu
= num
;
7771 name
= g_markup_printf_escaped(_("From: <i>%s</i>"),
7774 if (ac
== compose
->account
) {
7775 if (ac
->name
&& *ac
->name
) {
7777 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
7778 from
= g_strdup_printf("%s <%s>",
7780 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
7782 from
= g_strdup_printf("%s",
7784 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
7787 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
7792 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
7794 g_signal_connect(G_OBJECT(optmenu
), "changed",
7795 G_CALLBACK(account_activated
),
7797 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
7798 G_CALLBACK(compose_entry_popup_extend
),
7801 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
7802 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
7804 CLAWS_SET_TIP(optmenubox
,
7805 _("Account to use for this email"));
7806 CLAWS_SET_TIP(from_name
,
7807 _("Sender address to be used"));
7809 compose
->account_combo
= optmenu
;
7810 compose
->from_name
= from_name
;
7815 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
7817 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
7818 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
7819 Compose
*compose
= (Compose
*) data
;
7821 compose
->priority
= value
;
7825 static void compose_reply_change_mode(Compose
*compose
,
7828 gboolean was_modified
= compose
->modified
;
7830 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
7832 cm_return_if_fail(compose
->replyinfo
!= NULL
);
7834 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
7836 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
7838 if (action
== COMPOSE_REPLY_TO_ALL
)
7840 if (action
== COMPOSE_REPLY_TO_SENDER
)
7842 if (action
== COMPOSE_REPLY_TO_LIST
)
7845 compose_remove_header_entries(compose
);
7846 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
7847 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
7848 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
7850 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
7851 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
7853 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
7854 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
7855 compose_show_first_last_header(compose
, TRUE
);
7856 compose
->modified
= was_modified
;
7857 compose_set_title(compose
);
7860 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
7862 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
7863 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
7864 Compose
*compose
= (Compose
*) data
;
7867 compose_reply_change_mode(compose
, value
);
7870 static void compose_update_priority_menu_item(Compose
* compose
)
7872 GtkWidget
*menuitem
= NULL
;
7873 switch (compose
->priority
) {
7874 case PRIORITY_HIGHEST
:
7875 menuitem
= gtk_ui_manager_get_widget
7876 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
7879 menuitem
= gtk_ui_manager_get_widget
7880 (compose
->ui_manager
, "/Menu/Options/Priority/High");
7882 case PRIORITY_NORMAL
:
7883 menuitem
= gtk_ui_manager_get_widget
7884 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
7887 menuitem
= gtk_ui_manager_get_widget
7888 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
7890 case PRIORITY_LOWEST
:
7891 menuitem
= gtk_ui_manager_get_widget
7892 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
7895 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
7898 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
7900 Compose
*compose
= (Compose
*) data
;
7902 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
7904 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
7906 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
7909 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
7910 g_free(compose
->privacy_system
);
7911 compose
->privacy_system
= NULL
;
7912 if (systemid
!= NULL
) {
7913 compose
->privacy_system
= g_strdup(systemid
);
7915 can_sign
= privacy_system_can_sign(systemid
);
7916 can_encrypt
= privacy_system_can_encrypt(systemid
);
7919 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
7921 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
7922 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
7925 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
7927 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
7928 GtkWidget
*menuitem
= NULL
;
7929 GList
*children
, *amenu
;
7930 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
7931 gboolean found
= FALSE
;
7933 if (compose
->privacy_system
!= NULL
) {
7935 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7936 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
7937 cm_return_if_fail(menuitem
!= NULL
);
7939 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
7942 while (amenu
!= NULL
) {
7943 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
7944 if (systemid
!= NULL
) {
7945 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
7946 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
7947 menuitem
= GTK_WIDGET(amenu
->data
);
7949 can_sign
= privacy_system_can_sign(systemid
);
7950 can_encrypt
= privacy_system_can_encrypt(systemid
);
7954 } else if (strlen(compose
->privacy_system
) == 0 &&
7955 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
7956 menuitem
= GTK_WIDGET(amenu
->data
);
7959 can_encrypt
= FALSE
;
7964 amenu
= amenu
->next
;
7966 g_list_free(children
);
7967 if (menuitem
!= NULL
)
7968 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
7970 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
7971 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7972 "will not be able to sign or encrypt this message."),
7973 compose
->privacy_system
);
7977 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
7978 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
7981 static void compose_set_out_encoding(Compose
*compose
)
7983 CharSet out_encoding
;
7984 const gchar
*branch
= NULL
;
7985 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
7987 switch(out_encoding
) {
7988 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
7989 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
7990 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
7991 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
7992 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
7993 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
7994 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
7995 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
7996 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
7997 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
7998 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
7999 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8000 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8001 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8002 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8003 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8004 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8005 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8006 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8007 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8008 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8009 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8010 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8011 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8012 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8013 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8014 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8015 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8016 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8017 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8018 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8019 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8020 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8022 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8025 static void compose_set_template_menu(Compose
*compose
)
8027 GSList
*tmpl_list
, *cur
;
8031 tmpl_list
= template_get_config();
8033 menu
= gtk_menu_new();
8035 gtk_menu_set_accel_group (GTK_MENU (menu
),
8036 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8037 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8038 Template
*tmpl
= (Template
*)cur
->data
;
8039 gchar
*accel_path
= NULL
;
8040 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8041 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8042 g_signal_connect(G_OBJECT(item
), "activate",
8043 G_CALLBACK(compose_template_activate_cb
),
8045 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8046 gtk_widget_show(item
);
8047 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8048 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8052 gtk_widget_show(menu
);
8053 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8056 void compose_update_actions_menu(Compose
*compose
)
8058 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8061 static void compose_update_privacy_systems_menu(Compose
*compose
)
8063 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8064 GSList
*systems
, *cur
;
8066 GtkWidget
*system_none
;
8068 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8069 GtkWidget
*privacy_menu
= gtk_menu_new();
8071 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8072 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8074 g_signal_connect(G_OBJECT(system_none
), "activate",
8075 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8077 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8078 gtk_widget_show(system_none
);
8080 systems
= privacy_get_system_ids();
8081 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8082 gchar
*systemid
= cur
->data
;
8084 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8085 widget
= gtk_radio_menu_item_new_with_label(group
,
8086 privacy_system_get_name(systemid
));
8087 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8088 g_strdup(systemid
), g_free
);
8089 g_signal_connect(G_OBJECT(widget
), "activate",
8090 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8092 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8093 gtk_widget_show(widget
);
8096 g_slist_free(systems
);
8097 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8098 gtk_widget_show_all(privacy_menu
);
8099 gtk_widget_show_all(privacy_menuitem
);
8102 void compose_reflect_prefs_all(void)
8107 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8108 compose
= (Compose
*)cur
->data
;
8109 compose_set_template_menu(compose
);
8113 void compose_reflect_prefs_pixmap_theme(void)
8118 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8119 compose
= (Compose
*)cur
->data
;
8120 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8124 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8126 const gchar
*qmark
= NULL
;
8128 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8130 switch (compose
->mode
) {
8131 /* use forward-specific quote char */
8132 case COMPOSE_FORWARD
:
8133 case COMPOSE_FORWARD_AS_ATTACH
:
8134 case COMPOSE_FORWARD_INLINE
:
8135 if (compose
->folder
&& compose
->folder
->prefs
&&
8136 compose
->folder
->prefs
->forward_with_format
)
8137 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8138 else if (compose
->account
->forward_with_format
)
8139 qmark
= compose
->account
->forward_quotemark
;
8141 qmark
= prefs_common
.fw_quotemark
;
8144 /* use reply-specific quote char in all other modes */
8146 if (compose
->folder
&& compose
->folder
->prefs
&&
8147 compose
->folder
->prefs
->reply_with_format
)
8148 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8149 else if (compose
->account
->reply_with_format
)
8150 qmark
= compose
->account
->reply_quotemark
;
8152 qmark
= prefs_common
.quotemark
;
8156 if (qmark
== NULL
|| *qmark
== '\0')
8162 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8166 GtkTextBuffer
*buffer
;
8170 gchar
*parsed_str
= NULL
;
8171 gint cursor_pos
= 0;
8172 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8175 /* process the body */
8177 text
= GTK_TEXT_VIEW(compose
->text
);
8178 buffer
= gtk_text_view_get_buffer(text
);
8181 qmark
= compose_quote_char_from_context(compose
);
8183 if (compose
->replyinfo
!= NULL
) {
8186 gtk_text_buffer_set_text(buffer
, "", -1);
8187 mark
= gtk_text_buffer_get_insert(buffer
);
8188 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8190 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8191 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8193 } else if (compose
->fwdinfo
!= NULL
) {
8196 gtk_text_buffer_set_text(buffer
, "", -1);
8197 mark
= gtk_text_buffer_get_insert(buffer
);
8198 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8200 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8201 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8204 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8206 GtkTextIter start
, end
;
8209 gtk_text_buffer_get_start_iter(buffer
, &start
);
8210 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8211 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8213 /* clear the buffer now */
8215 gtk_text_buffer_set_text(buffer
, "", -1);
8217 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8218 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8219 procmsg_msginfo_free( dummyinfo
);
8225 gtk_text_buffer_set_text(buffer
, "", -1);
8226 mark
= gtk_text_buffer_get_insert(buffer
);
8227 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8230 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8231 compose_insert_sig(compose
, FALSE
);
8233 if (replace
&& parsed_str
) {
8234 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8235 gtk_text_buffer_place_cursor(buffer
, &iter
);
8239 cursor_pos
= quote_fmt_get_cursor_pos();
8240 compose
->set_cursor_pos
= cursor_pos
;
8241 if (cursor_pos
== -1)
8243 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8244 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8245 gtk_text_buffer_place_cursor(buffer
, &iter
);
8248 /* process the other fields */
8250 compose_template_apply_fields(compose
, tmpl
);
8251 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8252 quote_fmt_reset_vartable();
8253 compose_changed_cb(NULL
, compose
);
8256 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8257 gtkaspell_highlight_all(compose
->gtkaspell
);
8261 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8263 MsgInfo
* dummyinfo
= NULL
;
8264 MsgInfo
*msginfo
= NULL
;
8267 if (compose
->replyinfo
!= NULL
)
8268 msginfo
= compose
->replyinfo
;
8269 else if (compose
->fwdinfo
!= NULL
)
8270 msginfo
= compose
->fwdinfo
;
8272 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8273 msginfo
= dummyinfo
;
8276 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8278 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8279 compose
->gtkaspell
);
8281 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8283 quote_fmt_scan_string(tmpl
->from
);
8286 buf
= quote_fmt_get_buffer();
8288 alertpanel_error(_("Template From format error."));
8290 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
8294 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
8296 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8297 compose
->gtkaspell
);
8299 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8301 quote_fmt_scan_string(tmpl
->to
);
8304 buf
= quote_fmt_get_buffer();
8306 alertpanel_error(_("Template To format error."));
8308 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
8312 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
8314 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8315 compose
->gtkaspell
);
8317 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8319 quote_fmt_scan_string(tmpl
->cc
);
8322 buf
= quote_fmt_get_buffer();
8324 alertpanel_error(_("Template Cc format error."));
8326 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
8330 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
8332 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8333 compose
->gtkaspell
);
8335 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8337 quote_fmt_scan_string(tmpl
->bcc
);
8340 buf
= quote_fmt_get_buffer();
8342 alertpanel_error(_("Template Bcc format error."));
8344 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
8348 /* process the subject */
8349 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
8351 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8352 compose
->gtkaspell
);
8354 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8356 quote_fmt_scan_string(tmpl
->subject
);
8359 buf
= quote_fmt_get_buffer();
8361 alertpanel_error(_("Template subject format error."));
8363 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
8367 procmsg_msginfo_free( dummyinfo
);
8370 static void compose_destroy(Compose
*compose
)
8372 GtkAllocation allocation
;
8373 GtkTextBuffer
*buffer
;
8374 GtkClipboard
*clipboard
;
8376 compose_list
= g_list_remove(compose_list
, compose
);
8378 if (compose
->updating
) {
8379 debug_print("danger, not destroying anything now\n");
8380 compose
->deferred_destroy
= TRUE
;
8383 /* NOTE: address_completion_end() does nothing with the window
8384 * however this may change. */
8385 address_completion_end(compose
->window
);
8387 slist_free_strings(compose
->to_list
);
8388 g_slist_free(compose
->to_list
);
8389 slist_free_strings(compose
->newsgroup_list
);
8390 g_slist_free(compose
->newsgroup_list
);
8391 slist_free_strings(compose
->header_list
);
8392 g_slist_free(compose
->header_list
);
8394 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
8396 g_hash_table_destroy(compose
->email_hashtable
);
8398 procmsg_msginfo_free(compose
->targetinfo
);
8399 procmsg_msginfo_free(compose
->replyinfo
);
8400 procmsg_msginfo_free(compose
->fwdinfo
);
8402 g_free(compose
->replyto
);
8403 g_free(compose
->cc
);
8404 g_free(compose
->bcc
);
8405 g_free(compose
->newsgroups
);
8406 g_free(compose
->followup_to
);
8408 g_free(compose
->ml_post
);
8410 g_free(compose
->inreplyto
);
8411 g_free(compose
->references
);
8412 g_free(compose
->msgid
);
8413 g_free(compose
->boundary
);
8415 g_free(compose
->redirect_filename
);
8416 if (compose
->undostruct
)
8417 undo_destroy(compose
->undostruct
);
8419 g_free(compose
->sig_str
);
8421 g_free(compose
->exteditor_file
);
8423 g_free(compose
->orig_charset
);
8425 g_free(compose
->privacy_system
);
8427 if (addressbook_get_target_compose() == compose
)
8428 addressbook_set_target_compose(NULL
);
8431 if (compose
->gtkaspell
) {
8432 gtkaspell_delete(compose
->gtkaspell
);
8433 compose
->gtkaspell
= NULL
;
8437 if (!compose
->batch
) {
8438 gtk_widget_get_allocation(compose
->scrolledwin
, &allocation
);
8439 prefs_common
.compose_width
= allocation
.width
;
8440 prefs_common
.compose_height
= allocation
.height
;
8443 if (!gtk_widget_get_parent(compose
->paned
))
8444 gtk_widget_destroy(compose
->paned
);
8445 gtk_widget_destroy(compose
->popupmenu
);
8447 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
8448 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8449 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
8451 gtk_widget_destroy(compose
->window
);
8452 toolbar_destroy(compose
->toolbar
);
8453 g_free(compose
->toolbar
);
8454 g_mutex_free(compose
->mutex
);
8458 static void compose_attach_info_free(AttachInfo
*ainfo
)
8460 g_free(ainfo
->file
);
8461 g_free(ainfo
->content_type
);
8462 g_free(ainfo
->name
);
8463 g_free(ainfo
->charset
);
8467 static void compose_attach_update_label(Compose
*compose
)
8472 GtkTreeModel
*model
;
8477 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
8478 if(!gtk_tree_model_get_iter_first(model
, &iter
)) {
8479 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
8483 while(gtk_tree_model_iter_next(model
, &iter
))
8486 text
= g_strdup_printf("(%d)", i
);
8487 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
8491 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
8493 Compose
*compose
= (Compose
*)data
;
8494 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
8495 GtkTreeSelection
*selection
;
8497 GtkTreeModel
*model
;
8499 selection
= gtk_tree_view_get_selection(tree_view
);
8500 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
8505 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
8506 GtkTreePath
*path
= cur
->data
;
8507 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
8510 gtk_tree_path_free(path
);
8513 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
8514 GtkTreeRowReference
*ref
= cur
->data
;
8515 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
8518 if (gtk_tree_model_get_iter(model
, &iter
, path
))
8519 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
8521 gtk_tree_path_free(path
);
8522 gtk_tree_row_reference_free(ref
);
8526 compose_attach_update_label(compose
);
8529 static struct _AttachProperty
8532 GtkWidget
*mimetype_entry
;
8533 GtkWidget
*encoding_optmenu
;
8534 GtkWidget
*path_entry
;
8535 GtkWidget
*filename_entry
;
8537 GtkWidget
*cancel_btn
;
8540 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
8542 gtk_tree_path_free((GtkTreePath
*)ptr
);
8545 static void compose_attach_property(GtkAction
*action
, gpointer data
)
8547 Compose
*compose
= (Compose
*)data
;
8548 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
8550 GtkComboBox
*optmenu
;
8551 GtkTreeSelection
*selection
;
8553 GtkTreeModel
*model
;
8556 static gboolean cancelled
;
8558 /* only if one selected */
8559 selection
= gtk_tree_view_get_selection(tree_view
);
8560 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
8563 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
8567 path
= (GtkTreePath
*) sel
->data
;
8568 gtk_tree_model_get_iter(model
, &iter
, path
);
8569 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
8572 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
8578 if (!attach_prop
.window
)
8579 compose_attach_property_create(&cancelled
);
8580 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
8581 gtk_widget_grab_focus(attach_prop
.ok_btn
);
8582 gtk_widget_show(attach_prop
.window
);
8583 manage_window_set_transient(GTK_WINDOW(attach_prop
.window
));
8585 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
8586 if (ainfo
->encoding
== ENC_UNKNOWN
)
8587 combobox_select_by_data(optmenu
, ENC_BASE64
);
8589 combobox_select_by_data(optmenu
, ainfo
->encoding
);
8591 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
8592 ainfo
->content_type
? ainfo
->content_type
: "");
8593 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
8594 ainfo
->file
? ainfo
->file
: "");
8595 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
8596 ainfo
->name
? ainfo
->name
: "");
8599 const gchar
*entry_text
;
8601 gchar
*cnttype
= NULL
;
8608 gtk_widget_hide(attach_prop
.window
);
8609 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
8614 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
8615 if (*entry_text
!= '\0') {
8618 text
= g_strstrip(g_strdup(entry_text
));
8619 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
8620 cnttype
= g_strdup(text
);
8623 alertpanel_error(_("Invalid MIME type."));
8629 ainfo
->encoding
= combobox_get_active_data(optmenu
);
8631 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
8632 if (*entry_text
!= '\0') {
8633 if (is_file_exist(entry_text
) &&
8634 (size
= get_file_size(entry_text
)) > 0)
8635 file
= g_strdup(entry_text
);
8638 (_("File doesn't exist or is empty."));
8644 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
8645 if (*entry_text
!= '\0') {
8646 g_free(ainfo
->name
);
8647 ainfo
->name
= g_strdup(entry_text
);
8651 g_free(ainfo
->content_type
);
8652 ainfo
->content_type
= cnttype
;
8655 g_free(ainfo
->file
);
8659 ainfo
->size
= (goffset
)size
;
8661 /* update tree store */
8662 text
= to_human_readable(ainfo
->size
);
8663 gtk_tree_model_get_iter(model
, &iter
, path
);
8664 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
8665 COL_MIMETYPE
, ainfo
->content_type
,
8667 COL_NAME
, ainfo
->name
,
8668 COL_CHARSET
, ainfo
->charset
,
8674 gtk_tree_path_free(path
);
8677 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8679 label = gtk_label_new(str); \
8680 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8681 GTK_FILL, 0, 0, 0); \
8682 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8684 entry = gtk_entry_new(); \
8685 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8686 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8689 static void compose_attach_property_create(gboolean
*cancelled
)
8695 GtkWidget
*mimetype_entry
;
8698 GtkListStore
*optmenu_menu
;
8699 GtkWidget
*path_entry
;
8700 GtkWidget
*filename_entry
;
8703 GtkWidget
*cancel_btn
;
8704 GList
*mime_type_list
, *strlist
;
8707 debug_print("Creating attach_property window...\n");
8709 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
8710 gtk_widget_set_size_request(window
, 480, -1);
8711 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
8712 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
8713 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
8714 g_signal_connect(G_OBJECT(window
), "delete_event",
8715 G_CALLBACK(attach_property_delete_event
),
8717 g_signal_connect(G_OBJECT(window
), "key_press_event",
8718 G_CALLBACK(attach_property_key_pressed
),
8721 vbox
= gtk_vbox_new(FALSE
, 8);
8722 gtk_container_add(GTK_CONTAINER(window
), vbox
);
8724 table
= gtk_table_new(4, 2, FALSE
);
8725 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
8726 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
8727 gtk_table_set_col_spacings(GTK_TABLE(table
), 8);
8729 label
= gtk_label_new(_("MIME type"));
8730 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, (0 + 1),
8732 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
8733 mimetype_entry
= gtk_combo_box_entry_new_text();
8734 gtk_table_attach(GTK_TABLE(table
), mimetype_entry
, 1, 2, 0, (0 + 1),
8735 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
8737 /* stuff with list */
8738 mime_type_list
= procmime_get_mime_type_list();
8740 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
8741 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
8744 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
8746 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)strcmp2
))
8749 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
8750 (GCompareFunc
)strcmp2
);
8753 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
8754 mime_type_list
= mime_type_list
->next
) {
8755 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry
), mime_type_list
->data
);
8756 g_free(mime_type_list
->data
);
8758 g_list_free(strlist
);
8759 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
8760 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
8762 label
= gtk_label_new(_("Encoding"));
8763 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2,
8765 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
8767 hbox
= gtk_hbox_new(FALSE
, 0);
8768 gtk_table_attach(GTK_TABLE(table
), hbox
, 1, 2, 1, 2,
8769 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
8771 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
8772 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8774 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
8775 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
8776 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
8777 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
8778 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
8780 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
8782 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
8783 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
8785 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, GTK_STOCK_CANCEL
,
8786 &ok_btn
, GTK_STOCK_OK
,
8788 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
8789 gtk_widget_grab_default(ok_btn
);
8791 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
8792 G_CALLBACK(attach_property_ok
),
8794 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
8795 G_CALLBACK(attach_property_cancel
),
8798 gtk_widget_show_all(vbox
);
8800 attach_prop
.window
= window
;
8801 attach_prop
.mimetype_entry
= mimetype_entry
;
8802 attach_prop
.encoding_optmenu
= optmenu
;
8803 attach_prop
.path_entry
= path_entry
;
8804 attach_prop
.filename_entry
= filename_entry
;
8805 attach_prop
.ok_btn
= ok_btn
;
8806 attach_prop
.cancel_btn
= cancel_btn
;
8809 #undef SET_LABEL_AND_ENTRY
8811 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
8817 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
8823 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
8824 gboolean
*cancelled
)
8832 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
8834 gboolean
*cancelled
)
8836 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
8840 if (event
&& event
->keyval
== GDK_KEY_Return
) {
8848 static void compose_exec_ext_editor(Compose
*compose
)
8855 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8856 G_DIR_SEPARATOR
, compose
);
8858 if (pipe(pipe_fds
) < 0) {
8864 if ((pid
= fork()) < 0) {
8871 /* close the write side of the pipe */
8874 compose
->exteditor_file
= g_strdup(tmp
);
8875 compose
->exteditor_pid
= pid
;
8877 compose_set_ext_editor_sensitive(compose
, FALSE
);
8880 compose
->exteditor_ch
= g_io_channel_unix_new(pipe_fds
[0]);
8882 compose
->exteditor_ch
= g_io_channel_win32_new_fd(pipe_fds
[0]);
8884 compose
->exteditor_tag
= g_io_add_watch(compose
->exteditor_ch
,
8888 } else { /* process-monitoring process */
8894 /* close the read side of the pipe */
8897 if (compose_write_body_to_file(compose
, tmp
) < 0) {
8898 fd_write_all(pipe_fds
[1], "2\n", 2);
8902 pid_ed
= compose_exec_ext_editor_real(tmp
);
8904 fd_write_all(pipe_fds
[1], "1\n", 2);
8908 /* wait until editor is terminated */
8909 waitpid(pid_ed
, NULL
, 0);
8911 fd_write_all(pipe_fds
[1], "0\n", 2);
8918 #endif /* G_OS_UNIX */
8922 static gint
compose_exec_ext_editor_real(const gchar
*file
)
8929 cm_return_val_if_fail(file
!= NULL
, -1);
8931 if ((pid
= fork()) < 0) {
8936 if (pid
!= 0) return pid
;
8938 /* grandchild process */
8940 if (setpgid(0, getppid()))
8943 if (prefs_common_get_ext_editor_cmd() &&
8944 (p
= strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8945 *(p
+ 1) == 's' && !strchr(p
+ 2, '%')) {
8946 g_snprintf(buf
, sizeof(buf
), prefs_common_get_ext_editor_cmd(), file
);
8948 if (prefs_common_get_ext_editor_cmd())
8949 g_warning("External editor command-line is invalid: '%s'\n",
8950 prefs_common_get_ext_editor_cmd());
8951 g_snprintf(buf
, sizeof(buf
), DEFAULT_EDITOR_CMD
, file
);
8954 cmdline
= strsplit_with_quote(buf
, " ", 1024);
8955 execvp(cmdline
[0], cmdline
);
8958 g_strfreev(cmdline
);
8963 static gboolean
compose_ext_editor_kill(Compose
*compose
)
8965 pid_t pgid
= compose
->exteditor_pid
* -1;
8968 ret
= kill(pgid
, 0);
8970 if (ret
== 0 || (ret
== -1 && EPERM
== errno
)) {
8974 msg
= g_strdup_printf
8975 (_("The external editor is still working.\n"
8976 "Force terminating the process?\n"
8977 "process group id: %d"), -pgid
);
8978 val
= alertpanel_full(_("Notice"), msg
, GTK_STOCK_NO
, GTK_STOCK_YES
,
8979 NULL
, FALSE
, NULL
, ALERT_WARNING
, G_ALERTDEFAULT
);
8983 if (val
== G_ALERTALTERNATE
) {
8984 g_source_remove(compose
->exteditor_tag
);
8985 g_io_channel_shutdown(compose
->exteditor_ch
,
8987 g_io_channel_unref(compose
->exteditor_ch
);
8989 if (kill(pgid
, SIGTERM
) < 0) perror("kill");
8990 waitpid(compose
->exteditor_pid
, NULL
, 0);
8992 g_warning("Terminated process group id: %d", -pgid
);
8993 g_warning("Temporary file: %s",
8994 compose
->exteditor_file
);
8996 compose_set_ext_editor_sensitive(compose
, TRUE
);
8998 g_free(compose
->exteditor_file
);
8999 compose
->exteditor_file
= NULL
;
9000 compose
->exteditor_pid
= -1;
9001 compose
->exteditor_ch
= NULL
;
9002 compose
->exteditor_tag
= -1;
9010 static gboolean
compose_input_cb(GIOChannel
*source
, GIOCondition condition
,
9014 Compose
*compose
= (Compose
*)data
;
9017 debug_print("Compose: input from monitoring process\n");
9019 g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
);
9021 g_io_channel_shutdown(source
, FALSE
, NULL
);
9022 g_io_channel_unref(source
);
9024 waitpid(compose
->exteditor_pid
, NULL
, 0);
9026 if (buf
[0] == '0') { /* success */
9027 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9028 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9030 gtk_text_buffer_set_text(buffer
, "", -1);
9031 compose_insert_file(compose
, compose
->exteditor_file
);
9032 compose_changed_cb(NULL
, compose
);
9033 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9035 if (claws_unlink(compose
->exteditor_file
) < 0)
9036 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9037 } else if (buf
[0] == '1') { /* failed */
9038 g_warning("Couldn't exec external editor\n");
9039 if (claws_unlink(compose
->exteditor_file
) < 0)
9040 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9041 } else if (buf
[0] == '2') {
9042 g_warning("Couldn't write to file\n");
9043 } else if (buf
[0] == '3') {
9044 g_warning("Pipe read failed\n");
9047 compose_set_ext_editor_sensitive(compose
, TRUE
);
9049 g_free(compose
->exteditor_file
);
9050 compose
->exteditor_file
= NULL
;
9051 compose
->exteditor_pid
= -1;
9052 compose
->exteditor_ch
= NULL
;
9053 compose
->exteditor_tag
= -1;
9058 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9061 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Send", sensitive
);
9062 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/SendLater", sensitive
);
9063 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", sensitive
);
9064 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", sensitive
);
9065 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/WrapPara", sensitive
);
9066 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/WrapAllLines", sensitive
);
9067 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/ExtEditor", sensitive
);
9069 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9070 if (compose
->toolbar
->send_btn
)
9071 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9072 if (compose
->toolbar
->sendl_btn
)
9073 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9074 if (compose
->toolbar
->draft_btn
)
9075 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9076 if (compose
->toolbar
->insert_btn
)
9077 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9078 if (compose
->toolbar
->sig_btn
)
9079 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9080 if (compose
->toolbar
->exteditor_btn
)
9081 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9082 if (compose
->toolbar
->linewrap_current_btn
)
9083 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9084 if (compose
->toolbar
->linewrap_all_btn
)
9085 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9087 #endif /* G_OS_UNIX */
9090 * compose_undo_state_changed:
9092 * Change the sensivity of the menuentries undo and redo
9094 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
9095 gint redo_state
, gpointer data
)
9097 Compose
*compose
= (Compose
*)data
;
9099 switch (undo_state
) {
9100 case UNDO_STATE_TRUE
:
9101 if (!undostruct
->undo_state
) {
9102 undostruct
->undo_state
= TRUE
;
9103 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
9106 case UNDO_STATE_FALSE
:
9107 if (undostruct
->undo_state
) {
9108 undostruct
->undo_state
= FALSE
;
9109 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
9112 case UNDO_STATE_UNCHANGED
:
9114 case UNDO_STATE_REFRESH
:
9115 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
9118 g_warning("Undo state not recognized");
9122 switch (redo_state
) {
9123 case UNDO_STATE_TRUE
:
9124 if (!undostruct
->redo_state
) {
9125 undostruct
->redo_state
= TRUE
;
9126 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
9129 case UNDO_STATE_FALSE
:
9130 if (undostruct
->redo_state
) {
9131 undostruct
->redo_state
= FALSE
;
9132 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
9135 case UNDO_STATE_UNCHANGED
:
9137 case UNDO_STATE_REFRESH
:
9138 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
9141 g_warning("Redo state not recognized");
9146 /* callback functions */
9148 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9149 * includes "non-client" (windows-izm) in calculation, so this calculation
9150 * may not be accurate.
9152 #if !GTK_CHECK_VERSION(2, 24, 0)
9153 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
9154 GtkAllocation
*allocation
,
9155 GtkSHRuler
*shruler
)
9157 if (prefs_common
.show_ruler
) {
9158 gint char_width
= 0, char_height
= 0;
9159 gint line_width_in_chars
;
9161 gtkut_get_font_size(GTK_WIDGET(widget
),
9162 &char_width
, &char_height
);
9163 line_width_in_chars
=
9164 (allocation
->width
- allocation
->x
) / char_width
;
9166 /* got the maximum */
9167 gtk_ruler_set_range(GTK_RULER(shruler
),
9168 0.0, line_width_in_chars
, 0,
9169 /*line_width_in_chars*/ char_width
);
9179 ComposePrefType type
;
9180 gboolean entry_marked
;
9183 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
9185 Compose
*compose
= (Compose
*)data
;
9188 gchar
*folderidentifier
;
9189 gint account_id
= 0;
9192 GSList
*list
, *saved_list
= NULL
;
9193 HeaderEntryState
*state
;
9194 GtkRcStyle
*style
= NULL
;
9195 static GdkColor yellow
;
9196 static gboolean color_set
= FALSE
;
9198 /* Get ID of active account in the combo box */
9199 menu
= gtk_combo_box_get_model(optmenu
);
9200 gtk_combo_box_get_active_iter(optmenu
, &iter
);
9201 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
9203 ac
= account_find_from_id(account_id
);
9204 cm_return_if_fail(ac
!= NULL
);
9206 if (ac
!= compose
->account
) {
9207 compose_select_account(compose
, ac
, FALSE
);
9209 for (list
= compose
->header_list
; list
; list
= list
->next
) {
9210 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
9212 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
9213 compose_destroy_headerentry(compose
, hentry
);
9217 state
= g_malloc0(sizeof(HeaderEntryState
));
9218 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
9219 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
9220 state
->entry
= gtk_editable_get_chars(
9221 GTK_EDITABLE(hentry
->entry
), 0, -1);
9222 state
->type
= hentry
->type
;
9225 gdk_color_parse("#f5f6be", &yellow
);
9226 color_set
= gdk_colormap_alloc_color(
9227 gdk_colormap_get_system(),
9228 &yellow
, FALSE
, TRUE
);
9231 style
= gtk_widget_get_modifier_style(hentry
->entry
);
9232 state
->entry_marked
= gdk_color_equal(&yellow
,
9233 &style
->base
[GTK_STATE_NORMAL
]);
9235 saved_list
= g_slist_append(saved_list
, state
);
9236 compose_destroy_headerentry(compose
, hentry
);
9239 compose
->header_last
= NULL
;
9240 g_slist_free(compose
->header_list
);
9241 compose
->header_list
= NULL
;
9242 compose
->header_nextrow
= 1;
9243 compose_create_header_entry(compose
);
9245 if (ac
->set_autocc
&& ac
->auto_cc
)
9246 compose_entry_append(compose
, ac
->auto_cc
,
9247 COMPOSE_CC
, PREF_ACCOUNT
);
9249 if (ac
->set_autobcc
&& ac
->auto_bcc
)
9250 compose_entry_append(compose
, ac
->auto_bcc
,
9251 COMPOSE_BCC
, PREF_ACCOUNT
);
9253 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
9254 compose_entry_append(compose
, ac
->auto_replyto
,
9255 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
9257 for (list
= saved_list
; list
; list
= list
->next
) {
9258 state
= (HeaderEntryState
*) list
->data
;
9260 compose_add_header_entry(compose
, state
->header
,
9261 state
->entry
, state
->type
);
9262 if (state
->entry_marked
)
9263 compose_entry_mark_default_to(compose
, state
->entry
);
9265 g_free(state
->header
);
9266 g_free(state
->entry
);
9269 g_slist_free(saved_list
);
9271 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
9272 (ac
->protocol
== A_NNTP
) ?
9273 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
9276 /* Set message save folder */
9277 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
9278 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
9280 g_signal_connect(G_OBJECT(compose
->savemsg_checkbtn
), "toggled",
9281 G_CALLBACK(compose_savemsg_checkbtn_cb
), compose
);
9283 compose_set_save_to(compose
, NULL
);
9284 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
9285 folderidentifier
= folder_item_get_identifier(account_get_special_folder
9286 (compose
->account
, F_OUTBOX
));
9287 compose_set_save_to(compose
, folderidentifier
);
9288 g_free(folderidentifier
);
9292 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
9293 GtkTreeViewColumn
*column
, Compose
*compose
)
9295 compose_attach_property(NULL
, compose
);
9298 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
9301 Compose
*compose
= (Compose
*)data
;
9302 GtkTreeSelection
*attach_selection
;
9303 gint attach_nr_selected
;
9305 if (!event
) return FALSE
;
9307 if (event
->button
== 3) {
9308 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
9309 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
9311 if (attach_nr_selected
> 0)
9313 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", TRUE
);
9314 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", TRUE
);
9316 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
9317 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
9320 gtk_menu_popup(GTK_MENU(compose
->popupmenu
), NULL
, NULL
,
9321 NULL
, NULL
, event
->button
, event
->time
);
9328 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
9331 Compose
*compose
= (Compose
*)data
;
9333 if (!event
) return FALSE
;
9335 switch (event
->keyval
) {
9336 case GDK_KEY_Delete
:
9337 compose_attach_remove_selected(NULL
, compose
);
9343 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
9345 toolbar_comp_set_sensitive(compose
, allow
);
9346 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
9347 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
9349 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
9351 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
9352 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
9353 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
9355 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
9359 static void compose_send_cb(GtkAction
*action
, gpointer data
)
9361 Compose
*compose
= (Compose
*)data
;
9363 if (prefs_common
.work_offline
&&
9364 !inc_offline_should_override(TRUE
,
9365 _("Claws Mail needs network access in order "
9366 "to send this email.")))
9369 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
9370 g_source_remove(compose
->draft_timeout_tag
);
9371 compose
->draft_timeout_tag
= -1;
9374 compose_send(compose
);
9377 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
9379 Compose
*compose
= (Compose
*)data
;
9383 compose_allow_user_actions(compose
, FALSE
);
9384 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
9385 compose_allow_user_actions(compose
, TRUE
);
9389 compose_close(compose
);
9390 } else if (val
== -1) {
9391 alertpanel_error(_("Could not queue message."));
9392 } else if (val
== -2) {
9393 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno
));
9394 } else if (val
== -3) {
9395 if (privacy_peek_error())
9396 alertpanel_error(_("Could not queue message for sending:\n\n"
9397 "Signature failed: %s"), privacy_get_error());
9398 } else if (val
== -4) {
9399 alertpanel_error(_("Could not queue message for sending:\n\n"
9400 "Charset conversion failed."));
9401 } else if (val
== -5) {
9402 alertpanel_error(_("Could not queue message for sending:\n\n"
9403 "Couldn't get recipient encryption key."));
9404 } else if (val
== -6) {
9407 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9410 #define DRAFTED_AT_EXIT "drafted_at_exit"
9411 static void compose_register_draft(MsgInfo
*info
)
9413 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
9414 DRAFTED_AT_EXIT
, NULL
);
9415 FILE *fp
= g_fopen(filepath
, "ab");
9418 fprintf(fp
, "%s\t%d\n", folder_item_get_identifier(info
->folder
),
9426 gboolean
compose_draft (gpointer data
, guint action
)
9428 Compose
*compose
= (Compose
*)data
;
9432 MsgFlags flag
= {0, 0};
9433 static gboolean lock
= FALSE
;
9434 MsgInfo
*newmsginfo
;
9436 gboolean target_locked
= FALSE
;
9437 gboolean err
= FALSE
;
9439 if (lock
) return FALSE
;
9441 if (compose
->sending
)
9444 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
9445 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
9447 if (!g_mutex_trylock(compose
->mutex
)) {
9448 /* we don't want to lock the mutex once it's available,
9449 * because as the only other part of compose.c locking
9450 * it is compose_close - which means once unlocked,
9451 * the compose struct will be freed */
9452 debug_print("couldn't lock mutex, probably sending\n");
9458 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9459 G_DIR_SEPARATOR
, compose
);
9460 if ((fp
= g_fopen(tmp
, "wb")) == NULL
) {
9461 FILE_OP_ERROR(tmp
, "fopen");
9465 /* chmod for security */
9466 if (change_file_mode_rw(fp
, tmp
) < 0) {
9467 FILE_OP_ERROR(tmp
, "chmod");
9468 g_warning("can't change file mode\n");
9471 /* Save draft infos */
9472 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
9473 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
9475 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
9476 gchar
*savefolderid
;
9478 savefolderid
= compose_get_save_to(compose
);
9479 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
9480 g_free(savefolderid
);
9482 if (compose
->return_receipt
) {
9483 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
9485 if (compose
->privacy_system
) {
9486 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
9487 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
9488 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
9491 /* Message-ID of message replying to */
9492 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
9495 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
9496 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
9499 /* Message-ID of message forwarding to */
9500 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
9503 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
9504 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
9508 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
9509 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
9511 /* end of headers */
9512 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
9519 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
9523 if (fclose(fp
) == EOF
) {
9527 if (compose
->targetinfo
) {
9528 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
9529 flag
.perm_flags
= target_locked
?MSG_LOCKED
:0;
9531 flag
.tmp_flags
= MSG_DRAFT
;
9533 folder_item_scan(draft
);
9534 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
9535 MsgInfo
*tmpinfo
= NULL
;
9536 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
9537 if (compose
->msgid
) {
9538 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
9541 msgnum
= tmpinfo
->msgnum
;
9542 procmsg_msginfo_free(tmpinfo
);
9543 debug_print("got draft msgnum %d from scanning\n", msgnum
);
9545 debug_print("didn't get draft msgnum after scanning\n");
9548 debug_print("got draft msgnum %d from adding\n", msgnum
);
9554 if (action
!= COMPOSE_AUTO_SAVE
) {
9555 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
9556 alertpanel_error(_("Could not save draft."));
9559 gtkut_window_popup(compose
->window
);
9560 val
= alertpanel_full(_("Could not save draft"),
9561 _("Could not save draft.\n"
9562 "Do you want to cancel exit or discard this email?"),
9563 _("_Cancel exit"), _("_Discard email"), NULL
,
9564 FALSE
, NULL
, ALERT_QUESTION
, G_ALERTDEFAULT
);
9565 if (val
== G_ALERTALTERNATE
) {
9567 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
9568 compose_close(compose
);
9572 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
9581 if (compose
->mode
== COMPOSE_REEDIT
) {
9582 compose_remove_reedit_target(compose
, TRUE
);
9585 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
9588 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
9590 procmsg_msginfo_set_flags(newmsginfo
, MSG_LOCKED
, MSG_DRAFT
);
9592 procmsg_msginfo_set_flags(newmsginfo
, 0, MSG_DRAFT
);
9593 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
9594 procmsg_msginfo_set_flags(newmsginfo
, 0,
9595 MSG_HAS_ATTACHMENT
);
9597 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
9598 compose_register_draft(newmsginfo
);
9600 procmsg_msginfo_free(newmsginfo
);
9603 folder_item_scan(draft
);
9605 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
9607 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
9608 compose_close(compose
);
9614 path
= folder_item_fetch_msg(draft
, msgnum
);
9616 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
9619 if (g_stat(path
, &s
) < 0) {
9620 FILE_OP_ERROR(path
, "stat");
9626 procmsg_msginfo_free(compose
->targetinfo
);
9627 compose
->targetinfo
= procmsg_msginfo_new();
9628 compose
->targetinfo
->msgnum
= msgnum
;
9629 compose
->targetinfo
->size
= (goffset
)s
.st_size
;
9630 compose
->targetinfo
->mtime
= s
.st_mtime
;
9631 compose
->targetinfo
->folder
= draft
;
9633 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
9634 compose
->mode
= COMPOSE_REEDIT
;
9636 if (action
== COMPOSE_AUTO_SAVE
) {
9637 compose
->autosaved_draft
= compose
->targetinfo
;
9639 compose
->modified
= FALSE
;
9640 compose_set_title(compose
);
9644 g_mutex_unlock(compose
->mutex
);
9648 void compose_clear_exit_drafts(void)
9650 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
9651 DRAFTED_AT_EXIT
, NULL
);
9652 if (is_file_exist(filepath
))
9653 claws_unlink(filepath
);
9658 void compose_reopen_exit_drafts(void)
9660 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
9661 DRAFTED_AT_EXIT
, NULL
);
9662 FILE *fp
= g_fopen(filepath
, "rb");
9666 while (fgets(buf
, sizeof(buf
), fp
)) {
9667 gchar
**parts
= g_strsplit(buf
, "\t", 2);
9668 const gchar
*folder
= parts
[0];
9669 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
9671 if (folder
&& *folder
&& msgnum
> -1) {
9672 FolderItem
*item
= folder_find_item_from_identifier(folder
);
9673 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
9675 compose_reedit(info
, FALSE
);
9682 compose_clear_exit_drafts();
9685 static void compose_save_cb(GtkAction
*action
, gpointer data
)
9687 Compose
*compose
= (Compose
*)data
;
9688 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
9689 compose
->rmode
= COMPOSE_REEDIT
;
9692 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
9694 if (compose
&& file_list
) {
9697 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
9698 gchar
*file
= (gchar
*) tmp
->data
;
9699 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
9700 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
9701 compose_changed_cb(NULL
, compose
);
9706 g_free(utf8_filename
);
9711 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
9713 Compose
*compose
= (Compose
*)data
;
9716 if (compose
->redirect_filename
!= NULL
)
9719 file_list
= filesel_select_multiple_files_open(_("Select file"));
9722 compose_attach_from_list(compose
, file_list
, TRUE
);
9723 g_list_free(file_list
);
9727 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
9729 Compose
*compose
= (Compose
*)data
;
9731 gint files_inserted
= 0;
9733 file_list
= filesel_select_multiple_files_open(_("Select file"));
9738 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
9739 gchar
*file
= (gchar
*) tmp
->data
;
9740 gchar
*filedup
= g_strdup(file
);
9741 gchar
*shortfile
= g_path_get_basename(filedup
);
9742 ComposeInsertResult res
;
9743 /* insert the file if the file is short or if the user confirmed that
9744 he/she wants to insert the large file */
9745 res
= compose_insert_file(compose
, file
);
9746 if (res
== COMPOSE_INSERT_READ_ERROR
) {
9747 alertpanel_error(_("File '%s' could not be read."), shortfile
);
9748 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
9749 alertpanel_error(_("File '%s' contained invalid characters\n"
9750 "for the current encoding, insertion may be incorrect."),
9752 } else if (res
== COMPOSE_INSERT_SUCCESS
)
9759 g_list_free(file_list
);
9763 if (files_inserted
> 0 && compose
->gtkaspell
&&
9764 compose
->gtkaspell
->check_while_typing
)
9765 gtkaspell_highlight_all(compose
->gtkaspell
);
9769 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
9771 Compose
*compose
= (Compose
*)data
;
9773 compose_insert_sig(compose
, FALSE
);
9776 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
9780 Compose
*compose
= (Compose
*)data
;
9782 gtkut_widget_get_uposition(widget
, &x
, &y
);
9783 if (!compose
->batch
) {
9784 prefs_common
.compose_x
= x
;
9785 prefs_common
.compose_y
= y
;
9787 if (compose
->sending
|| compose
->updating
)
9789 compose_close_cb(NULL
, compose
);
9793 void compose_close_toolbar(Compose
*compose
)
9795 compose_close_cb(NULL
, compose
);
9798 static void compose_close_cb(GtkAction
*action
, gpointer data
)
9800 Compose
*compose
= (Compose
*)data
;
9804 if (compose
->exteditor_tag
!= -1) {
9805 if (!compose_ext_editor_kill(compose
))
9810 if (compose
->modified
) {
9811 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
9812 if (!g_mutex_trylock(compose
->mutex
)) {
9813 /* we don't want to lock the mutex once it's available,
9814 * because as the only other part of compose.c locking
9815 * it is compose_close - which means once unlocked,
9816 * the compose struct will be freed */
9817 debug_print("couldn't lock mutex, probably sending\n");
9821 val
= alertpanel(_("Discard message"),
9822 _("This message has been modified. Discard it?"),
9823 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL
);
9825 val
= alertpanel(_("Save changes"),
9826 _("This message has been modified. Save the latest changes?"),
9827 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL
);
9829 g_mutex_unlock(compose
->mutex
);
9831 case G_ALERTDEFAULT
:
9832 if (prefs_common
.autosave
&& !reedit
)
9833 compose_remove_draft(compose
);
9835 case G_ALERTALTERNATE
:
9836 compose_draft(data
, COMPOSE_QUIT_EDITING
);
9843 compose_close(compose
);
9846 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
9848 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
9849 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
9850 Compose
*compose
= (Compose
*) data
;
9853 compose
->out_encoding
= (CharSet
)value
;
9856 static void compose_address_cb(GtkAction
*action
, gpointer data
)
9858 Compose
*compose
= (Compose
*)data
;
9860 addressbook_open(compose
);
9863 static void about_show_cb(GtkAction
*action
, gpointer data
)
9868 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
9870 Compose
*compose
= (Compose
*)data
;
9875 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
9876 cm_return_if_fail(tmpl
!= NULL
);
9878 msg
= g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9880 val
= alertpanel(_("Apply template"), msg
,
9881 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL
);
9884 if (val
== G_ALERTDEFAULT
)
9885 compose_template_apply(compose
, tmpl
, TRUE
);
9886 else if (val
== G_ALERTALTERNATE
)
9887 compose_template_apply(compose
, tmpl
, FALSE
);
9890 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
9892 Compose
*compose
= (Compose
*)data
;
9894 compose_exec_ext_editor(compose
);
9897 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
9899 Compose
*compose
= (Compose
*)data
;
9900 gboolean prev_autowrap
= compose
->autowrap
;
9902 compose
->autowrap
= FALSE
;
9903 undo_undo(compose
->undostruct
);
9904 compose
->autowrap
= prev_autowrap
;
9907 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
9909 Compose
*compose
= (Compose
*)data
;
9910 gboolean prev_autowrap
= compose
->autowrap
;
9912 compose
->autowrap
= FALSE
;
9913 undo_redo(compose
->undostruct
);
9914 compose
->autowrap
= prev_autowrap
;
9917 static void entry_cut_clipboard(GtkWidget
*entry
)
9919 if (GTK_IS_EDITABLE(entry
))
9920 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
9921 else if (GTK_IS_TEXT_VIEW(entry
))
9922 gtk_text_buffer_cut_clipboard(
9923 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
9924 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
9928 static void entry_copy_clipboard(GtkWidget
*entry
)
9930 if (GTK_IS_EDITABLE(entry
))
9931 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
9932 else if (GTK_IS_TEXT_VIEW(entry
))
9933 gtk_text_buffer_copy_clipboard(
9934 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
9935 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
9938 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
9939 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
9941 if (GTK_IS_TEXT_VIEW(entry
)) {
9942 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
9943 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
9944 GtkTextIter start_iter
, end_iter
;
9946 gchar
*contents
= gtk_clipboard_wait_for_text(gtk_clipboard_get(clip
));
9948 if (contents
== NULL
)
9951 /* we shouldn't delete the selection when middle-click-pasting, or we
9952 * can't mid-click-paste our own selection */
9953 if (clip
!= GDK_SELECTION_PRIMARY
) {
9954 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
9955 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
9958 if (insert_place
== NULL
) {
9959 /* if insert_place isn't specified, insert at the cursor.
9960 * used for Ctrl-V pasting */
9961 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
9962 start
= gtk_text_iter_get_offset(&start_iter
);
9963 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
9965 /* if insert_place is specified, paste here.
9966 * used for mid-click-pasting */
9967 start
= gtk_text_iter_get_offset(insert_place
);
9968 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
9969 if (prefs_common
.primary_paste_unselects
)
9970 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
9974 /* paste unwrapped: mark the paste so it's not wrapped later */
9975 end
= start
+ strlen(contents
);
9976 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
9977 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
9978 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
9979 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
9980 /* rewrap paragraph now (after a mid-click-paste) */
9981 mark_start
= gtk_text_buffer_get_insert(buffer
);
9982 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
9983 gtk_text_iter_backward_char(&start_iter
);
9984 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
9986 } else if (GTK_IS_EDITABLE(entry
))
9987 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
9989 compose
->modified
= TRUE
;
9992 static void entry_allsel(GtkWidget
*entry
)
9994 if (GTK_IS_EDITABLE(entry
))
9995 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
9996 else if (GTK_IS_TEXT_VIEW(entry
)) {
9997 GtkTextIter startiter
, enditer
;
9998 GtkTextBuffer
*textbuf
;
10000 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10001 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
10002 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
10004 gtk_text_buffer_move_mark_by_name(textbuf
,
10005 "selection_bound", &startiter
);
10006 gtk_text_buffer_move_mark_by_name(textbuf
,
10007 "insert", &enditer
);
10011 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
10013 Compose
*compose
= (Compose
*)data
;
10014 if (compose
->focused_editable
10015 #ifndef GENERIC_UMPC
10016 && gtkut_widget_has_focus(compose
->focused_editable
)
10019 entry_cut_clipboard(compose
->focused_editable
);
10022 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
10024 Compose
*compose
= (Compose
*)data
;
10025 if (compose
->focused_editable
10026 #ifndef GENERIC_UMPC
10027 && gtkut_widget_has_focus(compose
->focused_editable
)
10030 entry_copy_clipboard(compose
->focused_editable
);
10033 static void compose_paste_cb(GtkAction
*action
, gpointer data
)
10035 Compose
*compose
= (Compose
*)data
;
10036 gint prev_autowrap
;
10037 GtkTextBuffer
*buffer
;
10039 if (compose
->focused_editable
&&
10040 gtkut_widget_has_focus(compose
->focused_editable
))
10041 entry_paste_clipboard(compose
, compose
->focused_editable
,
10042 prefs_common
.linewrap_pastes
,
10043 GDK_SELECTION_CLIPBOARD
, NULL
);
10047 if (gtkut_widget_has_focus(compose
->text
) &&
10048 compose
->gtkaspell
&&
10049 compose
->gtkaspell
->check_while_typing
)
10050 gtkaspell_highlight_all(compose
->gtkaspell
);
10054 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
10056 Compose
*compose
= (Compose
*)data
;
10057 gint wrap_quote
= prefs_common
.linewrap_quote
;
10058 if (compose
->focused_editable
10059 #ifndef GENERIC_UMPC
10060 && gtkut_widget_has_focus(compose
->focused_editable
)
10063 /* let text_insert() (called directly or at a later time
10064 * after the gtk_editable_paste_clipboard) know that
10065 * text is to be inserted as a quotation. implemented
10066 * by using a simple refcount... */
10067 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
10068 G_OBJECT(compose
->focused_editable
),
10069 "paste_as_quotation"));
10070 g_object_set_data(G_OBJECT(compose
->focused_editable
),
10071 "paste_as_quotation",
10072 GINT_TO_POINTER(paste_as_quotation
+ 1));
10073 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
10074 entry_paste_clipboard(compose
, compose
->focused_editable
,
10075 prefs_common
.linewrap_pastes
,
10076 GDK_SELECTION_CLIPBOARD
, NULL
);
10077 prefs_common
.linewrap_quote
= wrap_quote
;
10081 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
10083 Compose
*compose
= (Compose
*)data
;
10084 gint prev_autowrap
;
10085 GtkTextBuffer
*buffer
;
10087 if (compose
->focused_editable
10088 #ifndef GENERIC_UMPC
10089 && gtkut_widget_has_focus(compose
->focused_editable
)
10092 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
10093 GDK_SELECTION_CLIPBOARD
, NULL
);
10097 if (gtkut_widget_has_focus(compose
->text
) &&
10098 compose
->gtkaspell
&&
10099 compose
->gtkaspell
->check_while_typing
)
10100 gtkaspell_highlight_all(compose
->gtkaspell
);
10104 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
10106 Compose
*compose
= (Compose
*)data
;
10107 gint prev_autowrap
;
10108 GtkTextBuffer
*buffer
;
10110 if (compose
->focused_editable
10111 #ifndef GENERIC_UMPC
10112 && gtkut_widget_has_focus(compose
->focused_editable
)
10115 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
10116 GDK_SELECTION_CLIPBOARD
, NULL
);
10120 if (gtkut_widget_has_focus(compose
->text
) &&
10121 compose
->gtkaspell
&&
10122 compose
->gtkaspell
->check_while_typing
)
10123 gtkaspell_highlight_all(compose
->gtkaspell
);
10127 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
10129 Compose
*compose
= (Compose
*)data
;
10130 if (compose
->focused_editable
10131 #ifndef GENERIC_UMPC
10132 && gtkut_widget_has_focus(compose
->focused_editable
)
10135 entry_allsel(compose
->focused_editable
);
10138 static void textview_move_beginning_of_line (GtkTextView
*text
)
10140 GtkTextBuffer
*buffer
;
10144 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10146 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10147 mark
= gtk_text_buffer_get_insert(buffer
);
10148 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10149 gtk_text_iter_set_line_offset(&ins
, 0);
10150 gtk_text_buffer_place_cursor(buffer
, &ins
);
10153 static void textview_move_forward_character (GtkTextView
*text
)
10155 GtkTextBuffer
*buffer
;
10159 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10161 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10162 mark
= gtk_text_buffer_get_insert(buffer
);
10163 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10164 if (gtk_text_iter_forward_cursor_position(&ins
))
10165 gtk_text_buffer_place_cursor(buffer
, &ins
);
10168 static void textview_move_backward_character (GtkTextView
*text
)
10170 GtkTextBuffer
*buffer
;
10174 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10176 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10177 mark
= gtk_text_buffer_get_insert(buffer
);
10178 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10179 if (gtk_text_iter_backward_cursor_position(&ins
))
10180 gtk_text_buffer_place_cursor(buffer
, &ins
);
10183 static void textview_move_forward_word (GtkTextView
*text
)
10185 GtkTextBuffer
*buffer
;
10190 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10192 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10193 mark
= gtk_text_buffer_get_insert(buffer
);
10194 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10195 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
10196 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
10197 gtk_text_iter_backward_word_start(&ins
);
10198 gtk_text_buffer_place_cursor(buffer
, &ins
);
10202 static void textview_move_backward_word (GtkTextView
*text
)
10204 GtkTextBuffer
*buffer
;
10209 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10211 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10212 mark
= gtk_text_buffer_get_insert(buffer
);
10213 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10214 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
10215 if (gtk_text_iter_backward_word_starts(&ins
, 1))
10216 gtk_text_buffer_place_cursor(buffer
, &ins
);
10219 static void textview_move_end_of_line (GtkTextView
*text
)
10221 GtkTextBuffer
*buffer
;
10225 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10227 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10228 mark
= gtk_text_buffer_get_insert(buffer
);
10229 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10230 if (gtk_text_iter_forward_to_line_end(&ins
))
10231 gtk_text_buffer_place_cursor(buffer
, &ins
);
10234 static void textview_move_next_line (GtkTextView
*text
)
10236 GtkTextBuffer
*buffer
;
10241 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10243 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10244 mark
= gtk_text_buffer_get_insert(buffer
);
10245 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10246 offset
= gtk_text_iter_get_line_offset(&ins
);
10247 if (gtk_text_iter_forward_line(&ins
)) {
10248 gtk_text_iter_set_line_offset(&ins
, offset
);
10249 gtk_text_buffer_place_cursor(buffer
, &ins
);
10253 static void textview_move_previous_line (GtkTextView
*text
)
10255 GtkTextBuffer
*buffer
;
10260 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10262 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10263 mark
= gtk_text_buffer_get_insert(buffer
);
10264 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10265 offset
= gtk_text_iter_get_line_offset(&ins
);
10266 if (gtk_text_iter_backward_line(&ins
)) {
10267 gtk_text_iter_set_line_offset(&ins
, offset
);
10268 gtk_text_buffer_place_cursor(buffer
, &ins
);
10272 static void textview_delete_forward_character (GtkTextView
*text
)
10274 GtkTextBuffer
*buffer
;
10276 GtkTextIter ins
, end_iter
;
10278 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10280 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10281 mark
= gtk_text_buffer_get_insert(buffer
);
10282 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10284 if (gtk_text_iter_forward_char(&end_iter
)) {
10285 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
10289 static void textview_delete_backward_character (GtkTextView
*text
)
10291 GtkTextBuffer
*buffer
;
10293 GtkTextIter ins
, end_iter
;
10295 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10297 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10298 mark
= gtk_text_buffer_get_insert(buffer
);
10299 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10301 if (gtk_text_iter_backward_char(&end_iter
)) {
10302 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
10306 static void textview_delete_forward_word (GtkTextView
*text
)
10308 GtkTextBuffer
*buffer
;
10310 GtkTextIter ins
, end_iter
;
10312 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10314 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10315 mark
= gtk_text_buffer_get_insert(buffer
);
10316 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10318 if (gtk_text_iter_forward_word_end(&end_iter
)) {
10319 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
10323 static void textview_delete_backward_word (GtkTextView
*text
)
10325 GtkTextBuffer
*buffer
;
10327 GtkTextIter ins
, end_iter
;
10329 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10331 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10332 mark
= gtk_text_buffer_get_insert(buffer
);
10333 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10335 if (gtk_text_iter_backward_word_start(&end_iter
)) {
10336 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
10340 static void textview_delete_line (GtkTextView
*text
)
10342 GtkTextBuffer
*buffer
;
10344 GtkTextIter ins
, start_iter
, end_iter
;
10346 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10348 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10349 mark
= gtk_text_buffer_get_insert(buffer
);
10350 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10353 gtk_text_iter_set_line_offset(&start_iter
, 0);
10356 if (gtk_text_iter_ends_line(&end_iter
)){
10357 if (!gtk_text_iter_forward_char(&end_iter
))
10358 gtk_text_iter_backward_char(&start_iter
);
10361 gtk_text_iter_forward_to_line_end(&end_iter
);
10362 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
10365 static void textview_delete_to_line_end (GtkTextView
*text
)
10367 GtkTextBuffer
*buffer
;
10369 GtkTextIter ins
, end_iter
;
10371 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10373 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10374 mark
= gtk_text_buffer_get_insert(buffer
);
10375 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10377 if (gtk_text_iter_ends_line(&end_iter
))
10378 gtk_text_iter_forward_char(&end_iter
);
10380 gtk_text_iter_forward_to_line_end(&end_iter
);
10381 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
10384 #define DO_ACTION(name, act) { \
10385 if(!strcmp(name, a_name)) { \
10389 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
10391 const gchar
*a_name
= gtk_action_get_name(action
);
10392 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
10393 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
10394 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
10395 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
10396 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
10397 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
10398 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
10399 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
10400 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
10401 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
10402 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
10403 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
10404 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
10405 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
10409 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
10411 Compose
*compose
= (Compose
*)data
;
10412 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
10413 ComposeCallAdvancedAction action
= -1;
10415 action
= compose_call_advanced_action_from_path(gaction
);
10418 void (*do_action
) (GtkTextView
*text
);
10419 } action_table
[] = {
10420 {textview_move_beginning_of_line
},
10421 {textview_move_forward_character
},
10422 {textview_move_backward_character
},
10423 {textview_move_forward_word
},
10424 {textview_move_backward_word
},
10425 {textview_move_end_of_line
},
10426 {textview_move_next_line
},
10427 {textview_move_previous_line
},
10428 {textview_delete_forward_character
},
10429 {textview_delete_backward_character
},
10430 {textview_delete_forward_word
},
10431 {textview_delete_backward_word
},
10432 {textview_delete_line
},
10433 {textview_delete_to_line_end
}
10436 if (!gtkut_widget_has_focus(GTK_WIDGET(text
))) return;
10438 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
10439 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
10440 if (action_table
[action
].do_action
)
10441 action_table
[action
].do_action(text
);
10443 g_warning("Not implemented yet.");
10447 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
10449 GtkAllocation allocation
;
10453 if (GTK_IS_EDITABLE(widget
)) {
10454 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
10455 gtk_editable_set_position(GTK_EDITABLE(widget
),
10458 if ((parent
= gtk_widget_get_parent(widget
))
10459 && (parent
= gtk_widget_get_parent(parent
))
10460 && (parent
= gtk_widget_get_parent(parent
))) {
10461 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
10462 gtk_widget_get_allocation(widget
, &allocation
);
10463 gint y
= allocation
.y
;
10464 gint height
= allocation
.height
;
10465 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
10466 (GTK_SCROLLED_WINDOW(parent
));
10468 gfloat value
= gtk_adjustment_get_value(shown
);
10469 gfloat upper
= gtk_adjustment_get_upper(shown
);
10470 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
10471 if (y
< (int)value
) {
10472 gtk_adjustment_set_value(shown
, y
- 1);
10474 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
10475 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
10476 gtk_adjustment_set_value(shown
,
10477 y
+ height
- (int)page_size
- 1);
10479 gtk_adjustment_set_value(shown
,
10480 (int)upper
- (int)page_size
- 1);
10487 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
10488 compose
->focused_editable
= widget
;
10490 #ifdef GENERIC_UMPC
10491 if (GTK_IS_TEXT_VIEW(widget
)
10492 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
10493 g_object_ref(compose
->notebook
);
10494 g_object_ref(compose
->edit_vbox
);
10495 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
10496 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
10497 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
10498 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
10499 g_object_unref(compose
->notebook
);
10500 g_object_unref(compose
->edit_vbox
);
10501 g_signal_handlers_block_by_func(G_OBJECT(widget
),
10502 G_CALLBACK(compose_grab_focus_cb
),
10504 gtk_widget_grab_focus(widget
);
10505 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
10506 G_CALLBACK(compose_grab_focus_cb
),
10508 } else if (!GTK_IS_TEXT_VIEW(widget
)
10509 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
10510 g_object_ref(compose
->notebook
);
10511 g_object_ref(compose
->edit_vbox
);
10512 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
10513 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
10514 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
10515 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
10516 g_object_unref(compose
->notebook
);
10517 g_object_unref(compose
->edit_vbox
);
10518 g_signal_handlers_block_by_func(G_OBJECT(widget
),
10519 G_CALLBACK(compose_grab_focus_cb
),
10521 gtk_widget_grab_focus(widget
);
10522 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
10523 G_CALLBACK(compose_grab_focus_cb
),
10529 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
10531 compose
->modified
= TRUE
;
10532 // compose_beautify_paragraph(compose, NULL, TRUE);
10533 #ifndef GENERIC_UMPC
10534 compose_set_title(compose
);
10538 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
10540 Compose
*compose
= (Compose
*)data
;
10541 compose_beautify_paragraph(compose
, NULL
, TRUE
);
10544 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
10546 Compose
*compose
= (Compose
*)data
;
10547 compose_wrap_all_full(compose
, TRUE
);
10550 static void compose_find_cb(GtkAction
*action
, gpointer data
)
10552 Compose
*compose
= (Compose
*)data
;
10554 message_search_compose(compose
);
10557 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
10560 Compose
*compose
= (Compose
*)data
;
10561 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10562 if (compose
->autowrap
)
10563 compose_wrap_all_full(compose
, TRUE
);
10564 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10567 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
10570 Compose
*compose
= (Compose
*)data
;
10571 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10574 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
10576 Compose
*compose
= (Compose
*)data
;
10578 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10581 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
10583 Compose
*compose
= (Compose
*)data
;
10585 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10588 static void activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
10590 g_free(compose
->privacy_system
);
10592 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
10593 compose_update_privacy_system_menu_item(compose
, warn
);
10596 #if !GTK_CHECK_VERSION(2, 24, 0)
10597 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
10599 Compose
*compose
= (Compose
*)data
;
10601 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
10602 gtk_widget_show(compose
->ruler_hbox
);
10603 prefs_common
.show_ruler
= TRUE
;
10605 gtk_widget_hide(compose
->ruler_hbox
);
10606 gtk_widget_queue_resize(compose
->edit_vbox
);
10607 prefs_common
.show_ruler
= FALSE
;
10612 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
10613 GdkDragContext
*context
,
10616 GtkSelectionData
*data
,
10619 gpointer user_data
)
10621 Compose
*compose
= (Compose
*)user_data
;
10625 type
= gtk_selection_data_get_data_type(data
);
10626 if (((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
10628 || (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "DROPFILES_DND"))
10630 ) && gtk_drag_get_source_widget(context
) !=
10631 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
10632 list
= uri_list_extract_filenames(
10633 (const gchar
*)gtk_selection_data_get_data(data
));
10634 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
10635 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
10636 compose_attach_append
10637 (compose
, (const gchar
*)tmp
->data
,
10638 utf8_filename
, NULL
, NULL
);
10639 g_free(utf8_filename
);
10641 if (list
) compose_changed_cb(NULL
, compose
);
10642 list_free_strings(list
);
10644 } else if (gtk_drag_get_source_widget(context
)
10645 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
10646 /* comes from our summaryview */
10647 SummaryView
* summaryview
= NULL
;
10648 GSList
* list
= NULL
, *cur
= NULL
;
10650 if (mainwindow_get_mainwindow())
10651 summaryview
= mainwindow_get_mainwindow()->summaryview
;
10654 list
= summary_get_selected_msg_list(summaryview
);
10656 for (cur
= list
; cur
; cur
= cur
->next
) {
10657 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
10658 gchar
*file
= NULL
;
10660 file
= procmsg_get_message_file_full(msginfo
,
10663 compose_attach_append(compose
, (const gchar
*)file
,
10664 (const gchar
*)file
, "message/rfc822", NULL
);
10668 g_slist_free(list
);
10672 static gboolean
compose_drag_drop(GtkWidget
*widget
,
10673 GdkDragContext
*drag_context
,
10675 guint time
, gpointer user_data
)
10677 /* not handling this signal makes compose_insert_drag_received_cb
10682 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
10683 GdkDragContext
*drag_context
,
10686 GtkSelectionData
*data
,
10689 gpointer user_data
)
10691 Compose
*compose
= (Compose
*)user_data
;
10695 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10697 type
= gtk_selection_data_get_data_type(data
);
10699 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
10701 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "DROPFILES_DND")) {
10703 AlertValue val
= G_ALERTDEFAULT
;
10704 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
10706 list
= uri_list_extract_filenames(ddata
);
10707 if (list
== NULL
&& strstr(ddata
, "://")) {
10708 /* Assume a list of no files, and data has ://, is a remote link */
10709 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
10710 gchar
*tmpfile
= get_tmp_file();
10711 str_write_to_file(tmpdata
, tmpfile
);
10713 compose_insert_file(compose
, tmpfile
);
10714 claws_unlink(tmpfile
);
10716 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
10717 compose_beautify_paragraph(compose
, NULL
, TRUE
);
10720 switch (prefs_common
.compose_dnd_mode
) {
10721 case COMPOSE_DND_ASK
:
10722 val
= alertpanel_full(_("Insert or attach?"),
10723 _("Do you want to insert the contents of the file(s) "
10724 "into the message body, or attach it to the email?"),
10725 GTK_STOCK_CANCEL
, _("+_Insert"), _("_Attach"),
10726 TRUE
, NULL
, ALERT_QUESTION
, G_ALERTALTERNATE
);
10728 case COMPOSE_DND_INSERT
:
10729 val
= G_ALERTALTERNATE
;
10731 case COMPOSE_DND_ATTACH
:
10732 val
= G_ALERTOTHER
;
10735 /* unexpected case */
10736 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10739 if (val
& G_ALERTDISABLE
) {
10740 val
&= ~G_ALERTDISABLE
;
10741 /* remember what action to perform by default, only if we don't click Cancel */
10742 if (val
== G_ALERTALTERNATE
)
10743 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
10744 else if (val
== G_ALERTOTHER
)
10745 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
10748 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
10749 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
10750 list_free_strings(list
);
10753 } else if (val
== G_ALERTOTHER
) {
10754 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
10755 list_free_strings(list
);
10760 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
10761 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
10763 list_free_strings(list
);
10765 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
10770 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
10773 static void compose_header_drag_received_cb (GtkWidget
*widget
,
10774 GdkDragContext
*drag_context
,
10777 GtkSelectionData
*data
,
10780 gpointer user_data
)
10782 GtkEditable
*entry
= (GtkEditable
*)user_data
;
10783 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
10785 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10788 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
10789 gchar
*decoded
=g_new(gchar
, strlen(email
));
10792 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
10793 gtk_editable_delete_text(entry
, 0, -1);
10794 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
10795 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
10799 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
10802 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
10804 Compose
*compose
= (Compose
*)data
;
10806 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
10807 compose
->return_receipt
= TRUE
;
10809 compose
->return_receipt
= FALSE
;
10812 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
10814 Compose
*compose
= (Compose
*)data
;
10816 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
10817 compose
->remove_references
= TRUE
;
10819 compose
->remove_references
= FALSE
;
10822 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
10823 ComposeHeaderEntry
*headerentry
)
10825 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
10829 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
10830 GdkEventKey
*event
,
10831 ComposeHeaderEntry
*headerentry
)
10833 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
10834 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
10835 !(event
->state
& GDK_MODIFIER_MASK
) &&
10836 (event
->keyval
== GDK_KEY_BackSpace
) &&
10837 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
10838 gtk_container_remove
10839 (GTK_CONTAINER(headerentry
->compose
->header_table
),
10840 headerentry
->combo
);
10841 gtk_container_remove
10842 (GTK_CONTAINER(headerentry
->compose
->header_table
),
10843 headerentry
->entry
);
10844 headerentry
->compose
->header_list
=
10845 g_slist_remove(headerentry
->compose
->header_list
,
10847 g_free(headerentry
);
10848 } else if (event
->keyval
== GDK_KEY_Tab
) {
10849 if (headerentry
->compose
->header_last
== headerentry
) {
10850 /* Override default next focus, and give it to subject_entry
10851 * instead of notebook tabs
10853 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
10854 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
10861 static gboolean
scroll_postpone(gpointer data
)
10863 Compose
*compose
= (Compose
*)data
;
10865 cm_return_val_if_fail(!compose
->batch
, FALSE
);
10867 GTK_EVENTS_FLUSH();
10868 compose_show_first_last_header(compose
, FALSE
);
10872 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
10873 ComposeHeaderEntry
*headerentry
)
10875 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
10876 compose_create_header_entry(headerentry
->compose
);
10877 g_signal_handlers_disconnect_matched
10878 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
10879 0, 0, NULL
, NULL
, headerentry
);
10881 if (!headerentry
->compose
->batch
)
10882 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
10886 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
10888 compose
->draft_timeout_tag
= -1;
10889 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10893 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
10895 GtkAdjustment
*vadj
;
10897 cm_return_if_fail(compose
);
10898 cm_return_if_fail(!compose
->batch
);
10899 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
10900 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
10901 vadj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(
10902 gtk_widget_get_parent(compose
->header_table
)));
10903 gtk_adjustment_set_value(vadj
, (show_first
?
10904 gtk_adjustment_get_lower(vadj
) :
10905 (gtk_adjustment_get_upper(vadj
) -
10906 gtk_adjustment_get_page_size(vadj
))));
10907 gtk_adjustment_changed(vadj
);
10910 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
10911 const gchar
*text
, gint len
, Compose
*compose
)
10913 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
10914 (G_OBJECT(compose
->text
), "paste_as_quotation"));
10917 cm_return_if_fail(text
!= NULL
);
10919 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
10920 G_CALLBACK(text_inserted
),
10922 if (paste_as_quotation
) {
10924 const gchar
*qmark
;
10926 GtkTextIter start_iter
;
10929 len
= strlen(text
);
10931 new_text
= g_strndup(text
, len
);
10933 qmark
= compose_quote_char_from_context(compose
);
10935 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
10936 gtk_text_buffer_place_cursor(buffer
, iter
);
10938 pos
= gtk_text_iter_get_offset(iter
);
10940 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
10941 _("Quote format error at line %d."));
10942 quote_fmt_reset_vartable();
10944 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
10945 GINT_TO_POINTER(paste_as_quotation
- 1));
10947 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
10948 gtk_text_buffer_place_cursor(buffer
, iter
);
10949 gtk_text_buffer_delete_mark(buffer
, mark
);
10951 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
10952 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
10953 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
10954 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
10955 gtk_text_buffer_delete_mark(buffer
, mark
);
10957 if (strcmp(text
, "\n") || compose
->automatic_break
10958 || gtk_text_iter_starts_line(iter
)) {
10959 GtkTextIter before_ins
;
10960 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
10961 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
10962 before_ins
= *iter
;
10963 gtk_text_iter_backward_chars(&before_ins
, len
);
10964 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
10967 /* check if the preceding is just whitespace or quote */
10968 GtkTextIter start_line
;
10969 gchar
*tmp
= NULL
, *quote
= NULL
;
10970 gint quote_len
= 0, is_normal
= 0;
10971 start_line
= *iter
;
10972 gtk_text_iter_set_line_offset(&start_line
, 0);
10973 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
10976 if (*tmp
== '\0') {
10979 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
10987 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
10989 gtk_text_buffer_insert_with_tags_by_name(buffer
,
10990 iter
, text
, len
, "no_join", NULL
);
10995 if (!paste_as_quotation
) {
10996 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
10997 compose_beautify_paragraph(compose
, iter
, FALSE
);
10998 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
10999 gtk_text_buffer_delete_mark(buffer
, mark
);
11002 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
11003 G_CALLBACK(text_inserted
),
11005 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
11007 if (prefs_common
.autosave
&&
11008 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
11009 compose
->draft_timeout_tag
!= -2 /* disabled while loading */)
11010 compose
->draft_timeout_tag
= g_timeout_add
11011 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
11015 static void compose_check_all(GtkAction
*action
, gpointer data
)
11017 Compose
*compose
= (Compose
*)data
;
11018 if (!compose
->gtkaspell
)
11021 if (gtkut_widget_has_focus(compose
->subject_entry
))
11022 claws_spell_entry_check_all(
11023 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11025 gtkaspell_check_all(compose
->gtkaspell
);
11028 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
11030 Compose
*compose
= (Compose
*)data
;
11031 if (compose
->gtkaspell
) {
11032 claws_spell_entry_recheck_all(
11033 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11034 gtkaspell_highlight_all(compose
->gtkaspell
);
11038 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
11040 Compose
*compose
= (Compose
*)data
;
11041 if (!compose
->gtkaspell
) {
11042 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
11046 if (gtkut_widget_has_focus(compose
->subject_entry
))
11047 claws_spell_entry_check_backwards(
11048 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11050 gtkaspell_check_backwards(compose
->gtkaspell
);
11053 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
11055 Compose
*compose
= (Compose
*)data
;
11056 if (!compose
->gtkaspell
) {
11057 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
11061 if (gtkut_widget_has_focus(compose
->subject_entry
))
11062 claws_spell_entry_check_forwards_go(
11063 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11065 gtkaspell_check_forwards_go(compose
->gtkaspell
);
11070 *\brief Guess originating forward account from MsgInfo and several
11071 * "common preference" settings. Return NULL if no guess.
11073 static PrefsAccount
*compose_guess_forward_account_from_msginfo(MsgInfo
*msginfo
)
11075 PrefsAccount
*account
= NULL
;
11077 cm_return_val_if_fail(msginfo
, NULL
);
11078 cm_return_val_if_fail(msginfo
->folder
, NULL
);
11079 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
11081 if (msginfo
->folder
->prefs
->enable_default_account
)
11082 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
11085 account
= msginfo
->folder
->folder
->account
;
11087 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
11089 Xstrdup_a(to
, msginfo
->to
, return NULL
);
11090 extract_address(to
);
11091 account
= account_find_from_address(to
, FALSE
);
11094 if (!account
&& prefs_common
.forward_account_autosel
) {
11095 gchar cc
[BUFFSIZE
];
11096 if (!procheader_get_header_from_msginfo
11097 (msginfo
, cc
,sizeof cc
, "Cc:")) {
11098 gchar
*buf
= cc
+ strlen("Cc:");
11099 extract_address(buf
);
11100 account
= account_find_from_address(buf
, FALSE
);
11104 if (!account
&& prefs_common
.forward_account_autosel
) {
11105 gchar deliveredto
[BUFFSIZE
];
11106 if (!procheader_get_header_from_msginfo
11107 (msginfo
, deliveredto
,sizeof deliveredto
, "Delivered-To:")) {
11108 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
11109 extract_address(buf
);
11110 account
= account_find_from_address(buf
, FALSE
);
11117 gboolean
compose_close(Compose
*compose
)
11121 if (!g_mutex_trylock(compose
->mutex
)) {
11122 /* we have to wait for the (possibly deferred by auto-save)
11123 * drafting to be done, before destroying the compose under
11125 debug_print("waiting for drafting to finish...\n");
11126 compose_allow_user_actions(compose
, FALSE
);
11127 g_timeout_add (500, (GSourceFunc
) compose_close
, compose
);
11130 cm_return_val_if_fail(compose
, FALSE
);
11131 gtkut_widget_get_uposition(compose
->window
, &x
, &y
);
11132 if (!compose
->batch
) {
11133 prefs_common
.compose_x
= x
;
11134 prefs_common
.compose_y
= y
;
11136 g_mutex_unlock(compose
->mutex
);
11137 compose_destroy(compose
);
11142 * Add entry field for each address in list.
11143 * \param compose E-Mail composition object.
11144 * \param listAddress List of (formatted) E-Mail addresses.
11146 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
11149 node
= listAddress
;
11151 addr
= ( gchar
* ) node
->data
;
11152 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
11153 node
= g_list_next( node
);
11157 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
11158 guint action
, gboolean opening_multiple
)
11160 gchar
*body
= NULL
;
11161 GSList
*new_msglist
= NULL
;
11162 MsgInfo
*tmp_msginfo
= NULL
;
11163 gboolean originally_enc
= FALSE
;
11164 gboolean originally_sig
= FALSE
;
11165 Compose
*compose
= NULL
;
11166 gchar
*s_system
= NULL
;
11168 cm_return_if_fail(msgview
!= NULL
);
11170 cm_return_if_fail(msginfo_list
!= NULL
);
11172 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
) {
11173 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
11174 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
11176 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
11177 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
11178 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
11179 orig_msginfo
, mimeinfo
);
11180 if (tmp_msginfo
!= NULL
) {
11181 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
11183 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
11184 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
11185 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
11187 tmp_msginfo
->folder
= orig_msginfo
->folder
;
11188 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
11189 if (orig_msginfo
->tags
) {
11190 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
11191 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
11197 if (!opening_multiple
)
11198 body
= messageview_get_selection(msgview
);
11201 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
11202 procmsg_msginfo_free(tmp_msginfo
);
11203 g_slist_free(new_msglist
);
11205 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
11207 if (compose
&& originally_enc
) {
11208 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
11211 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
11212 compose_force_signing(compose
, compose
->account
, s_system
);
11216 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
11219 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
11222 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
11223 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
11224 GSList
*cur
= msginfo_list
;
11225 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
11226 "messages. Opening the windows "
11227 "could take some time. Do you "
11228 "want to continue?"),
11229 g_slist_length(msginfo_list
));
11230 if (g_slist_length(msginfo_list
) > 9
11231 && alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, "+" GTK_STOCK_YES
, NULL
)
11232 != G_ALERTALTERNATE
) {
11237 /* We'll open multiple compose windows */
11238 /* let the WM place the next windows */
11239 compose_force_window_origin
= FALSE
;
11240 for (; cur
; cur
= cur
->next
) {
11242 tmplist
.data
= cur
->data
;
11243 tmplist
.next
= NULL
;
11244 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
11246 compose_force_window_origin
= TRUE
;
11248 /* forwarding multiple mails as attachments is done via a
11249 * single compose window */
11250 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
11254 void compose_check_for_email_account(Compose
*compose
)
11256 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
11262 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
11263 ac
= account_get_cur_account();
11264 if (ac
->protocol
== A_NNTP
) {
11265 list
= account_get_list();
11267 for( ; list
!= NULL
; list
= g_list_next(list
)) {
11268 curr
= (PrefsAccount
*) list
->data
;
11269 if (curr
->protocol
!= A_NNTP
) {
11275 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
11280 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
11281 const gchar
*address
)
11283 GSList
*msginfo_list
= NULL
;
11284 gchar
*body
= messageview_get_selection(msgview
);
11287 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
11289 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
11290 compose_check_for_email_account(compose
);
11291 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
11292 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
11293 compose_reply_set_subject(compose
, msginfo
);
11296 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
11299 void compose_set_position(Compose
*compose
, gint pos
)
11301 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11303 gtkut_text_view_set_position(text
, pos
);
11306 gboolean
compose_search_string(Compose
*compose
,
11307 const gchar
*str
, gboolean case_sens
)
11309 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11311 return gtkut_text_view_search_string(text
, str
, case_sens
);
11314 gboolean
compose_search_string_backward(Compose
*compose
,
11315 const gchar
*str
, gboolean case_sens
)
11317 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11319 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
11322 /* allocate a msginfo structure and populate its data from a compose data structure */
11323 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
11325 MsgInfo
*newmsginfo
;
11327 gchar buf
[BUFFSIZE
];
11329 cm_return_val_if_fail( compose
!= NULL
, NULL
);
11331 newmsginfo
= procmsg_msginfo_new();
11334 get_rfc822_date(buf
, sizeof(buf
));
11335 newmsginfo
->date
= g_strdup(buf
);
11338 if (compose
->from_name
) {
11339 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
11340 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
11344 if (compose
->subject_entry
)
11345 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
11347 /* to, cc, reply-to, newsgroups */
11348 for (list
= compose
->header_list
; list
; list
= list
->next
) {
11349 gchar
*header
= gtk_editable_get_chars(
11351 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
11352 gchar
*entry
= gtk_editable_get_chars(
11353 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
11355 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
11356 if ( newmsginfo
->to
== NULL
) {
11357 newmsginfo
->to
= g_strdup(entry
);
11358 } else if (entry
&& *entry
) {
11359 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
11360 g_free(newmsginfo
->to
);
11361 newmsginfo
->to
= tmp
;
11364 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
11365 if ( newmsginfo
->cc
== NULL
) {
11366 newmsginfo
->cc
= g_strdup(entry
);
11367 } else if (entry
&& *entry
) {
11368 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
11369 g_free(newmsginfo
->cc
);
11370 newmsginfo
->cc
= tmp
;
11373 if ( strcasecmp(header
,
11374 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11375 if ( newmsginfo
->newsgroups
== NULL
) {
11376 newmsginfo
->newsgroups
= g_strdup(entry
);
11377 } else if (entry
&& *entry
) {
11378 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
11379 g_free(newmsginfo
->newsgroups
);
11380 newmsginfo
->newsgroups
= tmp
;
11388 /* other data is unset */
11394 /* update compose's dictionaries from folder dict settings */
11395 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
11396 FolderItem
*folder_item
)
11398 cm_return_if_fail(compose
!= NULL
);
11400 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
11401 FolderItemPrefs
*prefs
= folder_item
->prefs
;
11403 if (prefs
->enable_default_dictionary
)
11404 gtkaspell_change_dict(compose
->gtkaspell
,
11405 prefs
->default_dictionary
, FALSE
);
11406 if (folder_item
->prefs
->enable_default_alt_dictionary
)
11407 gtkaspell_change_alt_dict(compose
->gtkaspell
,
11408 prefs
->default_alt_dictionary
);
11409 if (prefs
->enable_default_dictionary
11410 || prefs
->enable_default_alt_dictionary
)
11411 compose_spell_menu_changed(compose
);