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

+1 -1
View File
@@ -92,7 +92,7 @@ naga = { version = "27", features = ["wgsl-in"] }
[target.'cfg(target_os = "windows")'.dependencies]
libloading = "0.8"
windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", "Win32_UI_Input", "Win32_System_LibraryLoader", "Win32_System_Threading", "Win32_Security", "Win32_System_Com", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_Storage_FileSystem", "Win32_Foundation", "Win32_Graphics_Printing", "Win32_Graphics_Gdi", "Win32_Storage_Xps", "Win32_Globalization", "Win32_Security_Authentication_Identity"] }
windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", "Win32_UI_Input", "Win32_System_LibraryLoader", "Win32_System_Threading", "Win32_Security", "Win32_System_Com", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_Storage_FileSystem", "Win32_Foundation", "Win32_Graphics_Printing", "Win32_Graphics_Gdi", "Win32_Storage_Xps", "Win32_Globalization", "Win32_Security_Authentication_Identity", "Win32_UI_Controls_Dialogs"] }
[target.'cfg(target_os = "linux")'.dependencies]
ashpd = { version = "0.13.13", default-features = false, features = ["async-io", "wayland"] }
+7
View File
@@ -3737,6 +3737,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3724,6 +3724,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3722,6 +3722,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3725,6 +3725,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3725,6 +3725,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3725,6 +3725,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3725,6 +3725,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3724,6 +3724,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3725,6 +3725,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3713,6 +3713,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3723,6 +3723,13 @@ insert =
.xref-attach-hide-details = Részletek elrejtése
.xref-attach-select-file = Hivatkozási fájl kiválasztása
.xref-attach-all-files = Minden hivatkozási fájl
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Külső fájlokra – például más rajzokra, raszterképekre és alávetítésekre – mutató hivatkozásokat szúr be.
.xref-attach-invalid-input = Érvénytelen bevitel.
.xref-insertion-point-x = Beszúrási pont X
+7
View File
@@ -3724,6 +3724,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3715,6 +3715,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3724,6 +3724,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3727,6 +3727,13 @@ insert =
.xref-attach-hide-details = ເຊື່ອງລາຍລະອຽດ
.xref-attach-select-file = ເລືອກໄຟລ໌ອ້າງອີງ
.xref-attach-all-files = ໄຟລ໌ອ້າງອີງທັງໝົດ
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = ແຊກການອ້າງອີງໃສ່ໄຟລ໌ພາຍນອກ ເຊັ່ນ: ຮູບແຕ້ມອື່ນ, ຮູບພາບແຣັສເຕີ ແລະ ຊັ້ນຮອງພື້ນ.
.xref-attach-invalid-input = ຂໍ້ມູນປ້ອນເຂົ້າບໍ່ຖືກຕ້ອງ.
.xref-insertion-point-x = ຈຸດແຊກ X
+7
View File
@@ -3723,6 +3723,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3724,6 +3724,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3725,6 +3725,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3726,6 +3726,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3698,6 +3698,13 @@ insert =
.xref-attach-hide-details = Ayrıntıları Gizle
.xref-attach-select-file = Referans Dosyası Seç
.xref-attach-all-files = Tüm Referans Dosyaları
.xref-attach-filter-images = Tüm görüntü dosyaları
.xref-attach-filter-dwf = DWF dosyaları (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = Tüm DGN Dosyaları (*.*)
.xref-attach-filter-pdf = PDF dosyaları (*.pdf)
.xref-attach-filter-point-cloud = Nokta Bulutu (*.rcp; *.rcs)
.xref-attach-filter-all = Tüm dosyalar (*.*)
.xref-attach-help = Diğer çizimler, raster görüntüler ve altlıklar gibi harici dosyalara referans ekler.
.xref-attach-invalid-input = Geçersiz giriş.
.xref-insertion-point-x = Ekleme noktası X
+7
View File
@@ -3712,6 +3712,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+7
View File
@@ -3724,6 +3724,13 @@ insert =
.xref-attach-hide-details = Hide Details
.xref-attach-select-file = Select Reference File
.xref-attach-all-files = All Reference Files
.xref-attach-filter-images = All image files
.xref-attach-filter-dwf = DWF files (*.dwfx; *.dwf)
.xref-attach-filter-dgn = MicroStation DGN (*.dgn)
.xref-attach-filter-all-dgn = All DGN Files (*.*)
.xref-attach-filter-pdf = PDF files (*.pdf)
.xref-attach-filter-point-cloud = Point Cloud (*.rcp; *.rcs)
.xref-attach-filter-all = All files (*.*)
.xref-attach-help = Inserts references to external files such as other drawings, raster images, and underlays.
.xref-attach-invalid-input = Invalid input.
.xref-insertion-point-x = Insertion point X
+1 -1
View File
@@ -869,7 +869,7 @@ impl OpenCADStudio {
.split_once(char::is_whitespace)
.map(|(verb, rest)| (verb.to_ascii_uppercase(), rest.trim()))
.unwrap_or_else(|| (cmd.to_ascii_uppercase(), ""));
if verb == "PDFATTACH" && rest.is_empty() {
if verb == "PDFATTACH" && rest.is_empty() && crate::sys::file_dia() {
return Some(Task::done(Message::PdfAttachPick));
}
let insunits = self.tabs[i].scene.document.header.insertion_units;
+2
View File
@@ -712,6 +712,7 @@ inventory::submit!(crate::command::CommandRegistration {
"PDFOSNAP",
"UOSNAP",
"FIELDDISPLAY",
"FILEDIA",
"SURFACEASSOCIATIVITY",
"LAYOUTCREATEVIEWPORT",
"FIELDEVAL",
@@ -944,6 +945,7 @@ inventory::submit!(crate::command::CommandRegistration {
"XR",
"XREF",
"-XREF",
"-ATTACH",
"XRELOAD",
"ZOOM",
"ZS",
+30
View File
@@ -1210,6 +1210,7 @@ impl OpenCADStudio {
| "DGNOSNAP"
| "UOSNAP"
| "FIELDDISPLAY"
| "FILEDIA"
| "SURFACEASSOCIATIVITY"
| "UCSORTHO"
| "UCSVIEW"
@@ -1593,6 +1594,35 @@ impl OpenCADStudio {
}
return Some(self.finish_dispatch(cmd));
}
if name == "FILEDIA" {
let current = i16::from(crate::sys::file_dia());
match value
.as_deref()
.map(|v| v.trim().parse::<i16>().ok().filter(|v| (0..=1).contains(v)))
{
Some(Some(mode)) => {
if current != mode {
crate::sys::set_file_dia(mode == 1);
self.save_config();
}
}
Some(None) => {
self.command_line
.push_error(crate::t!("Requires 0 or 1 only.").as_ref());
self.command_line.push_output(&format!(
"Enter new value for FILEDIA <{current}>:"
));
self.pending_setvar = Some(name.clone());
}
None => {
self.command_line.push_output(&format!(
"Enter new value for FILEDIA <{current}>:"
));
self.pending_setvar = Some(name.clone());
}
}
return Some(self.finish_dispatch(cmd));
}
if matches!(
name.as_str(),
"PDFOSNAP" | "DWFOSNAP" | "DGNOSNAP" | "UOSNAP"
+88 -45
View File
@@ -8,9 +8,23 @@ use crate::modules::insert::xattach::{
existing_reference, path_to_block_name, prepare_xref_definition, stored_path, XAttachCommand,
XrefAttachRequest, XrefPlacement,
};
use crate::modules::insert::xattach::IMAGE_EXTENSIONS;
use crate::modules::insert::xref_cmd::display_path;
use crate::ui::window::xref_attach::{XrefAttachMsg, XrefAttachState};
/// ATTACH's file types, in the reference order (the picker counts from 1).
const ATTACH_FILTERS: [(&str, &[&str]); 8] = [
("All image files", IMAGE_EXTENSIONS),
("DWF files (*.dwfx; *.dwf)", &["dwfx", "dwf"]),
("MicroStation DGN (*.dgn)", &["dgn"]),
("All DGN Files (*.*)", &["*"]),
("PDF files (*.pdf)", &["pdf"]),
("Point Cloud (*.rcp; *.rcs)", &["rcp", "rcs"]),
("Drawing (*.dwg)", &["dwg"]),
("All files (*.*)", &["*"]),
];
const ALL_DGN_FILTER: u32 = 4;
impl OpenCADStudio {
/// DWFATTACH / DGNATTACH on the command line: with a file, from its
/// sheet / model prompt; without one, from the path prompt.
@@ -42,12 +56,26 @@ impl OpenCADStudio {
Some((verb, rest)) => (verb.to_ascii_uppercase(), rest.trim()),
None => (upper.clone(), ""),
};
// FILEDIA 0 sends the picking commands to their command-line forms.
let dialog = crate::sys::file_dia();
match (verb.as_str(), rest.is_empty()) {
("ATTACH", true) => Some(Task::done(Message::AttachPick)),
("DWFATTACH", true) => Some(Task::done(Message::UnderlayAttachPick(
("ATTACH", true) if dialog => Some(Task::done(Message::AttachPick)),
("ATTACH", true) | ("-ATTACH", _) => {
use crate::command::CadCommand;
let command = crate::modules::insert::xref_cmd::AttachCommand::new();
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
if !rest.is_empty() {
return Some(
self.feed_command(crate::command::StepInput::Text(rest.to_string())),
);
}
Some(Task::none())
}
("DWFATTACH", true) if dialog => Some(Task::done(Message::UnderlayAttachPick(
codec::entities::UnderlayType::Dwf,
))),
("DGNATTACH", true) => Some(Task::done(Message::UnderlayAttachPick(
("DGNATTACH", true) if dialog => Some(Task::done(Message::UnderlayAttachPick(
codec::entities::UnderlayType::Dgn,
))),
// A file after DWFATTACH / DGNATTACH, or the - forms, go on the
@@ -62,7 +90,9 @@ impl OpenCADStudio {
}
// POINTCLOUDATTACH picks the file; with a file, or as
// -POINTCLOUDATTACH, it asks on the command line.
("POINTCLOUDATTACH", true) => Some(Task::done(Message::PointCloudAttachPick)),
("POINTCLOUDATTACH", true) if dialog => {
Some(Task::done(Message::PointCloudAttachPick))
}
("POINTCLOUDATTACH" | "-POINTCLOUDATTACH", _) => {
use crate::command::CadCommand;
let command = crate::modules::insert::pc_attach::PointCloudAttachCommand::new();
@@ -75,7 +105,14 @@ impl OpenCADStudio {
}
Some(Task::none())
}
("XATTACH", true) => Some(Task::done(Message::XAttachPick)),
("XATTACH", true) if dialog => Some(Task::done(Message::XAttachPick)),
("XATTACH", true) => {
let command =
crate::modules::insert::xref_cmd::XrefCommand::attach(self.saved_xref_paths(i));
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
Some(Task::none())
}
// A file named on the command line skips the picker.
// Handled at once, so the placement it starts belongs to this
// command line (automation included).
@@ -96,15 +133,8 @@ impl OpenCADStudio {
Some(self.finish_dispatch(cmd))
}
("-XREF", true) => {
let saved: Vec<(String, String)> = self.tabs[i]
.scene
.document
.block_records
.iter()
.filter(|br| br.flags.is_xref || br.flags.is_xref_overlay)
.map(|br| (br.name.clone(), br.xref_path.clone()))
.collect();
let command = crate::modules::insert::xref_cmd::XrefCommand::new(saved);
let command =
crate::modules::insert::xref_cmd::XrefCommand::new(self.saved_xref_paths(i));
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
Some(Task::none())
@@ -128,6 +158,18 @@ impl OpenCADStudio {
}
}
/// Saved paths by reference name, for -XREF.
fn saved_xref_paths(&self, i: usize) -> Vec<(String, String)> {
self.tabs[i]
.scene
.document
.block_records
.iter()
.filter(|br| br.flags.is_xref || br.flags.is_xref_overlay)
.map(|br| (br.name.clone(), br.xref_path.clone()))
.collect()
}
/// `-XREF ?` — the reference table as the reference application prints it.
fn list_xrefs(&mut self, i: usize, pattern: &str) {
let rows: Vec<(String, &'static str, String)> = self.tabs[i]
@@ -392,40 +434,41 @@ impl OpenCADStudio {
},
Message::AttachPickResult,
),
Message::AttachPick => Task::perform(
async {
let handle = crate::sys::file_dialog()
.set_title(crate::t!("Select Reference File").as_ref())
.add_filter(
crate::t!("All Reference Files").as_ref(),
&[
"dwg", "dxf", "pdf", "dwf", "dwfx", "dgn", "png", "jpg", "jpeg",
"bmp", "tif", "tiff", "rcp", "rcs",
],
)
.add_filter(crate::t!("Drawing").as_ref(), &["dwg", "dxf"])
.add_filter(crate::t!("PDF Files").as_ref(), &["pdf"])
.add_filter(crate::t!("DWF Files").as_ref(), &["dwf", "dwfx"])
.add_filter(crate::t!("DGN Files").as_ref(), &["dgn"])
.add_filter(
crate::t!("Images").as_ref(),
&["png", "jpg", "jpeg", "bmp", "tif", "tiff"],
)
.add_filter(crate::t!("Point Clouds").as_ref(), &["rcp", "rcs"])
.pick_file()
.await;
match handle {
Some(h) => Ok(crate::sys::handle_path(&h)),
None => Err("Cancelled".to_string()),
}
},
Message::AttachPickResult,
),
Message::AttachPick => {
let filters = ATTACH_FILTERS
.iter()
.map(|(label, extensions)| (crate::t!(label).to_string(), *extensions))
.collect();
let title = crate::t!("Select Reference File").to_string();
let index = crate::sys::attach_filter();
Task::perform(
async move {
match crate::sys::pick_file_with_filter(title, filters, index).await {
Some((path, index)) => {
crate::sys::set_attach_filter(index);
Ok(path)
}
None => Err("Cancelled".to_string()),
}
},
Message::AttachPickResult,
)
}
Message::AttachPickResult(Ok(path)) => {
let ext = path
// The file type the picker was left on is a profile setting.
self.save_config();
let mut ext = path
.extension()
.map(|e| e.to_string_lossy().to_ascii_lowercase())
.unwrap_or_default();
// Any file picked under "All DGN Files" is a DGN.
let known = ["dwg", "dxf", "pdf", "dwf", "dwfx", "dgn", "rcp", "rcs"];
if crate::sys::attach_filter() == ALL_DGN_FILTER
&& !known.contains(&ext.as_str())
&& !IMAGE_EXTENSIONS.contains(&ext.as_str())
{
ext = "dgn".to_string();
}
match ext.as_str() {
"pdf" => match std::fs::read(&path) {
Ok(bytes) => self.update(Message::PdfAttachPickResult(Ok((
@@ -477,7 +520,7 @@ impl OpenCADStudio {
self.open_point_cloud_attach_dialog(&path);
Task::none()
}
"png" | "jpg" | "jpeg" | "bmp" | "tif" | "tiff" => {
e if IMAGE_EXTENSIONS.contains(&e) => {
let result = image::image_dimensions(&path)
.map(|(w, h)| (path.clone(), w, h))
.map_err(|e| e.to_string());
+6
View File
@@ -417,6 +417,10 @@ pub struct UserSettings {
pub attdef_on_screen: bool,
/// FIELDDISPLAY: fields show on a gray background.
pub field_display: bool,
/// FILEDIA: file dialogs of commands that have a command-line form.
pub file_dia: bool,
/// The file type ATTACH's picker was last left on (1-based).
pub attach_filter: u32,
/// SURFACEASSOCIATIVITY: new surfaces stay associative to their sources.
pub surface_associativity: bool,
/// UCSORTHO: an orthographic view preset also sets its orthographic UCS.
@@ -842,6 +846,8 @@ impl Default for UserSettings {
image_frame: 1,
attdef_on_screen: true,
field_display: true,
file_dia: true,
attach_filter: 7,
surface_associativity: true,
ucs_ortho: true,
ucs_view: true,
+4
View File
@@ -793,6 +793,8 @@ impl OpenCADStudio {
image_frame: crate::scene::frame::profile_image_mode(),
attdef_on_screen: crate::modules::draw::draw::attdef::session().on_screen,
field_display: crate::entities::field::display(),
file_dia: crate::sys::file_dia(),
attach_filter: crate::sys::attach_filter(),
surface_associativity: crate::scene::model::sweep_model::surface_associativity(),
ucs_ortho: crate::modules::view::view_cmd::ucs_ortho(),
ucs_view: crate::modules::view::view_cmd::ucs_view(),
@@ -909,6 +911,8 @@ impl OpenCADStudio {
crate::scene::frame::set_profile_image_mode(s.image_frame);
crate::modules::draw::draw::attdef::session().on_screen = s.attdef_on_screen;
crate::entities::field::set_display(s.field_display);
crate::sys::set_file_dia(s.file_dia);
crate::sys::set_attach_filter(s.attach_filter);
crate::scene::model::sweep_model::set_surface_associativity(s.surface_associativity);
crate::modules::view::view_cmd::set_ucs_ortho(s.ucs_ortho);
crate::modules::view::view_cmd::set_ucs_view(s.ucs_view);
+7
View File
@@ -686,6 +686,13 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Hide Details" => Some(("insert", "xref-attach-hide-details")),
"Select Reference File" => Some(("insert", "xref-attach-select-file")),
"All Reference Files" => Some(("insert", "xref-attach-all-files")),
"All image files" => Some(("insert", "xref-attach-filter-images")),
"DWF files (*.dwfx; *.dwf)" => Some(("insert", "xref-attach-filter-dwf")),
"MicroStation DGN (*.dgn)" => Some(("insert", "xref-attach-filter-dgn")),
"All DGN Files (*.*)" => Some(("insert", "xref-attach-filter-all-dgn")),
"PDF files (*.pdf)" => Some(("insert", "xref-attach-filter-pdf")),
"Point Cloud (*.rcp; *.rcs)" => Some(("insert", "xref-attach-filter-point-cloud")),
"All files (*.*)" => Some(("insert", "xref-attach-filter-all")),
"Inserts references to external files such as other drawings, raster images, and underlays." => Some(("insert", "xref-attach-help")),
"Invalid input." => Some(("insert", "xref-attach-invalid-input")),
"Insertion point X" => Some(("insert", "xref-insertion-point-x")),
+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) {
+115
View File
@@ -587,6 +587,121 @@ pub fn file_dialog() -> rfd::AsyncFileDialog {
rfd::AsyncFileDialog::new()
}
// ── FILEDIA and file pickers that keep their file type ───────────────────────
static FILE_DIA: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
/// FILEDIA (a profile setting): commands that have a command-line form show
/// their file dialogs only while it is on.
pub fn file_dia() -> bool {
FILE_DIA.load(std::sync::atomic::Ordering::Relaxed)
}
pub fn set_file_dia(on: bool) {
FILE_DIA.store(on, std::sync::atomic::Ordering::Relaxed);
}
/// The file type ATTACH's picker was last left on (1-based; 7 = Drawing).
static ATTACH_FILTER: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(7);
pub fn attach_filter() -> u32 {
ATTACH_FILTER.load(std::sync::atomic::Ordering::Relaxed)
}
pub fn set_attach_filter(index: u32) {
ATTACH_FILTER.store(index, std::sync::atomic::Ordering::Relaxed);
}
/// A file type of [`pick_file_with_filter`]: its label and extensions
/// (`"*"` for every file).
pub type FileFilter = (String, &'static [&'static str]);
/// Open-file dialog that starts on file type `index` (1-based) and returns the
/// picked path with the file type the dialog was left on.
#[cfg(target_os = "windows")]
pub async fn pick_file_with_filter(
title: String,
filters: Vec<FileFilter>,
index: u32,
) -> Option<(std::path::PathBuf, u32)> {
// The dialog blocks, so it runs on its own thread like the async picker.
let (tx, rx) = iced::futures::channel::oneshot::channel();
std::thread::spawn(move || {
let _ = tx.send(open_file_dialog_blocking(&title, &filters, index));
});
rx.await.ok().flatten()
}
#[cfg(target_os = "windows")]
fn open_file_dialog_blocking(
title: &str,
filters: &[FileFilter],
index: u32,
) -> Option<(std::path::PathBuf, u32)> {
use windows_sys::Win32::System::Com::{CoInitializeEx, COINIT_APARTMENTTHREADED};
use windows_sys::Win32::UI::Controls::Dialogs::{
GetOpenFileNameW, OFN_EXPLORER, OFN_FILEMUSTEXIST, OFN_HIDEREADONLY, OFN_NOCHANGEDIR,
OFN_PATHMUSTEXIST, OPENFILENAMEW,
};
let wide = |s: &str| s.encode_utf16().chain([0]).collect::<Vec<u16>>();
let mut filter = Vec::new();
for (label, extensions) in filters {
let pattern = extensions
.iter()
.map(|e| format!("*.{e}"))
.collect::<Vec<_>>()
.join(";");
filter.extend(wide(label));
filter.extend(wide(&pattern));
}
filter.push(0);
let title = wide(title);
let dir = crate::config::last_dialog_dir().map(|d| wide(&d.to_string_lossy()));
let mut file = vec![0u16; 32768];
let mut ofn = OPENFILENAMEW {
lStructSize: std::mem::size_of::<OPENFILENAMEW>() as u32,
lpstrFilter: filter.as_ptr(),
nFilterIndex: index,
lpstrFile: file.as_mut_ptr(),
nMaxFile: file.len() as u32,
lpstrTitle: title.as_ptr(),
lpstrInitialDir: dir.as_ref().map_or(std::ptr::null(), |d| d.as_ptr()),
Flags: OFN_EXPLORER
| OFN_FILEMUSTEXIST
| OFN_PATHMUSTEXIST
| OFN_NOCHANGEDIR
| OFN_HIDEREADONLY,
..Default::default()
};
// SAFETY: every pointer in `ofn` points into buffers that outlive the call.
let picked = unsafe {
CoInitializeEx(std::ptr::null(), COINIT_APARTMENTTHREADED as u32);
GetOpenFileNameW(&mut ofn)
};
if picked == 0 {
return None;
}
let len = file.iter().position(|&c| c == 0).unwrap_or(file.len());
let path = std::path::PathBuf::from(String::from_utf16_lossy(&file[..len]));
crate::config::remember_dialog_dir(&path);
Some((path, ofn.nFilterIndex))
}
/// Elsewhere the picker cannot start on a file type; the index passes through.
#[cfg(not(target_os = "windows"))]
pub async fn pick_file_with_filter(
title: String,
filters: Vec<FileFilter>,
index: u32,
) -> Option<(std::path::PathBuf, u32)> {
let mut dialog = file_dialog().set_title(title);
for (label, extensions) in &filters {
dialog = dialog.add_filter(label, extensions);
}
let handle = dialog.pick_file().await?;
Some((handle_path(&handle), index))
}
/// Trigger a browser download of `bytes` as `name`. Builds a Blob, points a
/// hidden `<a download>` at it and clicks it programmatically — because this
/// runs inside the Save button's click (a user gesture), the file downloads