parent
d8f81cb596
commit
a20e7ac5c2
|
@ -25,6 +25,8 @@ pub struct AccountSettings {
|
||||||
pub root_folder: String,
|
pub root_folder: String,
|
||||||
pub format: String,
|
pub format: String,
|
||||||
pub sent_folder: String,
|
pub sent_folder: String,
|
||||||
|
pub identity: String,
|
||||||
|
pub display_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountSettings {
|
impl AccountSettings {
|
||||||
|
@ -34,7 +36,16 @@ impl AccountSettings {
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
pub fn set_name(&mut self, s: String) {
|
||||||
|
self.name = s;
|
||||||
|
}
|
||||||
pub fn root_folder(&self) -> &str {
|
pub fn root_folder(&self) -> &str {
|
||||||
&self.root_folder
|
&self.root_folder
|
||||||
}
|
}
|
||||||
|
pub fn identity(&self) -> &str {
|
||||||
|
&self.identity
|
||||||
|
}
|
||||||
|
pub fn display_name(&self) -> Option<&String> {
|
||||||
|
self.display_name.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,9 @@ impl Default for Draft {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Draft {
|
impl Draft {
|
||||||
|
pub fn headers_mut(&mut self) -> &mut FnvHashMap<String, String> {
|
||||||
|
&mut self.headers
|
||||||
|
}
|
||||||
pub fn headers(&self) -> &FnvHashMap<String, String> {
|
pub fn headers(&self) -> &FnvHashMap<String, String> {
|
||||||
&self.headers
|
&self.headers
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,13 @@ use melib::Draft;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Composer {
|
pub struct Composer {
|
||||||
dirty: bool,
|
|
||||||
mode: ViewMode,
|
mode: ViewMode,
|
||||||
pager: Pager,
|
pager: Pager,
|
||||||
|
|
||||||
draft: Draft,
|
draft: Draft,
|
||||||
|
account_cursor: usize,
|
||||||
|
|
||||||
|
dirty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Composer {
|
impl Default for Composer {
|
||||||
|
@ -36,8 +39,9 @@ impl Default for Composer {
|
||||||
Composer {
|
Composer {
|
||||||
dirty: true,
|
dirty: true,
|
||||||
mode: ViewMode::Overview,
|
mode: ViewMode::Overview,
|
||||||
pager: Pager::from_str("", None),
|
pager: Pager::default(),
|
||||||
draft: Draft::default(),
|
draft: Draft::default(),
|
||||||
|
account_cursor: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,9 +59,79 @@ impl fmt::Display for Composer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Composer {
|
||||||
|
fn draw_header_table(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
|
let upper_left = upper_left!(area);
|
||||||
|
let bottom_right = bottom_right!(area);
|
||||||
|
|
||||||
|
let headers = self.draft.headers();
|
||||||
|
{
|
||||||
|
let (mut x, mut y) = upper_left;
|
||||||
|
for k in &["Date", "From", "To", "Subject"] {
|
||||||
|
let update = {
|
||||||
|
let (x, y) = write_string_to_grid(
|
||||||
|
k,
|
||||||
|
grid,
|
||||||
|
Color::Default,
|
||||||
|
Color::Default,
|
||||||
|
((x, y), set_y(bottom_right, y)),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
let (x, y) = write_string_to_grid(
|
||||||
|
": ",
|
||||||
|
grid,
|
||||||
|
Color::Default,
|
||||||
|
Color::Default,
|
||||||
|
((x, y), set_y(bottom_right, y)),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
let (x, y) = if k == &"From" {
|
||||||
|
write_string_to_grid(
|
||||||
|
"◀ ",
|
||||||
|
grid,
|
||||||
|
Color::Byte(251),
|
||||||
|
Color::Default,
|
||||||
|
((x, y), set_y(bottom_right, y)),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(x, y)
|
||||||
|
};
|
||||||
|
let (x, y) = write_string_to_grid(
|
||||||
|
&headers[*k],
|
||||||
|
grid,
|
||||||
|
Color::Default,
|
||||||
|
Color::Default,
|
||||||
|
((x, y), set_y(bottom_right, y)),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
if k == &"From" {
|
||||||
|
write_string_to_grid(
|
||||||
|
" ▶",
|
||||||
|
grid,
|
||||||
|
Color::Byte(251),
|
||||||
|
Color::Default,
|
||||||
|
((x, y), set_y(bottom_right, y)),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
x = get_x(upper_left);
|
||||||
|
y = update.1 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for Composer {
|
impl Component for Composer {
|
||||||
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 self.dirty {
|
if self.dirty {
|
||||||
|
self.draft.headers_mut().insert(
|
||||||
|
"From".into(),
|
||||||
|
get_display_name(context, self.account_cursor),
|
||||||
|
);
|
||||||
clear_area(grid, area);
|
clear_area(grid, area);
|
||||||
}
|
}
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
|
@ -93,6 +167,7 @@ impl Component for Composer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let header_area = (set_x(upper_left, mid + 1), (mid + 78, header_height + 1));
|
||||||
let body_area = (
|
let body_area = (
|
||||||
(mid + 1, header_height + 2),
|
(mid + 1, header_height + 2),
|
||||||
(mid + 78, get_y(bottom_right)),
|
(mid + 78, get_y(bottom_right)),
|
||||||
|
@ -104,40 +179,7 @@ impl Component for Composer {
|
||||||
}
|
}
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViewMode::Overview => {
|
ViewMode::Overview => {
|
||||||
let headers = self.draft.headers();
|
self.draw_header_table(grid, header_area, context);
|
||||||
{
|
|
||||||
let (mut x, mut y) = set_x(upper_left, mid + 1);
|
|
||||||
for k in &["Date", "From", "To", "Subject"] {
|
|
||||||
let update = {
|
|
||||||
let (x, y) = write_string_to_grid(
|
|
||||||
k,
|
|
||||||
grid,
|
|
||||||
Color::Default,
|
|
||||||
Color::Default,
|
|
||||||
((x, y), (mid + 78, y)),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
let (x, y) = write_string_to_grid(
|
|
||||||
": ",
|
|
||||||
grid,
|
|
||||||
Color::Default,
|
|
||||||
Color::Default,
|
|
||||||
((x, y), (mid + 78, y)),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
write_string_to_grid(
|
|
||||||
&headers[*k],
|
|
||||||
grid,
|
|
||||||
Color::Default,
|
|
||||||
Color::Default,
|
|
||||||
((x, y), (mid + 78, y)),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
x = mid + 1;
|
|
||||||
y = update.1 + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.pager.draw(grid, body_area, context);
|
self.pager.draw(grid, body_area, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,6 +194,26 @@ impl Component for Composer {
|
||||||
UIEventType::Resize => {
|
UIEventType::Resize => {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
UIEventType::Input(Key::Left) => {
|
||||||
|
self.account_cursor = self.account_cursor.saturating_sub(1);
|
||||||
|
self.draft.headers_mut().insert(
|
||||||
|
"From".into(),
|
||||||
|
get_display_name(context, self.account_cursor),
|
||||||
|
);
|
||||||
|
self.dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEventType::Input(Key::Right) => {
|
||||||
|
if self.account_cursor + 1 < context.accounts.len() {
|
||||||
|
self.account_cursor += 1;
|
||||||
|
self.draft.headers_mut().insert(
|
||||||
|
"From".into(),
|
||||||
|
get_display_name(context, self.account_cursor),
|
||||||
|
);
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
UIEventType::Input(Key::Char('\n')) => {
|
UIEventType::Input(Key::Char('\n')) => {
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
/* Kill input thread so that spawned command can be sole receiver of stdin */
|
/* Kill input thread so that spawned command can be sole receiver of stdin */
|
||||||
|
@ -177,6 +239,16 @@ impl Component for Composer {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
UIEventType::Input(Key::Char('m')) => {
|
||||||
|
let mut f =
|
||||||
|
create_temp_file(self.draft.to_string().unwrap().as_str().as_bytes(), None);
|
||||||
|
context.replies.push_back(UIEvent {
|
||||||
|
id: 0,
|
||||||
|
event_type: UIEventType::EditDraft(f),
|
||||||
|
});
|
||||||
|
self.draft = Draft::default();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
@ -191,3 +263,12 @@ impl Component for Composer {
|
||||||
self.pager.set_dirty();
|
self.pager.set_dirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_display_name(context: &Context, idx: usize) -> String {
|
||||||
|
let settings = context.accounts[idx].runtime_settings.account();
|
||||||
|
if let Some(d) = settings.display_name.as_ref() {
|
||||||
|
format!("{} <{}>", d, settings.identity)
|
||||||
|
} else {
|
||||||
|
settings.identity.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -206,11 +206,11 @@ impl AccountMenu {
|
||||||
cell.set_fg(Color::Byte(243));
|
cell.set_fg(Color::Byte(243));
|
||||||
x += 1;
|
x += 1;
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
c if c.is_whitespace() => {
|
c if c.is_whitespace() => {
|
||||||
x += 1;
|
x += 1;
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ extern crate xdg;
|
||||||
pub mod pager;
|
pub mod pager;
|
||||||
|
|
||||||
use melib::conf::AccountSettings;
|
use melib::conf::AccountSettings;
|
||||||
|
use melib::error::*;
|
||||||
use pager::PagerSettings;
|
use pager::PagerSettings;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -34,10 +35,36 @@ pub struct FileAccount {
|
||||||
root_folder: String,
|
root_folder: String,
|
||||||
format: String,
|
format: String,
|
||||||
sent_folder: String,
|
sent_folder: String,
|
||||||
|
identity: String,
|
||||||
|
display_name: Option<String>,
|
||||||
threaded: bool,
|
threaded: bool,
|
||||||
folders: Option<HashMap<String, String>>,
|
folders: Option<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<FileAccount> for AccountConf {
|
||||||
|
fn from(x: FileAccount) -> Self {
|
||||||
|
let format = x.format.to_lowercase();
|
||||||
|
let sent_folder = x.sent_folder.clone();
|
||||||
|
let root_folder = x.root_folder.clone();
|
||||||
|
let identity = x.identity.clone();
|
||||||
|
let display_name = x.display_name.clone();
|
||||||
|
|
||||||
|
let acc = AccountSettings {
|
||||||
|
name: String::new(),
|
||||||
|
root_folder,
|
||||||
|
format,
|
||||||
|
sent_folder,
|
||||||
|
identity,
|
||||||
|
display_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
AccountConf {
|
||||||
|
account: acc,
|
||||||
|
conf: x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FileAccount {
|
impl FileAccount {
|
||||||
pub fn folders(&self) -> Option<&HashMap<String, String>> {
|
pub fn folders(&self) -> Option<&HashMap<String, String>> {
|
||||||
self.folders.as_ref()
|
self.folders.as_ref()
|
||||||
|
@ -85,45 +112,33 @@ pub struct Settings {
|
||||||
|
|
||||||
use self::config::{Config, File, FileFormat};
|
use self::config::{Config, File, FileFormat};
|
||||||
impl FileSettings {
|
impl FileSettings {
|
||||||
pub fn new() -> FileSettings {
|
pub fn new() -> Result<FileSettings> {
|
||||||
let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap();
|
let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap();
|
||||||
let config_path = xdg_dirs
|
let config_path = xdg_dirs
|
||||||
.place_config_file("config")
|
.place_config_file("config")
|
||||||
.expect("cannot create configuration directory");
|
.expect("cannot create configuration directory");
|
||||||
//let setts = Config::default().merge(File::new(config_path.to_str().unwrap_or_default(), config::FileFormat::Toml)).unwrap();
|
|
||||||
let mut s = Config::new();
|
let mut s = Config::new();
|
||||||
let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml));
|
let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml));
|
||||||
|
|
||||||
/* No point in returning without a config file.
|
/* No point in returning without a config file.
|
||||||
TODO: Error and exit instead of panic. */
|
TODO: Error and exit instead of panic. */
|
||||||
s.unwrap().deserialize().unwrap()
|
match s.unwrap().deserialize() {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(e) => Err(MeliError::new(e.to_string())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
pub fn new() -> Settings {
|
pub fn new() -> Settings {
|
||||||
let fs = FileSettings::new();
|
let fs = FileSettings::new().unwrap_or_else(|e| panic!(format!("{}", e)));
|
||||||
let mut s: HashMap<String, AccountConf> = HashMap::new();
|
let mut s: HashMap<String, AccountConf> = HashMap::new();
|
||||||
|
|
||||||
for (id, x) in fs.accounts {
|
for (id, x) in fs.accounts {
|
||||||
let format = x.format.to_lowercase();
|
let mut ac = AccountConf::from(x);
|
||||||
let sent_folder = x.sent_folder.clone();
|
ac.account.set_name(id.clone());
|
||||||
let root_folder = x.root_folder.clone();
|
|
||||||
|
|
||||||
let acc = AccountSettings {
|
s.insert(id, ac);
|
||||||
name: id.clone(),
|
|
||||||
root_folder,
|
|
||||||
format,
|
|
||||||
sent_folder,
|
|
||||||
};
|
|
||||||
|
|
||||||
s.insert(
|
|
||||||
id,
|
|
||||||
AccountConf {
|
|
||||||
account: acc,
|
|
||||||
conf: x,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
|
|
Loading…
Reference in New Issue