commit
351c9d9a29
|
@ -0,0 +1,10 @@
|
|||
target/
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
target/
|
||||
**/*.rs.bk
|
||||
target/
|
||||
**/*.rs.bk
|
||||
.gdb_history
|
||||
*.log
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "nutt"
|
||||
version = "0.1.0"
|
||||
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
|
||||
|
||||
[dependencies]
|
||||
mailparse = "0.5.1"
|
||||
maildir = "0.1.1"
|
||||
[dependencies.ncurses]
|
||||
features = ["wide"]
|
||||
optional = false
|
||||
version = "5.86.0"
|
|
@ -0,0 +1,40 @@
|
|||
extern crate ncurses;
|
||||
extern crate maildir;
|
||||
extern crate mailparse;
|
||||
|
||||
mod ui;
|
||||
|
||||
use ui::index::*;
|
||||
|
||||
fn main() {
|
||||
let locale_conf = ncurses::LcCategory::all;
|
||||
ncurses::setlocale(locale_conf, "en_US.UTF-8");
|
||||
ui::initialize();
|
||||
let maildir = maildir::Maildir::from("./Inbox");
|
||||
let iter = maildir.list_cur();
|
||||
let mut index = Index::new(iter);
|
||||
ncurses::refresh();
|
||||
|
||||
index.draw();
|
||||
|
||||
let mut ch;
|
||||
loop {
|
||||
ch = ncurses::get_wch();
|
||||
match ch {
|
||||
Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_UP)) |
|
||||
Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_DOWN)) => {
|
||||
index.scroll(k);
|
||||
continue;
|
||||
}
|
||||
Some(ncurses::WchResult::Char(10)) => {
|
||||
index.show_pager();
|
||||
index.draw();
|
||||
continue;
|
||||
}
|
||||
Some(ncurses::WchResult::KeyCode(ncurses::KEY_F1)) => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
extern crate ncurses;
|
||||
extern crate maildir;
|
||||
extern crate mailparse;
|
||||
use mailparse::*;
|
||||
|
||||
pub struct Index {
|
||||
mailbox: Vec<(usize, maildir::MailEntry)>,
|
||||
length: usize,
|
||||
|
||||
win: ncurses::WINDOW,
|
||||
pad: ncurses::WINDOW,
|
||||
screen_width: i32,
|
||||
screen_height: i32,
|
||||
|
||||
cursor_idx: usize,
|
||||
}
|
||||
impl Index {
|
||||
pub fn new(iter: maildir::MailEntries) -> Index {
|
||||
let mut collection: Vec<(usize, maildir::MailEntry)> = Vec::new();
|
||||
for (i, x) in iter.enumerate() {
|
||||
collection.push((i, x.unwrap()));
|
||||
}
|
||||
let length = collection.len();
|
||||
let mut screen_height = 0;
|
||||
let mut screen_width = 0;
|
||||
/* Get the screen bounds. */
|
||||
ncurses::getmaxyx(ncurses::stdscr(), &mut screen_height, &mut screen_width);
|
||||
// let win = ncurses::newwin( ncurses::LINES(), ncurses::COLS()-30, 0, 30);
|
||||
let win = ncurses::newwin(0, 0, 0, 0);
|
||||
ncurses::getmaxyx(win, &mut screen_height, &mut screen_width);
|
||||
ncurses::wbkgd(
|
||||
win,
|
||||
' ' as ncurses::chtype |
|
||||
ncurses::COLOR_PAIR(super::COLOR_PAIR_DEFAULT) as ncurses::chtype,
|
||||
);
|
||||
//eprintln!("length is {}\n", length);
|
||||
let pad = ncurses::newpad(length as i32, screen_width);
|
||||
Index {
|
||||
mailbox: collection,
|
||||
length: length,
|
||||
win: win,
|
||||
pad: pad,
|
||||
screen_width: 0,
|
||||
screen_height: 0,
|
||||
cursor_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&mut self) {
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
ncurses::getbegyx(self.win, &mut y, &mut x);
|
||||
|
||||
ncurses::wclear(self.pad);
|
||||
|
||||
for &mut (i, ref mut x) in self.mailbox.as_mut_slice() {
|
||||
if i == self.cursor_idx {
|
||||
ncurses::wattron(self.pad, ncurses::COLOR_PAIR(super::COLOR_PAIR_CURSOR));
|
||||
}
|
||||
Index::draw_entry(self.pad, x, i);
|
||||
if i == self.cursor_idx {
|
||||
ncurses::wattroff(self.pad, ncurses::COLOR_PAIR(super::COLOR_PAIR_CURSOR));
|
||||
}
|
||||
}
|
||||
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
||||
let pminrow =
|
||||
(self.cursor_idx as i32).wrapping_div(self.screen_height) * self.screen_height;
|
||||
ncurses::prefresh(
|
||||
self.pad,
|
||||
pminrow,
|
||||
0,
|
||||
y,
|
||||
x,
|
||||
self.screen_height - 1,
|
||||
self.screen_width - 1,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn scroll(&mut self, motion: i32) {
|
||||
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
||||
if self.screen_height == 0 {
|
||||
return;
|
||||
}
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
ncurses::getbegyx(self.win, &mut y, &mut x);
|
||||
let prev_idx = self.cursor_idx;
|
||||
match motion {
|
||||
ncurses::KEY_UP => {
|
||||
if self.cursor_idx > 0 {
|
||||
self.cursor_idx -= 1;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ncurses::KEY_DOWN => {
|
||||
if self.cursor_idx < self.length - 1 {
|
||||
self.cursor_idx += 1;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw newly highlighted entry */
|
||||
ncurses::wmove(self.pad, self.cursor_idx as i32, 0);
|
||||
/* Borrow x from self.mailbox in separate scopes or else borrow checker complains */
|
||||
{
|
||||
let (_, ref mut x) = self.mailbox.as_mut_slice()[self.cursor_idx];
|
||||
ncurses::wattron(self.pad, ncurses::COLOR_PAIR(super::COLOR_PAIR_CURSOR));
|
||||
Index::draw_entry(self.pad, x, self.cursor_idx);
|
||||
ncurses::wattroff(self.pad, ncurses::COLOR_PAIR(super::COLOR_PAIR_CURSOR));
|
||||
}
|
||||
/* Draw previous highlighted entry normally */
|
||||
ncurses::wmove(self.pad, prev_idx as i32, 0);
|
||||
{
|
||||
let (_, ref mut x) = self.mailbox.as_mut_slice()[prev_idx];
|
||||
Index::draw_entry(self.pad, x, prev_idx);
|
||||
}
|
||||
|
||||
/* Calculate the pad row of the first entry to be displayed in the window */
|
||||
let pminrow =
|
||||
(self.cursor_idx as i32).wrapping_div(self.screen_height) * self.screen_height;
|
||||
let pminrow_prev = (prev_idx as i32).wrapping_div(self.screen_height) * self.screen_height;
|
||||
/*
|
||||
* Refresh window if new page has less rows than window rows, ie
|
||||
* window rows = r
|
||||
* pad rows (total emails) = n
|
||||
* pminrow = i
|
||||
*
|
||||
* β- i
|
||||
* β i+1
|
||||
* β i+2
|
||||
* r β₯ ...
|
||||
* β n
|
||||
* β .. β
|
||||
* β i-2 β 'dead' entries (must be empty)
|
||||
* β i-1 β
|
||||
*/
|
||||
if pminrow != pminrow_prev && pminrow + self.screen_height > self.length as i32 {
|
||||
/* touch Index window (tell ncurses to redraw the entire window in next refresh */
|
||||
ncurses::touchwin(self.win);
|
||||
ncurses::wrefresh(self.win);
|
||||
}
|
||||
ncurses::prefresh(
|
||||
self.pad,
|
||||
pminrow,
|
||||
0,
|
||||
y,
|
||||
x,
|
||||
self.screen_height - 1,
|
||||
self.screen_width - 1,
|
||||
);
|
||||
}
|
||||
fn draw_entry(win: ncurses::WINDOW, entry: &mut maildir::MailEntry, i: usize) {
|
||||
ncurses::waddstr(win, &format!("{}\t", i));
|
||||
let d = entry.headers().unwrap().get_first_value("Date").unwrap();
|
||||
match d {
|
||||
Some(t) => {
|
||||
ncurses::waddstr(win, &t.to_string());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ncurses::waddch(win, ' ' as u64);
|
||||
let c = entry.headers().unwrap().get_first_value("Subject").unwrap();
|
||||
match c {
|
||||
Some(t) => {
|
||||
ncurses::waddstr(win, &t.to_string());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ncurses::wprintw(win, "\n");
|
||||
}
|
||||
pub fn show_pager(&mut self) {
|
||||
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
||||
let (_, ref mut mail) = self.mailbox[self.cursor_idx];
|
||||
let mut pager = super::pager::Pager::new(self.win, mail);
|
||||
pager.scroll(ncurses::KEY_DOWN);
|
||||
pager.scroll(ncurses::KEY_UP);
|
||||
let mut ch = ncurses::getch();
|
||||
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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// ncurses::wprintw(pager, "test\n");
|
||||
//ncurses::wrefresh(pager);
|
||||
ch = ncurses::getch();
|
||||
}
|
||||
drop(pager);
|
||||
ncurses::wrefresh(self.win);
|
||||
}
|
||||
}
|
||||
impl Drop for Index {
|
||||
fn drop(&mut self) {
|
||||
/* Final prompt before closing. */
|
||||
//ncurses::mv(self.screen_height - 1, 0);
|
||||
//prompt();
|
||||
ncurses::delwin(self.win);
|
||||
ncurses::delwin(self.pad);
|
||||
ncurses::endwin();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
extern crate ncurses;
|
||||
pub mod index;
|
||||
pub mod pager;
|
||||
/* Color pairs; foreground && background. */
|
||||
pub static COLOR_PAIR_DEFAULT: i16 = 1;
|
||||
pub static COLOR_PAIR_CURSOR: i16 = 2;
|
||||
pub static COLOR_PAIR_HEADERS: i16 = 3;
|
||||
pub fn initialize() {
|
||||
/* start ncurses */
|
||||
ncurses::initscr();
|
||||
ncurses::keypad(ncurses::stdscr(), true);
|
||||
ncurses::noecho();
|
||||
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
||||
/* Start colors. */
|
||||
|
||||
ncurses::start_color();
|
||||
|
||||
ncurses::init_pair(COLOR_PAIR_DEFAULT, 15, 0);
|
||||
ncurses::init_pair(COLOR_PAIR_CURSOR, 251, 235);
|
||||
ncurses::init_pair(COLOR_PAIR_HEADERS, 33, 0);
|
||||
|
||||
/* Set the window's background color. */
|
||||
ncurses::bkgd(
|
||||
' ' as ncurses::chtype | ncurses::COLOR_PAIR(COLOR_PAIR_DEFAULT) as ncurses::chtype,
|
||||
);
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
extern crate ncurses;
|
||||
extern crate maildir;
|
||||
extern crate mailparse;
|
||||
use mailparse::*;
|
||||
pub struct Pager {
|
||||
win: ncurses::WINDOW,
|
||||
pad: ncurses::WINDOW,
|
||||
rows: i32,
|
||||
header_height: i32,
|
||||
|
||||
curr_y: i32,
|
||||
}
|
||||
|
||||
impl Pager {
|
||||
pub fn new(parent: ncurses::WINDOW, entry: &mut maildir::MailEntry) -> Pager {
|
||||
|
||||
let mut screen_height = 0;
|
||||
let mut screen_width = 0;
|
||||
ncurses::getmaxyx(parent, &mut screen_height, &mut screen_width);
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
ncurses::getbegyx(parent, &mut y, &mut x);
|
||||
let win = ncurses::subwin(
|
||||
parent,
|
||||
screen_height - (screen_height / 3),
|
||||
screen_width,
|
||||
y + (screen_height / 3),
|
||||
x,
|
||||
);
|
||||
ncurses::wclear(win);
|
||||
ncurses::touchwin(win);
|
||||
for _ in 1..screen_width + 1 {
|
||||
ncurses::waddstr(win, "β");
|
||||
}
|
||||
//ncurses::wprintw(win, "\n");
|
||||
let (mail_pad, rows, header_height) = Pager::print_entry(win, entry);
|
||||
ncurses::wrefresh(win);
|
||||
Pager {
|
||||
pad: mail_pad,
|
||||
win: win,
|
||||
rows: rows,
|
||||
header_height: header_height,
|
||||
curr_y: 0,
|
||||
}
|
||||
}
|
||||
pub fn scroll(&mut self, motion: i32) {
|
||||
let mut h = 0;
|
||||
let mut w = 0;
|
||||
ncurses::getmaxyx(self.win, &mut h, &mut w);
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
ncurses::getparyx(self.win, &mut y, &mut x);
|
||||
let mut p_x = 0;
|
||||
let mut p_y = 0;
|
||||
ncurses::getbegyx(self.win, &mut p_y, &mut p_x);
|
||||
let pager_size: i32 = h - self.header_height;
|
||||
if pager_size == 0 {
|
||||
return;
|
||||
}
|
||||
match motion {
|
||||
ncurses::KEY_UP => {
|
||||
if self.curr_y > 0 {
|
||||
self.curr_y -= 1;
|
||||
}
|
||||
}
|
||||
ncurses::KEY_DOWN => {
|
||||
if self.curr_y < self.rows && self.rows - self.curr_y > pager_size {
|
||||
self.curr_y += 1;
|
||||
}
|
||||
}
|
||||
ncurses::KEY_NPAGE => {
|
||||
if self.curr_y + h < self.rows && self.rows - self.curr_y - h > pager_size {
|
||||
self.curr_y += pager_size;
|
||||
} else {
|
||||
self.curr_y = if self.rows > h {
|
||||
self.rows - pager_size
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
}
|
||||
ncurses::KEY_PPAGE => {
|
||||
if self.curr_y >= pager_size {
|
||||
self.curr_y -= pager_size;
|
||||
} else {
|
||||
self.curr_y = 0
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
/*
|
||||
* β βββββββββββ β
|
||||
* β β β β
|
||||
* y β β β
|
||||
* β β β β
|
||||
* β xββββββββββ« β β index
|
||||
* β β β β β
|
||||
* h β β β pager β
|
||||
* β ββββββββββw β β
|
||||
*/
|
||||
ncurses::touchwin(self.win);
|
||||
ncurses::prefresh(
|
||||
self.pad,
|
||||
self.curr_y,
|
||||
0,
|
||||
y + self.header_height,
|
||||
p_x + x,
|
||||
y + h - 1,
|
||||
w - 1,
|
||||
);
|
||||
}
|
||||
fn print_entry_headers(win: ncurses::WINDOW, mail: &mut maildir::MailEntry) -> i32 {
|
||||
let mut i = 0;
|
||||
ncurses::wattron(win, ncurses::COLOR_PAIR(super::COLOR_PAIR_HEADERS));
|
||||
ncurses::waddstr(win, "Date: ");
|
||||
ncurses::waddstr(
|
||||
win,
|
||||
&mail.headers()
|
||||
.unwrap()
|
||||
.get_first_value("Date")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
);
|
||||
ncurses::waddstr(win, "\n");
|
||||
i += 1;
|
||||
ncurses::waddstr(win, "From: ");
|
||||
ncurses::waddstr(
|
||||
win,
|
||||
&mail.headers()
|
||||
.unwrap()
|
||||
.get_first_value("From")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
);
|
||||
ncurses::waddstr(win, "\n");
|
||||
i += 1;
|
||||
ncurses::waddstr(win, "To: ");
|
||||
ncurses::waddstr(
|
||||
win,
|
||||
&mail.headers()
|
||||
.unwrap()
|
||||
.get_first_value("To")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
);
|
||||
ncurses::waddstr(win, "\n");
|
||||
i += 1;
|
||||
ncurses::waddstr(win, "Subject: ");
|
||||
ncurses::waddstr(
|
||||
win,
|
||||
&mail.headers()
|
||||
.unwrap()
|
||||
.get_first_value("Subject")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
);
|
||||
ncurses::waddstr(win, "\n");
|
||||
i += 1;
|
||||
ncurses::wattroff(win, ncurses::COLOR_PAIR(super::COLOR_PAIR_HEADERS));
|
||||
/* return number of header lines so we don't overwrite it */
|
||||
i
|
||||
}
|
||||
fn print_entry_content(
|
||||
win: ncurses::WINDOW,
|
||||
mail: &mut maildir::MailEntry,
|
||||
height: i32,
|
||||
) -> (ncurses::WINDOW, i32, i32) {
|
||||
let mut h = 0;
|
||||
let mut w = 0;
|
||||
/* height and width of self.win, the pager window */
|
||||
ncurses::getmaxyx(win, &mut h, &mut w);
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
/* y,x coordinates of upper left corner of win */
|
||||
ncurses::getparyx(win, &mut y, &mut x);
|
||||
match &mail.parsed() {
|
||||
&Ok(ref v) => {
|
||||
match &v.get_body() {
|
||||
&Ok(ref b) => {
|
||||
let lines: Vec<&str> = b.split('\n').collect();
|
||||
let lines_length = lines.len();
|
||||
let pad = ncurses::newpad(lines_length as i32, 1024);
|
||||
ncurses::wclear(pad);
|
||||
for l in lines {
|
||||
ncurses::waddstr(pad, &l.replace("%", "%%"));
|
||||
ncurses::waddstr(pad, "\n");
|
||||
}
|
||||
/*
|
||||
* β βββββββββββ β
|
||||
* β β β β
|
||||
* y β β β
|
||||
* β β β β
|
||||
* β xββββββββββ« β β index
|
||||
* β β β β β
|
||||
* h β β β pager β
|
||||
* β ββββββββββw β β
|
||||
*/
|
||||
ncurses::pnoutrefresh(pad, 0, 0, y + height, x, y + height - 1, w - 1);
|
||||
return (pad, lines_length as i32, height);
|
||||
}
|
||||
_ => {
|
||||
return (ncurses::newpad(0, 0), 0, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return (ncurses::newpad(0, 0), 0, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn print_entry(
|
||||
win: ncurses::WINDOW,
|
||||
mail: &mut maildir::MailEntry,
|
||||
) -> (ncurses::WINDOW, i32, i32) {
|
||||
let header_height = Pager::print_entry_headers(win, mail);
|
||||
Pager::print_entry_content(win, mail, header_height + 2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Drop for Pager {
|
||||
fn drop(&mut self) {
|
||||
//ncurses::touchwin(self.win);
|
||||
ncurses::delwin(self.win);
|
||||
ncurses::delwin(self.pad);
|
||||
}
|
||||
}
|
Loadingβ¦
Reference in New Issue