forked from 1b-agent/opencadstudio-upstream
ATTACH: FILEDIA, -ATTACH command line and reference file types
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b1d4c9bd9b
commit
0678c579c0
33 files changed
+519
-47
No files matched your search
+1
-1
@@ -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"] }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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")),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user