🐝 I really like where this mua is(was?) headed, but it seems as though there has not been much activity recently.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

283 lines
8.1 KiB

/*
* meli - ui crate.
*
* Copyright 2017-2018 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Entities that handle Mail specific functions.
*/
use super::*;
use melib::backends::Folder;
pub mod listing;
pub use listing::*;
pub mod view;
pub use view::*;
mod compose;
pub use self::compose::*;
mod accounts;
pub use self::accounts::*;
#[derive(Debug)]
struct AccountMenuEntry {
name: String,
// Index in the config account vector.
index: usize,
// Each entry and its index in the account
entries: Vec<(usize, Folder)>,
}
/// The account sidebar.
#[derive(Debug)]
pub struct AccountMenu {
accounts: Vec<AccountMenuEntry>,
dirty: bool,
cursor: Option<(usize, usize)>,
}
impl fmt::Display for AccountMenu {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO display subject/info
write!(f, "menu")
}
}
impl AccountMenu {
pub fn new(accounts: &[Account]) -> Self {
let accounts = accounts
.iter()
.enumerate()
.map(|(i, a)| AccountMenuEntry {
name: a.name().to_string(),
index: i,
entries: {
let mut entries = Vec::with_capacity(a.len());
for (idx, acc) in a.list_folders().into_iter().enumerate() {
entries.push((idx, acc));
}
entries
},
})
.collect();
AccountMenu {
accounts,
dirty: true,
cursor: None,
}
}
/*
* Print a single account in the menu area.
*/
fn print_account(
&self,
grid: &mut CellBuffer,
area: Area,
a: &AccountMenuEntry,
context: &mut Context,
) -> usize {
if !is_valid_area!(area) {
eprintln!("BUG: invalid area in print_account");
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let highlight = self.cursor.map(|(x, _)| x == a.index).unwrap_or(false);
let mut parents: Vec<Option<usize>> = vec![None; a.entries.len()];
for (idx, e) in a.entries.iter().enumerate() {
for c in e.1.children() {
parents[*c] = Some(idx);
}
}
let mut roots = Vec::new();
for (idx, c) in parents.iter().enumerate() {
if c.is_none() {
roots.push(idx);
}
}
let mut inc = 0;
let mut depth = String::from("");
let mut s = format!("{}\n", a.name);
fn pop(depth: &mut String) {
depth.pop();
depth.pop();
}
fn push(depth: &mut String, c: char) {
depth.push(c);
}
fn print(
root: usize,
parents: &[Option<usize>],
depth: &mut String,
entries: &[(usize, Folder)],
s: &mut String,
inc: &mut usize,
index: usize, //account index
context: &mut Context,
) {
let len = s.len();
match context.accounts[index].status(root) {
Ok(_) => {}
Err(_) => {
return;
// TODO: Show progress visually
}
}
let count = context.accounts[index][root]
.as_ref()
.unwrap()
.collection
.values()
.filter(|e| !e.is_seen())
.count();
s.insert_str(
len,
&format!("{} {} {}\n ", *inc, &entries[root].1.name(), count),
);
*inc += 1;
for child in entries[root].1.children().iter() {
let len = s.len();
s.insert_str(len, &format!("{} ", depth));
push(depth, ' ');
print(*child, parents, depth, entries, s, inc, index, context);
pop(depth);
}
}
for r in roots {
print(
r, &parents, &mut depth, &a.entries, &mut s, &mut inc, a.index, context,
);
}
let lines: Vec<&str> = s.lines().collect();
let lines_len = lines.len();
if lines_len < 2 {
return 0;
}
let mut idx = 0;
for y in get_y(upper_left)..get_y(bottom_right) {
if idx == lines_len {
break;
}
let s = lines[idx].to_string();
let (color_fg, color_bg) = if highlight {
if self.cursor.unwrap().1 + 1 == idx {
(Color::Byte(233), Color::Byte(15))
} else {
(Color::Byte(15), Color::Byte(233))
}
} else {
(Color::Default, Color::Default)
};
let (x, _) = write_string_to_grid(
&s,
grid,
color_fg,
color_bg,
(set_y(upper_left, y), bottom_right),
false,
);
{
let mut x = get_x(upper_left);
while let Some(cell) = grid.get_mut(x, y) {
if x == get_x(bottom_right) {
break;
}
match cell.ch() {
c if c.is_numeric() => {
cell.set_fg(Color::Byte(243));
x += 1;
continue;
}
c if c.is_whitespace() => {
x += 1;
continue;
}
_ => {
break;
}
}
}
}
if highlight && idx > 1 && self.cursor.unwrap().1 == idx - 1 {
change_colors(grid, ((x, y), (get_x(bottom_right), y)), color_fg, color_bg);
} else {
change_colors(grid, ((x, y), set_y(bottom_right, y)), color_fg, color_bg);
}
idx += 1;
}
if idx == 0 {
0
} else {
idx - 1
}
}
}
impl Component for AccountMenu {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.is_dirty() {
return;
}
clear_area(grid, area);
let upper_left = upper_left!(area);
let x = get_x(upper_left);
let bottom_right = bottom_right!(area);
let x_max = get_x(bottom_right);
self.dirty = false;
let mut y = get_y(upper_left);
for a in &self.accounts {
for x in x..=x_max {
grid[(x, y)].set_ch('━');
}
y += 1;
y += self.print_account(grid, (set_y(upper_left, y), bottom_right), &a, context);
}
context.dirty_areas.push_back(area);
}
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
match event.event_type {
UIEventType::RefreshMailbox(c) => {
self.cursor = Some(c);
self.dirty = true;
}
UIEventType::ChangeMode(UIMode::Normal) => {
self.dirty = true;
}
UIEventType::Resize => {
self.dirty = true;
}
_ => {}
}
false
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self) {
self.dirty = true;
}
}