Add option to highlight self in mailing list threads

Use under `listing` options such as:

globally
========

  [listing]
  highlight_self = true

per-account
===========

  [accounts.work]
  root_mailbox = '[Gmail]'
  format = "imap"
  subscribed_mailboxes = ["*"]
  listing.index_style = "compact"
  listing.highlight_self = true

per-mailbox
===========

  [accounts.work.mailboxes]
  "INBOX/Lists/project-devel" = { listing.highlight_self=true }

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/360/head
Manos Pitsidianakis 2 months ago
parent 3884c0da1f
commit e2cdebe89c
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -1589,6 +1589,15 @@ Flag to show if thread entry has been selected.
.It Ic attachment_flag Ar Option<String>
Flag to show if thread entry contains attachments.
.LiteralStringValueRenders 📎\e\uu{FE0E} 📎︎ \" default value
.It Ic highlight_self_flag Ar Option<String>
Flag to show if any thread entry contains your address as a receiver.
Useful to make mailing list threads that CC you stand out.
.Pq Em "✸" \" default value
.It Ic highlight_self Ar boolean
Show
.Ic highlight_self_flag
or not.
.Pq Em false \" default value
.It Ic thread_subject_pack Ar boolean
Should threads with differentiating Subjects show a list of those subjects on
the entry title?

