summaryrefslogtreecommitdiff
path: root/src/components/mail/listing/plain.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/mail/listing/plain.rs')
-rw-r--r--src/components/mail/listing/plain.rs274
1 files changed, 105 insertions, 169 deletions
diff --git a/src/components/mail/listing/plain.rs b/src/components/mail/listing/plain.rs
index c4073f11..c4296a4c 100644
--- a/src/components/mail/listing/plain.rs
+++ b/src/components/mail/listing/plain.rs
@@ -22,7 +22,7 @@
use super::EntryStrings;
use super::*;
use crate::components::PageMovement;
-use crate::jobs::{JobId, JoinHandle};
+use crate::jobs::JoinHandle;
use std::cmp;
use std::iter::FromIterator;
@@ -128,8 +128,7 @@ pub struct PlainListing {
length: usize,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
- all_envelopes: HashSet<EnvelopeHash>,
- order: HashMap<EnvelopeHash, usize>,
+ rows: RowsState<(ThreadHash, EnvelopeHash)>,
/// Cache current view.
data_columns: DataColumns,
@@ -138,9 +137,6 @@ pub struct PlainListing {
filter_term: String,
filtered_selection: Vec<EnvelopeHash>,
filtered_order: HashMap<EnvelopeHash, usize>,
- selection: HashMap<EnvelopeHash, bool>,
- _selection: HashMap<ThreadHash, bool>,
- thread_node_hashes: HashMap<EnvelopeHash, ThreadNodeHash>,
local_collection: Vec<EnvelopeHash>,
/// If we must redraw on next redraw event
dirty: bool,
@@ -148,40 +144,42 @@ pub struct PlainListing {
/// If `self.view` exists or not.
focus: Focus,
view: MailView,
- row_updates: SmallVec<[EnvelopeHash; 8]>,
- _row_updates: SmallVec<[ThreadHash; 8]>,
color_cache: ColorCache,
-
- active_jobs: HashMap<JobId, JoinHandle<Result<()>>>,
movement: Option<PageMovement>,
+ modifier_active: bool,
+ modifier_command: Option<Modifier>,
id: ComponentId,
}
impl MailListingTrait for PlainListing {
- fn row_updates(&mut self) -> &mut SmallVec<[ThreadHash; 8]> {
- &mut self._row_updates
+ fn row_updates(&mut self) -> &mut SmallVec<[EnvelopeHash; 8]> {
+ &mut self.rows.row_updates
}
- fn selection(&mut self) -> &mut HashMap<ThreadHash, bool> {
- &mut self._selection
+ fn selection(&mut self) -> &mut HashMap<EnvelopeHash, bool> {
+ &mut self.rows.selection
}
- fn get_focused_items(&self, _context: &Context) -> SmallVec<[ThreadHash; 8]> {
- SmallVec::new()
- /*
- let is_selection_empty = self.selection.values().cloned().any(std::convert::identity);
+ fn get_focused_items(&self, _context: &Context) -> SmallVec<[EnvelopeHash; 8]> {
+ let is_selection_empty: bool = !self
+ .rows
+ .selection
+ .values()
+ .cloned()
+ .any(std::convert::identity);
+ dbg!(is_selection_empty);
if is_selection_empty {
- self.selection
- .iter()
- .filter(|(_, v)| **v)
- .map(|(k, _)| self.thread_node_hashes[k])
- .collect()
- } else {
- let mut ret = SmallVec::new();
- ret.push(self.get_thread_under_cursor(self.cursor_pos.2, context));
- ret
+ return dbg!(self.get_env_under_cursor(self.cursor_pos.2))
+ .into_iter()
+ .collect::<_>();
}
- */
+ SmallVec::from_iter(
+ self.rows
+ .selection
+ .iter()
+ .filter(|(_, &v)| v)
+ .map(|(k, _)| *k),
+ )
}
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
@@ -253,12 +251,6 @@ impl MailListingTrait for PlainListing {
.envelopes
.read()
.unwrap();
- self.thread_node_hashes = context.accounts[&self.cursor_pos.0]
- .collection
- .get_mailbox(self.cursor_pos.1)
- .iter()
- .map(|h| (*h, env_lck[h].thread()))
- .collect();
let sort = self.sort;
self.local_collection.sort_by(|a, b| match sort {
(SortField::Date, SortOrder::Desc) => {
@@ -282,17 +274,13 @@ impl MailListingTrait for PlainListing {
mb.subject().cmp(&ma.subject())
}
});
- for &env_hash in &self.local_collection {
- self.all_envelopes.insert(env_hash);
- }
let items = Box::new(self.local_collection.clone().into_iter())
as Box<dyn Iterator<Item = EnvelopeHash>>;
self.redraw_list(context, items);
drop(env_lck);
- if self.length > 0 {
- let env_hash = self.get_env_under_cursor(self.cursor_pos.2, context);
+ if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) {
let temp = (self.new_cursor_pos.0, self.new_cursor_pos.1, env_hash);
if !force && old_cursor_pos == self.new_cursor_pos {
self.view.update(temp, context);
@@ -340,14 +328,16 @@ impl ListingTrait for PlainListing {
self.filtered_selection.clear();
self.filtered_order.clear();
self.filter_term.clear();
- self.row_updates.clear();
+ self.rows.row_updates.clear();
}
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
- if self.length == 0 {
+ let i = if let Some(i) = self.get_env_under_cursor(idx) {
+ i
+ } else {
+ // self.length == 0
return;
- }
- let i = self.get_env_under_cursor(idx, context);
+ };
let account = &context.accounts[&self.cursor_pos.0];
let envelope: EnvelopeRef = account.collection.get_env(i);
@@ -357,7 +347,7 @@ impl ListingTrait for PlainListing {
idx % 2 == 0,
!envelope.is_seen(),
self.cursor_pos.2 == idx,
- self.selection[&i]
+ self.rows.selection[&i]
);
let (upper_left, bottom_right) = area;
@@ -602,14 +592,12 @@ impl ListingTrait for PlainListing {
return;
}
- self.order.clear();
- self.selection.clear();
self.length = 0;
self.filtered_selection.clear();
self.filtered_order.clear();
self.filter_term = filter_term;
- self.row_updates.clear();
- for v in self.selection.values_mut() {
+ self.rows.row_updates.clear();
+ for v in self.rows.selection.values_mut() {
*v = false;
}
@@ -621,7 +609,7 @@ impl ListingTrait for PlainListing {
if self.filtered_order.contains_key(&env_hash) {
continue;
}
- if self.all_envelopes.contains(&env_hash) {
+ if self.rows.contains_env(env_hash) {
self.filtered_selection.push(env_hash);
self.filtered_order
.insert(env_hash, self.filtered_selection.len() - 1);
@@ -644,6 +632,18 @@ impl ListingTrait for PlainListing {
!matches!(self.focus, Focus::None)
}
+ fn set_modifier_active(&mut self, new_val: bool) {
+ self.modifier_active = new_val;
+ }
+
+ fn set_modifier_command(&mut self, new_val: Option<Modifier>) {
+ self.modifier_command = new_val;
+ }
+
+ fn modifier_command(&self) -> Option<Modifier> {
+ self.modifier_command
+ }
+
fn set_movement(&mut self, mvm: PageMovement) {
self.movement = Some(mvm);
self.set_dirty(true);
@@ -655,18 +655,19 @@ impl ListingTrait for PlainListing {
self.view
.process_event(&mut UIEvent::VisibilityChange(false), context);
self.dirty = true;
- /* If self.row_updates is not empty and we exit a thread, the row_update events
+ /* If self.rows.row_updates is not empty and we exit a thread, the row_update events
* will be performed but the list will not be drawn. So force a draw in any case.
* */
self.force_draw = true;
}
Focus::Entry => {
- let env_hash = self.get_env_under_cursor(self.cursor_pos.2, context);
- let temp = (self.cursor_pos.0, self.cursor_pos.1, env_hash);
- self.view = MailView::new(temp, None, None, context);
- self.force_draw = true;
- self.dirty = true;
- self.view.set_dirty(true);
+ if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) {
+ let temp = (self.cursor_pos.0, self.cursor_pos.1, env_hash);
+ self.view = MailView::new(temp, None, None, context);
+ self.force_draw = true;
+ self.dirty = true;
+ self.view.set_dirty(true);
+ }
}
Focus::EntryFullscreen => {
self.dirty = true;
@@ -696,32 +697,26 @@ impl PlainListing {
length: 0,
sort: (Default::default(), Default::default()),
subsort: (SortField::Date, SortOrder::Desc),
- all_envelopes: HashSet::default(),
+ rows: RowsState::default(),
local_collection: Vec::new(),
- thread_node_hashes: HashMap::default(),
- order: HashMap::default(),
filter_term: String::new(),
search_job: None,
filtered_selection: Vec::new(),
filtered_order: HashMap::default(),
- selection: HashMap::default(),
- _selection: HashMap::default(),
- row_updates: SmallVec::new(),
- _row_updates: SmallVec::new(),
data_columns: DataColumns::default(),
dirty: true,
force_draw: true,
focus: Focus::None,
view: MailView::default(),
color_cache: ColorCache::default(),
- active_jobs: HashMap::default(),
-
movement: None,
+ modifier_active: false,
+ modifier_command: None,
id: ComponentId::new_v4(),
})
}
- fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
+ fn make_entry_string(&self, e: &Envelope, context: &Context) -> EntryStrings {
let mut tags = String::new();
let mut colors = SmallVec::new();
let account = &context.accounts[&self.cursor_pos.0];
@@ -766,7 +761,7 @@ impl PlainListing {
subject: SubjectString(subject),
flag: FlagString(format!(
"{selected}{unseen}{attachments}{whitespace}",
- selected = if self.selection.get(&e.hash()).cloned().unwrap_or(false) {
+ selected = if self.rows.selection.get(&e.hash()).cloned().unwrap_or(false) {
mailbox_settings!(
context[self.cursor_pos.0][&self.cursor_pos.1]
.listing
@@ -802,7 +797,7 @@ impl PlainListing {
} else {
""
},
- whitespace = if self.selection.get(&e.hash()).cloned().unwrap_or(false)
+ whitespace = if self.rows.selection.get(&e.hash()).cloned().unwrap_or(false)
|| !e.is_seen()
|| e.has_attachments()
{
@@ -819,11 +814,10 @@ impl PlainListing {
fn redraw_list(&mut self, context: &Context, iter: Box<dyn Iterator<Item = EnvelopeHash>>) {
let account = &context.accounts[&self.cursor_pos.0];
let mailbox = &account[&self.cursor_pos.1];
+ let threads = account.collection.get_threads(self.cursor_pos.1);
- self.order.clear();
- self.selection.clear();
+ self.rows.clear();
self.length = 0;
- let mut rows = Vec::with_capacity(1024);
let mut min_width = (0, 0, 0, 0, 0);
for i in iter {
@@ -852,7 +846,7 @@ impl PlainListing {
}
}
- let entry_strings = self.make_entry_string(envelope, context);
+ let entry_strings = self.make_entry_string(&envelope, context);
min_width.1 = cmp::max(min_width.1, entry_strings.date.grapheme_width()); /* date */
min_width.2 = cmp::max(min_width.2, entry_strings.from.grapheme_width()); /* from */
min_width.3 = cmp::max(
@@ -862,10 +856,13 @@ impl PlainListing {
+ 1
+ entry_strings.tags.grapheme_width(),
); /* tags + subject */
- rows.push(entry_strings);
+ self.rows.insert_thread(
+ threads.envelope_to_thread[&i],
+ (threads.envelope_to_thread[&i], i),
+ smallvec::smallvec![i],
+ entry_strings,
+ );
- self.order.insert(i, self.length);
- self.selection.insert(i, false);
self.length += 1;
}
@@ -873,16 +870,16 @@ impl PlainListing {
/* index column */
self.data_columns.columns[0] =
- CellBuffer::new_with_context(min_width.0, rows.len(), None, context);
+ CellBuffer::new_with_context(min_width.0, self.rows.len(), None, context);
/* date column */
self.data_columns.columns[1] =
- CellBuffer::new_with_context(min_width.1, rows.len(), None, context);
+ CellBuffer::new_with_context(min_width.1, self.rows.len(), None, context);
/* from column */
self.data_columns.columns[2] =
- CellBuffer::new_with_context(min_width.2, rows.len(), None, context);
+ CellBuffer::new_with_context(min_width.2, self.rows.len(), None, context);
/* subject column */
self.data_columns.columns[3] =
- CellBuffer::new_with_context(min_width.3, rows.len(), None, context);
+ CellBuffer::new_with_context(min_width.3, self.rows.len(), None, context);
let iter = if self.filter_term.is_empty() {
Box::new(self.local_collection.iter().cloned())
@@ -893,7 +890,7 @@ impl PlainListing {
};
let columns = &mut self.data_columns.columns;
- for ((idx, i), strings) in iter.enumerate().zip(rows) {
+ for ((idx, i), (_, strings)) in iter.enumerate().zip(self.rows.entries.iter()) {
if !context.accounts[&self.cursor_pos.0].contains_key(i) {
//debug!("key = {}", i);
//debug!(
@@ -1003,7 +1000,7 @@ impl PlainListing {
}
/* Set fg color for flags */
let mut x = 0;
- if self.selection.get(&i).cloned().unwrap_or(false) {
+ if self.rows.selection.get(&i).cloned().unwrap_or(false) {
x += 1;
}
if !envelope.is_seen() {
@@ -1029,11 +1026,11 @@ impl PlainListing {
}
}
- fn get_env_under_cursor(&self, cursor: usize, _context: &Context) -> EnvelopeHash {
+ fn get_env_under_cursor(&self, cursor: usize) -> Option<EnvelopeHash> {
if self.filter_term.is_empty() {
- self.local_collection[cursor]
+ self.local_collection.get(cursor).cloned()
} else {
- self.filtered_selection[cursor]
+ self.filtered_selection.get(cursor).cloned()
}
}
@@ -1051,42 +1048,6 @@ impl PlainListing {
_ => melib::datetime::timestamp_to_string(envelope.datetime(), None, false),
}
}
-
- fn perform_action(&mut self, context: &mut Context, env_hash: EnvelopeHash, a: &ListingAction) {
- let account = &mut context.accounts[&self.cursor_pos.0];
- match {
- match a {
- ListingAction::SetSeen => account.backend.write().unwrap().set_flags(
- env_hash.into(),
- self.cursor_pos.1,
- smallvec::smallvec![(Ok(Flag::SEEN), true)],
- ),
- ListingAction::SetUnseen => account.backend.write().unwrap().set_flags(
- env_hash.into(),
- self.cursor_pos.1,
- smallvec::smallvec![(Ok(Flag::SEEN), false)],
- ),
- ListingAction::Delete => {
- /* do nothing */
- Err(MeliError::new("Delete is unimplemented"))
- }
- _ => unreachable!(),
- }
- } {
- Err(e) => {
- context
- .replies
- .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(
- e.to_string(),
- )));
- }
- Ok(fut) => {
- let handle = account.job_executor.spawn_specialized(fut);
- self.active_jobs.insert(handle.job_id, handle);
- }
- }
- self.row_updates.push(env_hash);
- }
}
impl Component for PlainListing {
@@ -1128,10 +1089,10 @@ impl Component for PlainListing {
area = (set_y(upper_left, y + 1), bottom_right);
}
- if !self.row_updates.is_empty() {
+ if !self.rows.row_updates.is_empty() {
let (upper_left, bottom_right) = area;
- while let Some(row) = self.row_updates.pop() {
- let row: usize = self.order[&row];
+ while let Some(row) = self.rows.row_updates.pop() {
+ let row: usize = self.rows.env_order[&row];
let rows = get_y(bottom_right) - get_y(upper_left) + 1;
let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
@@ -1233,8 +1194,14 @@ impl Component for PlainListing {
if !self.unfocused()
&& shortcut!(key == shortcuts[Listing::DESCRIPTION]["select_entry"]) =>
{
- let env_hash = self.get_env_under_cursor(self.cursor_pos.2, context);
- self.selection.entry(env_hash).and_modify(|e| *e = !*e);
+ if self.modifier_active && self.modifier_command.is_none() {
+ self.modifier_command = Some(Modifier::default());
+ } else {
+ if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) {
+ self.rows.update_selection_with_env(env_hash, |e| *e = !*e);
+ }
+ }
+ return true;
}
UIEvent::Action(ref action) => match action {
Action::SubSort(field, order) if !self.unfocused() => {
@@ -1253,37 +1220,6 @@ impl Component for PlainListing {
self.sort = (*field, *order);
return true;
}
- Action::Listing(a @ ListingAction::SetSeen)
- | Action::Listing(a @ ListingAction::SetUnseen)
- | Action::Listing(a @ ListingAction::Delete)
- if !self.unfocused() =>
- {
- let is_selection_empty =
- self.selection.values().cloned().any(std::convert::identity);
- let i = [self.get_env_under_cursor(self.cursor_pos.2, context)];
- let cursor_iter;
- let sel_iter = if is_selection_empty {
- cursor_iter = None;
- Some(self.selection.iter().filter(|(_, v)| **v).map(|(k, _)| k))
- } else {
- cursor_iter = Some(i.iter());
- None
- };
- let iter = sel_iter
- .into_iter()
- .flatten()
- .chain(cursor_iter.into_iter().flatten())
- .cloned();
- let stack: SmallVec<[_; 8]> = SmallVec::from_iter(iter);
- for i in stack {
- self.perform_action(context, i, a);
- }
- self.dirty = true;
- for v in self.selection.values_mut() {
- *v = false;
- }
- return true;
- }
_ => {}
},
@@ -1347,16 +1283,11 @@ impl Component for PlainListing {
return false;
}
- self.row_updates.push(*new_hash);
- if let Some(row) = self.order.remove(old_hash) {
- self.order.insert(*new_hash, row);
- let selection_status = self.selection.remove(old_hash).unwrap();
- self.selection.insert(*new_hash, selection_status);
- for h in self.filtered_selection.iter_mut() {
- if *h == *old_hash {
- *h = *new_hash;
- break;
- }
+ self.rows.rename_env(*old_hash, *new_hash);
+ for h in self.filtered_selection.iter_mut() {
+ if *h == *old_hash {
+ *h = *new_hash;
+ break;
}
}
@@ -1378,7 +1309,7 @@ impl Component for PlainListing {
return false;
}
- self.row_updates.push(*env_hash);
+ self.rows.row_updates.push(*env_hash);
self.dirty = true;
if self.unfocused() {
@@ -1394,9 +1325,14 @@ impl Component for PlainListing {
}
UIEvent::Input(Key::Esc)
if !self.unfocused()
- && self.selection.values().cloned().any(std::convert::identity) =>
+ && self
+ .rows
+ .selection
+ .values()
+ .cloned()
+ .any(std::convert::identity) =>
{
- for v in self.selection.values_mut() {
+ for v in self.rows.selection.values_mut() {
*v = false;
}
self.dirty = true;