#![allow(clippy::forget_copy)]
use std::{ptr, fmt, mem, convert::TryFrom, hash::{Hash, Hasher}};
use errno::errno;
use ncursesw::{
SCREEN, normal, form, form::{FormOptions, FORM, FIELD},
shims::nform, shims::constants::E_OK
};
use crate::{
Screen, Size, Window, HasHandle, NCurseswWinError, AttributesType,
form::{
Field, PostedForm,
callbacks::{
CallbackType, set_form_screen, set_form_callback, extern_field_init,
extern_field_term, extern_form_init, extern_form_term, form_tidyup
}
}
};
#[deprecated(since = "0.5.0")]
pub use ncursesw::form::Form_Hook;
pub struct Form {
screen: Option<SCREEN>, handle: FORM, field_handles: *mut FIELD, free_on_drop: bool }
impl Form {
pub(in crate::form) fn _from(screen: Option<SCREEN>, handle: FORM, field_handles: *mut FIELD, free_on_drop: bool) -> Self {
assert!(screen.map_or_else(|| true, |screen| !screen.is_null()), "Form::_from() : screen.is_null()");
assert!(!handle.is_null(), "Form::_from() : handle.is_null()");
assert!(!field_handles.is_null(), "Form::_from() : field_handles.is_null()");
if free_on_drop {
set_form_screen(handle, screen);
}
Self { screen, handle, field_handles, free_on_drop }
}
fn _allocate_form_fields<F>(func_str: &str, fields: &[&Field], mut func: F) -> result!(Option<Self>)
where F: FnMut(*mut FIELD) -> result!(Option<Self>)
{
let field_handles = unsafe { libc::calloc(fields.len() + 1, mem::size_of::<FIELD>()) as *mut FIELD };
if !field_handles.is_null() {
for (offset, field_handle) in fields.iter().map(|field| field._handle()).enumerate() {
unsafe { ptr::write(field_handles.offset(isize::try_from(offset)?), field_handle) };
}
mem::forget(field_handles);
func(field_handles)
} else {
Err(NCurseswWinError::OutOfMemory { func: format!("Form::{}", func_str) })
}
}
pub(in crate::form) fn _screen(&self) -> Option<SCREEN> {
self.screen
}
pub(in crate::form) fn _handle(&self) -> FORM {
self.handle
}
}
impl Form {
pub fn new(fields: &[&Field]) -> result!(Self) {
Self::_allocate_form_fields("new", fields, |field_handles| {
match unsafe { nform::new_form(field_handles) } {
Some(form) => Ok(Some(Self::_from(None, form, field_handles, true))),
None => Err(NCurseswWinError::FormError { source: form::ncursesw_form_error_from_rc("Form::new", errno().into()) })
}
}).map(|form| form.unwrap())
}
#[deprecated(since = "0.5.0", note = "Use Form::new() instead")]
pub fn new_form(fields: &[&Field]) -> result!(Self) {
Self::new(fields)
}
pub fn new_sp(screen: &Screen, fields: &[&Field]) -> result!(Self) {
Self::_allocate_form_fields("new_sp", fields, |field_handles| {
match unsafe { nform::new_form_sp(screen._handle(), field_handles) } {
Some(form) => Ok(Some(Self::_from(Some(screen._handle()), form, field_handles, true))),
None => Err(NCurseswWinError::FormError { source: form::ncursesw_form_error_from_rc("Form::new_sp", errno().into()) })
}
}).map(|form| form.unwrap())
}
#[deprecated(since = "0.5.0", note = "Use Form::new_sp() instead")]
pub fn new_form_sp(screen: &Screen, fields: &[&Field]) -> result!(Self) {
Self::new_sp(screen, fields)
}
pub fn screen(&self) -> Option<Screen> {
self.screen.map(|screen| Screen::_from(screen, false))
}
pub fn current_field(&self) -> result!(Field) {
Ok(Field::_from(form::current_field(Some(self.handle))?, false))
}
pub fn data_ahead(&self) -> bool {
form::data_ahead(self.handle)
}
pub fn data_behind(&self) -> bool {
form::data_behind(self.handle)
}
pub fn field_count(&self) -> result!(usize) {
Ok(usize::try_from(form::field_count(Some(self.handle))?)?)
}
#[deprecated(since = "0.5.0")]
pub fn field_init(&self) -> result!(Form_Hook) {
Ok(form::field_init(Some(self.handle))?)
}
#[deprecated(since = "0.5.0")]
pub fn field_term(&self) -> result!(Form_Hook) {
Ok(form::field_term(Some(self.handle))?)
}
pub fn form_fields(&self) -> result!(Vec<Field>) {
Ok(form::form_fields(Some(self.handle))?.iter().map(|handle| Field::_from(*handle, false)).collect())
}
#[deprecated(since = "0.5.0")]
pub fn form_init(&self) -> result!(Form_Hook) {
Ok(form::form_init(Some(self.handle))?)
}
pub fn form_opts(&self) -> FormOptions {
form::form_opts(Some(self.handle))
}
pub fn form_opts_off(&self, opts: FormOptions) -> result!(()) {
Ok(form::form_opts_off(Some(self.handle), opts)?)
}
pub fn form_opts_on(&self, opts: FormOptions) -> result!(()) {
Ok(form::form_opts_on(Some(self.handle), opts)?)
}
pub fn form_page(&self) -> result!(usize) {
Ok(usize::try_from(form::form_page(Some(self.handle))?)?)
}
pub fn form_sub(&self) -> result!(Window) {
Ok(Window::_from(self.screen, form::form_sub(Some(self.handle))?, false))
}
#[deprecated(since = "0.5.0")]
pub fn form_term(&self) -> result!(Form_Hook) {
Ok(form::form_term(Some(self.handle))?)
}
pub fn form_userptr<T>(&self) -> result!(Option<Box<T>>) {
Ok(unsafe { form::form_userptr(Some(self.handle))?.as_mut().map(|userptr| Box::from_raw(userptr as *mut libc::c_void as *mut T)) })
}
pub fn form_win(&self) -> result!(Window) {
Ok(Window::_from(self.screen, form::form_win(Some(self.handle))?, false))
}
pub fn post_form(&self, refresh: bool) -> result!(PostedForm) {
PostedForm::new(self, refresh)
}
pub fn scale_form(&self) -> result!(Size) {
Size::try_from(form::scale_form(self.handle)?)
}
pub fn set_current_field(&self, field: &Field) -> result!(()) {
Ok(form::set_current_field(self.handle, field._handle())?)
}
pub fn set_field_init<F>(&self, func: F) -> result!(())
where F: Fn(&Self) + 'static + Send
{
set_form_callback(Some(self.handle), CallbackType::FieldInit, func);
Ok(form::set_field_init(Some(self.handle), Some(extern_field_init))?)
}
pub fn set_field_term<F>(&self, func: F) -> result!(())
where F: Fn(&Self) + 'static + Send
{
set_form_callback(Some(self.handle), CallbackType::FieldTerm, func);
Ok(form::set_field_term(Some(self.handle), Some(extern_field_term))?)
}
pub fn set_form_fields(&mut self, fields: &[&Field]) -> result!(()) {
unsafe { libc::free(self.field_handles as *mut libc::c_void) };
Self::_allocate_form_fields("set_form_fields", fields, |field_handles| {
self.field_handles = field_handles;
match unsafe { nform::set_form_fields(self.handle, field_handles) } {
E_OK => Ok(None),
rc => Err(NCurseswWinError::FormError { source: form::ncursesw_form_error_from_rc("Form::set_form_fields", rc) })
}
}).map(|_| ())
}
pub fn set_form_init<F>(&self, func: F) -> result!(())
where F: Fn(&Self) + 'static + Send
{
set_form_callback(Some(self.handle), CallbackType::FormInit, func);
Ok(form::set_form_init(Some(self.handle), Some(extern_form_init))?)
}
pub fn set_form_opts(&self, opts: FormOptions) -> result!(()) {
Ok(form::set_form_opts(Some(self.handle), opts)?)
}
pub fn set_form_page(&self, number: usize) -> result!(()) {
Ok(form::set_form_page(self.handle, i32::try_from(number)?)?)
}
pub fn set_form_sub(&self, window: Option<&Window>) -> result!(()) {
assert!(self.screen == window.and_then(|window| window._screen()));
Ok(form::set_form_sub(Some(self.handle), window.map(|window| window._handle()))?)
}
pub fn set_form_term<F>(&self, func: F) -> result!(())
where F: Fn(&Self) + 'static + Send
{
set_form_callback(Some(self.handle), CallbackType::FormTerm, func);
Ok(form::set_form_term(Some(self.handle), Some(extern_form_term))?)
}
pub fn set_form_userptr<T>(&self, userptr: Option<Box<&T>>) -> result!(()) {
Ok(form::set_form_userptr(Some(self.handle), userptr.map(|userptr| Box::into_raw(userptr) as *mut libc::c_void))?)
}
pub fn set_form_win(&self, window: Option<&Window>) -> result!(()) {
assert!(self.screen == window.and_then(|window| window._screen()));
Ok(form::set_form_win(Some(self.handle), window.map(|window| window._handle()))?)
}
pub fn unfocus_current_field(&self) -> result!(()) {
Ok(form::unfocus_current_field(self.handle)?)
}
pub fn field_back(&self, field: &Field) -> normal::Attributes {
self.screenify_attributes(field.field_back())
}
pub fn field_fore(&self, field: &Field) -> normal::Attributes {
self.screenify_attributes(field.field_fore())
}
pub fn set_field_back(&self, field: &Field, attrs: normal::Attributes) -> result!(()) {
assert!(self.screen == attrs.screen());
field.set_field_back(attrs)
}
pub fn set_field_fore(&self, field: &Field, attrs: normal::Attributes) -> result!(()) {
assert!(self.screen == attrs.screen());
field.set_field_fore(attrs)
}
fn screenify_attributes(&self, attrs: normal::Attributes) -> normal::Attributes {
self.screen.map_or_else(|| attrs, |screen| normal::Attributes::new_sp(screen, attrs.into()))
}
}
impl Drop for Form {
fn drop(&mut self) {
if self.free_on_drop {
if let Err(source) = form::free_form(self.handle) {
panic!("{} @ {:?}", source, self)
}
unsafe { libc::free(self.field_handles as *mut libc::c_void) };
form_tidyup(self.handle);
}
}
}
unsafe impl Send for Form { } unsafe impl Sync for Form { }
impl PartialEq for Form {
fn eq(&self, rhs: &Self) -> bool {
self.screen == rhs.screen && ptr::eq(self.handle, rhs.handle)
}
}
impl Eq for Form { }
impl Hash for Form {
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
}
}
impl AsRef<Form> for Form {
fn as_ref(&self) -> &Self {
self
}
}
impl AsMut<Form> for Form {
fn as_mut(&mut self) -> &mut Self {
self
}
}
impl Clone for Form {
fn clone(&self) -> Self {
Self::_from(self.screen.clone(), self.handle.clone(), self.field_handles.clone(), false)
}
}
impl fmt::Debug for Form {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Form {{ screen: {:?}, handle: {:p}, field_handles: {:p}, free_on_drop: {} }}", self.screen, self.handle, self.field_handles, self.free_on_drop)
}
}