@ -62,7 +62,7 @@ use std::{
use indexmap::IndexMap;
use melib::{
conf::{AccountSettings, MailboxConf, ToggleFlag},
conf::{AccountSettings, ActionFlag, MailboxConf, ToggleFlag},
error::*,
};
use pager::PagerSettings;
@ -679,16 +679,29 @@ mod default_vals {
None
}
pub(in crate::conf) fn internal_value_false<T: std::convert::From<super::ToggleFlag>>() -> T {
super::ToggleFlag::InternalVal(false).into()
pub(in crate::conf) fn internal_value_false<T: std::convert::From<melib::conf::ToggleFlag>>(
) -> T {
melib::conf::ToggleFlag::InternalVal(false).into()
}
pub(in crate::conf) fn internal_value_true<T: std::convert::From<super::ToggleFlag>>() -> T {
super::ToggleFlag::InternalVal(true).into()
pub(in crate::conf) fn internal_value_true<T: std::convert::From<melib::conf::ToggleFlag>>() -> T
{
melib::conf::ToggleFlag::InternalVal(true).into()
}
pub(in crate::conf) fn action_internal_value_false<T: std::convert::From<melib::ActionFlag>>(
) -> T {
melib::conf::ActionFlag::InternalVal(false).into()
}
pub(in crate::conf) fn ask<T: std::convert::From<super::ToggleFlag>>() -> T {
super::ToggleFlag::Ask.into()
//pub(in crate::conf) fn action_internal_value_true<
// T: std::convert::From<melib::conf::ActionFlag>,
//>() -> T {
// melib::conf::ActionFlag::InternalVal(true).into()
//}
pub(in crate::conf) fn ask<T: std::convert::From<melib::conf::ActionFlag>>() -> T {
melib::conf::ActionFlag::Ask.into()
}
}
@ -1041,6 +1054,7 @@ mod dotaddressable {
impl DotAddressable for melib::LogLevel {}
impl DotAddressable for PathBuf {}
impl DotAddressable for ToggleFlag {}
impl DotAddressable for ActionFlag {}
impl DotAddressable for SearchBackend {}
impl DotAddressable for melib::SpecialUsageMailbox {}
impl<T: DotAddressable> DotAddressable for Option<T> {}

@ -22,7 +22,7 @@
//! Configuration for composing email.
use std::collections::HashMap;
use melib::{email::HeaderName, ToggleFlag};
use melib::{conf::ActionFlag, email::HeaderName};
use serde::{de, Deserialize, Deserializer};
use super::{
@ -92,7 +92,7 @@ pub struct ComposingSettings {
/// Forward emails as attachment? (Alternative is inline)
/// Default: ask
#[serde(default = "ask", alias = "forward-as-attachment")]
pub forward_as_attachment: ToggleFlag,
pub forward_as_attachment: ActionFlag,
/// Alternative lists of reply prefixes (etc. ["Re:", "RE:", ...]) to strip
/// Default: `["Re:", "RE:", "Fwd:", "Fw:", "回复:", "回覆:", "SV:", "Sv:",
/// "VS:", "Antw:", "Doorst:", "VS:", "VL:", "REF:", "TR:", "TR:", "AW:",
@ -126,7 +126,7 @@ impl Default for ComposingSettings {
wrap_header_preamble: None,
attribution_format_string: None,
attribution_use_posix_locale: true,
forward_as_attachment: ToggleFlag::Ask,
forward_as_attachment: ActionFlag::Ask,
reply_prefix_list_to_strip: None,
reply_prefix: res(),
custom_compose_hooks: vec![],

@ -19,7 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use melib::{search::Query, Error, Result};
use melib::{search::Query, Error, Result, ToggleFlag};
use super::{default_vals::*, DotAddressable, IndexStyle};
@ -130,6 +130,17 @@ pub struct ListingSettings {
#[serde(default)]
pub attachment_flag: Option<String>,
/// Flag to show if any thread entry contains your address as a receiver.
/// Useful to make mailing list threads that CC you stand out.
/// Default: "✸"
#[serde(default)]
pub highlight_self_flag: Option<String>,
/// Show `highlight_self_flag` or not.
/// Default: false
#[serde(default)]
pub highlight_self: ToggleFlag,
/// Should threads with different Subjects show a list of those
/// subjects on the entry title?
/// Default: "true"
@ -185,6 +196,8 @@ impl Default for ListingSettings {
thread_snoozed_flag: None,
selected_flag: None,
attachment_flag: None,
highlight_self_flag: None,
highlight_self: ToggleFlag::Unset,
thread_subject_pack: true,
threaded_repeat_identical_from_values: false,
relative_menu_indices: true,
@ -224,6 +237,8 @@ impl DotAddressable for ListingSettings {
"thread_snoozed_flag" => self.thread_snoozed_flag.lookup(field, tail),
"selected_flag" => self.selected_flag.lookup(field, tail),
"attachment_flag" => self.attachment_flag.lookup(field, tail),
"highlight_self_flag" => self.highlight_self_flag.lookup(field, tail),
"highlight_self" => self.highlight_self.lookup(field, tail),
"thread_subject_pack" => self.thread_subject_pack.lookup(field, tail),
"threaded_repeat_identical_from_values" => self
.threaded_repeat_identical_from_values

@ -29,15 +29,15 @@ use melib::HeaderName;
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct PagerSettingsOverride { # [doc = " Number of context lines when going to next page."] # [doc = " Default: 0"] # [serde (alias = "pager-context")] # [serde (default)] pub pager_context : Option < usize > , # [doc = " Stop at the end instead of displaying next mail."] # [doc = " Default: false"] # [serde (alias = "pager-stop")] # [serde (default)] pub pager_stop : Option < bool > , # [doc = " Always show headers when scrolling."] # [doc = " Default: true"] # [serde (alias = "sticky-headers" , alias = "headers-sticky" , alias = "headers_sticky")] # [serde (default)] pub sticky_headers : Option < bool > , # [doc = " The height of the pager in mail view, in percent."] # [doc = " Default: 80"] # [serde (alias = "pager-ratio")] # [serde (default)] pub pager_ratio : Option < usize > , # [doc = " A command to pipe mail output through for viewing in pager."] # [doc = " Default: None"] # [serde (deserialize_with = "non_empty_opt_string")] # [serde (default)] pub filter : Option < Option < String > > , # [doc = " A command to pipe html output before displaying it in a pager"] # [doc = " Default: None"] # [serde (deserialize_with = "non_empty_opt_string" , alias = "html-filter")] # [serde (default)] pub html_filter : Option < Option < String > > , # [doc = " Respect \"format=flowed\""] # [doc = " Default: true"] # [serde (alias = "format-flowed")] # [serde (default)] pub format_flowed : Option < bool > , # [doc = " Split long lines that would overflow on the x axis."] # [doc = " Default: true"] # [serde (alias = "split-long-lines")] # [serde (default)] pub split_long_lines : Option < bool > , # [doc = " Minimum text width in columns."] # [doc = " Default: 80"] # [serde (alias = "minimum-width")] # [serde (default)] pub minimum_width : Option < usize > , # [doc = " Choose `text/html` alternative if `text/plain` is empty in"] # [doc = " `multipart/alternative` attachments."] # [doc = " Default: true"] # [serde (alias = "auto-choose-multipart-alternative")] # [serde (default)] pub auto_choose_multipart_alternative : Option < ToggleFlag > , # [doc = " Show Date: in my timezone"] # [doc = " Default: true"] # [serde (alias = "show-date-in-my-timezone")] # [serde (default)] pub show_date_in_my_timezone : Option < ToggleFlag > , # [doc = " A command to launch URLs with. The URL will be given as the first"] # [doc = " argument of the command. Default: None"] # [serde (deserialize_with = "non_empty_opt_string")] # [serde (default)] pub url_launcher : Option < Option < String > > , # [doc = " A command to open html files."] # [doc = " Default: None"] # [serde (deserialize_with = "non_empty_opt_string" , alias = "html-open")] # [serde (default)] pub html_open : Option < Option < String > > , # [doc = " Extra headers to display, if present, in the default header preamble."] # [doc = " Default: []"] # [serde (alias = "show-extra-headers")] # [serde (default)] pub show_extra_headers : Option < Vec < String > > } impl Default for PagerSettingsOverride { fn default () -> Self { Self { pager_context : None , pager_stop : None , sticky_headers : None , pager_ratio : None , filter : None , html_filter : None , format_flowed : None , split_long_lines : None , minimum_width : None , auto_choose_multipart_alternative : None , show_date_in_my_timezone : None , url_launcher : None , html_open : None , show_extra_headers : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ListingSettingsOverride { # [doc = " Number of context lines when going to next page."] # [doc = " Default: 0"] # [serde (alias = "context-lines")] # [serde (default)] pub context_lines : Option < usize > , # [doc = " Show auto-hiding scrollbar in accounts sidebar menu."] # [doc = " Default: True"] # [serde (default)] pub show_menu_scrollbar : Option < bool > , # [doc = " Datetime formatting passed verbatim to strftime(3)."] # [doc = " Default: %Y-%m-%d %T"] # [serde (alias = "datetime-fmt")] # [serde (default)] pub datetime_fmt : Option < Option < String > > , # [doc = " Show recent dates as `X {minutes,hours,days} ago`, up to 7 days."] # [doc = " Default: true"] # [serde (alias = "recent-dates")] # [serde (default)] pub recent_dates : Option < bool > , # [doc = " Show only envelopes that match this query"] # [doc = " Default: None"] # [serde (default)] pub filter : Option < Option < Query > > , # [serde (alias = "index-style")] # [serde (default)] pub index_style : Option < IndexStyle > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_has_sibling : Option < Option < String > > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_no_sibling : Option < Option < String > > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_has_sibling_leaf : Option < Option < String > > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_no_sibling_leaf : Option < Option < String > > , # [doc = " Default: ' '"] # [serde (default)] pub sidebar_divider : Option < char > , # [doc = " Default: 90"] # [serde (default)] pub sidebar_ratio : Option < usize > , # [doc = " Flag to show if thread entry contains unseen mail."] # [doc = " Default: \"●\""] # [serde (default)] pub unseen_flag : Option < Option < String > > , # [doc = " Flag to show if thread has been snoozed."] # [doc = " Default: \"💤\""] # [serde (default)] pub thread_snoozed_flag : Option < Option < String > > , # [doc = " Flag to show if thread entry has been selected."] # [doc = " Default: \"☑\u{fe0f}\""] # [serde (default)] pub selected_flag : Option < Option < String > > , # [doc = " Flag to show if thread entry contains attachments."] # [doc = " Default: \"📎\""] # [serde (default)] pub attachment_flag : Option < Option < String > > , # [doc = " Should threads with different Subjects show a list of those"] # [doc = " subjects on the entry title?"] # [doc = " Default: \"true\""] # [serde (default)] pub thread_subject_pack : Option < bool > , # [doc = " In threaded listing style, repeat identical From column values within a"] # [doc = " thread. Not repeating adds empty space in the From column which"] # [doc = " might result in less visual clutter."] # [doc = " Default: \"false\""] # [serde (default)] pub threaded_repeat_identical_from_values : Option < bool > , # [doc = " Show relative indices in menu mailboxes to quickly help with jumping to"] # [doc = " them. Default: \"true\""] # [serde (alias = "relative-menu-indices")] # [serde (default)] pub relative_menu_indices : Option < bool > , # [doc = " Show relative indices in listings to quickly help with jumping to"] # [doc = " them. Default: \"true\""] # [serde (alias = "relative-list-indices")] # [serde (default)] pub relative_list_indices : Option < bool > , # [doc = " Hide sidebar on launch. Default: \"false\""] # [serde (alias = "hide-sidebar-on-launch")] # [serde (default)] pub hide_sidebar_on_launch : Option < bool > } impl Default for ListingSettingsOverride { fn default () -> Self { Self { context_lines : None , show_menu_scrollbar : None , datetime_fmt : None , recent_dates : None , filter : None , index_style : None , sidebar_mailbox_tree_has_sibling : None , sidebar_mailbox_tree_no_sibling : None , sidebar_mailbox_tree_has_sibling_leaf : None , sidebar_mailbox_tree_no_sibling_leaf : None , sidebar_divider : None , sidebar_ratio : None , unseen_flag : None , thread_snoozed_flag : None , selected_flag : None , attachment_flag : None , thread_subject_pack : None , threaded_repeat_identical_from_values : None , relative_menu_indices : None , relative_list_indices : None , hide_sidebar_on_launch : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ListingSettingsOverride { # [doc = " Number of context lines when going to next page."] # [doc = " Default: 0"] # [serde (alias = "context-lines")] # [serde (default)] pub context_lines : Option < usize > , # [doc = " Show auto-hiding scrollbar in accounts sidebar menu."] # [doc = " Default: True"] # [serde (default)] pub show_menu_scrollbar : Option < bool > , # [doc = " Datetime formatting passed verbatim to strftime(3)."] # [doc = " Default: %Y-%m-%d %T"] # [serde (alias = "datetime-fmt")] # [serde (default)] pub datetime_fmt : Option < Option < String > > , # [doc = " Show recent dates as `X {minutes,hours,days} ago`, up to 7 days."] # [doc = " Default: true"] # [serde (alias = "recent-dates")] # [serde (default)] pub recent_dates : Option < bool > , # [doc = " Show only envelopes that match this query"] # [doc = " Default: None"] # [serde (default)] pub filter : Option < Option < Query > > , # [serde (alias = "index-style")] # [serde (default)] pub index_style : Option < IndexStyle > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_has_sibling : Option < Option < String > > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_no_sibling : Option < Option < String > > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_has_sibling_leaf : Option < Option < String > > , # [doc = " Default: \" \""] # [serde (default)] pub sidebar_mailbox_tree_no_sibling_leaf : Option < Option < String > > , # [doc = " Default: ' '"] # [serde (default)] pub sidebar_divider : Option < char > , # [doc = " Default: 90"] # [serde (default)] pub sidebar_ratio : Option < usize > , # [doc = " Flag to show if thread entry contains unseen mail."] # [doc = " Default: \"●\""] # [serde (default)] pub unseen_flag : Option < Option < String > > , # [doc = " Flag to show if thread has been snoozed."] # [doc = " Default: \"💤\""] # [serde (default)] pub thread_snoozed_flag : Option < Option < String > > , # [doc = " Flag to show if thread entry has been selected."] # [doc = " Default: \"☑\u{fe0f}\""] # [serde (default)] pub selected_flag : Option < Option < String > > , # [doc = " Flag to show if thread entry contains attachments."] # [doc = " Default: \"📎\""] # [serde (default)] pub attachment_flag : Option < Option < String > > , # [doc = " Flag to show if any thread entry contains your address as a receiver."] # [doc = " Useful to make mailing list threads that CC you stand out."] # [doc = " Default: \"✸\""] # [serde (default)] pub highlight_self_flag : Option < Option < String > > , # [doc = " Show `highlight_self_flag` or not."] # [doc = " Default: false"] # [serde (default)] pub highlight_self : Option < ToggleFlag > , # [doc = " Should threads with different Subjects show a list of those"] # [doc = " subjects on the entry title?"] # [doc = " Default: \"true\""] # [serde (default)] pub thread_subject_pack : Option < bool > , # [doc = " In threaded listing style, repeat identical From column values within a"] # [doc = " thread. Not repeating adds empty space in the From column which"] # [doc = " might result in less visual clutter."] # [doc = " Default: \"false\""] # [serde (default)] pub threaded_repeat_identical_from_values : Option < bool > , # [doc = " Show relative indices in menu mailboxes to quickly help with jumping to"] # [doc = " them. Default: \"true\""] # [serde (alias = "relative-menu-indices")] # [serde (default)] pub relative_menu_indices : Option < bool > , # [doc = " Show relative indices in listings to quickly help with jumping to"] # [doc = " them. Default: \"true\""] # [serde (alias = "relative-list-indices")] # [serde (default)] pub relative_list_indices : Option < bool > , # [doc = " Hide sidebar on launch. Default: \"false\""] # [serde (alias = "hide-sidebar-on-launch")] # [serde (default)] pub hide_sidebar_on_launch : Option < bool > } impl Default for ListingSettingsOverride { fn default () -> Self { Self { context_lines : None , show_menu_scrollbar : None , datetime_fmt : None , recent_dates : None , filter : None , index_style : None , sidebar_mailbox_tree_has_sibling : None , sidebar_mailbox_tree_no_sibling : None , sidebar_mailbox_tree_has_sibling_leaf : None , sidebar_mailbox_tree_no_sibling_leaf : None , sidebar_divider : None , sidebar_ratio : None , unseen_flag : None , thread_snoozed_flag : None , selected_flag : None , attachment_flag : None , highlight_self_flag : None , highlight_self : None , thread_subject_pack : None , threaded_repeat_identical_from_values : None , relative_menu_indices : None , relative_list_indices : None , hide_sidebar_on_launch : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct NotificationsSettingsOverride { # [doc = " Enable notifications."] # [doc = " Default: True"] # [serde (default)] pub enable : Option < bool > , # [doc = " A command to pipe notifications through."] # [doc = " Default: None"] # [serde (default)] pub script : Option < Option < String > > , # [doc = " A command to pipe new mail notifications through (preferred over"] # [doc = " `script`). Default: None"] # [serde (default)] pub new_mail_script : Option < Option < String > > , # [doc = " A file location which has its size changed when new mail arrives (max"] # [doc = " 128 bytes). Can be used to trigger new mail notifications eg with"] # [doc = " `xbiff(1)`. Default: None"] # [serde (alias = "xbiff-file-path")] # [serde (default)] pub xbiff_file_path : Option < Option < String > > , # [serde (alias = "play-sound")] # [serde (default)] pub play_sound : Option < ToggleFlag > , # [serde (alias = "sound-file")] # [serde (default)] pub sound_file : Option < Option < String > > } impl Default for NotificationsSettingsOverride { fn default () -> Self { Self { enable : None , script : None , new_mail_script : None , xbiff_file_path : None , play_sound : None , sound_file : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ShortcutsOverride { # [serde (default)] pub general : Option < GeneralShortcuts > , # [serde (default)] pub listing : Option < ListingShortcuts > , # [serde (default)] pub composing : Option < ComposingShortcuts > , # [serde (alias = "contact-list")] # [serde (default)] pub contact_list : Option < ContactListShortcuts > , # [serde (alias = "envelope-view")] # [serde (default)] pub envelope_view : Option < EnvelopeViewShortcuts > , # [serde (alias = "thread-view")] # [serde (default)] pub thread_view : Option < ThreadViewShortcuts > , # [serde (default)] pub pager : Option < PagerShortcuts > } impl Default for ShortcutsOverride { fn default () -> Self { Self { general : None , listing : None , composing : None , contact_list : None , envelope_view : None , thread_view : None , pager : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ComposingSettingsOverride { # [doc = " A command to pipe new emails to"] # [doc = " Required"] # [serde (default)] pub send_mail : Option < SendMail > , # [doc = " Command to launch editor. Can have arguments. Draft filename is given as"] # [doc = " the last argument. If it's missing, the environment variable $EDITOR is"] # [doc = " looked up."] # [serde (alias = "editor-command" , alias = "editor-cmd" , alias = "editor_cmd")] # [serde (default)] pub editor_command : Option < Option < String > > , # [doc = " Embedded editor (for terminal interfaces) instead of forking and"] # [doc = " waiting."] # [serde (alias = "embed")] # [serde (default)] pub embedded_pty : Option < bool > , # [doc = " Set \"format=flowed\" in plain text attachments."] # [doc = " Default: true"] # [serde (alias = "format-flowed")] # [serde (default)] pub format_flowed : Option < bool > , # [doc = " Set User-Agent"] # [doc = " Default: empty"] # [serde (alias = "insert_user_agent")] # [serde (default)] pub insert_user_agent : Option < bool > , # [doc = " Set default header values for new drafts"] # [doc = " Default: empty"] # [serde (alias = "default-header-values")] # [serde (default)] pub default_header_values : Option < HashMap < HeaderName , String > > , # [doc = " Wrap header preamble when editing a draft in an editor. This allows you"] # [doc = " to write non-plain text email without the preamble creating syntax"] # [doc = " errors. They are stripped when you return from the editor. The"] # [doc = " values should be a two element array of strings, a prefix and suffix."] # [doc = " Default: None"] # [serde (alias = "wrap-header-preamble")] # [serde (default)] pub wrap_header_preamble : Option < Option < (String , String) > > , # [doc = " Store sent mail after successful submission. This setting is meant to be"] # [doc = " disabled for non-standard behaviour in gmail, which auto-saves sent"] # [doc = " mail on its own. Default: true"] # [serde (default)] pub store_sent_mail : Option < bool > , # [doc = " The attribution line appears above the quoted reply text."] # [doc = " The format specifiers for the replied address are:"] # [doc = " - `%+f` — the sender's name and email address."] # [doc = " - `%+n` — the sender's name (or email address, if no name is included)."] # [doc = " - `%+a` — the sender's email address."] # [doc = " The format string is passed to strftime(3) with the replied envelope's"] # [doc = " date. Default: \"On %a, %0e %b %Y %H:%M, %+f wrote:%n\""] # [serde (default)] pub attribution_format_string : Option < Option < String > > , # [doc = " Whether the strftime call for the attribution string uses the POSIX"] # [doc = " locale instead of the user's active locale"] # [doc = " Default: true"] # [serde (default)] pub attribution_use_posix_locale : Option < bool > , # [doc = " Forward emails as attachment? (Alternative is inline)"] # [doc = " Default: ask"] # [serde (alias = "forward-as-attachment")] # [serde (default)] pub forward_as_attachment : Option < ToggleFlag > , # [doc = " Alternative lists of reply prefixes (etc. [\"Re:\", \"RE:\", ...]) to strip"] # [doc = " Default: `[\"Re:\", \"RE:\", \"Fwd:\", \"Fw:\", \"回复:\", \"回覆:\", \"SV:\", \"Sv:\","] # [doc = " \"VS:\", \"Antw:\", \"Doorst:\", \"VS:\", \"VL:\", \"REF:\", \"TR:\", \"TR:\", \"AW:\","] # [doc = " \"WG:\", \"ΑΠ:\", \"Απ:\", \"απ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"ΣΧΕΤ:\", \"Σχετ:\","] # [doc = " \"σχετ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"Vá:\", \"Továbbítás:\", \"R:\", \"I:\","] # [doc = " \"RIF:\", \"FS:\", \"BLS:\", \"TRS:\", \"VS:\", \"VB:\", \"RV:\", \"RES:\", \"Res\","] # [doc = " \"ENC:\", \"Odp:\", \"PD:\", \"YNT:\", \"İLT:\", \"ATB:\", \"YML:\"]`"] # [serde (alias = "reply-prefix-list-to-strip")] # [serde (default)] pub reply_prefix_list_to_strip : Option < Option < Vec < String > > > , # [doc = " The prefix to use in reply subjects. The de facto prefix is \"Re:\"."] # [serde (alias = "reply-prefix")] # [serde (default)] pub reply_prefix : Option < String > , # [doc = " Custom `compose-hooks`."] # [serde (alias = "custom-compose-hooks")] # [serde (default)] pub custom_compose_hooks : Option < Vec < ComposeHook > > , # [doc = " Disabled `compose-hooks`."] # [serde (alias = "disabled-compose-hooks")] # [serde (default)] pub disabled_compose_hooks : Option < Vec < String > > } impl Default for ComposingSettingsOverride { fn default () -> Self { Self { send_mail : None , editor_command : None , embedded_pty : None , format_flowed : None , insert_user_agent : None , default_header_values : None , wrap_header_preamble : None , store_sent_mail : None , attribution_format_string : None , attribution_use_posix_locale : None , forward_as_attachment : None , reply_prefix_list_to_strip : None , reply_prefix : None , custom_compose_hooks : None , disabled_compose_hooks : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ComposingSettingsOverride { # [doc = " A command to pipe new emails to"] # [doc = " Required"] # [serde (default)] pub send_mail : Option < SendMail > , # [doc = " Command to launch editor. Can have arguments. Draft filename is given as"] # [doc = " the last argument. If it's missing, the environment variable $EDITOR is"] # [doc = " looked up."] # [serde (alias = "editor-command" , alias = "editor-cmd" , alias = "editor_cmd")] # [serde (default)] pub editor_command : Option < Option < String > > , # [doc = " Embedded editor (for terminal interfaces) instead of forking and"] # [doc = " waiting."] # [serde (alias = "embed")] # [serde (default)] pub embedded_pty : Option < bool > , # [doc = " Set \"format=flowed\" in plain text attachments."] # [doc = " Default: true"] # [serde (alias = "format-flowed")] # [serde (default)] pub format_flowed : Option < bool > , # [doc = " Set User-Agent"] # [doc = " Default: empty"] # [serde (alias = "insert_user_agent")] # [serde (default)] pub insert_user_agent : Option < bool > , # [doc = " Set default header values for new drafts"] # [doc = " Default: empty"] # [serde (alias = "default-header-values")] # [serde (default)] pub default_header_values : Option < HashMap < HeaderName , String > > , # [doc = " Wrap header preamble when editing a draft in an editor. This allows you"] # [doc = " to write non-plain text email without the preamble creating syntax"] # [doc = " errors. They are stripped when you return from the editor. The"] # [doc = " values should be a two element array of strings, a prefix and suffix."] # [doc = " Default: None"] # [serde (alias = "wrap-header-preamble")] # [serde (default)] pub wrap_header_preamble : Option < Option < (String , String) > > , # [doc = " Store sent mail after successful submission. This setting is meant to be"] # [doc = " disabled for non-standard behaviour in gmail, which auto-saves sent"] # [doc = " mail on its own. Default: true"] # [serde (default)] pub store_sent_mail : Option < bool > , # [doc = " The attribution line appears above the quoted reply text."] # [doc = " The format specifiers for the replied address are:"] # [doc = " - `%+f` — the sender's name and email address."] # [doc = " - `%+n` — the sender's name (or email address, if no name is included)."] # [doc = " - `%+a` — the sender's email address."] # [doc = " The format string is passed to strftime(3) with the replied envelope's"] # [doc = " date. Default: \"On %a, %0e %b %Y %H:%M, %+f wrote:%n\""] # [serde (default)] pub attribution_format_string : Option < Option < String > > , # [doc = " Whether the strftime call for the attribution string uses the POSIX"] # [doc = " locale instead of the user's active locale"] # [doc = " Default: true"] # [serde (default)] pub attribution_use_posix_locale : Option < bool > , # [doc = " Forward emails as attachment? (Alternative is inline)"] # [doc = " Default: ask"] # [serde (alias = "forward-as-attachment")] # [serde (default)] pub forward_as_attachment : Option < ActionFlag > , # [doc = " Alternative lists of reply prefixes (etc. [\"Re:\", \"RE:\", ...]) to strip"] # [doc = " Default: `[\"Re:\", \"RE:\", \"Fwd:\", \"Fw:\", \"回复:\", \"回覆:\", \"SV:\", \"Sv:\","] # [doc = " \"VS:\", \"Antw:\", \"Doorst:\", \"VS:\", \"VL:\", \"REF:\", \"TR:\", \"TR:\", \"AW:\","] # [doc = " \"WG:\", \"ΑΠ:\", \"Απ:\", \"απ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"ΣΧΕΤ:\", \"Σχετ:\","] # [doc = " \"σχετ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"Vá:\", \"Továbbítás:\", \"R:\", \"I:\","] # [doc = " \"RIF:\", \"FS:\", \"BLS:\", \"TRS:\", \"VS:\", \"VB:\", \"RV:\", \"RES:\", \"Res\","] # [doc = " \"ENC:\", \"Odp:\", \"PD:\", \"YNT:\", \"İLT:\", \"ATB:\", \"YML:\"]`"] # [serde (alias = "reply-prefix-list-to-strip")] # [serde (default)] pub reply_prefix_list_to_strip : Option < Option < Vec < String > > > , # [doc = " The prefix to use in reply subjects. The de facto prefix is \"Re:\"."] # [serde (alias = "reply-prefix")] # [serde (default)] pub reply_prefix : Option < String > , # [doc = " Custom `compose-hooks`."] # [serde (alias = "custom-compose-hooks")] # [serde (default)] pub custom_compose_hooks : Option < Vec < ComposeHook > > , # [doc = " Disabled `compose-hooks`."] # [serde (alias = "disabled-compose-hooks")] # [serde (default)] pub disabled_compose_hooks : Option < Vec < String > > } impl Default for ComposingSettingsOverride { fn default () -> Self { Self { send_mail : None , editor_command : None , embedded_pty : None , format_flowed : None , insert_user_agent : None , default_header_values : None , wrap_header_preamble : None , store_sent_mail : None , attribution_format_string : None , attribution_use_posix_locale : None , forward_as_attachment : None , reply_prefix_list_to_strip : None , reply_prefix : None , custom_compose_hooks : None , disabled_compose_hooks : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct TagsSettingsOverride { # [serde (deserialize_with = "tag_color_de")] # [serde (default)] pub colors : Option < HashMap < TagHash , Color > > , # [serde (deserialize_with = "tag_set_de" , alias = "ignore-tags")] # [serde (default)] pub ignore_tags : Option < HashSet < TagHash > > } impl Default for TagsSettingsOverride { fn default () -> Self { Self { colors : None , ignore_tags : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct PGPSettingsOverride { # [doc = " auto verify signed e-mail according to RFC3156"] # [doc = " Default: true"] # [serde (alias = "auto-verify-signatures")] # [serde (default)] pub auto_verify_signatures : Option < bool > , # [doc = " auto decrypt encrypted e-mail"] # [doc = " Default: true"] # [serde (alias = "auto-decrypt")] # [serde (default)] pub auto_decrypt : Option < bool > , # [doc = " always sign sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-sign")] # [serde (default)] pub auto_sign : Option < bool > , # [doc = " Auto encrypt sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-encrypt")] # [serde (default)] pub auto_encrypt : Option < bool > , # [doc = " Default: None"] # [serde (alias = "sign-key")] # [serde (default)] pub sign_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "decrypt-key")] # [serde (default)] pub decrypt_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "encrypt-key")] # [serde (default)] pub encrypt_key : Option < Option < String > > , # [doc = " Allow remote lookups"] # [doc = " Default: None"] # [serde (alias = "allow-remote-lookups")] # [serde (default)] pub allow_remote_lookup : Option < ToggleFlag > , # [doc = " Remote lookup mechanisms."] # [doc = " Default: \"local,wkd\""] # [cfg_attr (feature = "gpgme" , serde (alias = "remote-lookup-mechanisms"))] # [cfg (feature = "gpgme")] # [serde (default)] pub remote_lookup_mechanisms : Option < melib :: gpgme :: LocateKey > , # [cfg (not (feature = "gpgme"))] # [cfg_attr (not (feature = "gpgme") , serde (alias = "remote-lookup-mechanisms"))] # [serde (default)] pub remote_lookup_mechanisms : Option < String > } impl Default for PGPSettingsOverride { fn default () -> Self { Self { auto_verify_signatures : None , auto_decrypt : None , auto_sign : None , auto_encrypt : None , sign_key : None , decrypt_key : None , encrypt_key : None , allow_remote_lookup : None , remote_lookup_mechanisms : None } } }
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct PGPSettingsOverride { # [doc = " auto verify signed e-mail according to RFC3156"] # [doc = " Default: true"] # [serde (alias = "auto-verify-signatures")] # [serde (default)] pub auto_verify_signatures : Option < ActionFlag > , # [doc = " auto decrypt encrypted e-mail"] # [doc = " Default: true"] # [serde (alias = "auto-decrypt")] # [serde (default)] pub auto_decrypt : Option < ActionFlag > , # [doc = " always sign sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-sign")] # [serde (default)] pub auto_sign : Option < ActionFlag > , # [doc = " Auto encrypt sent e-mail"] # [doc = " Default: false"] # [serde (alias = "auto-encrypt")] # [serde (default)] pub auto_encrypt : Option < ActionFlag > , # [doc = " Default: None"] # [serde (alias = "sign-key")] # [serde (default)] pub sign_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "decrypt-key")] # [serde (default)] pub decrypt_key : Option < Option < String > > , # [doc = " Default: None"] # [serde (alias = "encrypt-key")] # [serde (default)] pub encrypt_key : Option < Option < String > > , # [doc = " Allow remote lookups"] # [doc = " Default: False"] # [serde (alias = "allow-remote-lookups")] # [serde (default)] pub allow_remote_lookup : Option < ActionFlag > , # [doc = " Remote lookup mechanisms."] # [doc = " Default: \"local,wkd\""] # [cfg_attr (feature = "gpgme" , serde (alias = "remote-lookup-mechanisms"))] # [cfg (feature = "gpgme")] # [serde (default)] pub remote_lookup_mechanisms : Option < melib :: gpgme :: LocateKey > , # [cfg (not (feature = "gpgme"))] # [cfg_attr (not (feature = "gpgme") , serde (alias = "remote-lookup-mechanisms"))] # [serde (default)] pub remote_lookup_mechanisms : Option < String > } impl Default for PGPSettingsOverride { fn default () -> Self { Self { auto_verify_signatures : None , auto_decrypt : None , auto_sign : None , auto_encrypt : None , sign_key : None , decrypt_key : None , encrypt_key : None , allow_remote_lookup : None , remote_lookup_mechanisms : None } } }

@ -19,7 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use melib::conf::ToggleFlag;
use melib::conf::ActionFlag;
use super::default_vals::*;
@ -30,22 +30,22 @@ pub struct PGPSettings {
/// auto verify signed e-mail according to RFC3156
/// Default: true
#[serde(default = "true_val", alias = "auto-verify-signatures")]
pub auto_verify_signatures: bool,
pub auto_verify_signatures: ActionFlag,
/// auto decrypt encrypted e-mail
/// Default: true
#[serde(default = "true_val", alias = "auto-decrypt")]
pub auto_decrypt: bool,
pub auto_decrypt: ActionFlag,
/// always sign sent e-mail
/// Default: false
#[serde(default = "false_val", alias = "auto-sign")]
pub auto_sign: bool,
pub auto_sign: ActionFlag,
/// Auto encrypt sent e-mail
/// Default: false
#[serde(default = "false_val", alias = "auto-encrypt")]
pub auto_encrypt: bool,
pub auto_encrypt: ActionFlag,
// https://tools.ietf.org/html/rfc4880#section-12.2
/// Default: None
@ -61,9 +61,12 @@ pub struct PGPSettings {
pub encrypt_key: Option<String>,
/// Allow remote lookups
/// Default: None
#[serde(default = "internal_value_false", alias = "allow-remote-lookups")]
pub allow_remote_lookup: ToggleFlag,
/// Default: False
#[serde(
default = "action_internal_value_false",
alias = "allow-remote-lookups"
)]
pub allow_remote_lookup: ActionFlag,
/// Remote lookup mechanisms.
/// Default: "local,wkd"
@ -92,14 +95,14 @@ fn default_lookup_mechanism() -> melib::gpgme::LocateKey {
impl Default for PGPSettings {
fn default() -> Self {
Self {
auto_verify_signatures: true,
auto_decrypt: true,
auto_sign: false,
auto_encrypt: false,
auto_verify_signatures: true.into(),
auto_decrypt: true.into(),
auto_sign: false.into(),
auto_encrypt: false.into(),
sign_key: None,
decrypt_key: None,
encrypt_key: None,
allow_remote_lookup: internal_value_false::<ToggleFlag>(),
allow_remote_lookup: action_internal_value_false::<ActionFlag>(),
#[cfg(feature = "gpgme")]
remote_lookup_mechanisms: default_lookup_mechanism(),
#[cfg(not(feature = "gpgme"))]

@ -62,6 +62,14 @@
clippy::cognitive_complexity,
clippy::manual_clamp
)]
/* Source Code Annotation Tags:
*
* Global tags (in tagref format <https://github.com/stepchowfun/tagref>) for source code
* annotation:
*
* - tags from melib/src/lib.rs.
* - [tag:hardcoded_color_value] Replace hardcoded color values with user configurable ones.
*/
//!
//! This crate contains the frontend stuff of the application. The application
@ -93,8 +101,8 @@ static GLOBAL: System = System;
pub extern crate melib;
pub use melib::{
error::*, log, AccountHash, Envelope, EnvelopeHash, EnvelopeRef, Flag, LogLevel, Mail, Mailbox,
MailboxHash, ThreadHash, ToggleFlag,
error::*, log, AccountHash, ActionFlag, Envelope, EnvelopeHash, EnvelopeRef, Flag, LogLevel,
Mail, Mailbox, MailboxHash, ThreadHash, ToggleFlag,
};
pub mod args;

@ -387,15 +387,11 @@ impl Composer {
to.extend(envelope.from().iter().cloned());
}
to.extend(envelope.to().iter().cloned());
if let Ok(ours) = TryInto::<Address>::try_into(
context.accounts[&coordinates.0]
.settings
.account()
.make_display_name()
.as_str(),
) {
to.remove(&ours);
}
let ours = context.accounts[&coordinates.0]
.settings
.account()
.make_display_name();
to.remove(&ours);
ret.draft.set_header(HeaderName::TO, {
let mut ret: String =
to.into_iter()
@ -671,7 +667,7 @@ To: {}
}
};
(addr, desc)
(addr.to_string(), desc)
})
.map(AutoCompleteEntry::from)
.collect::<Vec<AutoCompleteEntry>>()
@ -688,7 +684,12 @@ To: {}
let theme_default = crate::conf::value(context, "theme_default");
grid.clear_area(area, theme_default);
#[cfg(feature = "gpgme")]
if self.gpg_state.sign_mail.is_true() {
if self
.gpg_state
.sign_mail
.unwrap_or(ActionFlag::False)
.is_true()
{
let key_list = self
.gpg_state
.sign_keys
@ -731,7 +732,12 @@ To: {}
);
}
#[cfg(feature = "gpgme")]
if self.gpg_state.encrypt_mail.is_true() {
if self
.gpg_state
.encrypt_mail
.unwrap_or(ActionFlag::False)
.is_true()
{
let key_list = self
.gpg_state
.encrypt_keys
@ -867,10 +873,9 @@ impl Component for Composer {
if !self.initialized {
#[cfg(feature = "gpgme")]
if self.gpg_state.sign_mail.is_unset() {
self.gpg_state.sign_mail = ToggleFlag::InternalVal(*account_settings!(
context[self.account_hash].pgp.auto_sign
));
if self.gpg_state.sign_mail.is_none() {
self.gpg_state.sign_mail =
Some(*account_settings!(context[self.account_hash].pgp.auto_sign));
}
if !self.draft.headers().contains_key(HeaderName::FROM)
|| self.draft.headers()[HeaderName::FROM].is_empty()
@ -880,7 +885,8 @@ impl Component for Composer {
context.accounts[&self.account_hash]
.settings
.account()
.make_display_name(),
.make_display_name()
.to_string(),
);
}
self.pager.update_from_str(self.draft.body(), Some(77));
@ -1457,12 +1463,20 @@ impl Component for Composer {
#[cfg(feature = "gpgme")]
match self.cursor {
Cursor::Sign => {
let is_true = self.gpg_state.sign_mail.is_true();
self.gpg_state.sign_mail = ToggleFlag::from(!is_true);
let is_true = self
.gpg_state
.sign_mail
.unwrap_or(ActionFlag::False)
.is_true();
self.gpg_state.sign_mail = Some(ActionFlag::from(!is_true));
}
Cursor::Encrypt => {
let is_true = self.gpg_state.encrypt_mail.is_true();
self.gpg_state.encrypt_mail = ToggleFlag::from(!is_true);
let is_true = self
.gpg_state
.encrypt_mail
.unwrap_or(ActionFlag::False)
.is_true();
self.gpg_state.encrypt_mail = Some(ActionFlag::from(!is_true));
}
_ => {}
};
@ -1686,7 +1700,7 @@ impl Component for Composer {
)
}) {
Ok(widget) => {
self.gpg_state.sign_mail = ToggleFlag::from(true);
self.gpg_state.sign_mail = Some(ActionFlag::from(true));
self.mode = ViewMode::SelectEncryptKey(false, widget);
}
Err(err) => {
@ -1727,7 +1741,7 @@ impl Component for Composer {
)
}) {
Ok(widget) => {
self.gpg_state.encrypt_mail = ToggleFlag::from(true);
self.gpg_state.encrypt_mail = Some(ActionFlag::from(true));
self.mode = ViewMode::SelectEncryptKey(true, widget);
}
Err(err) => {
@ -2166,15 +2180,23 @@ impl Component for Composer {
}
#[cfg(feature = "gpgme")]
Action::Compose(ComposeAction::ToggleSign) => {
let is_true = self.gpg_state.sign_mail.is_true();
self.gpg_state.sign_mail = ToggleFlag::from(!is_true);
let is_true = self
.gpg_state
.sign_mail
.unwrap_or(ActionFlag::False)
.is_true();
self.gpg_state.sign_mail = Some(ActionFlag::from(!is_true));
self.set_dirty(true);
return true;
}
#[cfg(feature = "gpgme")]
Action::Compose(ComposeAction::ToggleEncrypt) => {
let is_true = self.gpg_state.encrypt_mail.is_true();
self.gpg_state.encrypt_mail = ToggleFlag::from(!is_true);
let is_true = self
.gpg_state
.encrypt_mail
.unwrap_or(ActionFlag::False)
.is_true();
self.gpg_state.encrypt_mail = Some(ActionFlag::from(!is_true));
self.set_dirty(true);
return true;
}
@ -2520,13 +2542,22 @@ pub fn send_draft_async(
>,
> = vec![];
#[cfg(feature = "gpgme")]
if gpg_state.sign_mail.is_true() && !gpg_state.encrypt_mail.is_true() {
if gpg_state.sign_mail.unwrap_or(ActionFlag::False).is_true()
&& !gpg_state
.encrypt_mail
.unwrap_or(ActionFlag::False)
.is_true()
{
filters_stack.push(Box::new(crate::mail::pgp::sign_filter(
gpg_state.sign_keys,
)?));
} else if gpg_state.encrypt_mail.is_true() {
} else if gpg_state
.encrypt_mail
.unwrap_or(ActionFlag::False)
.is_true()
{
filters_stack.push(Box::new(crate::mail::pgp::encrypt_filter(
if gpg_state.sign_mail.is_true() {
if gpg_state.sign_mail.unwrap_or(ActionFlag::False).is_true() {
Some(gpg_state.sign_keys.clone())
} else {
None

@ -29,7 +29,7 @@ pub enum KeySelection {
secret: bool,
local: bool,
pattern: String,
allow_remote_lookup: ToggleFlag,
allow_remote_lookup: ActionFlag,
},
Error {
id: ComponentId,
@ -52,7 +52,7 @@ impl KeySelection {
secret: bool,
local: bool,
pattern: String,
allow_remote_lookup: ToggleFlag,
allow_remote_lookup: ActionFlag,
context: &Context,
) -> Result<Self> {
use melib::gpgme::*;
@ -247,8 +247,8 @@ impl Component for KeySelection {
#[derive(Clone, Debug)]
pub struct GpgComposeState {
pub sign_mail: ToggleFlag,
pub encrypt_mail: ToggleFlag,
pub sign_mail: Option<ActionFlag>,
pub encrypt_mail: Option<ActionFlag>,
pub encrypt_keys: Vec<melib::gpgme::Key>,
pub encrypt_for_self: bool,
pub sign_keys: Vec<melib::gpgme::Key>,
@ -257,8 +257,8 @@ pub struct GpgComposeState {
impl Default for GpgComposeState {
fn default() -> Self {
Self {
sign_mail: ToggleFlag::Unset,
encrypt_mail: ToggleFlag::Unset,
sign_mail: None,
encrypt_mail: None,
encrypt_keys: vec![],
encrypt_for_self: true,
sign_keys: vec![],

@ -41,25 +41,11 @@ use crate::{
components::ExtendShortcutsMaps,
};
// [ref:TODO]: emoji_text_presentation_selector should be printed along with the chars
// before it but not as a separate Cell
//macro_rules! emoji_text_presentation_selector {
// () => {
// "\u{FE0E}"
// };
//}
//
//pub const DEFAULT_ATTACHMENT_FLAG: &str = concat!("📎",
// emoji_text_presentation_selector!()); pub const DEFAULT_SELECTED_FLAG: &str =
// concat!("☑️", emoji_text_presentation_selector!());
// pub const DEFAULT_UNSEEN_FLAG: &str = concat!("●",
// emoji_text_presentation_selector!()); pub const DEFAULT_SNOOZED_FLAG: &str =
// concat!("💤", emoji_text_presentation_selector!());
pub const DEFAULT_ATTACHMENT_FLAG: &str = "📎";
pub const DEFAULT_SELECTED_FLAG: &str = "☑️";
pub const DEFAULT_UNSEEN_FLAG: &str = "●";
pub const DEFAULT_SNOOZED_FLAG: &str = "💤";
pub const DEFAULT_ATTACHMENT_FLAG: &str = concat!("📎", emoji_text_presentation_selector!());
pub const DEFAULT_SELECTED_FLAG: &str = concat!("☑️", emoji_text_presentation_selector!());
pub const DEFAULT_UNSEEN_FLAG: &str = concat!("●", emoji_text_presentation_selector!());
pub const DEFAULT_SNOOZED_FLAG: &str = concat!("💤", emoji_text_presentation_selector!());
pub const DEFAULT_HIGHLIGHT_SELF_FLAG: &str = concat!("✸", emoji_text_presentation_selector!());
#[derive(Debug, Default)]
pub struct RowsState<T> {
@ -401,6 +387,7 @@ pub struct EntryStrings {
pub flag: FlagString,
pub from: FromString,
pub tags: TagString,
pub highlight_self: bool,
}
#[macro_export]

@ -324,6 +324,11 @@ impl MailListingTrait for CompactListing {
let mut from_address_list = Vec::new();
let mut from_address_set: std::collections::HashSet<Vec<u8>> =
std::collections::HashSet::new();
let mut highlight_self: bool;
let my_address: Address = context.accounts[&self.cursor_pos.0]
.settings
.account
.make_display_name();
'items_for_loop: for thread in items {
let thread_node = &threads.thread_nodes()[&threads.thread_ref(thread).root()];
let root_env_hash = if let Some(h) = thread_node.message().or_else(|| {
@ -373,6 +378,7 @@ impl MailListingTrait for CompactListing {
tags.clear();
from_address_list.clear();
from_address_set.clear();
highlight_self = false;
for (envelope, show_subject) in threads
.thread_iter(thread)
.filter_map(|(_, h)| {
@ -399,6 +405,11 @@ impl MailListingTrait for CompactListing {
}
}
highlight_self |= envelope
.to()
.iter()
.chain(envelope.cc().iter())
.any(|a| a == &my_address);
for addr in envelope.from().iter() {
if from_address_set.contains(addr.address_spec_raw()) {
continue;
@ -407,6 +418,15 @@ impl MailListingTrait for CompactListing {
from_address_list.push(addr.clone());
}
}
if !mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self
)
.is_true()
{
highlight_self = false;
}
let row_attr = row_attr!(
self.color_cache,
@ -425,6 +445,7 @@ impl MailListingTrait for CompactListing {
&threads,
&other_subjects,
&tags,
highlight_self,
thread,
);
row_widths
@ -937,6 +958,7 @@ impl CompactListing {
threads: &Threads,
other_subjects: &IndexSet<String>,
tags: &IndexSet<TagHash>,
highlight_self: bool,
hash: ThreadHash,
) -> EntryStrings {
let thread = threads.thread_ref(hash);
@ -1096,6 +1118,7 @@ impl CompactListing {
)),
from: FromString(Address::display_name_slice(from)),
tags: TagString(tags_string, colors),
highlight_self,
}
}
@ -1140,6 +1163,11 @@ impl CompactListing {
let mut from_address_list = Vec::new();
let mut from_address_set: std::collections::HashSet<Vec<u8>> =
std::collections::HashSet::new();
let mut highlight_self: bool = false;
let my_address: Address = context.accounts[&self.cursor_pos.0]
.settings
.account
.make_display_name();
for (envelope, show_subject) in threads
.thread_iter(thread_hash)
.filter_map(|(_, h)| {
@ -1164,6 +1192,11 @@ impl CompactListing {
tags.insert(t);
}
}
highlight_self |= envelope
.to()
.iter()
.chain(envelope.cc().iter())
.any(|a| a == &my_address);
for addr in envelope.from().iter() {
if from_address_set.contains(addr.address_spec_raw()) {
continue;
@ -1172,6 +1205,15 @@ impl CompactListing {
from_address_list.push(addr.clone());
}
}
if !mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self
)
.is_true()
{
highlight_self = false;
}
let strings = self.make_entry_string(
&envelope,
@ -1181,6 +1223,7 @@ impl CompactListing {
&threads,
&other_subjects,
&tags,
highlight_self,
thread_hash,
);
drop(envelope);
@ -1458,7 +1501,34 @@ impl CompactListing {
)
};
let x = {
let area = columns[3].area().nth_row(idx).skip_cols(x);
let mut area = columns[3].area().nth_row(idx).skip_cols(x);
if strings.highlight_self {
// [ref:hardcoded_color_value]: add highlight_self theme attr
let x = columns[3]
.grid_mut()
.write_string(
mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
.highlight_self_flag
)
.as_ref()
.map(|s| s.as_str())
.unwrap_or(super::DEFAULT_HIGHLIGHT_SELF_FLAG),
Color::BLUE,
row_attr.bg,
row_attr.attrs | Attr::FORCE_TEXT,
area,
None,
)
.0;
for row in columns[3].grid().bounds_iter(area.nth_row(0).take_cols(x)) {
for c in row {
columns[3].grid_mut()[c].set_keep_fg(true);
}
}
area = area.skip_cols(x + 1);
}
columns[3]
.grid_mut()
.write_string(

@ -713,7 +713,7 @@ impl ConversationsListing {
}
#[allow(clippy::too_many_arguments)]
pub(super) fn make_entry_string(
fn make_entry_string(
&self,
root_envelope: &Envelope,
context: &Context,
@ -795,6 +795,7 @@ impl ConversationsListing {
)),
from: FromString(Address::display_name_slice(from)),
tags: TagString(tags_string, colors),
highlight_self: false,
}
}

@ -785,6 +785,7 @@ impl PlainListing {
)),
from: FromString(Address::display_name_slice(e.from())),
tags: TagString(tags, colors),
highlight_self: false,
}
}

@ -953,6 +953,7 @@ impl ThreadListing {
)),
from: FromString(Address::display_name_slice(e.from())),
tags: TagString(tags, colors),
highlight_self: false,
}
}

@ -675,7 +675,8 @@ impl Component for MailView {
context.accounts[&coordinates.0]
.settings
.account()
.make_display_name(),
.make_display_name()
.to_string(),
);
/* Manually drop stuff because borrowck doesn't do it
* on its own */

@ -257,7 +257,7 @@ impl EnvelopeView {
}
#[cfg(feature = "gpgme")]
{
if view_settings.auto_verify_signatures {
if view_settings.auto_verify_signatures.is_true() {
let verify_fut = crate::mail::pgp::verify(a.clone());
let handle = main_loop_handler
.job_executor
@ -317,7 +317,7 @@ impl EnvelopeView {
}
#[cfg(feature = "gpgme")]
{
if view_settings.auto_decrypt {
if view_settings.auto_decrypt.is_true() {
let decrypt_fut = crate::mail::pgp::decrypt(a.raw().to_vec());
let handle = main_loop_handler
.job_executor

@ -21,7 +21,10 @@
use std::fmt::Write as IoWrite;
use melib::{attachment_types::Charset, error::*, pgp::DecryptionMetadata, Attachment, Result};
use melib::{
attachment_types::Charset, conf::ActionFlag, error::*, pgp::DecryptionMetadata, Attachment,
Result,
};
use crate::{
conf::shortcuts::EnvelopeViewShortcuts,
@ -43,8 +46,8 @@ pub struct ViewSettings {
pub sticky_headers: bool,
pub show_date_in_my_timezone: bool,
pub show_extra_headers: Vec<String>,
pub auto_verify_signatures: bool,
pub auto_decrypt: bool,
pub auto_verify_signatures: ActionFlag,
pub auto_decrypt: ActionFlag,
}
impl Default for ViewSettings {
@ -61,8 +64,8 @@ impl Default for ViewSettings {
sticky_headers: false,
show_date_in_my_timezone: false,
show_extra_headers: vec![],
auto_verify_signatures: true,
auto_decrypt: true,
auto_verify_signatures: ActionFlag::InternalVal(true),
auto_decrypt: ActionFlag::InternalVal(true),
}
}
}
@ -184,7 +187,13 @@ impl ViewOptions {
.collect::<Vec<Link>>();
}
for (lidx, l) in links.iter().enumerate().rev() {
text.insert_str(l.start, &format!("[{}]", lidx));
let mut start = l.start;
while start < text.len() && !text.is_char_boundary(start) {
start += 1;
}
if start < text.len() {
text.insert_str(start, &format!("[{}]", lidx));
}
}
}

@ -58,6 +58,13 @@ pub enum Alignment {
Center,
}
#[macro_export]
macro_rules! emoji_text_presentation_selector {
() => {
'\u{FE0E}'
};
}
/*
* CSI events we use
*/

@ -650,6 +650,7 @@ impl CellBuffer {
let upper_left = area.upper_left();
let bottom_right = area.bottom_right();
let (mut x, mut y) = upper_left;
let mut prev_coords = upper_left;
if y == get_y(bounds) || x == get_x(bounds) {
if self.growable {
if !self.resize(
@ -684,10 +685,17 @@ impl CellBuffer {
}
}
for c in s.chars() {
if c == crate::emoji_text_presentation_selector!() {
let prev_attrs = self[prev_coords].attrs();
self[prev_coords].set_attrs(prev_attrs | Attr::FORCE_TEXT);
continue;
}
if c == '\r' {
continue;
}
if c == '\n' {
prev_coords = (x, y);
y += 1;
if let Some(_x) = line_break {
x = _x + get_x(upper_left);
@ -710,6 +718,7 @@ impl CellBuffer {
}
break;
}
prev_coords = (x, y);
if c == '\t' {
self[(x, y)].set_ch(' ');
x += 1;
@ -996,7 +1005,10 @@ impl Cell {
self.attrs
}
pub fn set_attrs(&mut self, newattrs: Attr) -> &mut Self {
pub fn set_attrs(&mut self, mut newattrs: Attr) -> &mut Self {
if self.attrs.intersects(Attr::FORCE_TEXT) {
newattrs |= Attr::FORCE_TEXT;
}
if !self.keep_attrs {
self.attrs = newattrs;
}
@ -1079,14 +1091,15 @@ bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Attr: u8 {
/// Terminal default.
const DEFAULT = 0b000_0000;
const BOLD = 0b000_0001;
const DIM = 0b000_0010;
const ITALICS = 0b000_0100;
const UNDERLINE = 0b000_1000;
const BLINK = 0b001_0000;
const REVERSE = 0b010_0000;
const HIDDEN = 0b100_0000;
const DEFAULT = 0;
const BOLD = 1;
const DIM = Self::BOLD.bits() << 1;
const ITALICS = Self::DIM.bits() << 1;
const UNDERLINE = Self::ITALICS.bits() << 1;
const BLINK = Self::UNDERLINE.bits() << 1;
const REVERSE = Self::BLINK.bits() << 1;
const HIDDEN = Self::REVERSE.bits() << 1;
const FORCE_TEXT = Self::HIDDEN.bits() << 1;
}
}
@ -1107,6 +1120,7 @@ impl std::fmt::Display for Attr {
Self::BLINK => write!(f, "Blink"),
Self::REVERSE => write!(f, "Reverse"),
Self::HIDDEN => write!(f, "Hidden"),
Self::FORCE_TEXT => write!(f, "ForceTextRepresentation"),
combination => {
let mut ctr = 0;
if combination.intersects(Self::BOLD) {
@ -1154,6 +1168,12 @@ impl std::fmt::Display for Attr {
}
Self::HIDDEN.fmt(f)?;
}
if combination.intersects(Self::FORCE_TEXT) {
if ctr > 0 {
write!(f, "|")?;
}
Self::FORCE_TEXT.fmt(f)?;
}
write!(f, "")
}
}
@ -1196,6 +1216,7 @@ impl Attr {
"Blink" => Ok(Self::BLINK),
"Reverse" => Ok(Self::REVERSE),
"Hidden" => Ok(Self::HIDDEN),
"ForceTextRepresentation" => Ok(Self::FORCE_TEXT),
combination if combination.contains('|') => {
let mut ret = Self::DEFAULT;
for c in combination.trim().split('|') {

@ -405,6 +405,9 @@ impl Screen<Tty> {
}
if !c.empty() {
write!(stdout, "{}", c.ch()).unwrap();
if c.attrs().intersects(Attr::FORCE_TEXT) {
_ = write!(stdout, "\u{FE0E}");
}
}
}
}
@ -432,6 +435,9 @@ impl Screen<Tty> {
}
if !c.empty() {
write!(stdout, "{}", c.ch()).unwrap();
if c.attrs().intersects(Attr::FORCE_TEXT) {
_ = write!(stdout, "\u{FE0E}");
}
}
}
}

@ -23,7 +23,7 @@
//! [`backends`](./backends/index.html)
use std::{collections::HashMap, path::Path};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::{Deserialize, Serialize};
use crate::{
backends::SpecialUsageMailbox,
@ -55,8 +55,8 @@ pub struct AccountSettings {
impl AccountSettings {
/// Create the account's display name from fields
/// [`AccountSettings::identity`] and [`AccountSettings::display_name`].
pub fn make_display_name(&self) -> String {
Address::new(self.display_name.clone(), self.identity.clone()).to_string()
pub fn make_display_name(&self) -> Address {
Address::new(self.display_name.clone(), self.identity.clone())
}
pub fn order(&self) -> Option<(SortField, SortOrder)> {
@ -184,117 +184,196 @@ pub const fn none<T>() -> Option<T> {
None
}
macro_rules! named_unit_variant {
($variant:ident) => {
pub mod $variant {
pub fn deserialize<'de, D>(deserializer: D) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(concat!("\"", stringify!($variant), "\""))
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
if value == stringify!($variant) {
Ok(())
} else {
Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
pub use config_field_types::*;
pub mod config_field_types {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
macro_rules! named_unit_variant {
($variant:ident) => {
pub mod $variant {
pub fn deserialize<'de, D>(deserializer: D) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(concat!("\"", stringify!($variant), "\""))
}
fn visit_str<E: serde::de::Error>(
self,
value: &str,
) -> Result<Self::Value, E> {
if value == stringify!($variant) {
Ok(())
} else {
Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
}
}
}
deserializer.deserialize_str(V)
}
deserializer.deserialize_str(V)
}
};
}
pub mod strings {
named_unit_variant!(ask);
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ToggleFlag {
#[default]
Unset,
InternalVal(bool),
False,
True,
}
impl From<bool> for ToggleFlag {
fn from(val: bool) -> Self {
if val {
Self::True
} else {
Self::False
}
}
};
}
}
pub mod strings {
named_unit_variant!(ask);
}
impl ToggleFlag {
pub fn is_unset(&self) -> bool {
Self::Unset == *self
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ToggleFlag {
#[default]
Unset,
InternalVal(bool),
False,
True,
Ask,
}
pub fn is_internal(&self) -> bool {
matches!(self, Self::InternalVal(_))
}
impl From<bool> for ToggleFlag {
fn from(val: bool) -> Self {
if val {
Self::True
} else {
Self::False
pub fn is_false(&self) -> bool {
matches!(self, Self::False | Self::InternalVal(false))
}
pub fn is_true(&self) -> bool {
matches!(self, Self::True | Self::InternalVal(true))
}
}
}
impl ToggleFlag {
pub fn is_unset(&self) -> bool {
Self::Unset == *self
impl Serialize for ToggleFlag {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Unset | Self::InternalVal(_) => serializer.serialize_none(),
Self::False => serializer.serialize_bool(false),
Self::True => serializer.serialize_bool(true),
}
}
}
pub fn is_internal(&self) -> bool {
matches!(self, Self::InternalVal(_))
impl<'de> Deserialize<'de> for ToggleFlag {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum InnerToggleFlag {
Bool(bool),
}
let s = <InnerToggleFlag>::deserialize(deserializer);
Ok(
match s.map_err(|err| {
serde::de::Error::custom(format!(
r#"expected one of "true", "false", found `{}`"#,
err
))
})? {
InnerToggleFlag::Bool(true) => Self::True,
InnerToggleFlag::Bool(false) => Self::False,
},
)
}
}
pub fn is_ask(&self) -> bool {
matches!(self, Self::Ask)
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ActionFlag {
InternalVal(bool),
False,
True,
#[default]
Ask,
}
pub fn is_false(&self) -> bool {
matches!(self, Self::False | Self::InternalVal(false))
impl From<bool> for ActionFlag {
fn from(val: bool) -> Self {
if val {
Self::True
} else {
Self::False
}
}
}
pub fn is_true(&self) -> bool {
matches!(self, Self::True | Self::InternalVal(true))
impl ActionFlag {
pub fn is_internal(&self) -> bool {
matches!(self, Self::InternalVal(_))
}
pub fn is_ask(&self) -> bool {
matches!(self, Self::Ask)
}
pub fn is_false(&self) -> bool {
matches!(self, Self::False | Self::InternalVal(false))
}
pub fn is_true(&self) -> bool {
matches!(self, Self::True | Self::InternalVal(true))
}
}
}
impl Serialize for ToggleFlag {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Unset | Self::InternalVal(_) => serializer.serialize_none(),
Self::False => serializer.serialize_bool(false),
Self::True => serializer.serialize_bool(true),
Self::Ask => serializer.serialize_str("ask"),
impl Serialize for ActionFlag {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::InternalVal(_) => serializer.serialize_none(),
Self::False => serializer.serialize_bool(false),
Self::True => serializer.serialize_bool(true),
Self::Ask => serializer.serialize_str("ask"),
}
}
}
}
impl<'de> Deserialize<'de> for ToggleFlag {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum InnerToggleFlag {
Bool(bool),
#[serde(with = "strings::ask")]
Ask,
impl<'de> Deserialize<'de> for ActionFlag {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum InnerActionFlag {
Bool(bool),
#[serde(with = "strings::ask")]
Ask,
}
let s = <InnerActionFlag>::deserialize(deserializer);
Ok(
match s.map_err(|err| {
serde::de::Error::custom(format!(
r#"expected one of "true", "false", "ask", found `{}`"#,
err
))
})? {
InnerActionFlag::Bool(true) => Self::True,
InnerActionFlag::Bool(false) => Self::False,
InnerActionFlag::Ask => Self::Ask,
},
)
}
let s = <InnerToggleFlag>::deserialize(deserializer);
Ok(
match s.map_err(|err| {
serde::de::Error::custom(format!(
r#"expected one of "true", "false", "ask", found `{}`"#,
err
))
})? {
InnerToggleFlag::Bool(true) => Self::True,
InnerToggleFlag::Bool(false) => Self::False,
InnerToggleFlag::Ask => Self::Ask,
},
)
}
}

@ -1186,7 +1186,7 @@ impl JmapType {
let store = Arc::new(Store {
account_name: Arc::new(s.name.clone()),
account_hash,
main_identity: s.make_display_name(),
main_identity: s.make_display_name().to_string(),
extra_identities: s.extra_identities.clone(),
online_status,
event_consumer,

@ -68,7 +68,6 @@
* Global tags (in tagref format <https://github.com/stepchowfun/tagref>) for source code
* annotation:
*
* - [tag:hardcoded_color_value] Replace hardcoded color values with user configurable ones.
* - [tag:needs_unit_test]
* - [tag:needs_user_doc]
* - [tag:needs_dev_doc]

Loading…
Cancel
Save