Sheet sets: import statuses, view navigation fields, drawing lock files

Import Layouts as Sheets shows the reference's statuses (already part of
a sheet set / may belong to another sheet set / available) and Browse
replaces the list; the Field dialog SheetView tree lists sheet views with
their properties, scale formats and hyperlinks; drawings open in tabs get
.dwl/.dwl2 lock files that other instances read as open; the codec pin
moves to the revision with sheet views and the default output folder.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-05 13:08:37 +03:00
1 parent 0a45e59c2f
commit 5c9f19a4dc
29 files changed
+236 -62

No files matched your search

Generated
+1 -1
View File
@@ -4206,7 +4206,7 @@ dependencies = [
[[package]]
name = "opencadcodec"
version = "0.5.5"
source = "git+https://github.com/ramox81/cadcodec.git?rev=d86f57d#d86f57d5d179bc57fe7826d70454bece40c20320"
source = "git+https://github.com/ramox81/cadcodec.git?rev=29aff7e#29aff7e8e6f4669cd1f41fc82d39521127c07d03"
dependencies = [
"ahash 0.8.12",
"anyhow",
+1 -1
View File
@@ -91,7 +91,7 @@ block2 = "0.5"
objc2-foundation = { version = "0.2", features = ["NSArray", "NSURL", "NSString", "NSObject", "NSNotification", "NSThread", "NSEnumerator"] }
[patch."https://github.com/HakanSeven12/opencadcodec.git"]
opencadcodec = { git = "https://github.com/ramox81/cadcodec.git", rev = "d86f57d" }
opencadcodec = { git = "https://github.com/ramox81/cadcodec.git", rev = "29aff7e" }
[patch.crates-io]
iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
+2 -1
View File
@@ -4407,6 +4407,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4427,7 +4429,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4394,6 +4394,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4414,7 +4416,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4392,6 +4392,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4412,7 +4414,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4393,6 +4393,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4413,7 +4415,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4395,6 +4395,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4415,7 +4417,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4396,6 +4396,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4416,7 +4418,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4395,6 +4395,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4415,7 +4417,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4394,6 +4394,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4414,7 +4416,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4395,6 +4395,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4415,7 +4417,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4383,6 +4383,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4403,7 +4405,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4394,6 +4394,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4414,7 +4416,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4394,6 +4394,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4414,7 +4416,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4383,6 +4383,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4403,7 +4405,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4394,6 +4394,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4414,7 +4416,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4393,6 +4393,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4413,7 +4415,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4394,6 +4394,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4414,7 +4416,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4395,6 +4395,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4415,7 +4417,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4396,6 +4396,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4416,7 +4418,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4355,6 +4355,8 @@ view =
.ss-new-sheet-location = Yeni sayfa konumu
.ss-remove-subset = Alt Kümeyi Kaldır
.ss-rename-subset = Alt Kümeyi Yeniden Adlandır...
.ss-layout-already-part = Bu düzen zaten bir sayfa kümesinde - içe aktarılamaz.
.ss-layout-may-belong = Uyarı: bu düzen başka bir sayfa kümesine ait olabilir.
.ss-status-missing = yok
.ss-status-open = açık
.ss-view-by-category = Kategoriye göre
@@ -4375,7 +4377,6 @@ view =
.ss-prefix-with-sheet-number = Önüne sayfa numarası
.ss-rename-drawing-file-to-match = Çizim dosyasını eşleşecek şekilde adlandır:
.ss-available-for-import = Hazır
.ss-belongs-to-sheet-set = Başka kümede: __ocs_fmt_0__
.ss-drawings = Çizimler
.ss-select-drawing-files-containing-layouts = Düzen içeren çizim dosyalarını seçin
.ss-browse-for-drawings = Çizimlere Gözat…
+2 -1
View File
@@ -4378,6 +4378,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4398,7 +4400,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+2 -1
View File
@@ -4390,6 +4390,8 @@ view =
.ss-new-sheet-location = New sheet location
.ss-remove-subset = Remove Subset
.ss-rename-subset = Rename Subset...
.ss-layout-already-part = This layout is already part of a sheet set - not available for import.
.ss-layout-may-belong = Warning: this layout may belong to another sheet set.
.ss-status-missing = missing
.ss-status-open = open
.ss-view-by-category = View by category
@@ -4410,7 +4412,6 @@ view =
.ss-prefix-with-sheet-number = Prefix with sheet number
.ss-rename-drawing-file-to-match = Rename drawing file to match:
.ss-available-for-import = Available for import
.ss-belongs-to-sheet-set = Belongs to sheet set __ocs_fmt_0__
.ss-drawings = Drawings
.ss-select-drawing-files-containing-layouts = Select drawing files containing layouts
.ss-browse-for-drawings = Browse for Drawings...
+90 -17
View File
@@ -9,6 +9,7 @@ use crate::ui::window::sheet_set::{
};
use codec::sheet_set::{self as ss, ComponentKind, LayoutReference, SheetSetData, SheetSetDatabase};
use iced::Task;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
/// The variables this family answers for.
@@ -63,6 +64,55 @@ fn folder_of(path: &str) -> String {
/// Template of a component for new sheets: its own `DefDwtLayout`, else
/// the nearest parent's.
/// The lock files' date: the long date of the user's locale, two spaces and
/// the 24-hour time (`5 Ekim 2026 Pazartesi 09:26:39`).
#[cfg(windows)]
fn lock_stamp() -> String {
use windows_sys::Win32::Globalization::{GetDateFormatEx, GetTimeFormatEx, DATE_LONGDATE};
let wide = |s: &str| s.encode_utf16().chain(std::iter::once(0)).collect::<Vec<u16>>();
let mut date = [0u16; 128];
let mut time = [0u16; 64];
let format = wide("HH:mm:ss");
// SAFETY: the buffers outlive the calls and their lengths are passed.
let (d, t) = unsafe {
(
GetDateFormatEx(std::ptr::null(), DATE_LONGDATE, std::ptr::null(), std::ptr::null(), date.as_mut_ptr(), date.len() as i32, std::ptr::null()),
GetTimeFormatEx(std::ptr::null(), 0, std::ptr::null(), format.as_ptr(), time.as_mut_ptr(), time.len() as i32),
)
};
let text = |b: &[u16], n: i32| String::from_utf16_lossy(&b[..(n.max(1) as usize - 1)]);
format!("{} {}", text(&date, d), text(&time, t))
}
#[cfg(not(windows))]
fn lock_stamp() -> String {
String::new()
}
/// `<drawing>.dwl` (user, machine, date lines) and `<drawing>.dwl2` (the same
/// as XML), as the reference writes them for a drawing it has open.
// ponytail: .dwl is written as UTF-8; the reference's encoding of non-ASCII
// day names was not measured.
#[cfg(not(target_arch = "wasm32"))]
fn write_lock(path: &Path) -> std::io::Result<()> {
let user = std::env::var("USERNAME").or_else(|_| std::env::var("USER")).unwrap_or_default();
let machine = std::env::var("COMPUTERNAME").unwrap_or_default();
let stamp = lock_stamp();
std::fs::write(path.with_extension("dwl"), format!("{user}\n{machine} \n{stamp}"))?;
std::fs::write(
path.with_extension("dwl2"),
format!(
"<?xml version=\"1.0\" encoding=\"UTF-8\">\n<whprops>\n<username>{user}</username>\n<machinename>{machine} </machinename>\n<fullname></fullname>\n<datetime>{stamp}</datetime>\n</whprops>"
),
)
}
#[cfg(not(target_arch = "wasm32"))]
pub(in crate::app) fn release_lock(path: &Path) {
let _ = std::fs::remove_file(path.with_extension("dwl"));
let _ = std::fs::remove_file(path.with_extension("dwl2"));
}
/// Whether new sheets of `component` prompt for their template: its own
/// "Prompt for template", else the nearest parent's.
fn prompts_for_template(db: &SheetSetDatabase, component: &str) -> bool {
@@ -92,16 +142,15 @@ fn import_rows(path: &Path, db: &SheetSetDatabase) -> Result<Vec<ImportRow>, Str
let mine = db.sheet_for(&drawing, Some(&layout)).is_some_and(|s| {
s.named("Layout").and_then(|r| r.prop("Name")).is_some_and(|n| n.eq_ignore_ascii_case(&layout))
});
let owner = if mine {
Some(db.name().to_string())
} else {
link.as_ref().filter(|l| l.layout_name.eq_ignore_ascii_case(&layout) && !l.sheet_set_file_name.is_empty()).map(|l| {
SheetSetDatabase::read(&l.sheet_set_file_name)
.map(|d| d.name().to_string())
.unwrap_or_else(|_| Path::new(&l.sheet_set_file_name).file_stem().map(|s| s.to_string_lossy().to_string()).unwrap_or_default())
})
};
ImportRow { drawing: drawing.clone(), layout, handle, on: owner.is_none(), owner }
// A layout of this set is taken; one the drawing's sheet link
// names for another set is only warned about (still importable).
let warn = !mine
&& link.as_ref().is_some_and(|l| {
l.layout_name.eq_ignore_ascii_case(&layout)
&& !l.sheet_set_file_name.is_empty()
&& db.path.as_deref().is_none_or(|p| ss::path_key(p) != ss::path_key(&l.sheet_set_file_name))
});
ImportRow { drawing: drawing.clone(), layout, handle, on: !mine, taken: mine, warn }
})
.collect())
}
@@ -331,6 +380,30 @@ impl OpenCADStudio {
Ok(())
}
/// Lock files for the drawings open in tabs, as other instances expect
/// them (the sheet status shows such a sheet as open); a drawing that
/// already has someone else's lock file is left alone.
#[cfg(not(target_arch = "wasm32"))]
fn sync_drawing_locks(&mut self) {
let open: HashSet<PathBuf> = self.tabs.iter().filter_map(|t| t.current_path.clone()).filter(|p| p.exists()).collect();
let gone: Vec<PathBuf> = self.sheet_set.locks.difference(&open).cloned().collect();
for p in gone {
release_lock(&p);
self.sheet_set.locks.remove(&p);
}
for p in open {
if self.sheet_set.locks.contains(&p) || p.with_extension("dwl").exists() {
continue;
}
if write_lock(&p).is_ok() {
self.sheet_set.locks.insert(p);
}
}
}
#[cfg(target_arch = "wasm32")]
fn sync_drawing_locks(&mut self) {}
/// Reload every open set whose `.dst` was changed by someone else.
fn poll_sheet_sets(&mut self) {
let mut changed = false;
@@ -463,6 +536,7 @@ impl OpenCADStudio {
}
}
SheetSetMsg::Poll => {
self.sync_drawing_locks();
self.poll_sheet_sets();
// SSMSHEETSTATUS 2: the status is taken again every SSMPOLLTIME seconds.
let every = std::time::Duration::from_secs(u64::from(self.sheet_set.settings.poll_time.max(20)));
@@ -605,7 +679,7 @@ impl OpenCADStudio {
(FieldId::ImportPrefix, Some(SsDialog::Form(f))) => f.prefix = v,
(FieldId::ImportRow(k), Some(SsDialog::Form(f))) => {
if let Some(r) = f.rows.get_mut(k) {
r.on = v && r.owner.is_none();
r.on = v && !r.taken;
}
}
(FieldId::RenameOption(k), Some(SsDialog::Form(f))) => {
@@ -1098,11 +1172,10 @@ impl OpenCADStudio {
Err(e) => self.command_line.push_error(&format!("{}: {e}", p.display())),
}
}
// The drawings picked replace the list.
if let Some(SsDialog::Form(f)) = self.sheet_set.dialog.as_mut() {
for r in added {
if !f.rows.iter().any(|o| ss::path_key(&o.drawing) == ss::path_key(&r.drawing) && o.layout == r.layout) {
f.rows.push(r);
}
if !added.is_empty() {
f.rows = added;
}
}
}
@@ -1309,7 +1382,7 @@ impl OpenCADStudio {
let path = Path::new(&r.file_name);
let state = if !path.exists() {
Some(SheetStatus::Missing)
} else if path.with_extension("dwl").exists() || self.tab_showing(path).is_some() {
} else if path.with_extension("dwl").exists() {
Some(SheetStatus::Locked)
} else {
None
@@ -1500,7 +1573,7 @@ impl OpenCADStudio {
}
}
FormKind::ImportLayout => {
let chosen: Vec<&ImportRow> = f.rows.iter().filter(|r| r.on && r.owner.is_none()).collect();
let chosen: Vec<&ImportRow> = f.rows.iter().filter(|r| r.on && !r.taken).collect();
if chosen.is_empty() {
f.error = Some(crate::t!("The drawing has no layout to import.").into_owned());
self.sheet_set.dialog = Some(SsDialog::Form(f));
+4
View File
@@ -3308,6 +3308,10 @@ impl OpenCADStudio {
for i in 0..self.tabs.len() {
let _ = std::fs::remove_file(self.autosave_target(i));
}
#[cfg(not(target_arch = "wasm32"))]
for path in &self.sheet_set.locks {
crate::app::commands::sheet_set::release_lock(path);
}
}
/// Remove the autosave recovery files, then quit the application.
+4 -6
View File
@@ -2646,12 +2646,10 @@ impl OpenCADStudio {
let plugin_drain = Subscription::none();
// Open sheet sets reload when their .dst changes on disk.
#[cfg(not(target_arch = "wasm32"))]
let sheet_set_poll = if self.sheet_set.sets.is_empty() {
Subscription::none()
} else {
iced::time::every(std::time::Duration::from_secs(1))
.map(|_| Message::SheetSet(crate::ui::window::sheet_set::SheetSetMsg::Poll))
};
// Open sheet sets reload when their .dst changes on disk; the
// drawings' .dwl lock files follow the open tabs.
let sheet_set_poll = iced::time::every(std::time::Duration::from_secs(1))
.map(|_| Message::SheetSet(crate::ui::window::sheet_set::SheetSetMsg::Poll));
#[cfg(target_arch = "wasm32")]
let sheet_set_poll = Subscription::none();
let hatch_pattern_keys = if self.tabs[self.active_tab]
+2 -1
View File
@@ -50,6 +50,8 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"New sheet location" => Some(("view", "ss-new-sheet-location")),
"Remove Subset" => Some(("view", "ss-remove-subset")),
"Rename Subset..." => Some(("view", "ss-rename-subset")),
"This layout is already part of a sheet set - not available for import." => Some(("view", "ss-layout-already-part")),
"Warning: this layout may belong to another sheet set." => Some(("view", "ss-layout-may-belong")),
"missing" => Some(("view", "ss-status-missing")),
"open" => Some(("view", "ss-status-open")),
"View by category" => Some(("view", "ss-view-by-category")),
@@ -70,7 +72,6 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Prefix with sheet number" => Some(("view", "ss-prefix-with-sheet-number")),
"Rename drawing file to match:" => Some(("view", "ss-rename-drawing-file-to-match")),
"Available for import" => Some(("view", "ss-available-for-import")),
"Belongs to sheet set {}" => Some(("view", "ss-belongs-to-sheet-set")),
"Drawings" => Some(("view", "ss-drawings")),
"Select drawing files containing layouts" => Some(("view", "ss-select-drawing-files-containing-layouts")),
"Browse for Drawings..." => Some(("view", "ss-browse-for-drawings")),
+73 -9
View File
@@ -97,8 +97,21 @@ pub fn nav_nodes(db: &codec::sheet_set::SheetSetDatabase, views: bool) -> Vec<(u
let set = db.sheet_set();
let mut out = vec![(0, set.id().to_string(), db.name().to_string(), false)];
if views {
for c in db.view_categories() {
// Views of the unnamed (default) category sit under the set, then each
// named category with its views.
let all: Vec<&SsElement> = db.sheets().into_iter().flat_map(|s| db.sheet_views(s)).collect();
let named: Vec<&SsElement> = db.view_categories();
let in_named = |v: &SsElement| {
codec::sheet_set::SheetSetDatabase::view_category_of(v).is_some_and(|c| named.iter().any(|n| n.id() == c))
};
for v in all.iter().filter(|v| !in_named(v)) {
out.push((1, v.id().to_string(), number_and_title(v), true));
}
for c in &named {
out.push((1, c.id().to_string(), c.prop("Name").unwrap_or("").to_string(), false));
for v in all.iter().filter(|v| codec::sheet_set::SheetSetDatabase::view_category_of(v) == Some(c.id())) {
out.push((2, v.id().to_string(), number_and_title(v), true));
}
}
return out;
}
@@ -156,10 +169,32 @@ pub fn nav_props(db: &codec::sheet_set::SheetSetDatabase, id: &str) -> Vec<(Stri
v.extend(custom(CUSTOM_SHEET_PROP));
v
}
None if el.name == "AcSmSheetView" => fixed(&[
("ViewNumberAndTitle", "NumberAndTitle", ""),
("ViewTitle", "Title", ""),
("ViewNumber", "Number", ""),
("ViewportScale", "ViewportScale", ".16.2"),
]),
None => fixed(&[("ViewCategoryName", "Name", "")]),
}
}
/// The sheet drawing, named view and layout a sheet or sheet view node links to.
pub fn nav_link(db: &codec::sheet_set::SheetSetDatabase, id: &str) -> Option<(String, String, String)> {
let el = db.find(id)?;
let (sheet, view) = if el.name == "AcSmSheetView" {
let views = db.parent_of(id)?;
let sheet = db.parent_of(views.id())?;
(sheet.id().to_string(), el.named("NamedView").and_then(|v| v.prop("Name")).unwrap_or("").to_string())
} else if codec::sheet_set::ComponentKind::of(el) == Some(codec::sheet_set::ComponentKind::Sheet) {
(id.to_string(), String::new())
} else {
return None;
};
let r = db.layout_reference(&sheet, "Layout")?;
Some((r.file_name, view, r.name))
}
impl FieldDialogState {
/// `\AcSm Database("…").SheetSet("…")[.Component("…")].Property` of the
/// picked node, with the text case and, on a sheet, the hyperlink to it.
@@ -170,19 +205,36 @@ impl FieldDialogState {
};
let set = db.sheet_set().id().to_string();
let path = codec::sheet_set::native_path(db.path.as_deref().unwrap_or(""));
// ViewportScale takes a scale format (and its version), not a text case.
let scale = prop == "ViewportScale";
let version = if scale {
codec::fields::PLOT_SCALE_FORMATS
.get(self.ss_scale_format)
.map_or("", |(_, c)| if c.starts_with("\\AcVar.16.2") { ".16.2" } else { "" })
} else {
version
};
let mut code = format!("\\AcSm{version} Database(\"{path}\").SheetSet(\"{set}\")");
if self.ss_node != set {
code.push_str(&format!(".Component(\"{}\")", self.ss_node));
}
code.push('.');
code.push_str(&prop);
if scale {
let f = viewport_scale_picture(self.ss_scale_format);
if !f.is_empty() {
code.push(' ');
code.push_str(&f);
}
return code;
}
if self.text_case != 0 {
code.push_str(&format!(" \\f \"%tc{}\"", self.text_case));
}
let sheet = db.find(&self.ss_node).and_then(codec::sheet_set::ComponentKind::of) == Some(codec::sheet_set::ComponentKind::Sheet);
if sheet && self.ss_href {
if let Some(r) = db.layout_reference(&self.ss_node, "Layout") {
code.push_str(&format!(" \\href \"{}#,{}##1\"", r.file_name, r.name));
// A sheet links to its layout, a view to its named view on the sheet.
if self.ss_href {
if let Some((file, view, layout)) = nav_link(db, &self.ss_node) {
code.push_str(&format!(" \\href \"{file}#{view},{layout}##1\""));
}
}
code
@@ -959,11 +1011,23 @@ fn format_panel<'a>(state: &'a FieldDialogState) -> Element<'a, Message> {
props.iter().map(|(l, _, _)| (l.clone(), *l == state.ss_prop, FieldDialogMsg::SsProp(l.clone()))).collect(),
92.0,
);
let sheet = nodes.iter().any(|(_, id, _, sheet)| *sheet && *id == state.ss_node);
let mut href = checkbox(state.ss_href && sheet).label(t!("Associate hyperlink").into_owned()).text_size(12).size(14);
if sheet {
let linkable = db.is_some_and(|d| nav_link(d, &state.ss_node).is_some()) && state.ss_prop != "ViewportScale";
let mut href = checkbox(state.ss_href && linkable).label(t!("Associate hyperlink").into_owned()).text_size(12).size(14);
if linkable {
href = href.on_toggle(|v| msg(FieldDialogMsg::SsHref(v)));
}
let format: Element<'a, Message> = if state.ss_prop == "ViewportScale" {
list(
codec::fields::PLOT_SCALE_FORMATS
.iter()
.enumerate()
.map(|(i, (n, _))| (t!(*n).into_owned(), state.ss_scale_format == i, FieldDialogMsg::SsScaleFormat(i)))
.collect(),
120.0,
)
} else {
case_list(state)
};
column![
text(t!("Sheet set")).size(12),
set_row,
@@ -972,7 +1036,7 @@ fn format_panel<'a>(state: &'a FieldDialogState) -> Element<'a, Message> {
text(t!("Property")).size(12),
prop_list,
text(t!("Format")).size(12),
case_list(state),
format,
href,
]
.spacing(8)
+19 -6
View File
@@ -72,6 +72,8 @@ pub struct SheetSetManager {
pub by_category: bool,
/// Model Views: location id, folder and its drawings.
pub locations: Vec<(String, String, Vec<String>)>,
/// Drawings this application holds `.dwl` / `.dwl2` lock files for.
pub locks: HashSet<std::path::PathBuf>,
}
/// A sheet whose drawing is missing, or open (a `.dwl` lock file, or a tab here).
@@ -294,8 +296,10 @@ pub struct ImportRow {
pub drawing: String,
pub layout: String,
pub handle: String,
/// The sheet set the layout already belongs to (cannot be imported).
pub owner: Option<String>,
/// Already a sheet of this set: cannot be imported.
pub taken: bool,
/// The drawing's sheet link names another sheet set (importable, warned).
pub warn: bool,
pub on: bool,
}
@@ -1277,11 +1281,20 @@ fn form_view<'a>(f: &'a Form) -> Element<'a, Message> {
let mut list = vec![head.into()];
for (k, r) in f.rows.iter().enumerate() {
let file = std::path::Path::new(&r.drawing).file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default();
let status: Element<'a, Message> = match &r.owner {
None => text(t!("Available for import").into_owned()).size(12).style(|t: &Theme| text::Style { color: Some(t.palette().success.base.color) }).into(),
Some(owner) => text(crate::tf!("Belongs to sheet set {}", owner).into_owned()).size(12).style(|t: &Theme| text::Style { color: Some(t.palette().warning.base.color) }).into(),
let (label, warn) = if r.taken {
("This layout is already part of a sheet set - not available for import.", true)
} else if r.warn {
("Warning: this layout may belong to another sheet set.", true)
} else {
("Available for import", false)
};
let enabled = r.owner.is_none();
let status: Element<'a, Message> = text(t!(label).into_owned())
.size(12)
.style(move |t: &Theme| text::Style {
color: Some(if warn { t.palette().warning.base.color } else { t.palette().success.base.color }),
})
.into();
let enabled = !r.taken;
let cb = checkbox(r.on).size(14);
let cb: Element<'a, Message> = if enabled { cb.on_toggle(move |v| msg(SheetSetMsg::Toggle(FieldId::ImportRow(k), v))).into() } else { cb.into() };
let name = text(r.layout.clone()).size(12).width(Fill);