Add watcher and input threads
parent
5ed4c37e52
commit
577889f7da
|
@ -24,6 +24,7 @@ crossbeam = "^0.3.0"
|
||||||
fnv = "1.0.3"
|
fnv = "1.0.3"
|
||||||
encoding = "0.2.33"
|
encoding = "0.2.33"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
|
notify = "4.0.1"
|
||||||
|
|
||||||
[dependencies.ncurses]
|
[dependencies.ncurses]
|
||||||
features = ["wide"]
|
features = ["wide"]
|
||||||
|
|
8
README
8
README
|
@ -1,10 +1,10 @@
|
||||||
__
|
__
|
||||||
__/ \__
|
__/ \__
|
||||||
/ \__/ \__ .
|
/ \__/ \__ .
|
||||||
\__/ \__/ \ , _ , _ ___ | '
|
\__/ \__/ \ , _ , _ ___ │ '
|
||||||
/ \__ \__/ |' `|' `. .' ` | |
|
/ \__ \__/ │' `│ `┒ .' ` │ │
|
||||||
\__/ \__/ \ | | | |----' | |
|
\__/ \__/ \ │ │ │ |────' │ │
|
||||||
\__/ \__/ / ' / `.___, /\__ /
|
\__/ \__/ │ / `.___, /\__ /
|
||||||
\__/
|
\__/
|
||||||
|
|
||||||
terminal mail user agent
|
terminal mail user agent
|
||||||
|
|
96
src/bin.rs
96
src/bin.rs
|
@ -21,22 +21,47 @@
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
use ui::index::*;
|
use ui::index::*;
|
||||||
|
use ui::ThreadEvent;
|
||||||
|
|
||||||
extern crate melib;
|
extern crate melib;
|
||||||
use melib::*;
|
use melib::*;
|
||||||
use mailbox::*;
|
|
||||||
use conf::*;
|
|
||||||
|
|
||||||
extern crate ncurses;
|
extern crate ncurses;
|
||||||
|
|
||||||
|
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let locale_conf = ncurses::LcCategory::all;
|
let locale_conf = ncurses::LcCategory::all;
|
||||||
ncurses::setlocale(locale_conf, "en_US.UTF-8");
|
ncurses::setlocale(locale_conf, "en_US.UTF-8");
|
||||||
let set = Settings::new();
|
let set = Settings::new();
|
||||||
let ui = ui::TUI::initialize();
|
let ui = ui::TUI::initialize();
|
||||||
|
|
||||||
|
let (sender, receiver): (SyncSender<ThreadEvent>, Receiver<ThreadEvent>) =
|
||||||
|
sync_channel(::std::mem::size_of::<ThreadEvent>());
|
||||||
|
{
|
||||||
|
let sender = sender.clone();
|
||||||
|
let mut ch = None;
|
||||||
|
thread::Builder::new()
|
||||||
|
.name("input-thread".to_string())
|
||||||
|
.spawn(move || loop {
|
||||||
|
ch = ncurses::get_wch();
|
||||||
|
if let Some(k) = ch {
|
||||||
|
sender.send(ThreadEvent::Input(k)).unwrap();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
let mut j = 0;
|
let mut j = 0;
|
||||||
let folder_length = set.accounts["norn"].folders.len();
|
let folder_length = set.accounts["norn"].folders.len();
|
||||||
let mut account = Account::new("norn".to_string(), set.accounts["norn"].clone());
|
let mut account = Account::new("norn".to_string(), set.accounts["norn"].clone());
|
||||||
|
{
|
||||||
|
let sender = sender.clone();
|
||||||
|
account.watch(RefreshEventConsumer::new(Box::new(move |r| {
|
||||||
|
sender.send(ThreadEvent::from(r)).unwrap();
|
||||||
|
})));
|
||||||
|
}
|
||||||
'main: loop {
|
'main: loop {
|
||||||
ncurses::touchwin(ncurses::stdscr());
|
ncurses::touchwin(ncurses::stdscr());
|
||||||
ncurses::mv(0, 0);
|
ncurses::mv(0, 0);
|
||||||
|
@ -50,37 +75,48 @@ fn main() {
|
||||||
|
|
||||||
index.draw();
|
index.draw();
|
||||||
|
|
||||||
let mut ch;
|
|
||||||
'inner: loop {
|
'inner: loop {
|
||||||
ch = ncurses::get_wch();
|
match receiver.recv().unwrap() {
|
||||||
match ch {
|
ThreadEvent::Input(k) => match k {
|
||||||
Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_UP)) |
|
ncurses::WchResult::KeyCode(k @ ncurses::KEY_UP)
|
||||||
Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_DOWN)) => {
|
| ncurses::WchResult::KeyCode(k @ ncurses::KEY_DOWN) => {
|
||||||
index.handle_input(k);
|
index.handle_input(k);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Some(ncurses::WchResult::Char(k @ 10)) => {
|
ncurses::WchResult::Char(k @ 10) => {
|
||||||
index.handle_input(k as i32);
|
index.handle_input(k as i32);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Some(ncurses::WchResult::KeyCode(ncurses::KEY_F1)) |
|
ncurses::WchResult::KeyCode(k @ ncurses::KEY_F1) => {
|
||||||
Some(ncurses::WchResult::Char(113)) => {
|
if !index.handle_input(k) {
|
||||||
break 'main;
|
break 'main;
|
||||||
}
|
}
|
||||||
Some(ncurses::WchResult::Char(74)) => if j < folder_length - 1 {
|
}
|
||||||
j += 1;
|
ncurses::WchResult::Char(113) => {
|
||||||
break 'inner;
|
break 'main;
|
||||||
|
}
|
||||||
|
ncurses::WchResult::Char(74) => {
|
||||||
|
if j < folder_length - 1 {
|
||||||
|
j += 1;
|
||||||
|
break 'inner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ncurses::WchResult::Char(75) => {
|
||||||
|
if j > 0 {
|
||||||
|
j -= 1;
|
||||||
|
break 'inner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ncurses::WchResult::KeyCode(ncurses::KEY_RESIZE) => {
|
||||||
|
eprintln!("key_resize");
|
||||||
|
index.redraw();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
},
|
},
|
||||||
Some(ncurses::WchResult::Char(75)) => if j > 0 {
|
ThreadEvent::RefreshMailbox { name: n } => {
|
||||||
j -= 1;
|
eprintln!("Refresh mailbox {}", n);
|
||||||
break 'inner;
|
|
||||||
},
|
|
||||||
Some(ncurses::WchResult::KeyCode(ncurses::KEY_RESIZE)) => {
|
|
||||||
eprintln!("key_resize");
|
|
||||||
index.redraw();
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,3 +35,8 @@ extern crate encoding;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
|
||||||
|
pub use mailbox::*;
|
||||||
|
pub use conf::*;
|
||||||
|
|
||||||
|
pub use mailbox::backends::RefreshEventConsumer;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use mailbox::*;
|
use mailbox::*;
|
||||||
|
use mailbox::backends::RefreshEventConsumer;
|
||||||
use conf::AccountSettings;
|
use conf::AccountSettings;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -30,9 +31,11 @@ pub struct Account {
|
||||||
sent_folder: Option<usize>,
|
sent_folder: Option<usize>,
|
||||||
|
|
||||||
settings: AccountSettings,
|
settings: AccountSettings,
|
||||||
|
pub backend: Box<MailBackend>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
use mailbox::backends::maildir::MaildirType;
|
||||||
impl Account {
|
impl Account {
|
||||||
pub fn new(name: String, settings: AccountSettings) -> Self {
|
pub fn new(name: String, settings: AccountSettings) -> Self {
|
||||||
eprintln!("new acc");
|
eprintln!("new acc");
|
||||||
|
@ -44,6 +47,7 @@ impl Account {
|
||||||
for _ in 0..settings.folders.len() {
|
for _ in 0..settings.folders.len() {
|
||||||
folders.push(None);
|
folders.push(None);
|
||||||
}
|
}
|
||||||
|
let backend = Box::new(MaildirType::new(""));
|
||||||
Account {
|
Account {
|
||||||
name: name,
|
name: name,
|
||||||
folders: folders,
|
folders: folders,
|
||||||
|
@ -51,8 +55,12 @@ impl Account {
|
||||||
sent_folder: sent_folder,
|
sent_folder: sent_folder,
|
||||||
|
|
||||||
settings: settings,
|
settings: settings,
|
||||||
|
backend: backend,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn watch(&self, r: RefreshEventConsumer) -> () {
|
||||||
|
self.backend.watch(r, &self.settings.folders);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<usize> for Account {
|
impl Index<usize> for Account {
|
||||||
|
|
|
@ -21,18 +21,23 @@
|
||||||
|
|
||||||
use mailbox::email::{Envelope, Flag};
|
use mailbox::email::{Envelope, Flag};
|
||||||
use error::{MeliError, Result};
|
use error::{MeliError, Result};
|
||||||
use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend};
|
use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend, RefreshEvent, RefreshEventConsumer};
|
||||||
use mailbox::email::parser;
|
use mailbox::email::parser;
|
||||||
|
|
||||||
|
extern crate notify;
|
||||||
|
|
||||||
|
use self::notify::{Watcher, RecursiveMode, watcher};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
//use std::sync::mpsc::sync_channel;
|
||||||
|
//use std::sync::mpsc::SyncSender;
|
||||||
|
//use std::time::Duration;
|
||||||
|
use std::thread;
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use memmap::{Mmap, Protection};
|
use memmap::{Mmap, Protection};
|
||||||
|
|
||||||
|
|
||||||
pub struct MaildirType {
|
|
||||||
path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct MaildirOp {
|
pub struct MaildirOp {
|
||||||
path: String,
|
path: String,
|
||||||
|
@ -64,9 +69,10 @@ impl BackendOp for MaildirOp {
|
||||||
fn as_bytes(&mut self) -> Result<&[u8]> {
|
fn as_bytes(&mut self) -> Result<&[u8]> {
|
||||||
if self.slice.is_none() {
|
if self.slice.is_none() {
|
||||||
self.slice = Some(
|
self.slice = Some(
|
||||||
Mmap::open_path(self.path.to_string(), Protection::Read).unwrap(),
|
Mmap::open_path(self.path.to_string(), Protection::Read)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
/* Unwrap is safe since we use ? above. */
|
||||||
Ok(unsafe { self.slice.as_ref().unwrap().as_slice() })
|
Ok(unsafe { self.slice.as_ref().unwrap().as_slice() })
|
||||||
}
|
}
|
||||||
fn fetch_headers(&mut self) -> Result<&[u8]> {
|
fn fetch_headers(&mut self) -> Result<&[u8]> {
|
||||||
|
@ -107,6 +113,12 @@ impl BackendOp for MaildirOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MaildirType {
|
||||||
|
path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl MailBackend for MaildirType {
|
impl MailBackend for MaildirType {
|
||||||
fn get(&self) -> Result<Vec<Envelope>> {
|
fn get(&self) -> Result<Vec<Envelope>> {
|
||||||
self.get_multicore(4)
|
self.get_multicore(4)
|
||||||
|
@ -132,6 +144,34 @@ impl MailBackend for MaildirType {
|
||||||
Ok(r)
|
Ok(r)
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
fn watch(&self, sender: RefreshEventConsumer, folders: &[String]) -> () {
|
||||||
|
let folders = folders.to_vec();
|
||||||
|
|
||||||
|
thread::Builder::new().name("folder watch".to_string()).spawn(move || {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
|
||||||
|
for f in folders {
|
||||||
|
if MaildirType::is_valid(&f).is_err() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut p = PathBuf::from(&f);
|
||||||
|
p.push("cur");
|
||||||
|
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
|
||||||
|
p.pop();
|
||||||
|
p.push("new");
|
||||||
|
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
|
||||||
|
eprintln!("watching {:?}", f);
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
match rx.recv() {
|
||||||
|
Ok(event) => {
|
||||||
|
sender.send(RefreshEvent { folder: format!("{:?}", event) });
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("watch error: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaildirType {
|
impl MaildirType {
|
||||||
|
@ -198,9 +238,10 @@ panic!("didn't parse"); },
|
||||||
let mut local_r: Vec<Envelope> = Vec::with_capacity(chunk.len());
|
let mut local_r: Vec<Envelope> = Vec::with_capacity(chunk.len());
|
||||||
for e in chunk {
|
for e in chunk {
|
||||||
let e_copy = e.to_string();
|
let e_copy = e.to_string();
|
||||||
if let Some(e) = Envelope::from(Box::new(BackendOpGenerator::new(
|
if let Some(mut e) = Envelope::from(Box::new(BackendOpGenerator::new(
|
||||||
Box::new(move || Box::new(MaildirOp::new(e_copy.clone()))),
|
Box::new(move || Box::new(MaildirOp::new(e_copy.clone()))),
|
||||||
))) {
|
))) {
|
||||||
|
e.populate_headers();
|
||||||
local_r.push(e);
|
local_r.push(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,26 @@ use error::Result;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub trait MailBackend {
|
pub struct RefreshEvent {
|
||||||
fn get(&self) -> Result<Vec<Envelope>>;
|
pub folder: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RefreshEventConsumer(Box<Fn(RefreshEvent) -> ()>);
|
||||||
|
unsafe impl Send for RefreshEventConsumer {}
|
||||||
|
unsafe impl Sync for RefreshEventConsumer {}
|
||||||
|
impl RefreshEventConsumer {
|
||||||
|
pub fn new(b: Box<Fn(RefreshEvent) -> ()>) -> Self {
|
||||||
|
RefreshEventConsumer(b)
|
||||||
|
}
|
||||||
|
pub fn send(&self, r: RefreshEvent) -> () {
|
||||||
|
self.0(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub trait MailBackend: ::std::fmt::Debug {
|
||||||
|
fn get(&self) -> Result<Vec<Envelope>>;
|
||||||
|
fn watch(&self, sender:RefreshEventConsumer, folders: &[String]) -> ();
|
||||||
|
//fn new(folders: &Vec<String>) -> Box<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
/// A `BackendOp` manages common operations for the various mail backends. They only live for the
|
/// A `BackendOp` manages common operations for the various mail backends. They only live for the
|
||||||
/// duration of the operation. They are generated by `BackendOpGenerator` on demand.
|
/// duration of the operation. They are generated by `BackendOpGenerator` on demand.
|
||||||
|
|
|
@ -156,6 +156,7 @@ impl AttachmentBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn decode(&self) -> String {
|
fn decode(&self) -> String {
|
||||||
|
// TODO: Use charset for decoding
|
||||||
match self.content_transfer_encoding {
|
match self.content_transfer_encoding {
|
||||||
ContentTransferEncoding::Base64 => {
|
ContentTransferEncoding::Base64 => {
|
||||||
match ::base64::decode(&::std::str::from_utf8(&self.raw)
|
match ::base64::decode(&::std::str::from_utf8(&self.raw)
|
||||||
|
@ -178,8 +179,8 @@ impl AttachmentBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_text(&self.raw)
|
ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_text(&self.raw)
|
||||||
.to_full_result()
|
.to_full_result()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
ContentTransferEncoding::_7Bit |
|
ContentTransferEncoding::_7Bit |
|
||||||
ContentTransferEncoding::_8Bit |
|
ContentTransferEncoding::_8Bit |
|
||||||
ContentTransferEncoding::Other { .. } => {
|
ContentTransferEncoding::Other { .. } => {
|
||||||
|
|
|
@ -41,31 +41,25 @@ pub struct Mailbox {
|
||||||
pub collection: Vec<Envelope>,
|
pub collection: Vec<Envelope>,
|
||||||
pub threaded_collection: Vec<usize>,
|
pub threaded_collection: Vec<usize>,
|
||||||
threads: Vec<Container>,
|
threads: Vec<Container>,
|
||||||
length: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Mailbox {
|
impl Mailbox {
|
||||||
pub fn new(path: &str, sent_folder: &Option<Result<Mailbox>>) -> Result<Mailbox> {
|
pub fn new(path: &str, sent_folder: &Option<Result<Mailbox>>) -> Result<Mailbox> {
|
||||||
let mut collection: Vec<Envelope> = maildir::MaildirType::new(path).get()?;
|
let mut collection: Vec<Envelope> = maildir::MaildirType::new(path).get()?;
|
||||||
for e in &mut collection {
|
|
||||||
e.populate_headers();
|
|
||||||
}
|
|
||||||
collection.sort_by(|a, b| a.get_date().cmp(&b.get_date()));
|
collection.sort_by(|a, b| a.get_date().cmp(&b.get_date()));
|
||||||
let (threads, threaded_collection) = build_threads(&mut collection, sent_folder);
|
let (threads, threaded_collection) = build_threads(&mut collection, sent_folder);
|
||||||
|
|
||||||
let length = collection.len();
|
|
||||||
|
|
||||||
Ok(Mailbox {
|
Ok(Mailbox {
|
||||||
path: path.to_string(),
|
path: path.to_string(),
|
||||||
collection: collection,
|
collection: collection,
|
||||||
threads: threads,
|
threads: threads,
|
||||||
length: length,
|
|
||||||
threaded_collection: threaded_collection,
|
threaded_collection: threaded_collection,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn get_length(&self) -> usize {
|
pub fn get_length(&self) -> usize {
|
||||||
self.length
|
self.collection.len()
|
||||||
}
|
}
|
||||||
pub fn get_threaded_mail(&self, i: usize) -> usize {
|
pub fn get_threaded_mail(&self, i: usize) -> usize {
|
||||||
let thread = self.threads[self.threaded_collection[i]];
|
let thread = self.threads[self.threaded_collection[i]];
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
use mailbox::email::Envelope;
|
use mailbox::email::Envelope;
|
||||||
use mailbox::*;
|
use mailbox::*;
|
||||||
use error::MeliError;
|
use error::MeliError;
|
||||||
|
|
||||||
|
use super::pager::Pager;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
extern crate ncurses;
|
extern crate ncurses;
|
||||||
|
@ -28,7 +30,7 @@ extern crate ncurses;
|
||||||
pub trait Window {
|
pub trait Window {
|
||||||
fn draw(&mut self) -> ();
|
fn draw(&mut self) -> ();
|
||||||
fn redraw(&mut self) -> ();
|
fn redraw(&mut self) -> ();
|
||||||
fn handle_input(&mut self, input: i32) -> ();
|
fn handle_input(&mut self, input: i32) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ErrorWindow {
|
pub struct ErrorWindow {
|
||||||
|
@ -46,7 +48,7 @@ impl Window for ErrorWindow {
|
||||||
ncurses::waddstr(self.win, &self.description);
|
ncurses::waddstr(self.win, &self.description);
|
||||||
ncurses::wrefresh(self.win);
|
ncurses::wrefresh(self.win);
|
||||||
}
|
}
|
||||||
fn handle_input(&mut self, _: i32) -> () {}
|
fn handle_input(&mut self, _: i32) -> bool { false }
|
||||||
}
|
}
|
||||||
impl ErrorWindow {
|
impl ErrorWindow {
|
||||||
pub fn new(err: MeliError) -> Self {
|
pub fn new(err: MeliError) -> Self {
|
||||||
|
@ -78,12 +80,14 @@ pub struct Index {
|
||||||
threaded: bool,
|
threaded: bool,
|
||||||
|
|
||||||
cursor_idx: usize,
|
cursor_idx: usize,
|
||||||
|
|
||||||
|
pager: Option<Pager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Window for Index {
|
impl Window for Index {
|
||||||
fn draw(&mut self) {
|
fn draw(&mut self) {
|
||||||
if self.mailbox.get_length() == 0 {
|
if self.get_length() == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
|
@ -199,7 +203,7 @@ impl Window for Index {
|
||||||
fn redraw(&mut self) -> () {
|
fn redraw(&mut self) -> () {
|
||||||
ncurses::wnoutrefresh(self.win);
|
ncurses::wnoutrefresh(self.win);
|
||||||
ncurses::doupdate();
|
ncurses::doupdate();
|
||||||
if self.mailbox.get_length() == 0 {
|
if self.get_length() == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Draw newly highlighted entry */
|
/* Draw newly highlighted entry */
|
||||||
|
@ -224,13 +228,31 @@ impl Window for Index {
|
||||||
);
|
);
|
||||||
ncurses::wrefresh(self.win);
|
ncurses::wrefresh(self.win);
|
||||||
}
|
}
|
||||||
fn handle_input(&mut self, motion: i32) {
|
fn handle_input(&mut self, motion: i32) -> bool {
|
||||||
if self.mailbox.get_length() == 0 {
|
if self.get_length() == 0 {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
||||||
if self.screen_height == 0 {
|
if self.screen_height == 0 {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
if self.pager.is_some() {
|
||||||
|
match motion {
|
||||||
|
m @ ncurses::KEY_UP |
|
||||||
|
m @ ncurses::KEY_DOWN |
|
||||||
|
m @ ncurses::KEY_NPAGE |
|
||||||
|
m @ ncurses::KEY_PPAGE => {
|
||||||
|
self.pager.as_mut().unwrap().scroll(m);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ncurses::KEY_F1 => {
|
||||||
|
self.pager = None;
|
||||||
|
self.redraw();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
|
@ -240,19 +262,19 @@ impl Window for Index {
|
||||||
ncurses::KEY_UP => if self.cursor_idx > 0 {
|
ncurses::KEY_UP => if self.cursor_idx > 0 {
|
||||||
self.cursor_idx -= 1;
|
self.cursor_idx -= 1;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return false;
|
||||||
},
|
},
|
||||||
ncurses::KEY_DOWN => if self.cursor_idx < self.mailbox.get_length() - 1 {
|
ncurses::KEY_DOWN => if self.cursor_idx < self.get_length() - 1 {
|
||||||
self.cursor_idx += 1;
|
self.cursor_idx += 1;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return false;
|
||||||
},
|
},
|
||||||
10 => {
|
10 => {
|
||||||
self.show_pager();
|
self.show_pager();
|
||||||
self.redraw();
|
return true;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,10 +340,10 @@ impl Window for Index {
|
||||||
* └ i-1 ┘
|
* └ i-1 ┘
|
||||||
*/
|
*/
|
||||||
if pminrow != pminrow_prev &&
|
if pminrow != pminrow_prev &&
|
||||||
pminrow + self.screen_height > self.mailbox.get_length() as i32
|
pminrow + self.screen_height > self.get_length() as i32
|
||||||
{
|
{
|
||||||
/* touch dead entries in index (tell ncurses to redraw the empty lines next refresh) */
|
/* touch dead entries in index (tell ncurses to redraw the empty lines next refresh) */
|
||||||
let live_entries = self.mailbox.get_length() as i32 - pminrow;
|
let live_entries = self.get_length() as i32 - pminrow;
|
||||||
ncurses::wredrawln(self.win, live_entries, self.screen_height);
|
ncurses::wredrawln(self.win, live_entries, self.screen_height);
|
||||||
ncurses::wrefresh(self.win);
|
ncurses::wrefresh(self.win);
|
||||||
}
|
}
|
||||||
|
@ -334,6 +356,7 @@ impl Window for Index {
|
||||||
self.screen_height - 1,
|
self.screen_height - 1,
|
||||||
self.screen_width - 1,
|
self.screen_width - 1,
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Index {
|
impl Index {
|
||||||
|
@ -365,15 +388,6 @@ impl Index {
|
||||||
ncurses::printw(&format!("Mailbox {} is empty.\n", mailbox.path));
|
ncurses::printw(&format!("Mailbox {} is empty.\n", mailbox.path));
|
||||||
ncurses::refresh();
|
ncurses::refresh();
|
||||||
}
|
}
|
||||||
let mut color = true;
|
|
||||||
let mut thread_color = Vec::with_capacity(mailbox_length);
|
|
||||||
for i in &mailbox.threaded_collection {
|
|
||||||
let container = mailbox.get_thread(*i);
|
|
||||||
if !container.has_parent() {
|
|
||||||
color = !color;
|
|
||||||
}
|
|
||||||
thread_color.push(color);
|
|
||||||
}
|
|
||||||
Index {
|
Index {
|
||||||
mailbox: mailbox,
|
mailbox: mailbox,
|
||||||
win: win,
|
win: win,
|
||||||
|
@ -382,9 +396,16 @@ impl Index {
|
||||||
screen_height: 0,
|
screen_height: 0,
|
||||||
threaded: true,
|
threaded: true,
|
||||||
cursor_idx: 0,
|
cursor_idx: 0,
|
||||||
|
pager: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_length(&self) -> usize {
|
||||||
|
if self.threaded {
|
||||||
|
self.mailbox.threaded_collection.len()
|
||||||
|
} else {
|
||||||
|
self.mailbox.get_length()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* draw_entry() doesn't take &mut self because borrow checker complains if it's called from
|
/* draw_entry() doesn't take &mut self because borrow checker complains if it's called from
|
||||||
* another method. */
|
* another method. */
|
||||||
fn draw_entry(
|
fn draw_entry(
|
||||||
|
@ -465,7 +486,7 @@ impl Index {
|
||||||
ncurses::wattroff(win, attr);
|
ncurses::wattroff(win, attr);
|
||||||
}
|
}
|
||||||
fn show_pager(&mut self) {
|
fn show_pager(&mut self) {
|
||||||
if self.mailbox.get_length() == 0 {
|
if self.get_length() == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
||||||
|
@ -475,22 +496,11 @@ impl Index {
|
||||||
} else {
|
} else {
|
||||||
&mut self.mailbox.collection[self.cursor_idx]
|
&mut self.mailbox.collection[self.cursor_idx]
|
||||||
};
|
};
|
||||||
let mut pager = super::pager::Pager::new(self.win, x);
|
let mut pager = Pager::new(self.win, x);
|
||||||
|
/* TODO: Fix this: */
|
||||||
pager.scroll(ncurses::KEY_DOWN);
|
pager.scroll(ncurses::KEY_DOWN);
|
||||||
pager.scroll(ncurses::KEY_UP);
|
pager.scroll(ncurses::KEY_UP);
|
||||||
let mut ch = ncurses::getch();
|
self.pager = Some(pager);
|
||||||
while ch != ncurses::KEY_F(1) {
|
|
||||||
match ch {
|
|
||||||
m @ ncurses::KEY_UP |
|
|
||||||
m @ ncurses::KEY_DOWN |
|
|
||||||
m @ ncurses::KEY_NPAGE |
|
|
||||||
m @ ncurses::KEY_PPAGE => {
|
|
||||||
pager.scroll(m);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
ch = ncurses::getch();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Drop for Index {
|
impl Drop for Index {
|
||||||
|
|
|
@ -23,6 +23,8 @@ pub mod index;
|
||||||
pub mod pager;
|
pub mod pager;
|
||||||
|
|
||||||
extern crate ncurses;
|
extern crate ncurses;
|
||||||
|
extern crate melib;
|
||||||
|
use melib::mailbox::backends::RefreshEvent;
|
||||||
|
|
||||||
/* Color pairs; foreground && background. */
|
/* Color pairs; foreground && background. */
|
||||||
pub static COLOR_PAIR_DEFAULT: i16 = 1;
|
pub static COLOR_PAIR_DEFAULT: i16 = 1;
|
||||||
|
@ -64,3 +66,15 @@ impl Drop for TUI {
|
||||||
ncurses::endwin();
|
ncurses::endwin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ThreadEvent {
|
||||||
|
Input(ncurses::WchResult),
|
||||||
|
RefreshMailbox{ name: String },
|
||||||
|
//Decode { _ }, // For gpg2 signature check
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RefreshEvent> for ThreadEvent {
|
||||||
|
fn from(event: RefreshEvent) -> Self {
|
||||||
|
ThreadEvent::RefreshMailbox { name: event.folder }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue