use bladvak::eframe::egui::{self, FontId, RichText, TextEdit};
use bladvak::eframe::egui::{Color32, Widget};
use bladvak::errors::ErrorManager;
use std::path::{Path, PathBuf};
use crate::selection::Selection;
#[derive(Debug, PartialEq, Clone, serde::Deserialize, serde::Serialize)]
pub(crate) enum ExportType {
Hex,
Binary,
Octal,
Decimal,
}
#[derive(serde::Deserialize, serde::Serialize, Debug)]
pub(crate) struct Exporter {
pub(crate) is_open: bool,
prefix: bool,
separator: String,
pub(crate) value_type: ExportType,
#[serde(skip)]
export_error: Option<String>,
}
impl Exporter {
pub(crate) fn new() -> Self {
Self {
is_open: false,
prefix: true,
separator: " ".to_string(),
value_type: ExportType::Hex,
export_error: None,
}
}
pub(crate) fn reset(&mut self) {
self.export_error = None;
}
fn format_export(
selection: &[u8],
export_type: &ExportType,
prefix: bool,
separator: &str,
) -> String {
let prefix = if prefix {
match export_type {
ExportType::Binary => "0b",
ExportType::Decimal => "",
ExportType::Hex => "0x",
ExportType::Octal => "0o",
}
} else {
""
};
let tokens = selection
.iter()
.map(|one_u8| match export_type {
ExportType::Binary => format!("{prefix}{one_u8:08b}"),
ExportType::Hex => format!("{prefix}{one_u8:02X}"),
ExportType::Octal => format!("{prefix}{one_u8:03o}"),
ExportType::Decimal => format!("{one_u8}"),
})
.collect::<Vec<String>>();
tokens.join(separator)
}
pub(crate) fn windows_ui(
&mut self,
binary_file: &[u8],
file_path: &Path,
selection: &Selection,
ui: &mut egui::Ui,
error_manager: &mut ErrorManager,
) {
let previous_import_type = self.value_type.clone();
ui.horizontal(|ui| {
ui.label("Export selection to:");
ui.selectable_value(&mut self.value_type, ExportType::Hex, "Hex");
ui.selectable_value(&mut self.value_type, ExportType::Binary, "Binary");
ui.selectable_value(&mut self.value_type, ExportType::Octal, "Octal");
ui.selectable_value(&mut self.value_type, ExportType::Decimal, "Decimal");
});
if previous_import_type != self.value_type {
self.export_error = None;
}
ui.horizontal(|ui| {
ui.label("Separator");
ui.text_edit_singleline(&mut self.separator);
});
if self.value_type == ExportType::Decimal {
ui.add_enabled_ui(false, |ui| {
let mut value = false;
ui.checkbox(&mut value, "Prefix");
});
} else {
ui.checkbox(&mut self.prefix, "Prefix");
}
if binary_file.is_empty() {
ui.label("File is empty - no selection");
} else {
ui.horizontal(|ui| {
let export_selection = match selection.range {
Some(curr_select) => curr_select.0..=curr_select.1,
None => 0..=(binary_file.len() - 1),
};
if ui.button("Copy to clipboard").clicked() {
if let Some(file_selection) = binary_file.get(export_selection.clone()) {
let data = Self::format_export(
file_selection,
&self.value_type,
self.prefix,
&self.separator,
);
ui.ctx().copy_text(data);
} else {
self.export_error = Some("Cannot determine selection".to_string());
}
}
if let Some(slice) = binary_file.get(export_selection)
&& ui.button("Export as raw").clicked()
{
let file_name = file_path
.file_name()
.map_or_else(|| PathBuf::from("exported"), PathBuf::from);
if let Err(e) =
bladvak::utils::save_file(slice, &file_name.with_extension("bin"))
{
error_manager.add_error(e);
}
}
});
let selected_preview = if let Some(range) = selection.range {
let stop = range.1.min(range.0 + 49);
range.0..=stop
} else {
let max = (binary_file.len() - 1).min(49);
0..=max
};
ui.horizontal(|ui| {
ui.label(format!(
"Preview on {} bytes (of {})",
selected_preview.clone().count(),
if let Some(range) = selection.range {
let range_len = range.1 - range.0 + 1;
format!("{range_len} bytes selection")
} else {
"file".to_string()
}
));
if let Some(err) = &self.export_error {
ui.label(RichText::new(err).color(Color32::LIGHT_RED));
}
});
if let Some(preview_value) = binary_file.get(selected_preview) {
let mut formatted = Self::format_export(
preview_value,
&self.value_type,
self.prefix,
&self.separator,
);
TextEdit::multiline(&mut formatted)
.min_size(ui.available_size())
.desired_width(f32::INFINITY)
.font(FontId::monospace(12.0))
.ui(ui);
}
}
}
pub(crate) fn ui(
&mut self,
binary_file: &[u8],
file_path: &Path,
selection: &Selection,
ui: &mut egui::Ui,
error_manager: &mut ErrorManager,
) -> Option<Vec<u8>> {
if self.is_open {
let mut is_open = self.is_open;
egui::Window::new("Exporter")
.open(&mut is_open)
.vscroll(true)
.show(ui.ctx(), |ui| {
self.windows_ui(binary_file, file_path, selection, ui, error_manager);
});
self.is_open = is_open;
}
None
}
}