parent
6dd247b371
commit
8c98d3a5a0
22
Cargo.toml
22
Cargo.toml
|
@ -3,32 +3,16 @@ name = "meli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
|
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "melib"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "meli"
|
name = "meli"
|
||||||
path = "src/bin.rs"
|
path = "src/bin.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4"
|
|
||||||
xdg = "2.1.0"
|
|
||||||
config = "0.6"
|
|
||||||
serde_derive = "^1.0.8"
|
|
||||||
serde = "^1.0.8"
|
|
||||||
nom = "3.2.0"
|
|
||||||
memmap = "0.5.2"
|
|
||||||
base64 = "*"
|
|
||||||
crossbeam = "^0.3.0"
|
|
||||||
fnv = "1.0.3"
|
|
||||||
encoding = "0.2.33"
|
|
||||||
bitflags = "1.0"
|
|
||||||
notify = "4.0.1"
|
|
||||||
termion = "1.5.1"
|
|
||||||
chan = "0.1.21"
|
chan = "0.1.21"
|
||||||
chan-signal = "0.3.1"
|
chan-signal = "0.3.1"
|
||||||
notify-rust = "^3"
|
melib = { path = "melib", version = "*" }
|
||||||
|
|
||||||
|
ui = { path = "ui", version = "*" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
#lto = true
|
#lto = true
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
[package]
|
||||||
|
name = "melib"
|
||||||
|
version = "0.0.1" #:version
|
||||||
|
authors = []
|
||||||
|
workspace = ".."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
notify = "4.0.1"
|
||||||
|
notify-rust = "^3"
|
||||||
|
crossbeam = "^0.3.0"
|
||||||
|
fnv = "1.0.3"
|
||||||
|
config = "0.6"
|
||||||
|
xdg = "2.1.0"
|
||||||
|
chrono = "0.4"
|
||||||
|
serde_derive = "^1.0.8"
|
||||||
|
serde = "^1.0.8"
|
||||||
|
nom = "3.2.0"
|
||||||
|
memmap = "0.5.2"
|
||||||
|
base64 = "*"
|
||||||
|
encoding = "0.2.33"
|
||||||
|
bitflags = "1.0"
|
||||||
|
termion = "1.5.1"
|
|
@ -204,7 +204,7 @@ impl AttachmentBuilder {
|
||||||
};
|
};
|
||||||
AttachmentType::Multipart {
|
AttachmentType::Multipart {
|
||||||
of_type: multipart_type,
|
of_type: multipart_type,
|
||||||
subattachments: Attachment::subattachments(&self.raw, b),
|
subattachments: Self::subattachments(&self.raw, b),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ContentType::Unsupported { ref tag } => AttachmentType::Data { tag: tag.clone() },
|
ContentType::Unsupported { ref tag } => AttachmentType::Data { tag: tag.clone() },
|
||||||
|
@ -216,6 +216,48 @@ impl AttachmentBuilder {
|
||||||
attachment_type: attachment_type,
|
attachment_type: attachment_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn subattachments(raw: &[u8], boundary: &str) -> Vec<Attachment> {
|
||||||
|
let boundary_length = boundary.len();
|
||||||
|
match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result()
|
||||||
|
{
|
||||||
|
Ok(attachments) => {
|
||||||
|
let mut vec = Vec::with_capacity(attachments.len());
|
||||||
|
for a in attachments {
|
||||||
|
let (headers, body) = match parser::attachment(a).to_full_result() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("error in parsing attachment");
|
||||||
|
eprintln!("\n-------------------------------");
|
||||||
|
eprintln!("{}\n", ::std::string::String::from_utf8_lossy(a));
|
||||||
|
eprintln!("-------------------------------\n");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut builder = AttachmentBuilder::new(body);
|
||||||
|
for (name, value) in headers {
|
||||||
|
if name.eq_ignore_ascii_case("content-type") {
|
||||||
|
builder.content_type(value);
|
||||||
|
} else if name.eq_ignore_ascii_case("content-transfer-encoding") {
|
||||||
|
builder.content_transfer_encoding(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec.push(builder.build());
|
||||||
|
}
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
a => {
|
||||||
|
eprintln!(
|
||||||
|
"error {:?}\n\traw: {:?}\n\tboundary: {:?}",
|
||||||
|
a,
|
||||||
|
::std::str::from_utf8(raw).unwrap(),
|
||||||
|
boundary
|
||||||
|
);
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -267,45 +309,28 @@ impl Attachment {
|
||||||
pub fn get_tag(&self) -> String {
|
pub fn get_tag(&self) -> String {
|
||||||
format!("{}/{}", self.content_type.0, self.content_type.1).to_string()
|
format!("{}/{}", self.content_type.0, self.content_type.1).to_string()
|
||||||
}
|
}
|
||||||
pub fn subattachments(raw: &[u8], boundary: &str) -> Vec<Attachment> {
|
pub fn count_attachments(&mut self) -> usize {
|
||||||
let boundary_length = boundary.len();
|
let mut counter = 0;
|
||||||
match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result()
|
|
||||||
{
|
|
||||||
Ok(attachments) => {
|
|
||||||
let mut vec = Vec::with_capacity(attachments.len());
|
|
||||||
for a in attachments {
|
|
||||||
let (headers, body) = match parser::attachment(a).to_full_result() {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(_) => {
|
|
||||||
eprintln!("error in parsing attachment");
|
|
||||||
eprintln!("\n-------------------------------");
|
|
||||||
eprintln!("{}\n", ::std::string::String::from_utf8_lossy(a));
|
|
||||||
eprintln!("-------------------------------\n");
|
|
||||||
|
|
||||||
continue;
|
fn count_recursive(att: &Attachment, counter: &mut usize) {
|
||||||
}
|
match att.attachment_type {
|
||||||
};
|
AttachmentType::Data { .. } => {
|
||||||
let mut builder = AttachmentBuilder::new(body);
|
*counter += 1;
|
||||||
for (name, value) in headers {
|
|
||||||
if name.eq_ignore_ascii_case("content-type") {
|
|
||||||
builder.content_type(value);
|
|
||||||
} else if name.eq_ignore_ascii_case("content-transfer-encoding") {
|
|
||||||
builder.content_transfer_encoding(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vec.push(builder.build());
|
|
||||||
}
|
}
|
||||||
vec
|
AttachmentType::Text { .. } => {
|
||||||
}
|
}
|
||||||
a => {
|
AttachmentType::Multipart {
|
||||||
eprintln!(
|
of_type: ref multipart_type,
|
||||||
"error in 469 {:?}\n\traw: {:?}\n\tboundary: {:?}",
|
subattachments: ref sub_att_vec,
|
||||||
a,
|
} => if *multipart_type != MultipartType::Alternative {
|
||||||
::std::str::from_utf8(raw).unwrap(),
|
for a in sub_att_vec {
|
||||||
boundary
|
count_recursive(a, counter);
|
||||||
);
|
}
|
||||||
Vec::new()
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count_recursive(&self, &mut counter);
|
||||||
|
counter
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,12 +25,8 @@ The mail handling stuff is done in the `melib` crate which includes all backend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern crate melib;
|
extern crate melib;
|
||||||
#[macro_use]
|
extern crate ui;
|
||||||
extern crate nom;
|
|
||||||
extern crate termion;
|
|
||||||
extern crate notify_rust;
|
|
||||||
|
|
||||||
pub mod ui;
|
|
||||||
use ui::*;
|
use ui::*;
|
||||||
pub use melib::*;
|
pub use melib::*;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#![cfg(feature = "python")]
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[pymodinit(pythonmeli)]
|
||||||
|
fn pythonmeli(py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
|
// pyo3 aware function. All of our python interface could be declared in a separate module.
|
||||||
|
// Note that the `#[pyfn()]` annotation automatically converts the arguments from
|
||||||
|
// Python objects to Rust values; and the Rust return value back into a Python object.
|
||||||
|
#[pyfn(m, "sum_as_string")]
|
||||||
|
fn sum_as_string_py(_py: Python, a:i64, b:i64) -> PyResult<String> {
|
||||||
|
let out = sum_as_string(a, b);
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// logic implemented as a normal rust function
|
||||||
|
fn sum_as_string(a:i64, b:i64) -> String {
|
||||||
|
format!("{}", a + b).to_string()
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "ui"
|
||||||
|
version = "0.0.1" #:version
|
||||||
|
authors = []
|
||||||
|
workspace = ".."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
melib = { path = "../melib", version = "*" }
|
||||||
|
termion = "1.5.1"
|
||||||
|
chan = "0.1.21"
|
||||||
|
notify = "4.0.1"
|
||||||
|
notify-rust = "^3"
|
||||||
|
nom = "3.2.0"
|
|
@ -1,9 +1,4 @@
|
||||||
/*! Entities that handle Mail specific functions.
|
use super::*;
|
||||||
*/
|
|
||||||
use ui::components::*;
|
|
||||||
use ui::cells::*;
|
|
||||||
|
|
||||||
|
|
||||||
const MAX_COLS: usize = 500;
|
const MAX_COLS: usize = 500;
|
||||||
|
|
||||||
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
|
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
|
||||||
|
@ -30,9 +25,6 @@ impl MailListing {
|
||||||
format!("{} {} {:.85}",idx,&e.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string(),e.get_subject())
|
format!("{} {} {:.85}",idx,&e.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string(),e.get_subject())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let content = CellBuffer::new(0, 0, Cell::with_char(' '));
|
let content = CellBuffer::new(0, 0, Cell::with_char(' '));
|
||||||
MailListing {
|
MailListing {
|
||||||
|
@ -60,7 +52,12 @@ impl MailListing {
|
||||||
// Inform State that we changed the current folder view.
|
// Inform State that we changed the current folder view.
|
||||||
context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)) });
|
context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)) });
|
||||||
|
|
||||||
self.length = mailbox.len();
|
self.length = if threaded {
|
||||||
|
mailbox.threaded_collection.len()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
mailbox.len()
|
||||||
|
};
|
||||||
let mut content = CellBuffer::new(MAX_COLS, self.length+1, Cell::with_char(' '));
|
let mut content = CellBuffer::new(MAX_COLS, self.length+1, Cell::with_char(' '));
|
||||||
if self.length == 0 {
|
if self.length == 0 {
|
||||||
write_string_to_grid(&format!("Folder `{}` is empty.",
|
write_string_to_grid(&format!("Folder `{}` is empty.",
|
||||||
|
@ -68,7 +65,8 @@ impl MailListing {
|
||||||
&mut content,
|
&mut content,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
((0, 0), (MAX_COLS-1, 0)));
|
((0, 0), (MAX_COLS-1, 0)),
|
||||||
|
true);
|
||||||
self.content = content;
|
self.content = content;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -118,11 +116,12 @@ impl MailListing {
|
||||||
} else {
|
} else {
|
||||||
Color::Default
|
Color::Default
|
||||||
};
|
};
|
||||||
let x = write_string_to_grid(&MailListing::make_thread_entry(envelope, idx, indentation, container, &indentations),
|
let (x, y) = write_string_to_grid(&MailListing::make_thread_entry(envelope, idx, indentation, container, &indentations),
|
||||||
&mut content,
|
&mut content,
|
||||||
fg_color,
|
fg_color,
|
||||||
bg_color,
|
bg_color,
|
||||||
((0, idx) , (MAX_COLS-1, idx)));
|
((0, idx) , (MAX_COLS-1, idx)),
|
||||||
|
false);
|
||||||
for x in x..MAX_COLS {
|
for x in x..MAX_COLS {
|
||||||
content[(x,idx)].set_ch(' ');
|
content[(x,idx)].set_ch(' ');
|
||||||
content[(x,idx)].set_bg(bg_color);
|
content[(x,idx)].set_bg(bg_color);
|
||||||
|
@ -171,11 +170,12 @@ impl MailListing {
|
||||||
} else {
|
} else {
|
||||||
Color::Default
|
Color::Default
|
||||||
};
|
};
|
||||||
let x = write_string_to_grid(&MailListing::make_entry_string(envelope, idx),
|
let (x, y)= write_string_to_grid(&MailListing::make_entry_string(envelope, idx),
|
||||||
&mut content,
|
&mut content,
|
||||||
fg_color,
|
fg_color,
|
||||||
bg_color,
|
bg_color,
|
||||||
((0, y) , (MAX_COLS-1, y)));
|
((0, y) , (MAX_COLS-1, y)),
|
||||||
|
false);
|
||||||
|
|
||||||
for x in x..MAX_COLS {
|
for x in x..MAX_COLS {
|
||||||
content[(x,y)].set_ch(' ');
|
content[(x,y)].set_ch(' ');
|
||||||
|
@ -189,9 +189,15 @@ impl MailListing {
|
||||||
|
|
||||||
self.content = content;
|
self.content = content;
|
||||||
}
|
}
|
||||||
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &mut Context) {
|
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
|
||||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
||||||
let envelope: &Envelope = &mailbox.collection[idx];
|
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
||||||
|
let envelope: &Envelope = if threaded {
|
||||||
|
let i = mailbox.get_threaded_mail(idx);
|
||||||
|
&mailbox.collection[i]
|
||||||
|
} else {
|
||||||
|
&mailbox.collection[idx]
|
||||||
|
};
|
||||||
|
|
||||||
let fg_color = if !envelope.is_seen() {
|
let fg_color = if !envelope.is_seen() {
|
||||||
Color::Byte(0)
|
Color::Byte(0)
|
||||||
|
@ -202,7 +208,7 @@ impl MailListing {
|
||||||
Color::Byte(246)
|
Color::Byte(246)
|
||||||
} else {
|
} else {
|
||||||
if !envelope.is_seen() {
|
if !envelope.is_seen() {
|
||||||
Color::Byte(252)
|
Color::Byte(251)
|
||||||
} else if idx % 2 == 0 {
|
} else if idx % 2 == 0 {
|
||||||
Color::Byte(236)
|
Color::Byte(236)
|
||||||
} else {
|
} else {
|
||||||
|
@ -256,40 +262,6 @@ impl MailListing {
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a pager for the `Envelope` currently under the cursor.
|
|
||||||
fn draw_header_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
|
||||||
{
|
|
||||||
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
|
||||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
|
||||||
let envelope: &Envelope = if threaded {
|
|
||||||
let i = mailbox.get_threaded_mail(self.cursor_pos.2);
|
|
||||||
&mailbox.collection[i]
|
|
||||||
} else {
|
|
||||||
&mailbox.collection[self.cursor_pos.2]
|
|
||||||
};
|
|
||||||
|
|
||||||
let pager_filter = context.settings.pager.filter.clone();
|
|
||||||
self.pager = Some(Pager::new(&envelope, pager_filter));
|
|
||||||
}
|
|
||||||
self.pager.as_mut().map(|p| p.draw(grid, area, context));
|
|
||||||
}
|
|
||||||
/// Create a pager for the `Envelope` currently under the cursor.
|
|
||||||
fn draw_attachment_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
|
||||||
{
|
|
||||||
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
|
||||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
|
||||||
let envelope: &Envelope = if threaded {
|
|
||||||
let i = mailbox.get_threaded_mail(self.cursor_pos.2);
|
|
||||||
&mailbox.collection[i]
|
|
||||||
} else {
|
|
||||||
&mailbox.collection[self.cursor_pos.2]
|
|
||||||
};
|
|
||||||
|
|
||||||
let pager_filter = context.settings.pager.filter.clone();
|
|
||||||
self.pager = Some(Pager::new(&envelope, pager_filter));
|
|
||||||
}
|
|
||||||
self.pager.as_mut().map(|p| p.draw(grid, area, context));
|
|
||||||
}
|
|
||||||
/// Create a pager for the `Envelope` currently under the cursor.
|
/// Create a pager for the `Envelope` currently under the cursor.
|
||||||
fn draw_mail_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw_mail_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
{
|
{
|
||||||
|
@ -335,6 +307,8 @@ impl MailListing {
|
||||||
}
|
}
|
||||||
s.push('─'); s.push('>');
|
s.push('─'); s.push('>');
|
||||||
}
|
}
|
||||||
|
s.push_str(&format!(" {}∞ ", envelope.get_body().count_attachments()));
|
||||||
|
|
||||||
if show_subject {
|
if show_subject {
|
||||||
s.push_str(&format!("{:.85}", envelope.get_subject()));
|
s.push_str(&format!("{:.85}", envelope.get_subject()));
|
||||||
}
|
}
|
||||||
|
@ -360,37 +334,23 @@ impl Component for MailListing {
|
||||||
clear_area(grid, area);
|
clear_area(grid, area);
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
}
|
}
|
||||||
// TODO: Make this configurable. User should be able to choose what headers to display,
|
|
||||||
// and toggle between full header view and custom header view like in mutt.
|
|
||||||
let headers_rows: usize = 6;
|
|
||||||
|
|
||||||
/* Render the mail body in a pager, basically copy what HSplit does */
|
/* Render the mail body in a pager, basically copy what HSplit does */
|
||||||
let total_rows = get_y(bottom_right) - get_y(upper_left);
|
let total_rows = get_y(bottom_right) - get_y(upper_left);
|
||||||
let pager_ratio = context.settings.pager.pager_ratio;
|
let pager_ratio = context.settings.pager.pager_ratio;
|
||||||
let mut bottom_entity_rows = (pager_ratio*total_rows )/100;
|
let bottom_entity_rows = (pager_ratio*total_rows )/100;
|
||||||
if bottom_entity_rows < headers_rows + 2 {
|
|
||||||
bottom_entity_rows = headers_rows + 2;
|
|
||||||
}
|
|
||||||
if bottom_entity_rows > total_rows {
|
if bottom_entity_rows > total_rows {
|
||||||
clear_area(grid, area);
|
clear_area(grid, area);
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
|
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
|
||||||
|
|
||||||
if !self.dirty {
|
|
||||||
if let Some(ref mut p) = self.pager {
|
|
||||||
p.draw(grid,
|
|
||||||
((get_x(upper_left), get_y(upper_left) + mid + headers_rows + 1), bottom_right),
|
|
||||||
context);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.dirty = false;
|
|
||||||
self.draw_list(grid,
|
self.draw_list(grid,
|
||||||
(upper_left, (get_x(bottom_right), get_y(upper_left)+ mid-1)),
|
(upper_left, (get_x(bottom_right), get_y(upper_left)+ mid-1)),
|
||||||
context);
|
context);
|
||||||
if self.length == 0 {
|
if self.length == 0 {
|
||||||
|
self.dirty = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -405,8 +365,10 @@ impl Component for MailListing {
|
||||||
grid[(i, mid)].set_ch('─');
|
grid[(i, mid)].set_ch('─');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: Make headers view configurable
|
||||||
|
|
||||||
/* Draw header */
|
/* Draw header */
|
||||||
|
let y =
|
||||||
{
|
{
|
||||||
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
||||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
||||||
|
@ -417,66 +379,82 @@ impl Component for MailListing {
|
||||||
&mailbox.collection[self.cursor_pos.2]
|
&mailbox.collection[self.cursor_pos.2]
|
||||||
};
|
};
|
||||||
|
|
||||||
let x = write_string_to_grid(&format!("Date: {}", envelope.get_date_as_str()),
|
let (x,y) = write_string_to_grid(&format!("Date: {}", envelope.get_date_as_str()),
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(33),
|
Color::Byte(33),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
(set_y(upper_left, mid+1), set_y(bottom_right, mid+1)));
|
(set_y(upper_left, mid+1), bottom_right),
|
||||||
|
true);
|
||||||
for x in x..=get_x(bottom_right) {
|
for x in x..=get_x(bottom_right) {
|
||||||
grid[(x, mid+1)].set_ch(' ');
|
grid[(x, y)].set_ch(' ');
|
||||||
grid[(x, mid+1)].set_bg(Color::Default);
|
grid[(x, y)].set_bg(Color::Default);
|
||||||
grid[(x, mid+1)].set_fg(Color::Default);
|
grid[(x, y)].set_fg(Color::Default);
|
||||||
}
|
}
|
||||||
let x = write_string_to_grid(&format!("From: {}", envelope.get_from()),
|
let (x,y) = write_string_to_grid(&format!("From: {}", envelope.get_from()),
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(33),
|
Color::Byte(33),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
(set_y(upper_left, mid+2), set_y(bottom_right, mid+2)));
|
(set_y(upper_left, y+1), bottom_right),
|
||||||
|
true);
|
||||||
for x in x..=get_x(bottom_right) {
|
for x in x..=get_x(bottom_right) {
|
||||||
grid[(x, mid+2)].set_ch(' ');
|
grid[(x, y)].set_ch(' ');
|
||||||
grid[(x, mid+2)].set_bg(Color::Default);
|
grid[(x, y)].set_bg(Color::Default);
|
||||||
grid[(x, mid+2)].set_fg(Color::Default);
|
grid[(x, y)].set_fg(Color::Default);
|
||||||
}
|
}
|
||||||
let x = write_string_to_grid(&format!("To: {}", envelope.get_to()),
|
let (x,y) = write_string_to_grid(&format!("To: {}", envelope.get_to()),
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(33),
|
Color::Byte(33),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
(set_y(upper_left, mid+3), set_y(bottom_right, mid+3)));
|
(set_y(upper_left, y+1), bottom_right),
|
||||||
|
true);
|
||||||
for x in x..=get_x(bottom_right) {
|
for x in x..=get_x(bottom_right) {
|
||||||
grid[(x, mid+3)].set_ch(' ');
|
grid[(x, y)].set_ch(' ');
|
||||||
grid[(x, mid+3)].set_bg(Color::Default);
|
grid[(x, y)].set_bg(Color::Default);
|
||||||
grid[(x, mid+3)].set_fg(Color::Default);
|
grid[(x, y)].set_fg(Color::Default);
|
||||||
}
|
}
|
||||||
let x = write_string_to_grid(&format!("Subject: {}", envelope.get_subject()),
|
let (x,y) = write_string_to_grid(&format!("Subject: {}", envelope.get_subject()),
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(33),
|
Color::Byte(33),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
(set_y(upper_left, mid+4), set_y(bottom_right, mid+4)));
|
(set_y(upper_left, y+1), bottom_right),
|
||||||
|
true);
|
||||||
for x in x..=get_x(bottom_right) {
|
for x in x..=get_x(bottom_right) {
|
||||||
grid[(x, mid+4)].set_ch(' ');
|
grid[(x, y)].set_ch(' ');
|
||||||
grid[(x, mid+4)].set_bg(Color::Default);
|
grid[(x, y)].set_bg(Color::Default);
|
||||||
grid[(x, mid+4)].set_fg(Color::Default);
|
grid[(x, y)].set_fg(Color::Default);
|
||||||
}
|
}
|
||||||
let x = write_string_to_grid(&format!("Message-ID: {}", envelope.get_message_id_raw()),
|
let (x, y) = write_string_to_grid(&format!("Message-ID: {}", envelope.get_message_id_raw()),
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(33),
|
Color::Byte(33),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
(set_y(upper_left, mid+5), set_y(bottom_right, mid+5)));
|
(set_y(upper_left, y+1), bottom_right),
|
||||||
|
true);
|
||||||
for x in x..=get_x(bottom_right) {
|
for x in x..=get_x(bottom_right) {
|
||||||
grid[(x, mid+5)].set_ch(' ');
|
grid[(x, y)].set_ch(' ');
|
||||||
grid[(x, mid+5)].set_bg(Color::Default);
|
grid[(x, y)].set_bg(Color::Default);
|
||||||
grid[(x, mid+5)].set_fg(Color::Default);
|
grid[(x, y)].set_fg(Color::Default);
|
||||||
}
|
}
|
||||||
|
clear_area(grid,
|
||||||
|
(set_y(upper_left, y+1), set_y(bottom_right, y+2)));
|
||||||
|
context.dirty_areas.push_back((set_y(upper_left, mid), set_y(bottom_right, y+1)));
|
||||||
|
y + 2
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if !self.dirty {
|
||||||
|
if let Some(ref mut p) = self.pager {
|
||||||
|
p.draw(grid,
|
||||||
|
((get_x(upper_left), y), bottom_right),
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
clear_area(grid,
|
|
||||||
(set_y(upper_left, mid+headers_rows), set_y(bottom_right, mid+headers_rows)));
|
|
||||||
|
|
||||||
|
self.dirty = false;
|
||||||
context.dirty_areas.push_back((set_y(upper_left, mid), set_y(bottom_right, mid+headers_rows)));
|
|
||||||
|
|
||||||
/* Draw body */
|
/* Draw body */
|
||||||
self.draw_mail_view(grid,
|
self.draw_mail_view(grid,
|
||||||
((get_x(upper_left), get_y(upper_left) + mid + headers_rows + 1), bottom_right),
|
((get_x(upper_left), y), bottom_right),
|
||||||
context);
|
context);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -563,180 +541,3 @@ impl Component for MailListing {
|
||||||
self.dirty || self.pager.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|
self.dirty || self.pager.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct AccountMenuEntry {
|
|
||||||
name: String,
|
|
||||||
index: usize,
|
|
||||||
entries: Vec<(usize, Folder)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AccountMenu {
|
|
||||||
accounts: Vec<AccountMenuEntry>,
|
|
||||||
dirty: bool,
|
|
||||||
cursor: Option<(usize, usize)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AccountMenu {
|
|
||||||
pub fn new(accounts: &Vec<Account>) -> Self {
|
|
||||||
let accounts = accounts.iter().enumerate().map(|(i, a)| {
|
|
||||||
AccountMenuEntry {
|
|
||||||
name: a.get_name().to_string(),
|
|
||||||
index: i,
|
|
||||||
entries: {
|
|
||||||
let mut entries = Vec::with_capacity(a.len());
|
|
||||||
for (idx, acc) in a.list_folders().iter().enumerate() {
|
|
||||||
entries.push((idx, acc.clone()));
|
|
||||||
}
|
|
||||||
entries}
|
|
||||||
}
|
|
||||||
}).collect();
|
|
||||||
AccountMenu {
|
|
||||||
accounts: accounts,
|
|
||||||
dirty: true,
|
|
||||||
cursor: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn print_account(&self, grid: &mut CellBuffer, area: Area, a: &AccountMenuEntry) -> 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.get_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 = String::from(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: &Vec<Option<usize>>, depth: &mut String, entries: &Vec<(usize, Folder)>, s: &mut String, inc: &mut usize) -> () {
|
|
||||||
let len = s.len();
|
|
||||||
s.insert_str(len, &format!("{} {}\n ", *inc, &entries[root].1.get_name()));
|
|
||||||
*inc += 1;
|
|
||||||
let children_no = entries[root].1.get_children().len();
|
|
||||||
for (idx, child) in entries[root].1.get_children().iter().enumerate() {
|
|
||||||
let len = s.len();
|
|
||||||
s.insert_str(len, &format!("{}├─", depth));
|
|
||||||
push(depth, if idx == children_no - 1 {'│'} else { ' ' });
|
|
||||||
print(*child, parents, depth, entries, s, inc);
|
|
||||||
pop(depth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for r in roots {
|
|
||||||
print(r, &parents, &mut depth, &a.entries, &mut s, &mut inc);
|
|
||||||
}
|
|
||||||
|
|
||||||
let lines: Vec<&str> = s.lines().collect();
|
|
||||||
let lines_len = lines.len();
|
|
||||||
let mut idx = 0;
|
|
||||||
for y in get_y(upper_left)..get_y(bottom_right) {
|
|
||||||
if idx == lines_len {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let s = if idx == lines_len - 2 {
|
|
||||||
format!("{}", lines[idx].replace("├", "└"))
|
|
||||||
} else {
|
|
||||||
format!("{}", lines[idx])
|
|
||||||
};
|
|
||||||
let color_fg = if highlight {
|
|
||||||
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
|
||||||
Color::Byte(233)
|
|
||||||
} else {
|
|
||||||
Color::Byte(15)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Color::Default
|
|
||||||
};
|
|
||||||
|
|
||||||
let color_bg = if highlight {
|
|
||||||
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
|
||||||
Color::Byte(15)
|
|
||||||
} else {
|
|
||||||
Color::Byte(233)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Color::Default
|
|
||||||
};
|
|
||||||
|
|
||||||
let x = write_string_to_grid(&s,
|
|
||||||
grid,
|
|
||||||
color_fg,
|
|
||||||
color_bg,
|
|
||||||
(set_y(upper_left, y), bottom_right));
|
|
||||||
|
|
||||||
if highlight && idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
|
||||||
change_colors(grid, ((x, y),(get_x(bottom_right)+1, y)), color_fg , color_bg);
|
|
||||||
} else {
|
|
||||||
change_colors(grid, ((x, y),set_y(bottom_right, y)), color_fg , color_bg);
|
|
||||||
}
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
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 bottom_right = bottom_right!(area);
|
|
||||||
self.dirty = false;
|
|
||||||
let mut y = get_y(upper_left) + 1;
|
|
||||||
for a in &self.accounts {
|
|
||||||
y += self.print_account(grid,
|
|
||||||
(set_y(upper_left, y), bottom_right),
|
|
||||||
&a);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.dirty_areas.push_back(area);
|
|
||||||
}
|
|
||||||
fn process_event(&mut self, event: &UIEvent, _context: &mut Context) {
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn is_dirty(&self) -> bool {
|
|
||||||
self.dirty
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*! Entities that handle Mail specific functions.
|
||||||
|
*/
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub mod listing;
|
||||||
|
pub use listing::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct AccountMenuEntry {
|
||||||
|
name: String,
|
||||||
|
index: usize,
|
||||||
|
entries: Vec<(usize, Folder)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AccountMenu {
|
||||||
|
accounts: Vec<AccountMenuEntry>,
|
||||||
|
dirty: bool,
|
||||||
|
cursor: Option<(usize, usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountMenu {
|
||||||
|
pub fn new(accounts: &Vec<Account>) -> Self {
|
||||||
|
let accounts = accounts.iter().enumerate().map(|(i, a)| {
|
||||||
|
AccountMenuEntry {
|
||||||
|
name: a.get_name().to_string(),
|
||||||
|
index: i,
|
||||||
|
entries: {
|
||||||
|
let mut entries = Vec::with_capacity(a.len());
|
||||||
|
for (idx, acc) in a.list_folders().iter().enumerate() {
|
||||||
|
entries.push((idx, acc.clone()));
|
||||||
|
}
|
||||||
|
entries}
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
AccountMenu {
|
||||||
|
accounts: accounts,
|
||||||
|
dirty: true,
|
||||||
|
cursor: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn print_account(&self, grid: &mut CellBuffer, area: Area, a: &AccountMenuEntry) -> 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.get_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 = String::from(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: &Vec<Option<usize>>, depth: &mut String, entries: &Vec<(usize, Folder)>, s: &mut String, inc: &mut usize) -> () {
|
||||||
|
let len = s.len();
|
||||||
|
s.insert_str(len, &format!("{} {}\n ", *inc, &entries[root].1.get_name()));
|
||||||
|
*inc += 1;
|
||||||
|
let children_no = entries[root].1.get_children().len();
|
||||||
|
for (idx, child) in entries[root].1.get_children().iter().enumerate() {
|
||||||
|
let len = s.len();
|
||||||
|
s.insert_str(len, &format!("{}├─", depth));
|
||||||
|
push(depth, if idx == children_no - 1 {'│'} else { ' ' });
|
||||||
|
print(*child, parents, depth, entries, s, inc);
|
||||||
|
pop(depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for r in roots {
|
||||||
|
print(r, &parents, &mut depth, &a.entries, &mut s, &mut inc);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lines: Vec<&str> = s.lines().collect();
|
||||||
|
let lines_len = lines.len();
|
||||||
|
let mut idx = 0;
|
||||||
|
for y in get_y(upper_left)..get_y(bottom_right) {
|
||||||
|
if idx == lines_len {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let s = if idx == lines_len - 2 {
|
||||||
|
format!("{}", lines[idx].replace("├", "└"))
|
||||||
|
} else {
|
||||||
|
format!("{}", lines[idx])
|
||||||
|
};
|
||||||
|
let color_fg = if highlight {
|
||||||
|
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
||||||
|
Color::Byte(233)
|
||||||
|
} else {
|
||||||
|
Color::Byte(15)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Color::Default
|
||||||
|
};
|
||||||
|
|
||||||
|
let color_bg = if highlight {
|
||||||
|
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
||||||
|
Color::Byte(15)
|
||||||
|
} else {
|
||||||
|
Color::Byte(233)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Color::Default
|
||||||
|
};
|
||||||
|
|
||||||
|
let (x, _) = write_string_to_grid(&s,
|
||||||
|
grid,
|
||||||
|
color_fg,
|
||||||
|
color_bg,
|
||||||
|
(set_y(upper_left, y), bottom_right),
|
||||||
|
false);
|
||||||
|
|
||||||
|
if highlight && idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
||||||
|
change_colors(grid, ((x, y),(get_x(bottom_right)+1, 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 bottom_right = bottom_right!(area);
|
||||||
|
self.dirty = false;
|
||||||
|
let mut y = get_y(upper_left) + 1;
|
||||||
|
for a in &self.accounts {
|
||||||
|
y += self.print_account(grid,
|
||||||
|
(set_y(upper_left, y), bottom_right),
|
||||||
|
&a);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.dirty_areas.push_back(area);
|
||||||
|
}
|
||||||
|
fn process_event(&mut self, event: &UIEvent, _context: &mut Context) {
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn is_dirty(&self) -> bool {
|
||||||
|
self.dirty
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,11 +25,11 @@
|
||||||
See the `Component` Trait for more details.
|
See the `Component` Trait for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use super::*;
|
||||||
pub mod utilities;
|
pub mod utilities;
|
||||||
pub mod mail;
|
pub mod mail;
|
||||||
pub mod notifications;
|
pub mod notifications;
|
||||||
|
|
||||||
use super::*;
|
|
||||||
pub use utilities::*;
|
pub use utilities::*;
|
||||||
pub use mail::*;
|
pub use mail::*;
|
||||||
|
|
||||||
|
@ -126,34 +126,43 @@ pub fn change_colors(grid: &mut CellBuffer, area: Area, fg_color: Color, bg_colo
|
||||||
|
|
||||||
|
|
||||||
/// Write an `&str` to a `CellBuffer` in a specified `Area` with the passed colors.
|
/// Write an `&str` to a `CellBuffer` in a specified `Area` with the passed colors.
|
||||||
fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_color: Color, area: Area) -> usize {
|
fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_color: Color, area: Area, line_break: bool) -> Pos {
|
||||||
let bounds = grid.size();
|
let bounds = grid.size();
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
let (mut x, mut y) = upper_left;
|
let (mut x, mut y) = upper_left;
|
||||||
if y > (get_y(bottom_right)) || x > get_x(bottom_right) ||
|
if y > (get_y(bottom_right)) || x > get_x(bottom_right) ||
|
||||||
y > get_y(bounds) || x > get_x(bounds) {
|
y > get_y(bounds) || x > get_x(bounds) {
|
||||||
return 0;
|
eprintln!(" Invalid area with string {} and area {:?}", s, area);
|
||||||
|
return (x, y);
|
||||||
}
|
}
|
||||||
for c in s.chars() {
|
for l in s.lines() {
|
||||||
grid[(x,y)].set_ch(c);
|
'char: for c in l.chars() {
|
||||||
grid[(x,y)].set_fg(fg_color);
|
grid[(x,y)].set_ch(c);
|
||||||
grid[(x,y)].set_bg(bg_color);
|
grid[(x,y)].set_fg(fg_color);
|
||||||
x += 1;
|
grid[(x,y)].set_bg(bg_color);
|
||||||
|
x += 1;
|
||||||
|
|
||||||
if x == (get_x(bottom_right))+1 || x > get_x(bounds) {
|
if x == (get_x(bottom_right))+1 || x > get_x(bounds) {
|
||||||
x = get_x(upper_left);
|
x = get_x(upper_left);
|
||||||
y += 1;
|
y += 1;
|
||||||
if y == (get_y(bottom_right))+1 || y > get_y(bounds) {
|
if y >= (get_y(bottom_right)) || y > get_y(bounds) {
|
||||||
return x;
|
return (x, y - 1);
|
||||||
|
}
|
||||||
|
if !line_break {
|
||||||
|
break 'char;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x
|
(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Completely clear an `Area` with an empty char and the terminal's default colors.
|
/// Completely clear an `Area` with an empty char and the terminal's default colors.
|
||||||
fn clear_area(grid: &mut CellBuffer, area: Area) {
|
fn clear_area(grid: &mut CellBuffer, area: Area) {
|
||||||
|
if !is_valid_area!(area) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
for y in get_y(upper_left)..=get_y(bottom_right) {
|
for y in get_y(upper_left)..=get_y(bottom_right) {
|
|
@ -3,8 +3,8 @@
|
||||||
*/
|
*/
|
||||||
use notify_rust::Notification as notify_Notification;
|
use notify_rust::Notification as notify_Notification;
|
||||||
|
|
||||||
use ui::*;
|
use super::*;
|
||||||
use ui::components::*;
|
use super::components::*;
|
||||||
|
|
||||||
/// Passes notifications to the OS using the XDG specifications.
|
/// Passes notifications to the OS using the XDG specifications.
|
||||||
pub struct XDGNotifications {}
|
pub struct XDGNotifications {}
|
|
@ -1,8 +1,6 @@
|
||||||
/*! Various useful components that can be used in a generic fashion.
|
/*! Various useful components that can be used in a generic fashion.
|
||||||
*/
|
*/
|
||||||
|
use super::*;
|
||||||
use ui::components::*;
|
|
||||||
use ui::cells::*;
|
|
||||||
|
|
||||||
/// A horizontally split in half container.
|
/// A horizontally split in half container.
|
||||||
pub struct HSplit {
|
pub struct HSplit {
|
||||||
|
@ -24,6 +22,9 @@ impl HSplit {
|
||||||
|
|
||||||
impl Component for HSplit {
|
impl Component for HSplit {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
|
if !is_valid_area!(area) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
let total_rows = get_y(bottom_right) - get_y(upper_left);
|
let total_rows = get_y(bottom_right) - get_y(upper_left);
|
||||||
|
@ -71,6 +72,9 @@ impl VSplit {
|
||||||
|
|
||||||
impl Component for VSplit {
|
impl Component for VSplit {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
|
if !is_valid_area!(area) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
let total_cols = get_x(bottom_right) - get_x(upper_left);
|
let total_cols = get_x(bottom_right) - get_x(upper_left);
|
||||||
|
@ -186,7 +190,8 @@ impl Pager {
|
||||||
content,
|
content,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
((0, i), (width -1, i)));
|
((0, i), (width -1, i)),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,6 +199,9 @@ impl Pager {
|
||||||
|
|
||||||
impl Component for Pager {
|
impl Component for Pager {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
|
if !is_valid_area!(area) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if !self.is_dirty() {
|
if !self.is_dirty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -272,7 +280,7 @@ impl StatusBar {
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(123),
|
Color::Byte(123),
|
||||||
Color::Byte(26),
|
Color::Byte(26),
|
||||||
area);
|
area, false);
|
||||||
change_colors(grid, area, Color::Byte(123), Color::Byte(26));
|
change_colors(grid, area, Color::Byte(123), Color::Byte(26));
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
}
|
}
|
||||||
|
@ -282,7 +290,7 @@ impl StatusBar {
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(219),
|
Color::Byte(219),
|
||||||
Color::Byte(88),
|
Color::Byte(88),
|
||||||
area);
|
area, false);
|
||||||
change_colors(grid, area, Color::Byte(219), Color::Byte(88));
|
change_colors(grid, area, Color::Byte(219), Color::Byte(88));
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
}
|
}
|
||||||
|
@ -291,6 +299,9 @@ impl StatusBar {
|
||||||
|
|
||||||
impl Component for StatusBar {
|
impl Component for StatusBar {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
|
if !is_valid_area!(area) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
|
|
||||||
|
@ -359,3 +370,26 @@ impl Component for StatusBar {
|
||||||
self.dirty || self.container.component.is_dirty()
|
self.dirty || self.container.component.is_dirty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A box with a text content.
|
||||||
|
pub struct TextBox {
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextBox {
|
||||||
|
pub fn new(s: String) -> Self {
|
||||||
|
TextBox {
|
||||||
|
content: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for TextBox {
|
||||||
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
fn process_event(&mut self, event: &UIEvent, context: &mut Context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,20 +30,20 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod position;
|
mod position;
|
||||||
pub mod components;
|
|
||||||
mod cells;
|
mod cells;
|
||||||
|
pub mod components;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod execute;
|
mod execute;
|
||||||
use self::execute::goto;
|
use execute::goto;
|
||||||
pub use self::position::*;
|
pub use self::position::*;
|
||||||
use self::cells::*;
|
use self::cells::*;
|
||||||
pub use self::components::*;
|
pub use self::components::*;
|
||||||
|
|
||||||
extern crate melib;
|
extern crate melib;
|
||||||
|
extern crate notify_rust;
|
||||||
use melib::*;
|
use melib::*;
|
||||||
|
|
||||||
use std;
|
|
||||||
use std::io::{Write, };
|
use std::io::{Write, };
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -53,6 +53,9 @@ use termion::raw::IntoRawMode;
|
||||||
use termion::event::{Key as TermionKey, };
|
use termion::event::{Key as TermionKey, };
|
||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
|
|
||||||
|
extern crate chan;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate nom;
|
||||||
use chan::Sender;
|
use chan::Sender;
|
||||||
|
|
||||||
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads
|
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads
|
Loading…
Reference in New Issue