ATTACH: FILEDIA, -ATTACH command line and reference file types

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-13 15:22:44 +03:00
1 parent b1d4c9bd9b
commit 0678c579c0
33 files changed
+519 -47

No files matched your search

+3
View File
@@ -147,6 +147,9 @@ pub struct XAttachCommand {
const NONZERO: &str = "Value must be nonzero.";
/// Raster image files ATTACH places as images.
pub const IMAGE_EXTENSIONS: &[&str] = &["bmp", "gif", "jpg", "jpeg", "png", "tif", "tiff", "webp"];
impl XAttachCommand {
pub fn new(request: XrefAttachRequest, placement: XrefPlacement, label: &'static str) -> Self {
let block_name = path_to_block_name(&request.path);
+108
View File
@@ -36,6 +36,14 @@ impl XrefCommand {
}
}
/// -XREF already at its Attach file prompt (XATTACH while FILEDIA is 0).
pub fn attach(saved_paths: Vec<(String, String)>) -> Self {
Self {
step: Step::File { overlay: false },
saved_paths,
}
}
/// Whether any reference name matches `pattern` (`*` / `?` wildcards).
fn matches(&self, pattern: &str) -> bool {
self.saved_paths
@@ -197,6 +205,106 @@ impl CadCommand for XrefCommand {
}
}
/// -ATTACH (and ATTACH while FILEDIA is 0): the file on the command line,
/// then the attach flow of its file type.
pub struct AttachCommand {
/// The drawing whose reference type is asked; `None` while asking for the file.
drawing: Option<String>,
}
impl AttachCommand {
pub fn new() -> Self {
Self { drawing: None }
}
fn reference(path: &str, overlay: bool) -> CmdResult {
let verb = if overlay {
"XREFOVERLAYFILE"
} else {
"XREFATTACHFILE"
};
CmdResult::Dispatch(format!("{verb} {path}"))
}
}
impl CadCommand for AttachCommand {
fn name(&self) -> &'static str {
"-ATTACH"
}
fn prompt(&self) -> String {
match self.drawing {
None => "-ATTACH Path to file to attach:".to_string(),
Some(_) => {
"-ATTACH Specify reference type [Attachment/Overlay] <Attachment>:".to_string()
}
}
}
fn options(&self) -> Vec<CmdOption> {
match self.drawing {
None => Vec::new(),
Some(_) => vec![
CmdOption::new("Attachment", "A"),
CmdOption::new("Overlay", "O"),
],
}
}
fn input_kind(&self) -> InputKind {
match self.drawing {
None => InputKind::FreeText,
Some(_) => InputKind::SingleToken,
}
}
fn on_point(&mut self, _pt: DVec3) -> CmdResult {
CmdResult::NeedPoint
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
let value = text.trim().trim_matches('"');
if let Some(path) = self.drawing.clone() {
return Some(match value.to_uppercase().as_str() {
"A" | "ATTACHMENT" => Self::reference(&path, false),
"O" | "OVERLAY" => Self::reference(&path, true),
_ => CmdResult::ReportError("Invalid option keyword.".to_string()),
});
}
if value.is_empty() {
return Some(self.on_enter());
}
if !std::path::Path::new(value).is_file() {
return Some(CmdResult::ReportError(format!("{value} not found.")));
}
let ext = std::path::Path::new(value)
.extension()
.map(|e| e.to_string_lossy().to_ascii_lowercase())
.unwrap_or_default();
Some(match ext.as_str() {
"pdf" => CmdResult::Dispatch(format!("-PDFATTACH {value}")),
"dwf" | "dwfx" => CmdResult::Dispatch(format!("-DWFATTACH {value}")),
"dgn" => CmdResult::Dispatch(format!("-DGNATTACH {value}")),
"rcp" | "rcs" => CmdResult::Dispatch(format!("-POINTCLOUDATTACH {value}")),
// Images have no dialog after the file: ATTACH places them.
e if crate::modules::insert::xattach::IMAGE_EXTENSIONS.contains(&e) => {
CmdResult::Dispatch(format!("ATTACH {value}"))
}
_ => {
self.drawing = Some(value.to_string());
CmdResult::NeedPoint
}
})
}
fn on_enter(&mut self) -> CmdResult {
match self.drawing.clone() {
None => CmdResult::ReportError("Filename cannot be blank".to_string()),
Some(path) => Self::reference(&path, false),
}
}
}
/// A stored path shown the way the platform writes it.
pub fn display_path(path: &str) -> String {
if cfg!(windows) {