Remove ui crate
Merge ui crate with root crate. In preparation for uploading `meli` as a separate crate on crates.io. Workspace crates will need to be published as well and having a separate `ui` crate and binary perhaps doesn't make sense anymore.async
parent
6fcc792b83
commit
8b6ea8de9a
|
@ -236,15 +236,6 @@ dependencies = [
|
|||
"libdbus-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debug_printer"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"melib 0.4.1",
|
||||
"ui 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "1.0.5"
|
||||
|
@ -743,13 +734,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
name = "meli"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linkify 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"melib 0.4.1",
|
||||
"nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"notify-rust 3.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp-serde 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmpv 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"signal-hook 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ui 0.4.1",
|
||||
"smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"text_processing 0.4.1",
|
||||
"toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xdg-utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1839,37 +1850,6 @@ name = "try-lock"
|
|||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ui"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linkify 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"melib 0.4.1",
|
||||
"nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"notify-rust 3.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp-serde 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmpv 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"text_processing 0.4.1",
|
||||
"toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xdg-utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
|
|
35
Cargo.toml
35
Cargo.toml
|
@ -14,21 +14,44 @@ crossbeam = "0.7.2"
|
|||
signal-hook = "0.1.12"
|
||||
signal-hook-registry = "1.2.0"
|
||||
nix = "0.16.1"
|
||||
melib = { path = "melib", version = "*" }
|
||||
ui = { path = "ui", version = "*" }
|
||||
melib = { path = "melib", version = "0.4.1" }
|
||||
|
||||
serde = "1.0.71"
|
||||
serde_derive = "1.0.71"
|
||||
serde_json = "1.0"
|
||||
toml = "0.5.3"
|
||||
fnv = "1.0.3" # >:c
|
||||
linkify = "0.3.1" # >:c
|
||||
xdg-utils = "0.3.0"
|
||||
nom = "3.2.0"
|
||||
notify = "4.0.1" # >:c
|
||||
notify-rust = "^3" # >:c
|
||||
termion = "1.5.1"
|
||||
bincode = "1.2.0"
|
||||
uuid = { version = "0.7.4", features = ["serde", "v4"] }
|
||||
unicode-segmentation = "1.2.1" # >:c
|
||||
text_processing = { path = "text_processing", version = "0.4.1" }
|
||||
libc = {version = "0.2.59", features = ["extra_traits",]}
|
||||
rusqlite = {version = "0.20.0", optional =true }
|
||||
rmp = "^0.8"
|
||||
rmpv = { version = "^0.4.2", features=["with-serde",] }
|
||||
rmp-serde = "^0.14.0"
|
||||
smallvec = { version = "1.1.0", features = ["serde", ] }
|
||||
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
debug = false
|
||||
|
||||
[workspace]
|
||||
members = ["melib", "ui", "debug_printer", "testing", "text_processing"]
|
||||
members = ["melib", "testing", "text_processing"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
notmuch = ["melib/notmuch_backend", "ui/notmuch"]
|
||||
default = ["sqlite3"]
|
||||
notmuch = ["melib/notmuch_backend", ]
|
||||
jmap = ["melib/jmap_backend",]
|
||||
sqlite3 = ["rusqlite"]
|
||||
|
||||
# Print tracing logs as meli runs in stderr
|
||||
# enable for debug tracing logs: build with --features=debug-tracing
|
||||
debug-tracing = ["melib/debug-tracing", "ui/debug-tracing"]
|
||||
debug-tracing = ["melib/debug-tracing", ]
|
||||
|
|
11
Makefile
11
Makefile
|
@ -46,17 +46,18 @@ GREEN ?= `[ -z $${NO_COLOR+x} ] && tput setaf 2 || echo ""`
|
|||
help:
|
||||
@echo "For a quick start, build and install locally:\n ${BOLD}${GREEN}PREFIX=~/.local make install${ANSI_RESET}\n"
|
||||
@echo "Available subcommands:"
|
||||
@echo " - ${BOLD}install${ANSI_RESET} (installs binary and documentation)"
|
||||
@echo " - ${BOLD}meli${ANSI_RESET} (builds meli with optimizations in \$$CARGO_TARGET_DIR)"
|
||||
@echo " - ${BOLD}install${ANSI_RESET} (installs binary in \$$BINDIR and documentation to \$$MANDIR)"
|
||||
@echo " - ${BOLD}uninstall${ANSI_RESET}"
|
||||
@echo "Secondary subcommands:"
|
||||
@echo " - ${BOLD}clean${ANSI_RESET} (cleans build artifacts)"
|
||||
@echo " - ${BOLD}check-deps${ANSI_RESET} (checks dependencies)"
|
||||
@echo " - ${BOLD}install-bin${ANSI_RESET} (installs binary to BINDIR)"
|
||||
@echo " - ${BOLD}install-doc${ANSI_RESET} (installs manpages to MANDIR)"
|
||||
@echo " - ${BOLD}install-bin${ANSI_RESET} (installs binary to \$$BINDIR)"
|
||||
@echo " - ${BOLD}install-doc${ANSI_RESET} (installs manpages to \$$MANDIR)"
|
||||
@echo " - ${BOLD}help${ANSI_RESET} (prints this information)"
|
||||
|
||||
@echo " - ${BOLD}dist${ANSI_RESET} (creates release tarball named meli-"${VERSION}".tar.gz)"
|
||||
@echo " - ${BOLD}deb-dist${ANSI_RESET} (builds debian package)"
|
||||
@echo " - ${BOLD}dist${ANSI_RESET} (creates release tarball named meli-"${VERSION}".tar.gz in this directory)"
|
||||
@echo " - ${BOLD}deb-dist${ANSI_RESET} (builds debian package in the parent directory)"
|
||||
@echo " - ${BOLD}distclean${ANSI_RESET} (cleans distribution build artifacts)"
|
||||
@echo "\nENVIRONMENT variables of interest:"
|
||||
@echo "* PREFIX = ${UNDERLINE}${PREFIX}${ANSI_RESET}"
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
[package]
|
||||
name = "debug_printer"
|
||||
version = "0.0.1" #:version
|
||||
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
|
||||
workspace = ".."
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "debugprinter"
|
||||
crate-type = ["dylib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
|
||||
[dependencies]
|
||||
libc = {version = "0.2.55", features = ["extra_traits",] }
|
||||
melib = { path = "../melib", version = "*" }
|
||||
ui = { path = "../ui", version = "*" }
|
|
@ -1,44 +0,0 @@
|
|||
extern crate libc;
|
||||
extern crate melib;
|
||||
|
||||
use melib::Envelope;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn print_envelope(ptr: *const Envelope) -> *const c_char {
|
||||
unsafe {
|
||||
assert!(!ptr.is_null(), "Null pointer in print_envelope");
|
||||
//println!("got addr {}", p as u64);
|
||||
//unsafe { CString::new("blah".to_string()).unwrap().as_ptr() }
|
||||
let s = CString::new(format!("{:?}", *ptr)).unwrap();
|
||||
drop(ptr);
|
||||
let p = s.as_ptr();
|
||||
std::mem::forget(s);
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn get_empty_envelope() -> *mut Envelope {
|
||||
let mut ret = Envelope::default();
|
||||
let ptr = std::ptr::NonNull::new(&mut ret as *mut Envelope)
|
||||
.expect("Envelope::default() has a NULL pointer?");
|
||||
|
||||
let ptr = ptr.as_ptr();
|
||||
std::mem::forget(ret);
|
||||
ptr
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn destroy_cstring(ptr: *mut c_char) {
|
||||
unsafe {
|
||||
let slice = CString::from_raw(ptr);
|
||||
drop(slice);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn envelope_size() -> libc::size_t {
|
||||
std::mem::size_of::<Envelope>()
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2019 Manos Pitsidianakis
|
||||
*
|
||||
|
|
83
src/bin.rs
83
src/bin.rs
|
@ -21,24 +21,74 @@
|
|||
|
||||
//!
|
||||
//! This crate contains the frontend stuff of the application. The application entry way on
|
||||
//! `src/bin.rs` creates an event loop and passes input to the `ui` module.
|
||||
//! `src/bin.rs` creates an event loop and passes input to a thread.
|
||||
//!
|
||||
//! The mail handling stuff is done in the `melib` crate which includes all backend needs. The
|
||||
//! split is done to theoretically be able to create different frontends with the same innards.
|
||||
//!
|
||||
|
||||
use std::alloc::System;
|
||||
use std::collections::VecDeque;
|
||||
use std::path::{Path, PathBuf};
|
||||
extern crate notify_rust;
|
||||
extern crate text_processing;
|
||||
use text_processing::*;
|
||||
extern crate xdg_utils;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate linkify;
|
||||
extern crate uuid;
|
||||
|
||||
extern crate fnv;
|
||||
extern crate termion;
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
extern crate serde_json;
|
||||
extern crate smallvec;
|
||||
|
||||
#[global_allocator]
|
||||
static GLOBAL: System = System;
|
||||
|
||||
// Re export to put crates in the documentation's start page.
|
||||
pub use melib;
|
||||
pub use ui;
|
||||
|
||||
#[macro_use]
|
||||
extern crate melib;
|
||||
use melib::*;
|
||||
use ui::*;
|
||||
|
||||
mod unix;
|
||||
use unix::*;
|
||||
|
||||
#[macro_use]
|
||||
pub mod types;
|
||||
use crate::types::*;
|
||||
|
||||
#[macro_use]
|
||||
pub mod terminal;
|
||||
use crate::terminal::*;
|
||||
|
||||
#[macro_use]
|
||||
pub mod execute;
|
||||
use crate::execute::*;
|
||||
|
||||
pub mod state;
|
||||
use crate::state::*;
|
||||
|
||||
pub mod components;
|
||||
use crate::components::*;
|
||||
|
||||
#[macro_use]
|
||||
pub mod conf;
|
||||
use crate::conf::*;
|
||||
|
||||
pub mod workers;
|
||||
use crate::workers::*;
|
||||
|
||||
#[cfg(feature = "sqlite3")]
|
||||
pub mod sqlite3;
|
||||
|
||||
pub mod cache;
|
||||
pub mod mailcap;
|
||||
pub mod plugins;
|
||||
|
||||
use nix;
|
||||
use std::os::raw::c_int;
|
||||
|
@ -152,15 +202,12 @@ fn run_app() -> Result<()> {
|
|||
args.version = true;
|
||||
}
|
||||
"--print-loaded-themes" => {
|
||||
let s = ui::conf::FileSettings::new()?;
|
||||
let s = conf::FileSettings::new()?;
|
||||
print!("{}", s.terminal.themes.to_string());
|
||||
return Ok(());
|
||||
}
|
||||
"--print-default-theme" => {
|
||||
print!(
|
||||
"{}",
|
||||
ui::conf::Theme::default().key_to_string("dark", false)
|
||||
);
|
||||
print!("{}", conf::Theme::default().key_to_string("dark", false));
|
||||
return Ok(());
|
||||
}
|
||||
e => match prev {
|
||||
|
@ -215,7 +262,7 @@ fn run_app() -> Result<()> {
|
|||
};
|
||||
|
||||
if let Some(config_path) = args.test_config.as_ref() {
|
||||
ui::conf::FileSettings::validate(config_path)?;
|
||||
conf::FileSettings::validate(config_path)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -235,7 +282,7 @@ fn run_app() -> Result<()> {
|
|||
if config_path.exists() {
|
||||
return Err(MeliError::new(format!("File `{}` already exists.\nMaybe you meant to specify another path with --create-config=PATH", config_path.display())));
|
||||
}
|
||||
ui::conf::create_config_file(&config_path)?;
|
||||
conf::create_config_file(&config_path)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -268,19 +315,17 @@ fn run_app() -> Result<()> {
|
|||
let status_bar = Box::new(StatusBar::new(window));
|
||||
state.register_component(status_bar);
|
||||
|
||||
let xdg_notifications = Box::new(ui::components::notifications::XDGNotifications::new());
|
||||
let xdg_notifications = Box::new(components::notifications::XDGNotifications::new());
|
||||
state.register_component(xdg_notifications);
|
||||
state.register_component(Box::new(
|
||||
ui::components::notifications::NotificationFilter {},
|
||||
));
|
||||
state.register_component(Box::new(components::notifications::NotificationFilter {}));
|
||||
|
||||
/* Keep track of the input mode. See ui::UIMode for details */
|
||||
/* Keep track of the input mode. See UIMode for details */
|
||||
'main: loop {
|
||||
state.render();
|
||||
|
||||
'inner: loop {
|
||||
/* Check if any components have sent reply events to State. */
|
||||
let events: ui::smallvec::SmallVec<[UIEvent; 8]> = state.context.replies();
|
||||
let events: smallvec::SmallVec<[UIEvent; 8]> = state.context.replies();
|
||||
for e in events {
|
||||
state.rcv_event(e);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
||||
|
@ -19,6 +19,8 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! Search queries.
|
||||
*/
|
||||
use melib::parsec::*;
|
||||
use melib::UnixTimestamp;
|
||||
use melib::{
|
||||
|
@ -27,6 +29,7 @@ use melib::{
|
|||
thread::{SortField, SortOrder},
|
||||
Result,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub use query_parser::query;
|
||||
|
@ -334,7 +337,6 @@ pub mod query_parser {
|
|||
|
||||
pub fn query_to_imap(q: &Query) -> String {
|
||||
fn rec(q: &Query, s: &mut String) {
|
||||
use crate::sqlite3::escape_double_quote;
|
||||
match q {
|
||||
Subject(t) => {
|
||||
s.push_str(" SUBJECT \"");
|
||||
|
@ -440,3 +442,12 @@ pub fn imap_search(
|
|||
panic!("Could not downcast ImapType backend. BUG");
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn escape_double_quote(w: &str) -> Cow<str> {
|
||||
if w.contains('"') {
|
||||
Cow::from(w.replace('"', "\"\""))
|
||||
} else {
|
||||
Cow::from(w)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
||||
|
@ -19,11 +19,11 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Components are ways to handle application data. They can draw on the terminal and receive events, but also do other stuff as well. (For example, see the `notifications` module.)
|
||||
|
||||
See the `Component` Trait for more details.
|
||||
*/
|
||||
/*! Components visual and logical separations of application interfaces.
|
||||
*
|
||||
* They can draw on the terminal and receive events, but also do other stuff as well. (For example, see the `notifications` module.)
|
||||
* See the `Component` Trait for more details.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -32,9 +32,6 @@ pub use crate::mail::*;
|
|||
|
||||
pub mod notifications;
|
||||
|
||||
pub mod indexer;
|
||||
pub use self::indexer::*;
|
||||
|
||||
pub mod utilities;
|
||||
pub use self::utilities::*;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
||||
|
@ -74,6 +74,7 @@ pub(super) struct EntryStrings {
|
|||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Creates a comma separated list `String` out of an `Address` iterable.
|
||||
macro_rules! address_list {
|
||||
(($name:expr) as comma_sep_list) => {{
|
||||
let mut ret: String =
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2019 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2020 Manos Pitsidianakis
|
||||
*
|
|
@ -19,6 +19,8 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! Configuration logic and `config.toml` interfaces. */
|
||||
|
||||
extern crate bincode;
|
||||
extern crate serde;
|
||||
extern crate toml;
|
||||
|
@ -637,7 +639,7 @@ pub fn create_config_file(p: &Path) -> Result<()> {
|
|||
.create_new(true)
|
||||
.open(p)
|
||||
.expect("Could not create config file.");
|
||||
file.write_all(include_bytes!("../../sample-config"))
|
||||
file.write_all(include_bytes!("../sample-config"))
|
||||
.expect("Could not write to config file.");
|
||||
println!("Written example configuration to {}", p.display());
|
||||
let metadata = file.metadata()?;
|
||||
|
@ -649,6 +651,7 @@ pub fn create_config_file(p: &Path) -> Result<()> {
|
|||
}
|
||||
|
||||
mod pp {
|
||||
//! Preprocess configuration files by unfolding `include` macros.
|
||||
use melib::{
|
||||
error::{MeliError, Result},
|
||||
parsec::*,
|
||||
|
@ -656,6 +659,7 @@ mod pp {
|
|||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Try to parse line into a path to be included.
|
||||
fn include_directive<'a>() -> impl Parser<'a, Option<&'a str>> {
|
||||
move |input: &'a str| {
|
||||
enum State {
|
||||
|
@ -712,6 +716,7 @@ mod pp {
|
|||
}
|
||||
}
|
||||
|
||||
/// Expands `include` macros in path.
|
||||
fn pp_helper(path: &Path, level: u8) -> Result<String> {
|
||||
if level > 7 {
|
||||
return Err(MeliError::new(format!("Maximum recursion limit reached while unfolding include directives in {}. Have you included a config file within itself?", path.display())));
|
||||
|
@ -752,6 +757,8 @@ mod pp {
|
|||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Expands `include` macros in configuration file and other configuration files (eg. themes)
|
||||
/// in the filesystem.
|
||||
pub fn pp<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||
let p_buf: PathBuf = if path.as_ref().is_relative() {
|
||||
path.as_ref().canonicalize()?
|
|
@ -1106,10 +1106,9 @@ impl Account {
|
|||
#[cfg(not(feature = "sqlite3"))]
|
||||
{
|
||||
let mut ret = SmallVec::new();
|
||||
let envelopes = self.collection.envelopes.clone().read();
|
||||
let envelopes = envelopes.unwrap();
|
||||
let envelopes = self.collection.envelopes.read().unwrap();
|
||||
|
||||
for env_hash in self.folders[folder_hash].as_result()?.envelopes {
|
||||
for &env_hash in &self.folders[&folder_hash].as_result()?.envelopes {
|
||||
let envelope = &envelopes[&env_hash];
|
||||
if envelope.subject().contains(&search_term) {
|
||||
ret.push(env_hash);
|
||||
|
@ -1127,7 +1126,7 @@ impl Account {
|
|||
ret.push(env_hash);
|
||||
}
|
||||
}
|
||||
ret
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,8 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Configuration for composing email.
|
||||
use super::default_vals::{false_val, none, true_val};
|
||||
|
||||
/// Settings for writing and sending new e-mail
|
|
@ -19,6 +19,8 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Settings for the pager function.
|
||||
|
||||
use super::default_vals::*;
|
||||
use super::deserializers::*;
|
||||
use melib::ToggleFlag;
|
|
@ -19,6 +19,8 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! E-mail tag configuration and {de,}serializing.
|
||||
|
||||
use crate::terminal::Color;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
|
|
@ -19,6 +19,8 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Settings for terminal display
|
||||
|
||||
use super::deserializers::non_empty_string;
|
||||
use super::Theme;
|
||||
use super::ToggleFlag;
|
|
@ -19,6 +19,15 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Application themes.
|
||||
//!
|
||||
//! * An attribute is a triple of foreground color, background color and terminal attribute `ThemeValue`s.
|
||||
//! * A `ThemeValue<T>` is either an actual value or the key name of another value to which it depends. The value is either `Color` or `Attr`.
|
||||
//! * `ThemeAttributeInner` is an attribute triplet.
|
||||
//! * `ThemeAttribute` is an attribute triplet with the links resolved.
|
||||
//!
|
||||
//! On startup a [DFS](https://en.wikipedia.org/wiki/Depth-first_search) is performed to see if there are any cycles in the link graph.
|
||||
|
||||
use crate::terminal::{Attr, Color};
|
||||
use crate::Context;
|
||||
use melib::{MeliError, Result};
|
||||
|
@ -187,6 +196,7 @@ const DEFAULT_KEYS: &'static [&'static str] = &[
|
|||
"mail.listing.thread_snooze_flag",
|
||||
];
|
||||
|
||||
/// `ThemeAttributeInner` but with the links resolved.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Default, Copy, Serialize, Deserialize)]
|
||||
pub struct ThemeAttribute {
|
||||
pub fg: Color,
|
||||
|
@ -194,6 +204,7 @@ pub struct ThemeAttribute {
|
|||
pub attrs: Attr,
|
||||
}
|
||||
|
||||
/// Holds {fore,back}ground color and terminal attribute values.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct ThemeAttributeInner {
|
||||
#[serde(default)]
|
||||
|
@ -205,6 +216,7 @@ pub struct ThemeAttributeInner {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Holds either an actual value or refers to the key name of the attribute that holds the value.
|
||||
pub enum ThemeValue<T> {
|
||||
Value(T),
|
||||
Link(Cow<'static, str>),
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
||||
|
@ -19,7 +19,7 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! A parser module for user commands passed through the Ex mode.
|
||||
/*! A parser module for user commands passed through the Execute mode.
|
||||
*/
|
||||
use melib::backends::FolderOperation;
|
||||
pub use melib::thread::{SortField, SortOrder};
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2019 Manos Pitsidianakis
|
||||
*
|
|
@ -18,6 +18,9 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! Find mailcap entries to execute attachments.
|
||||
*/
|
||||
use crate::split_command;
|
||||
use crate::state::Context;
|
||||
use crate::types::{create_temp_file, ForkType, UIEvent};
|
|
@ -19,6 +19,8 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! Plugins are executed by meli and communication is done by `messagepack` IPC.
|
||||
*/
|
||||
use melib::error::{MeliError, Result};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
|
@ -19,9 +19,10 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! Use an sqlite3 database for fast searching.
|
||||
*/
|
||||
use smallvec::SmallVec;
|
||||
use crate::cache::query;
|
||||
use crate::cache::Query::{self, *};
|
||||
use crate::cache::{escape_double_quote, query, Query::{self, *}};
|
||||
use crate::melib::parsec::Parser;
|
||||
use melib::{
|
||||
backends::MailBackend,
|
||||
|
@ -31,20 +32,10 @@ use melib::{
|
|||
MeliError, Result, ERROR,
|
||||
};
|
||||
use rusqlite::{params, Connection};
|
||||
use std::borrow::Cow;
|
||||
use std::path::PathBuf;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn escape_double_quote(w: &str) -> Cow<str> {
|
||||
if w.contains('"') {
|
||||
Cow::from(w.replace('"', "\"\""))
|
||||
} else {
|
||||
Cow::from(w)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn db_path() -> Result<PathBuf> {
|
||||
let data_dir =
|
||||
xdg::BaseDirectories::with_prefix("meli").map_err(|e| MeliError::new(e.to_string()))?;
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
||||
|
@ -641,6 +641,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
AccountAction(ref account_name, ReIndex) => {
|
||||
#[cfg(feature = "sqlite3")]
|
||||
match crate::sqlite3::index(&mut self.context, account_name) {
|
||||
Ok(()) => {
|
||||
self.context.replies.push_back(UIEvent::Notification(
|
||||
|
@ -657,6 +658,15 @@ impl State {
|
|||
));
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "sqlite3"))]
|
||||
{
|
||||
self.context.replies.push_back(UIEvent::Notification(
|
||||
None,
|
||||
"Message index rebuild failed: meli is not built with sqlite3 support."
|
||||
.to_string(),
|
||||
Some(NotificationType::ERROR),
|
||||
));
|
||||
}
|
||||
}
|
||||
v => {
|
||||
self.rcv_event(UIEvent::Action(v));
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
||||
|
@ -18,6 +18,9 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! Terminal grid cells, keys, colors, etc.
|
||||
*/
|
||||
extern crate serde;
|
||||
use self::serde::de::Visitor;
|
||||
use self::serde::{de, Deserialize, Deserializer};
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
||||
|
@ -36,6 +36,9 @@ use termion::color::{AnsiValue, Rgb as TermionRgb};
|
|||
|
||||
/// In a scroll region up and down cursor movements shift the region vertically. The new lines are
|
||||
/// empty.
|
||||
///
|
||||
/// See `CellBuffer::scroll_up` and `CellBuffer::scroll_down` for an explanation of how `xterm`
|
||||
/// scrolling works.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct ScrollRegion {
|
||||
pub top: usize,
|
||||
|
@ -56,7 +59,9 @@ pub struct CellBuffer {
|
|||
cols: usize,
|
||||
rows: usize,
|
||||
buf: Vec<Cell>,
|
||||
/// ASCII-only flag.
|
||||
pub ascii_drawing: bool,
|
||||
/// If printing to this buffer and we run out of space, expand it.
|
||||
growable: bool,
|
||||
}
|
||||
|
||||
|
@ -170,7 +175,7 @@ impl CellBuffer {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
/// ```no_run
|
||||
///
|
||||
/// let mut term = Terminal::new().unwrap();
|
||||
///
|
||||
|
@ -188,7 +193,7 @@ impl CellBuffer {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
/// ```no_run
|
||||
///
|
||||
/// let mut term = Terminal::new().unwrap();
|
||||
///
|
||||
|
@ -427,6 +432,8 @@ impl fmt::Display for CellBuffer {
|
|||
pub struct Cell {
|
||||
ch: char,
|
||||
|
||||
/// Set a `Cell` as empty when a previous cell spans multiple columns and it would
|
||||
/// "overflow" to this cell.
|
||||
empty: bool,
|
||||
fg: Color,
|
||||
bg: Color,
|
||||
|
@ -440,8 +447,7 @@ impl Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// let cell = Cell::new('x', Color::Default, Color::Green, Attr::Default);
|
||||
/// assert_eq!(cell.ch(), 'x');
|
||||
/// assert_eq!(cell.fg(), Color::Default);
|
||||
|
@ -464,8 +470,7 @@ impl Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut cell = Cell::with_char('x');
|
||||
/// assert_eq!(cell.ch(), 'x');
|
||||
/// assert_eq!(cell.fg(), Color::Default);
|
||||
|
@ -480,8 +485,7 @@ impl Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut cell = Cell::with_style(Color::Default, Color::Red, Attr::Bold);
|
||||
/// assert_eq!(cell.fg(), Color::Default);
|
||||
/// assert_eq!(cell.bg(), Color::Red);
|
||||
|
@ -496,8 +500,7 @@ impl Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut cell = Cell::with_char('x');
|
||||
/// assert_eq!(cell.ch(), 'x');
|
||||
/// ```
|
||||
|
@ -509,8 +512,7 @@ impl Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut cell = Cell::with_char('x');
|
||||
/// assert_eq!(cell.ch(), 'x');
|
||||
///
|
||||
|
@ -528,8 +530,7 @@ impl Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut cell = Cell::with_style(Color::Blue, Color::Default, Attr::Default);
|
||||
/// assert_eq!(cell.fg(), Color::Blue);
|
||||
/// ```
|
||||
|
@ -541,8 +542,7 @@ impl Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut cell = Cell::default();
|
||||
/// assert_eq!(cell.fg(), Color::Default);
|
||||
///
|
||||
|
@ -560,7 +560,7 @@ impl Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
/// ```no_run
|
||||
/// let mut cell = Cell::with_style(Color::Default, Color::Green, Attr::Default);
|
||||
/// assert_eq!(cell.bg(), Color::Green);
|
||||
/// ```
|
||||
|
@ -572,7 +572,7 @@ impl Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
/// ```no_run
|
||||
/// let mut cell = Cell::default();
|
||||
/// assert_eq!(cell.bg(), Color::Default);
|
||||
///
|
||||
|
@ -626,8 +626,7 @@ impl Default for Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut cell = Cell::default();
|
||||
/// assert_eq!(cell.ch(), ' ');
|
||||
/// assert_eq!(cell.fg(), Color::Default);
|
||||
|
@ -650,8 +649,7 @@ impl Default for Cell {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// // The default color.
|
||||
/// let default = Color::Default;
|
||||
///
|
||||
|
@ -676,6 +674,7 @@ pub enum Color {
|
|||
White,
|
||||
Byte(u8),
|
||||
Rgb(u8, u8, u8),
|
||||
/// Terminal default.
|
||||
Default,
|
||||
}
|
||||
|
||||
|
@ -1371,8 +1370,7 @@ impl Serialize for Color {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```norun
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Default attribute.
|
||||
/// let def = Attr::Default;
|
||||
///
|
||||
|
@ -1384,6 +1382,7 @@ impl Serialize for Color {
|
|||
/// ```
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Attr {
|
||||
/// Terminal default.
|
||||
Default = 0b000,
|
||||
Bold = 0b001,
|
||||
Underline = 0b100,
|
||||
|
@ -2003,7 +2002,7 @@ pub mod ansi {
|
|||
/// the iterator will simply return `None` when it reaches the end of the row.
|
||||
/// `RowIterator` can be created via the `CellBuffer::row_iter` method and can be returned by
|
||||
/// `BoundsIterator` which iterates each row.
|
||||
/// ```norun
|
||||
/// ```no_run
|
||||
/// for c in grid.row_iter(
|
||||
/// x..(x + 11),
|
||||
/// 0,
|
||||
|
@ -2017,7 +2016,7 @@ pub struct RowIterator {
|
|||
}
|
||||
|
||||
/// `BoundsIterator` iterates each row returning a `RowIterator`.
|
||||
/// ```norun
|
||||
/// ```no_run
|
||||
/// /* Visit each `Cell` in `area`. */
|
||||
/// for c in grid.bounds_iter(area) {
|
||||
/// grid[c].set_ch('w');
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2020 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2020 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
||||
|
@ -135,6 +135,7 @@ impl PartialEq<Key> for &Key {
|
|||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
/// Keep track of whether we're accepting normal user input or a pasted string.
|
||||
enum InputMode {
|
||||
Normal,
|
||||
Paste,
|
||||
|
@ -142,10 +143,13 @@ enum InputMode {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Main process sends commands to the input thread.
|
||||
pub enum InputCommand {
|
||||
/// Exit thread
|
||||
Kill,
|
||||
/// Send Raw bytes as well
|
||||
/// Send raw bytes as well
|
||||
Raw,
|
||||
/// Ignore raw bytes
|
||||
NoRaw,
|
||||
}
|
||||
|
||||
|
@ -158,6 +162,7 @@ use termion::input::TermReadEventsAndRaw;
|
|||
*
|
||||
* The main loop uses try_wait_on_child() to check if child has exited.
|
||||
*/
|
||||
/// The thread function that listens for user input and forwards it to the main event loop.
|
||||
pub fn get_events(
|
||||
mut closure: impl FnMut(Key),
|
||||
closure_raw: impl FnMut((Key, Vec<u8>)),
|
||||
|
@ -202,6 +207,7 @@ pub fn get_events(
|
|||
}
|
||||
}
|
||||
|
||||
/// Same as `get_events` but also forwards the raw bytes of the input as well
|
||||
pub fn get_events_raw(
|
||||
closure_nonraw: impl FnMut(Key),
|
||||
mut closure: impl FnMut((Key, Vec<u8>)),
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2018 Manos Pitsidianakis
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
* meli
|
||||
*
|
||||
* Copyright 2017-2020 Manos Pitsidianakis
|
||||
*
|