use crate::{
helper::{chars_to_cow, to_c_string, uninit},
try_gp_internal, Result,
};
use std::{
borrow::Cow,
ffi, fmt,
marker::PhantomData,
os::raw::{c_char, c_float, c_int, c_void},
};
macro_rules! get_widget_value {
($widget:expr, $tp:ty) => {{
let mut value: $tp = unsafe { $crate::helper::uninit() };
$crate::try_gp_internal!(libgphoto2_sys::gp_widget_get_value(
$widget,
&mut value as *mut $tp as *mut c_void
))?;
value
}};
}
#[derive(Debug, PartialEq)]
pub enum WidgetValue {
Text(String),
Range(f32),
Toggle(bool),
Menu(String),
Date(c_int),
}
#[derive(Debug, PartialEq)]
pub enum WidgetType {
Window,
Section,
Text,
Range {
min: f32,
max: f32,
increment: f32,
},
Toggle,
Menu {
choices: Vec<String>,
radio: bool,
},
Button,
Date,
}
pub struct WidgetIterator<'a> {
parent_widget: &'a Widget<'a>,
count: usize,
index: usize,
}
pub struct Widget<'a> {
pub(crate) inner: *mut libgphoto2_sys::CameraWidget,
_phantom: PhantomData<&'a ffi::c_void>,
}
impl Drop for Widget<'_> {
fn drop(&mut self) {
unsafe {
libgphoto2_sys::gp_widget_unref(self.inner);
}
}
}
impl fmt::Debug for Widget<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Widget")
.field("id", &self.id().ok())
.field("name", &self.name().ok())
.field("label", &self.label().ok())
.field("readonly", &self.readonly().ok())
.field("widget_type", &self.widget_type().ok())
.field(
"value",
&match self.value() {
Ok((Some(value), _)) => Some(value),
_ => None,
},
)
.field("children", &self.children_iter().map(|iter| iter.collect::<Vec<Widget>>()))
.finish()
}
}
impl<'a> Widget<'a> {
pub(crate) fn new(widget: *mut libgphoto2_sys::CameraWidget) -> Self {
unsafe { libgphoto2_sys::gp_widget_ref(widget) };
Self { inner: widget, _phantom: PhantomData }
}
pub fn readonly(&self) -> Result<bool> {
let mut readonly = unsafe { uninit() };
try_gp_internal!(libgphoto2_sys::gp_widget_get_readonly(self.inner, &mut readonly))?;
Ok(readonly == 1)
}
pub fn label(&self) -> Result<Cow<str>> {
let mut label = unsafe { uninit() };
try_gp_internal!(libgphoto2_sys::gp_widget_get_label(self.inner, &mut label))?;
Ok(chars_to_cow(label))
}
pub fn name(&self) -> Result<Cow<str>> {
let mut name = unsafe { uninit() };
try_gp_internal!(libgphoto2_sys::gp_widget_get_name(self.inner, &mut name))?;
Ok(chars_to_cow(name))
}
pub fn id(&self) -> Result<i32> {
let mut id = unsafe { uninit() };
try_gp_internal!(libgphoto2_sys::gp_widget_get_id(self.inner, &mut id))?;
Ok(id)
}
pub fn info(&self) -> Result<Cow<str>> {
let mut info = unsafe { uninit() };
try_gp_internal!(libgphoto2_sys::gp_widget_get_info(self.inner, &mut info))?;
Ok(chars_to_cow(info))
}
pub fn children_iter(&'a self) -> Result<WidgetIterator<'a>> {
Ok(WidgetIterator { parent_widget: self, count: self.children_count()?, index: 0 })
}
pub fn children_count(&self) -> Result<usize> {
try_gp_internal!(libgphoto2_sys::gp_widget_count_children(self.inner))
.map(|count| count as usize)
}
pub fn get_child(&self, index: usize) -> Result<Widget<'a>> {
let mut child = unsafe { uninit() };
try_gp_internal!(libgphoto2_sys::gp_widget_get_child(self.inner, index as c_int, &mut child))?;
Ok(Self::new(child))
}
pub fn get_child_by_id(&self, id: usize) -> Result<Widget<'a>> {
let mut child = unsafe { uninit() };
try_gp_internal!(libgphoto2_sys::gp_widget_get_child_by_id(
self.inner,
id as c_int,
&mut child
))?;
Ok(Self::new(child))
}
pub fn get_child_by_label(&self, label: &str) -> Result<Widget<'a>> {
let mut child = unsafe { uninit() };
to_c_string!(label);
try_gp_internal!(libgphoto2_sys::gp_widget_get_child_by_label(
self.inner,
label.as_ptr() as *const c_char,
&mut child
))?;
Ok(Self::new(child))
}
pub fn get_child_by_name(&self, name: &str) -> Result<Widget<'a>> {
let mut child = unsafe { uninit() };
to_c_string!(name);
try_gp_internal!(libgphoto2_sys::gp_widget_get_child_by_name(
self.inner,
name.as_ptr() as *const c_char,
&mut child
))?;
Ok(Self::new(child))
}
pub fn widget_type(&self) -> Result<WidgetType> {
use libgphoto2_sys::CameraWidgetType;
let mut widget_type = unsafe { uninit() };
try_gp_internal!(libgphoto2_sys::gp_widget_get_type(self.inner, &mut widget_type))?;
Ok(match widget_type {
CameraWidgetType::GP_WIDGET_WINDOW => WidgetType::Window,
CameraWidgetType::GP_WIDGET_SECTION => WidgetType::Section,
CameraWidgetType::GP_WIDGET_TEXT => WidgetType::Text,
CameraWidgetType::GP_WIDGET_RANGE => {
let (mut min, mut max, mut increment) = unsafe { (uninit(), uninit(), uninit()) };
try_gp_internal!(libgphoto2_sys::gp_widget_get_range(
self.inner,
&mut min,
&mut max,
&mut increment
))?;
WidgetType::Range { min, max, increment }
}
CameraWidgetType::GP_WIDGET_TOGGLE => WidgetType::Toggle,
CameraWidgetType::GP_WIDGET_MENU | CameraWidgetType::GP_WIDGET_RADIO => {
let choice_count = try_gp_internal!(libgphoto2_sys::gp_widget_count_choices(self.inner))?;
let mut choices = Vec::with_capacity(choice_count as usize);
for choice_i in 0..choice_count {
let mut choice = unsafe { uninit() };
try_gp_internal!(libgphoto2_sys::gp_widget_get_choice(
self.inner,
choice_i,
&mut choice
))?;
choices.push(chars_to_cow(choice).to_string());
}
WidgetType::Menu {
choices: choices,
radio: widget_type == CameraWidgetType::GP_WIDGET_RADIO,
}
}
CameraWidgetType::GP_WIDGET_BUTTON => WidgetType::Button,
CameraWidgetType::GP_WIDGET_DATE => WidgetType::Date,
})
}
pub fn value(&self) -> Result<(Option<WidgetValue>, WidgetType)> {
let widget_type = self.widget_type()?;
Ok((
match widget_type {
WidgetType::Window | WidgetType::Button | WidgetType::Section => None,
WidgetType::Text => {
let text = chars_to_cow(get_widget_value!(self.inner, *const c_char));
Some(WidgetValue::Text(text.to_string()))
}
WidgetType::Range { .. } => {
let range_value = get_widget_value!(self.inner, c_float);
Some(WidgetValue::Range(range_value))
}
WidgetType::Toggle => {
let boolean = get_widget_value!(self.inner, c_int);
Some(WidgetValue::Toggle(boolean == 0))
}
WidgetType::Date => {
let date_int = get_widget_value!(self.inner, c_int);
Some(WidgetValue::Date(date_int))
}
WidgetType::Menu { .. } => {
let choice = chars_to_cow(get_widget_value!(self.inner, *const c_char));
Some(WidgetValue::Menu(choice.to_string()))
}
},
widget_type,
))
}
pub fn set_value(&mut self, value: WidgetValue) -> Result<()> {
let self_type = self.widget_type()?;
match self_type {
WidgetType::Window => Err("Window has no value")?,
WidgetType::Section => Err("Section has no value")?,
WidgetType::Button => Err("Button has no value")?,
WidgetType::Text => {
if let WidgetValue::Text(text) = value {
to_c_string!(text);
try_gp_internal!(libgphoto2_sys::gp_widget_set_value(
self.inner,
text.as_ptr() as *const c_void
))?;
} else {
Err("Expected value to be a string")?;
}
}
WidgetType::Range { min, max, .. } => {
if let WidgetValue::Range(range_value) = value {
if (range_value < min) || (range_value > max) {
Err("Value out of range")?;
}
try_gp_internal!(libgphoto2_sys::gp_widget_set_value(
self.inner,
&range_value as *const f32 as *const c_void
))?;
} else {
Err("Expected value to be Range")?;
}
}
WidgetType::Toggle => {
if let WidgetValue::Toggle(toggle_value) = value {
let toggle_value = if toggle_value { 1 } else { 0 };
try_gp_internal!(libgphoto2_sys::gp_widget_set_value(
self.inner,
&toggle_value as *const c_int as *const c_void
))?;
} else {
Err("Expected value to be Toggle")?;
}
}
WidgetType::Date => {
if let WidgetValue::Date(unix_date) = value {
try_gp_internal!(libgphoto2_sys::gp_widget_set_value(
self.inner,
&unix_date as *const c_int as *const c_void
))?;
} else {
Err("Expected value to be Date")?;
}
}
WidgetType::Menu { choices, .. } => {
if let WidgetValue::Menu(choice) = value {
if !choices.contains(&choice) {
Err("Choice not in choices")?;
}
to_c_string!(choice);
try_gp_internal!(libgphoto2_sys::gp_widget_set_value(
self.inner,
choice.as_ptr() as *const c_void
))?;
} else {
Err("Expected value to be Menu")?;
}
}
}
Ok(())
}
}
impl<'a> Iterator for WidgetIterator<'a> {
type Item = Widget<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.count {
None
} else {
let child = self.parent_widget.get_child(self.index).ok();
self.index += 1;
child
}
}
}