use std::str;
use rustc_serialize::hex::FromHex;
use std::path::PathBuf;
use util::string_with_repeat;
use rustbox::{RustBox};
use rustbox::keyboard::Key;
use super::super::buffer::Buffer;
use super::RustBoxEx::{RustBoxEx, Style};
use super::input::Input;
use super::common::{Rect, Canceled};
pub enum BaseInputLineActions {
Edit(char),
Ctrl(char),
MoveLeft,
MoveRight,
DeleteWithMove,
Ok,
Cancel
}
pub trait InputLine {
fn input(&mut self, input: &Input, key: Key) -> bool;
fn draw(&mut self, rb: &RustBox, area: Rect<isize>, has_focus: bool);
}
struct BaseInputLine {
prefix: String,
data: Vec<u8>,
input_pos: isize,
}
impl BaseInputLine {
fn new(prefix: String) -> BaseInputLine {
BaseInputLine {
prefix: prefix,
data: vec!(),
input_pos: 0,
}
}
}
impl InputLine for BaseInputLine {
fn input(&mut self, input: &Input, key: Key) -> bool {
let action = if let Some(action) = input.inputline_input(key) { action } else {
return false;
};
match action {
BaseInputLineActions::MoveLeft => {
if self.input_pos > 0 {
self.input_pos -= 1;
}
}
BaseInputLineActions::MoveRight => {
if self.input_pos < self.data.len() as isize {
self.input_pos += 1;
}
}
BaseInputLineActions::Edit(ch) => {
if ch.len_utf8() == 1 && ch.is_alphanumeric() {
self.data.insert(self.input_pos as usize, ch as u8);
self.input_pos += 1;
} else {
}
}
BaseInputLineActions::DeleteWithMove => {
if self.input_pos > 0 {
self.input_pos -= 1;
self.data.remove(self.input_pos as usize);
}
}
_ => return false
};
return true;
}
fn draw(&mut self, rb: &RustBox, area: Rect<isize>, has_focus: bool) {
rb.print_style(area.left as usize, area.top as usize, Style::InputLine,
&string_with_repeat(' ', area.width as usize));
rb.print_style(area.left as usize, area.top as usize, Style::InputLine,
&format!("{}{}", self.prefix, str::from_utf8(&self.data).unwrap()));
if has_focus {
rb.set_cursor(self.prefix.len() as isize + self.input_pos, (area.top as isize));
}
}
}
enum RadixType {
DecRadix,
HexRadix,
OctRadix,
}
signal_decl!{GotoEvent(isize)}
pub struct GotoInputLine {
base: BaseInputLine,
radix: RadixType,
pub on_done: GotoEvent,
pub on_cancel: Canceled,
}
impl GotoInputLine {
pub fn new() -> GotoInputLine {
GotoInputLine {
base: BaseInputLine::new("Goto (Dec):".to_string()),
radix: RadixType::DecRadix,
on_done: Default::default(),
on_cancel: Default::default(),
}
}
fn set_radix(&mut self, r: RadixType) {
self.radix = r;
self.base.prefix = match self.radix {
RadixType::DecRadix => "Goto (Dec):".to_string(),
RadixType::HexRadix => "Goto (Hex):".to_string(),
RadixType::OctRadix => "Goto (Oct):".to_string(),
}
}
fn do_goto(&mut self) {
let radix = match self.radix {
RadixType::DecRadix => 10,
RadixType::HexRadix => 16,
RadixType::OctRadix => 8,
};
let pos: Option<isize> = match str::from_utf8(&self.base.data) {
Ok(gs) => isize::from_str_radix(&gs, radix).ok(),
Err(_) => None
};
match pos {
Some(pos) => {
self.on_done.signal(pos)
}
None => {
self.on_cancel.signal(Some(format!("Bad position!")));
}
};
}
}
impl InputLine for GotoInputLine {
fn input(&mut self, input: &Input, key: Key) -> bool {
if self.base.input(input, key) { return true }
let action = if let Some(action) = input.inputline_input(key) { action } else {
return false;
};
match action {
BaseInputLineActions::Ok => {
self.do_goto();
true
}
BaseInputLineActions::Cancel => {
self.on_cancel.signal(None);
true
}
BaseInputLineActions::Ctrl('d') => {
self.set_radix(RadixType::DecRadix);
true
}
BaseInputLineActions::Ctrl('h') => {
self.set_radix(RadixType::HexRadix);
true
}
BaseInputLineActions::Ctrl('o') => {
self.set_radix(RadixType::OctRadix);
true
}
_ => false
}
}
fn draw(&mut self, rb: &RustBox, area: Rect<isize>, has_focus: bool) {
self.base.draw(rb, area, has_focus)
}
}
enum DataType {
AsciiStr,
UnicodeStr,
HexStr,
}
signal_decl!{FindEvent(Vec<u8>)}
pub struct FindInputLine {
base: BaseInputLine,
data_type: DataType,
pub on_find: FindEvent,
pub on_cancel: Canceled,
}
impl FindInputLine {
pub fn new() -> FindInputLine {
FindInputLine {
base: BaseInputLine::new("Find(Ascii): ".to_string()),
data_type: DataType::AsciiStr,
on_find: Default::default(),
on_cancel: Default::default(),
}
}
fn set_search_data_type(&mut self, dt: DataType) {
self.data_type = dt;
self.base.prefix = match self.data_type {
DataType::AsciiStr => "Find(Ascii): ".to_string(),
DataType::UnicodeStr => "Find(Uni): ".to_string(),
DataType::HexStr => "Find(Hex): ".to_string(),
}
}
fn do_find(&mut self) {
let ll = str::from_utf8(&self.base.data).unwrap().from_hex();
let needle: Vec<u8> = match self.data_type {
DataType::AsciiStr => self.base.data.clone().into(),
DataType::UnicodeStr => self.base.data.clone().into(),
DataType::HexStr => {
match ll {
Ok(n) => n,
Err(_) => {
self.on_cancel.signal(Some(format!("Bad hex value")));
return;
}
}
}
};
self.on_find.signal(needle);
}
}
impl InputLine for FindInputLine {
fn input(&mut self, input: &Input, key: Key) -> bool {
if self.base.input(input, key) { return true }
let action = if let Some(action) = input.inputline_input(key) { action } else {
return false;
};
match action {
BaseInputLineActions::Ok => {
self.do_find();
true
}
BaseInputLineActions::Cancel => {
self.on_cancel.signal(None);
true
}
BaseInputLineActions::Ctrl('a') => {
self.set_search_data_type(DataType::AsciiStr);
true
}
BaseInputLineActions::Ctrl('u') => {
self.set_search_data_type(DataType::UnicodeStr);
true
}
BaseInputLineActions::Ctrl('h') => {
self.set_search_data_type(DataType::HexStr);
true
}
_ => false
}
}
fn draw(&mut self, rb: &RustBox, area: Rect<isize>, has_focus: bool) {
self.base.draw(rb, area, has_focus)
}
}
signal_decl!{PathEvent(PathBuf)}
pub struct PathInputLine {
base: BaseInputLine,
pub on_done: PathEvent,
pub on_cancel: Canceled,
}
impl PathInputLine {
pub fn new(prefix: String) -> PathInputLine {
PathInputLine {
base: BaseInputLine::new(prefix),
on_done: Default::default(),
on_cancel: Default::default()
}
}
}
impl InputLine for PathInputLine {
fn input(&mut self, input: &Input, key: Key) -> bool {
if self.base.input(input, key) { return true }
let action = if let Some(action) = input.inputline_input(key) { action } else {
return false;
};
match action {
BaseInputLineActions::Ok => {
self.on_done.signal(PathBuf::from(str::from_utf8(&self.base.data).unwrap()));
true
}
BaseInputLineActions::Cancel => {
self.on_cancel.signal(None);
true
}
_ => false
}
}
fn draw(&mut self, rb: &RustBox, area: Rect<isize>, has_focus: bool) {
self.base.draw(rb, area, has_focus)
}
}