#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
use core::any::Any;
use core::ffi::c_int;
use std::sync::Mutex;
use crate::ported::crt::{
ColorElements, KEY_DC, KEY_DEL_MAC, KEY_DOWN, KEY_ENTER, KEY_F, KEY_LEFT, KEY_MOUSE,
KEY_RECLICK, KEY_RIGHT, KEY_UP,
};
use crate::ported::functionbar::{FunctionBar, FunctionBar_new};
use crate::ported::header::{Header, Header_calculateHeight};
use crate::ported::listitem::ListItem;
use crate::ported::meter::{
Meter, Meter_class, Meter_nextSupportedMode, Meter_setMode, Meter_toListItem,
};
use crate::ported::panel::{
HandlerResult, Panel, PanelClass, Panel_add, Panel_getSelectedIndex, Panel_insert,
Panel_moveSelectedDown, Panel_moveSelectedUp, Panel_new, Panel_remove, Panel_set,
Panel_setDefaultBar, Panel_setHeader, Panel_setSelected, Panel_setSelectionColor, Panel_size,
EVENT_PANEL_LOST_FOCUS,
};
use crate::ported::screenmanager::{ScreenManager, ScreenManager_resize};
use crate::ported::settings::Settings;
use crate::ported::vector::{
Vector, Vector_get, Vector_insert, Vector_moveDown, Vector_moveUp, Vector_new, Vector_remove,
Vector_size, Vector_take,
};
const MetersFunctions: [&str; 10] = [
" ", " ", " ", "Style ", " ", " ", "MoveUp", "MoveDn", "Delete",
"Done ",
];
const MetersMovingFunctions: [&str; 10] = [
" ", " ", " ", "Style ", "MoveLt", "MoveRt", "MoveUp", "MoveDn", "Delete",
"Done ",
];
static Meters_movingBar: Mutex<Option<FunctionBar>> = Mutex::new(None);
pub fn MetersPanel_cleanup() {
let mut bar = Meters_movingBar.lock().unwrap();
if bar.is_some() {
*bar = None;
}
}
pub fn MetersPanel_delete(this: MetersPanel) {
drop(this);
}
pub struct MetersPanel {
pub super_: Panel,
pub settings: *mut Settings,
pub meters: Vector,
pub scr: *mut ScreenManager,
pub leftNeighbor: *mut MetersPanel,
pub rightNeighbor: *mut MetersPanel,
pub moving: bool,
pub header: *mut Header,
pub column: usize,
}
impl Drop for MetersPanel {
fn drop(&mut self) {
if self.header.is_null() {
return;
}
let mut meters =
core::mem::replace(&mut self.meters, Vector_new(&Meter_class.super_, true, 1));
let mut column: Vec<Meter> = Vec::with_capacity(meters.array.len());
for obj in meters.array.drain(..).flatten() {
let any: Box<dyn Any> = obj;
if let Ok(meter) = any.downcast::<Meter>() {
column.push(*meter);
}
}
unsafe {
let h = &mut *self.header;
if self.column < h.columns.len() {
h.columns[self.column] = column;
}
}
}
}
impl PanelClass for MetersPanel {
fn as_panel(&self) -> &Panel {
&self.super_
}
fn as_panel_mut(&mut self) -> &mut Panel {
&mut self.super_
}
fn event_handler(&mut self, ev: i32) -> HandlerResult {
MetersPanel_eventHandler(self, ev)
}
}
pub fn MetersPanel_setMoving(this: &mut MetersPanel, moving: bool) {
this.moving = moving;
let super_ = &mut this.super_;
if !moving {
let n = Panel_size(super_);
for i in 0..n {
let any: &mut dyn core::any::Any = super_.items[i as usize].object_mut();
if let Some(item) = any.downcast_mut::<ListItem>() {
item.moving = false;
}
}
Panel_setSelectionColor(super_, ColorElements::PANEL_SELECTION_FOCUS);
Panel_setDefaultBar(super_);
} else {
if !super_.items.is_empty() {
let sel = super_.selected as usize;
let any: &mut dyn core::any::Any = super_.items[sel].object_mut();
if let Some(selected) = any.downcast_mut::<ListItem>() {
selected.moving = true;
}
}
Panel_setSelectionColor(super_, ColorElements::PANEL_SELECTION_FOLLOW);
super_.currentBar = Meters_movingBar.lock().unwrap().clone();
}
super_.needsRedraw = true;
}
pub fn moveToNeighbor(
this: &mut MetersPanel,
neighbor: Option<&mut MetersPanel>,
selected: c_int,
) -> bool {
if this.moving {
if let Some(neighbor) = neighbor {
if selected < Vector_size(&this.meters) {
MetersPanel_setMoving(this, false);
let meter = Vector_take(&mut this.meters, selected);
let _ = Panel_remove(&mut this.super_, selected);
Vector_insert(&mut neighbor.meters, selected, meter);
let item = {
let obj = Vector_get(&neighbor.meters, selected as usize);
let any: &dyn Any = obj;
let m = any
.downcast_ref::<Meter>()
.expect("moveToNeighbor: meters element is not a Meter");
Meter_toListItem(m, false)
};
Panel_insert(&mut neighbor.super_, selected, Box::new(item));
Panel_setSelected(&mut neighbor.super_, selected);
MetersPanel_setMoving(neighbor, true);
return true;
}
}
}
false
}
pub fn MetersPanel_eventHandler(this: &mut MetersPanel, ch: i32) -> HandlerResult {
const KEY_F4: i32 = KEY_F(4);
const KEY_F5: i32 = KEY_F(5);
const KEY_F6: i32 = KEY_F(6);
const KEY_F7: i32 = KEY_F(7);
const KEY_F8: i32 = KEY_F(8);
const KEY_F9: i32 = KEY_F(9);
const SPACE: i32 = b' ' as i32;
const T_KEY: i32 = b't' as i32;
const LEFT_BRACKET: i32 = b'[' as i32;
const RIGHT_BRACKET: i32 = b']' as i32;
const MINUS: i32 = b'-' as i32;
const PLUS: i32 = b'+' as i32;
let selected = Panel_getSelectedIndex(&this.super_);
let mut result = HandlerResult::IGNORED;
let mut sideMove = false;
match ch {
0x0a | 0x0d | KEY_ENTER | KEY_RECLICK => {
if Vector_size(&this.meters) != 0 {
MetersPanel_setMoving(this, !this.moving);
result = HandlerResult::HANDLED;
}
}
KEY_MOUSE => {
if this.moving {
MetersPanel_setMoving(this, false);
result = HandlerResult::HANDLED;
}
}
SPACE | KEY_F4 | T_KEY => {
if Vector_size(&this.meters) != 0 {
let moving = this.moving;
let item = {
let slot = this.meters.array[selected as usize]
.as_mut()
.expect("MetersPanel_eventHandler: meters hole");
let any: &mut dyn Any = slot.as_mut() as &mut dyn Any;
let meter = any
.downcast_mut::<Meter>()
.expect("MetersPanel_eventHandler: meters element is not a Meter");
let mode = Meter_nextSupportedMode(meter);
Meter_setMode(meter, mode);
Meter_toListItem(meter, moving)
};
Panel_set(&mut this.super_, selected, Box::new(item));
result = HandlerResult::HANDLED;
}
}
KEY_UP if !this.moving => {}
KEY_UP | KEY_F7 | LEFT_BRACKET | MINUS => {
Vector_moveUp(&mut this.meters, selected);
Panel_moveSelectedUp(&mut this.super_);
result = HandlerResult::HANDLED;
}
KEY_DOWN if !this.moving => {}
KEY_DOWN | KEY_F8 | RIGHT_BRACKET | PLUS => {
Vector_moveDown(&mut this.meters, selected);
Panel_moveSelectedDown(&mut this.super_);
result = HandlerResult::HANDLED;
}
KEY_F6 | KEY_RIGHT => {
let right = this.rightNeighbor;
let neighbor = if right.is_null() {
None
} else {
Some(unsafe { &mut *right })
};
sideMove = moveToNeighbor(this, neighbor, selected);
if this.moving && !sideMove {
result = HandlerResult::HANDLED;
}
}
KEY_F5 | KEY_LEFT => {
let left = this.leftNeighbor;
let neighbor = if left.is_null() {
None
} else {
Some(unsafe { &mut *left })
};
sideMove = moveToNeighbor(this, neighbor, selected);
if this.moving && !sideMove {
result = HandlerResult::HANDLED;
}
}
KEY_F9 | KEY_DC | KEY_DEL_MAC => {
if Vector_size(&this.meters) != 0 && selected < Vector_size(&this.meters) {
Vector_remove(&mut this.meters, selected);
Panel_remove(&mut this.super_, selected);
}
MetersPanel_setMoving(this, false);
result = HandlerResult::HANDLED;
}
EVENT_PANEL_LOST_FOCUS => {
if this.moving {
MetersPanel_setMoving(this, false);
}
result = HandlerResult::HANDLED;
}
_ => {}
}
if result == HandlerResult::HANDLED || sideMove {
let settings = unsafe { &mut *this.settings };
settings.changed = true;
settings.lastUpdate += 1;
let scr = unsafe { &mut *this.scr };
{
let header = unsafe { scr.header.as_mut() }
.expect("MetersPanel_eventHandler: scr->header is NULL");
Header_calculateHeight(header);
}
ScreenManager_resize(scr);
}
result
}
pub fn MetersPanel_new(
settings: *mut Settings,
header: &str,
meters: Vector,
scr: *mut ScreenManager,
) -> MetersPanel {
let fu_bar = FunctionBar_new(Some(&MetersFunctions), None, None);
{
let mut bar = Meters_movingBar.lock().unwrap();
if bar.is_none() {
*bar = Some(FunctionBar_new(Some(&MetersMovingFunctions), None, None));
}
}
let super_ = Panel_new(1, 1, 1, 1, Some(fu_bar));
let mut this = MetersPanel {
super_,
settings,
meters,
scr,
leftNeighbor: core::ptr::null_mut(),
rightNeighbor: core::ptr::null_mut(),
moving: false,
header: core::ptr::null_mut(),
column: 0,
};
Panel_setHeader(&mut this.super_, header);
for i in 0..Vector_size(&this.meters) {
let item = {
let obj = Vector_get(&this.meters, i as usize);
let any: &dyn Any = obj;
let m = any
.downcast_ref::<Meter>()
.expect("MetersPanel_new: meters element is not a Meter");
Meter_toListItem(m, false)
};
Panel_add(&mut this.super_, Box::new(item));
}
this
}
#[cfg(test)]
use crate::ported::panel::PanelItem;
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::meter::Meter_class;
use crate::ported::object::Object;
use crate::ported::panel::{Panel_get, Panel_new, Panel_size};
use crate::ported::richstring::RichString_sizeVal;
use crate::ported::vector::{Vector_add, Vector_new};
static BAR_TEST_LOCK: Mutex<()> = Mutex::new(());
fn moving_bar() -> FunctionBar {
FunctionBar {
functions: vec!["Style ".into(), "MoveLt".into()],
keys: vec!["F4".into(), "F5".into()],
events: vec![4, 5],
staticData: false,
}
}
fn default_bar() -> FunctionBar {
FunctionBar {
functions: vec!["Style ".into()],
keys: vec!["F4".into()],
events: vec![4],
staticData: false,
}
}
fn li(value: &str, moving: bool) -> Box<dyn Object> {
Box::new(ListItem {
value: value.to_string(),
key: 0,
moving,
})
}
fn empty_meters() -> Vector {
Vector_new(&Meter_class.super_, true, 4)
}
fn meter(name: &'static str) -> Box<dyn Object> {
Box::new(Meter {
host: core::ptr::null(),
uiName: name,
mode: 0,
..Meter::empty()
})
}
fn mp(n: usize) -> MetersPanel {
let mut super_ = Panel_new(1, 1, 1, 1, Some(default_bar()));
for i in 0..n {
super_
.items
.push(PanelItem::Owned(li(&format!("meter{i}"), false)));
}
MetersPanel {
super_,
settings: core::ptr::null_mut(),
meters: empty_meters(),
scr: core::ptr::null_mut(),
leftNeighbor: core::ptr::null_mut(),
rightNeighbor: core::ptr::null_mut(),
moving: false,
header: core::ptr::null_mut(),
column: 0,
}
}
fn row_value(m: &MetersPanel, i: usize) -> String {
let any: &dyn Any = Panel_get(&m.super_, i as i32);
any.downcast_ref::<ListItem>().unwrap().value.clone()
}
fn item_moving(m: &MetersPanel, i: usize) -> bool {
let any: &dyn core::any::Any = m.super_.items[i].object();
any.downcast_ref::<ListItem>().unwrap().moving
}
#[test]
fn cleanup_clears_bar_and_is_noop_on_null() {
let _guard = BAR_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
*Meters_movingBar.lock().unwrap() = Some(moving_bar());
assert!(Meters_movingBar.lock().unwrap().is_some());
MetersPanel_cleanup();
assert!(Meters_movingBar.lock().unwrap().is_none());
MetersPanel_cleanup();
assert!(Meters_movingBar.lock().unwrap().is_none());
}
#[test]
fn set_moving_true_marks_selected_and_swaps_to_moving_bar() {
let _guard = BAR_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
*Meters_movingBar.lock().unwrap() = Some(moving_bar());
let mut m = mp(3);
m.super_.selected = 1;
m.super_.needsRedraw = false;
MetersPanel_setMoving(&mut m, true);
assert!(m.moving);
assert!(!item_moving(&m, 0));
assert!(item_moving(&m, 1));
assert!(!item_moving(&m, 2));
assert_eq!(
m.super_.selectionColorId,
ColorElements::PANEL_SELECTION_FOLLOW
);
assert_eq!(m.super_.currentBar, Some(moving_bar()));
assert!(m.super_.needsRedraw);
*Meters_movingBar.lock().unwrap() = None;
}
#[test]
fn set_moving_false_clears_all_flags_and_restores_default_bar() {
let mut m = mp(3);
for i in 0..3 {
let any: &mut dyn core::any::Any = m.super_.items[i].object_mut();
any.downcast_mut::<ListItem>().unwrap().moving = true;
}
m.moving = true;
m.super_.selectionColorId = ColorElements::PANEL_SELECTION_FOLLOW;
m.super_.currentBar = Some(moving_bar());
m.super_.needsRedraw = false;
MetersPanel_setMoving(&mut m, false);
assert!(!m.moving);
for i in 0..3 {
assert!(!item_moving(&m, i), "item {i} still moving");
}
assert_eq!(
m.super_.selectionColorId,
ColorElements::PANEL_SELECTION_FOCUS
);
assert_eq!(m.super_.currentBar, Some(default_bar()));
assert!(m.super_.needsRedraw);
}
#[test]
fn set_moving_true_on_empty_panel_does_not_panic() {
let _guard = BAR_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
*Meters_movingBar.lock().unwrap() = Some(moving_bar());
let mut m = mp(0);
MetersPanel_setMoving(&mut m, true);
assert!(m.moving);
assert_eq!(
m.super_.selectionColorId,
ColorElements::PANEL_SELECTION_FOLLOW
);
assert_eq!(m.super_.currentBar, Some(moving_bar()));
*Meters_movingBar.lock().unwrap() = None;
}
#[test]
fn new_owns_meters_populates_rows_and_sets_header() {
let _guard = BAR_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
*Meters_movingBar.lock().unwrap() = None;
let mut meters = empty_meters();
Vector_add(&mut meters, meter("CPU"));
Vector_add(&mut meters, meter("Mem"));
let m = MetersPanel_new(
core::ptr::null_mut(),
"Meters",
meters,
core::ptr::null_mut(),
);
assert!(!m.moving);
assert_eq!(Vector_size(&m.meters), 2);
assert_eq!(Panel_size(&m.super_), 2);
assert_eq!(row_value(&m, 0), "CPU");
assert_eq!(row_value(&m, 1), "Mem");
assert_eq!(RichString_sizeVal(&m.super_.header), "Meters".len() as i32);
assert_eq!(
m.super_.currentBar.as_ref().unwrap().functions,
MetersFunctions
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
);
assert!(Meters_movingBar.lock().unwrap().is_some());
*Meters_movingBar.lock().unwrap() = None;
}
#[test]
fn new_with_empty_meters_has_no_rows() {
let _guard = BAR_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let m = MetersPanel_new(
core::ptr::null_mut(),
"Empty",
empty_meters(),
core::ptr::null_mut(),
);
assert_eq!(Vector_size(&m.meters), 0);
assert_eq!(Panel_size(&m.super_), 0);
*Meters_movingBar.lock().unwrap() = None;
}
#[test]
fn move_to_neighbor_relocates_meter_and_row_when_moving() {
let _guard = BAR_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
*Meters_movingBar.lock().unwrap() = Some(moving_bar());
let mut meters = empty_meters();
Vector_add(&mut meters, meter("CPU"));
let mut this =
MetersPanel_new(core::ptr::null_mut(), "Left", meters, core::ptr::null_mut());
let mut neighbor = MetersPanel_new(
core::ptr::null_mut(),
"Right",
empty_meters(),
core::ptr::null_mut(),
);
this.super_.selected = 0;
MetersPanel_setMoving(&mut this, true);
assert!(this.moving);
let moved = moveToNeighbor(&mut this, Some(&mut neighbor), 0);
assert!(moved);
assert_eq!(Vector_size(&this.meters), 0);
assert_eq!(Panel_size(&this.super_), 0);
assert!(!this.moving); assert_eq!(Vector_size(&neighbor.meters), 1);
assert_eq!(Panel_size(&neighbor.super_), 1);
assert_eq!(row_value(&neighbor, 0), "CPU");
assert_eq!(neighbor.super_.selected, 0);
assert!(neighbor.moving);
*Meters_movingBar.lock().unwrap() = None;
}
#[test]
fn move_to_neighbor_is_noop_when_not_moving() {
let _guard = BAR_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let mut meters = empty_meters();
Vector_add(&mut meters, meter("CPU"));
let mut this =
MetersPanel_new(core::ptr::null_mut(), "Left", meters, core::ptr::null_mut());
let mut neighbor = MetersPanel_new(
core::ptr::null_mut(),
"Right",
empty_meters(),
core::ptr::null_mut(),
);
assert!(!moveToNeighbor(&mut this, Some(&mut neighbor), 0));
assert_eq!(Vector_size(&this.meters), 1);
assert_eq!(Vector_size(&neighbor.meters), 0);
*Meters_movingBar.lock().unwrap() = None;
}
#[test]
fn move_to_neighbor_is_noop_when_neighbor_is_none() {
let _guard = BAR_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
*Meters_movingBar.lock().unwrap() = Some(moving_bar());
let mut meters = empty_meters();
Vector_add(&mut meters, meter("CPU"));
let mut this =
MetersPanel_new(core::ptr::null_mut(), "Left", meters, core::ptr::null_mut());
this.super_.selected = 0;
MetersPanel_setMoving(&mut this, true);
assert!(!moveToNeighbor(&mut this, None, 0));
assert_eq!(Vector_size(&this.meters), 1);
assert!(this.moving);
*Meters_movingBar.lock().unwrap() = None;
}
#[test]
fn move_to_neighbor_is_noop_when_selected_out_of_range() {
let _guard = BAR_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
*Meters_movingBar.lock().unwrap() = Some(moving_bar());
let mut meters = empty_meters();
Vector_add(&mut meters, meter("CPU"));
let mut this =
MetersPanel_new(core::ptr::null_mut(), "Left", meters, core::ptr::null_mut());
let mut neighbor = MetersPanel_new(
core::ptr::null_mut(),
"Right",
empty_meters(),
core::ptr::null_mut(),
);
MetersPanel_setMoving(&mut this, true);
assert!(!moveToNeighbor(&mut this, Some(&mut neighbor), 1));
assert_eq!(Vector_size(&this.meters), 1);
assert_eq!(Vector_size(&neighbor.meters), 0);
*Meters_movingBar.lock().unwrap() = None;
}
}