diff --git a/Cargo.lock b/Cargo.lock index 864e44cb2..5c8b0f296 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4206,7 +4206,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.5.5" -source = "git+https://github.com/ramox81/cadcodec.git?rev=3f5d40a#3f5d40a82368cd2dd5e5c44388f8630647ff3533" +source = "git+https://github.com/ramox81/cadcodec.git?rev=d86f57d#d86f57d5d179bc57fe7826d70454bece40c20320" dependencies = [ "ahash 0.8.12", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 8efe2a874..fdb912af0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 = "3f5d40a" } +opencadcodec = { git = "https://github.com/ramox81/cadcodec.git", rev = "d86f57d" } [patch.crates-io] iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" } diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 20ccb54ce..f53bed195 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -4407,6 +4407,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 457edab5c..e2e9bbfa8 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -4394,6 +4394,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 762a52473..72691ae7f 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -4392,6 +4392,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 14b3a2d12..f7bc3e6df 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -4393,6 +4393,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index e393c26e6..915802762 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -4395,6 +4395,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index d7498da27..8e2758d61 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -4396,6 +4396,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index c62ac3377..f73d3dfbd 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -4395,6 +4395,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index cf0c70d6b..d1165dea6 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -4394,6 +4394,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 3392b9e2a..2aa271973 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -4395,6 +4395,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 0d2435a77..024cb6214 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -4383,6 +4383,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 7b4b9834c..7e3230eec 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -4394,6 +4394,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index 9cf03a923..9bea307db 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -4394,6 +4394,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index 1479bab43..8e551ef06 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -4383,6 +4383,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 29fa2501e..62ed4f766 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -4394,6 +4394,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index c9b107482..1cb7147ef 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -4393,6 +4393,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index 1b8174e41..834d3a027 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -4394,6 +4394,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index f573797fe..454ca237e 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -4395,6 +4395,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 61d26d9bd..b6ba273b7 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -4396,6 +4396,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index ff6b922ff..c5264c1f7 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -4355,6 +4355,45 @@ 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-status-missing = yok + .ss-status-open = açık + .ss-view-by-category = Kategoriye göre + .ss-view-by-sheet = Sayfaya göre + .ss-import-layouts-as-sheets = Düzenleri Sayfa Olarak İçe Aktar + .ss-remove-sheets = Sayfaları Kaldır + .ss-view-category = Görünüm Kategorisi + .ss-new-view-category = Yeni Görünüm Kategorisi… + .ss-add-new-location = Yeni Konum Ekle… + .ss-remove-location = Konumu Kaldır + .ss-publish-by-sheet = Sayfanın 'Yayımlamaya dahil et' ayarına göre + .ss-do-not-publish-sheets = Sayfaları yayımlama + .ss-open-in-drawing-editor = Çizim düzenleyicide aç + .ss-create-folder-hierarchy = Klasör hiyerarşisi oluştur + .ss-publish-sheets-in-subset = Alt kümedeki sayfaları yayımla + .ss-rename-options = Yeniden adlandırma seçenekleri + .ss-rename-layout-to-match = Düzeni eşleşecek şekilde adlandır: + .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… + .ss-a-layout-can-belong-to-only-one-sheet-set = Bir düzen yalnız bir sayfa kümesine ait olabilir. Başka bir kümeye ait düzeni içe aktarmak için düzenin kopyasını oluşturun. + .ss-prefix-sheet-titles-with-file-name = Sayfa başlıklarına dosya adını önek olarak ekle + .ss-import-checked = İşaretlileri İçe Aktar + .ss-template = Şablon + .ss-drawing-template-file-name = Çizim şablonu dosya adı + .ss-select-a-layout-to-create-new-sheets = Yeni sayfalar için bir düzen seçin + .ss-category-name = Kategori adı + .ss-select-the-callout-blocks = Bu kategoride kullanılacak açıklama blokları + .ss-selected-blocks-will-be-available = Seçilen bloklar görünüm sekmesinden açıklama bloğu eklerken sunulur. + .ss-add-blocks = Blok Ekle… + .ss-layout-and-drawing-not-deleted = (Düzen ve çizim dosyası silinmez.) + .ss-remove-sheet-question = "__ocs_fmt_0__" sayfası "__ocs_fmt_1__" sayfa kümesinden kaldırılsın mı? + .ss-drawing-open-cannot-rename = Çizim açık; dosyasını yeniden adlandırmak için kapatın. + .ss-category-name-empty = Kategori adı boş olamaz. + .ss-associate-hyperlink = Köprü ilişkilendir .ss-remove-sheet = Sayfayı Kaldır .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Çizim içeren bir veya daha fazla klasör belirtmenizi sağlar. Bu çizimlerdeki düzenler sayfa kümesine otomatik olarak aktarılabilir. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Alt kümesi ve sayfası olmayan bir sayfa kümesi oluşturur. diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 5943c0419..508d87136 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -4378,6 +4378,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 96c93c254..497b58348 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -4390,6 +4390,45 @@ view = .ss-new-sheet-location = New sheet location .ss-remove-subset = Remove Subset .ss-rename-subset = Rename Subset... + .ss-status-missing = missing + .ss-status-open = open + .ss-view-by-category = View by category + .ss-view-by-sheet = View by sheet + .ss-import-layouts-as-sheets = Import Layouts as Sheets + .ss-remove-sheets = Remove Sheets + .ss-view-category = View Category + .ss-new-view-category = New View Category... + .ss-add-new-location = Add New Location... + .ss-remove-location = Remove Location + .ss-publish-by-sheet = Publish by Sheet 'Include for Publish' Setting + .ss-do-not-publish-sheets = Do Not Publish Sheets + .ss-open-in-drawing-editor = Open in drawing editor + .ss-create-folder-hierarchy = Create folder hierarchy + .ss-publish-sheets-in-subset = Publish sheets in subset + .ss-rename-options = Rename options + .ss-rename-layout-to-match = Rename layout to match: + .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... + .ss-a-layout-can-belong-to-only-one-sheet-set = A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it. + .ss-prefix-sheet-titles-with-file-name = Prefix sheet titles with file name + .ss-import-checked = Import Checked + .ss-template = Template + .ss-drawing-template-file-name = Drawing template file name + .ss-select-a-layout-to-create-new-sheets = Select a layout to create new sheets + .ss-category-name = Category name + .ss-select-the-callout-blocks = Select the callout blocks to be used in this category + .ss-selected-blocks-will-be-available = Selected blocks will be available for selection when you insert a callout block from the view tab. + .ss-add-blocks = Add Blocks... + .ss-layout-and-drawing-not-deleted = (The layout and the drawing file will not be deleted.) + .ss-remove-sheet-question = Are you sure that you want to remove __ocs_fmt_0__ Sheet from __ocs_fmt_1__ Sheet Set? + .ss-drawing-open-cannot-rename = The drawing is open; close it to rename its file. + .ss-category-name-empty = The category name cannot be empty. + .ss-associate-hyperlink = Associate hyperlink .ss-remove-sheet = Remove Sheet .ss-lets-you-specify-one-or-more-folders-containing-drawings-the = Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set. .ss-creates-a-sheet-set-with-no-subsets-or-sheets = Creates a sheet set with no subsets or sheets. diff --git a/src/app/commands/field.rs b/src/app/commands/field.rs index f6c1f6820..6d1cf2499 100644 --- a/src/app/commands/field.rs +++ b/src/app/commands/field.rs @@ -4,7 +4,7 @@ use crate::app::{Message, OpenCADStudio}; use crate::command::CadCommand; use crate::modules::annotate::field_cmd::{FieldObjectPickCommand, FieldPlaceCommand, FieldTablePickCommand}; -use crate::ui::window::field_dialog::{ +use crate::ui::window::field_dialog::{nav_props, fields_of, object_properties, FieldDialogMsg, FieldDialogState, FieldTarget, DATE_FORMATS, NAMED_TYPES, }; @@ -110,6 +110,8 @@ impl OpenCADStudio { state.ss_set_custom = names(codec::sheet_set::CUSTOM_SHEET_SET_PROP); state.ss_set_name = db.name().to_string(); } + state.ss_sets = self.sheet_set.sets.clone(); + state.ss_set = self.sheet_set.current.unwrap_or(0); self.field_dialog = Some(state); self.fill_named_objects(); self.refresh_field_preview(); @@ -190,8 +192,57 @@ impl OpenCADStudio { // Sheet set fields start in Title case. state.text_case = if crate::ui::window::field_dialog::SHEET_SET_FIELDS.contains(&n) { 4 } else { 0 }; state.ss_custom.clear(); + state.ss_node.clear(); + state.ss_prop.clear(); + // SheetSet starts on the set and its first property; SheetView on nothing. + if n == "SheetSet" { + if let Some(db) = state.ss_sets.get(state.ss_set) { + state.ss_node = db.sheet_set().id().to_string(); + state.ss_prop = nav_props(db, &state.ss_node).first().map(|p| p.0.clone()).unwrap_or_default(); + } + } } FieldDialogMsg::SsCustom(name) => state.ss_custom = name, + FieldDialogMsg::SsSet(k) => { + state.ss_set = k; + state.ss_node.clear(); + state.ss_prop.clear(); + } + FieldDialogMsg::SsNode(id) => { + state.ss_prop = state.ss_sets.get(state.ss_set).and_then(|db| nav_props(db, &id).first().map(|p| p.0.clone())).unwrap_or_default(); + state.ss_node = id; + } + FieldDialogMsg::SsProp(p) => state.ss_prop = p, + FieldDialogMsg::SsHref(v) => state.ss_href = v, + FieldDialogMsg::SsBrowse => { + return Task::perform( + async { + crate::sys::file_dialog() + .set_title(crate::t!("Open Sheet Set").as_ref()) + .add_filter(crate::t!("Sheet Set (*.dst)").as_ref(), &["dst", "DST"]) + .pick_file() + .await + .map(|h| crate::sys::handle_path(&h)) + }, + |p| Message::FieldDialog(FieldDialogMsg::SsPicked(p)), + ); + } + FieldDialogMsg::SsPicked(None) => {} + FieldDialogMsg::SsPicked(Some(path)) => match codec::sheet_set::SheetSetDatabase::read(&path.to_string_lossy()) { + Ok(db) => { + let key = codec::sheet_set::path_key(&path.to_string_lossy()); + state.ss_set = match state.ss_sets.iter().position(|d| d.path.as_deref().is_some_and(|p| codec::sheet_set::path_key(p) == key)) { + Some(k) => k, + None => { + state.ss_sets.push(db); + state.ss_sets.len() - 1 + } + }; + state.ss_node.clear(); + state.ss_prop.clear(); + } + Err(e) => self.command_line.push_error(&format!("{}: {e}", path.display())), + }, FieldDialogMsg::SsPlaceholder(k) => { state.ss_placeholder = k; state.ss_custom.clear(); diff --git a/src/app/commands/sheet_set.rs b/src/app/commands/sheet_set.rs index a32707a9f..da1036de1 100644 --- a/src/app/commands/sheet_set.rs +++ b/src/app/commands/sheet_set.rs @@ -4,8 +4,8 @@ use crate::app::{Message, OpenCADStudio}; use crate::ui::window::sheet_set::{ - FieldId, Form, FormKind, FoundLayout, MenuAction, PropRow, Properties, RowKey, SheetSetMsg, SsDialog, - Wizard, WizardMsg, + Category, FieldId, Form, FormKind, FoundLayout, ImportRow, MenuAction, PropRow, Properties, RowKey, SheetSetMsg, + SheetStatus, SsDialog, TemplatePick, Wizard, WizardMsg, }; use codec::sheet_set::{self as ss, ComponentKind, LayoutReference, SheetSetData, SheetSetDatabase}; use iced::Task; @@ -63,6 +63,77 @@ fn folder_of(path: &str) -> String { /// Template of a component for new sheets: its own `DefDwtLayout`, else /// the nearest parent's. +/// 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 { + let mut id = component.to_string(); + for _ in 0..32 { + let Some(el) = db.find(&id) else { return false }; + if let Some(v) = el.prop("PromptForDwt") { + return !matches!(v.trim(), "" | "0"); + } + match db.parent_of(&id) { + Some(p) => id = p.id().to_string(), + None => return false, + } + } + false +} + +/// The layouts of a drawing that can become sheets, each with the sheet set +/// it already belongs to: the drawing's sheet link, or this set. +fn import_rows(path: &Path, db: &SheetSetDatabase) -> Result, String> { + let doc = crate::io::load_file(path).map_err(|e| e.to_string())?; + let drawing = path.to_string_lossy().to_string(); + let link = doc.sheet_set_data(); + Ok(paper_layouts(&doc) + .into_iter() + .map(|(layout, handle)| { + 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 } + }) + .collect()) +} + +/// Rename & Renumber: layout and file names follow the options. +fn follow_rename(f: &mut Form) { + let name = |prefix: bool| if prefix { format!("{} {}", f.number.trim(), f.title.trim()).trim().to_string() } else { f.title.trim().to_string() }; + if f.rename[0] { + f.layout_name = name(f.rename[1]); + } + if f.rename[2] { + f.file_name = format!("{}.dwg", name(f.rename[3])); + } +} + +fn rename_form(db: &SheetSetDatabase, set: usize, id: &str) -> Form { + let el = db.find(id); + let reference = db.layout_reference(id, "Layout").unwrap_or_default(); + let file = Path::new(&reference.file_name); + Form { + kind: FormKind::Rename, + set, + component: id.to_string(), + number: el.and_then(|e| e.prop("Number")).unwrap_or("").to_string(), + title: el.and_then(|e| e.prop("Title")).unwrap_or("").to_string(), + layout_name: reference.name.clone(), + file_name: file.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default(), + folder: file.parent().map(|p| p.to_string_lossy().to_string()).unwrap_or_default(), + ..Form::default() + } +} + fn template_of(db: &SheetSetDatabase, component: &str) -> Option { let mut id = component.to_string(); for _ in 0..32 { @@ -218,6 +289,12 @@ impl OpenCADStudio { /// Publish the open sets to the field engine and redraw every field. fn sheet_sets_changed(&mut self) { crate::entities::field::set_sheet_sets(self.sheet_set.sets.clone()); + self.refresh_locations(); + if self.sheet_set.settings.sheet_status >= 1 { + self.refresh_sheet_status(); + } else { + self.sheet_set.status.clear(); + } for tab in &mut self.tabs { let changes: Vec<_> = tab .scene @@ -365,6 +442,7 @@ impl OpenCADStudio { SheetSetMsg::PickSet(Some(k)) => { if k < self.sheet_set.sets.len() { self.sheet_set.current = Some(k); + self.sheet_sets_changed(); } } SheetSetMsg::PickSet(None) => return self.dispatch_command("OPENSHEETSET"), @@ -384,7 +462,80 @@ impl OpenCADStudio { self.sheet_set.collapsed.insert(id); } } - SheetSetMsg::Poll => self.poll_sheet_sets(), + SheetSetMsg::Poll => { + 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))); + if self.sheet_set.settings.sheet_status == 2 && self.sheet_set.status_at.is_none_or(|t| t.elapsed() >= every) { + self.refresh_sheet_status(); + } + } + SheetSetMsg::BrowseDrawings => { + return Task::perform( + async { + crate::sys::file_dialog() + .set_title(crate::t!("Select Drawing").as_ref()) + .add_filter(crate::t!("Drawing (*.dwg)").as_ref(), &["dwg", "DWG"]) + .pick_files() + .await + .map(|hs| hs.iter().map(crate::sys::handle_path).collect::>()) + .unwrap_or_default() + }, + |p| Message::SheetSet(SheetSetMsg::ImportAdd(p)), + ) + } + SheetSetMsg::ImportAdd(paths) => self.import_add(paths), + SheetSetMsg::Previous => self.rename_step(false), + SheetSetMsg::Next => self.rename_step(true), + SheetSetMsg::ViewsByCategory(v) => self.sheet_set.by_category = v, + SheetSetMsg::NewCategory => self.open_category(None), + SheetSetMsg::CategoryProperties(id) => self.open_category(Some(id)), + SheetSetMsg::CategoryRemove(id) => { + if let Some(db) = self.sheet_set.db_mut() { + db.remove(&id); + } + self.save_current_sheet_set(); + } + SheetSetMsg::AddBlocks => { + return Task::perform( + async { + crate::sys::file_dialog() + .set_title(crate::t!("Select Drawing").as_ref()) + .add_filter(crate::t!("Drawing (*.dwg)").as_ref(), &["dwg", "DWG", "dwt", "DWT"]) + .pick_file() + .await + .map(|h| crate::sys::handle_path(&h)) + }, + |p| Message::SheetSet(SheetSetMsg::BlocksPicked(p)), + ) + } + SheetSetMsg::BlocksPicked(Some(path)) => self.category_add_blocks(path), + SheetSetMsg::BlocksPicked(None) | SheetSetMsg::LocationPicked(None) => {} + SheetSetMsg::AddLocation => { + return Task::perform( + async { + crate::sys::file_dialog() + .set_title(crate::t!("Browse for Folder").as_ref()) + .pick_folder() + .await + .map(|h| crate::sys::handle_path(&h)) + }, + |p| Message::SheetSet(SheetSetMsg::LocationPicked(p)), + ) + } + SheetSetMsg::LocationPicked(Some(path)) => { + if let Some(db) = self.sheet_set.db_mut() { + db.add_resource(&path.to_string_lossy()); + } + self.save_current_sheet_set(); + } + SheetSetMsg::RemoveLocation(id) => { + if let Some(db) = self.sheet_set.db_mut() { + db.remove(&id); + } + self.save_current_sheet_set(); + } + SheetSetMsg::OpenDrawing(path) => return self.update(Message::OpenRecent(PathBuf::from(path))), SheetSetMsg::Refresh => { if let Some(path) = self.sheet_set.db().and_then(|db| db.path.clone()) { match SheetSetDatabase::read(&path) { @@ -437,12 +588,52 @@ impl OpenCADStudio { } SheetSetMsg::Input(field, v) => self.dialog_input(field, v), SheetSetMsg::Toggle(field, v) => { - if let (FieldId::Hierarchy, Some(SsDialog::Wizard(w))) = (field, self.sheet_set.dialog.as_mut()) { - w.hierarchy = v; + let parent_folder = match self.sheet_set.dialog.as_ref() { + Some(SsDialog::Form(f)) if f.kind == FormKind::NewSubset => { + self.sheet_set.sets.get(f.set).map(|db| sheet_folder_of(db, &f.component)).unwrap_or_default() + } + _ => String::new(), + }; + match (field, self.sheet_set.dialog.as_mut()) { + (FieldId::Hierarchy, Some(SsDialog::Wizard(w))) => w.hierarchy = v, + (FieldId::Hierarchy, Some(SsDialog::Form(f))) => { + f.hierarchy = v; + f.folder = if v { Path::new(&parent_folder).join(f.title.trim()).to_string_lossy().to_string() } else { parent_folder }; + } + (FieldId::Publish, Some(SsDialog::Form(f))) => f.publish = v, + (FieldId::OpenAfter, Some(SsDialog::Form(f))) => f.open_after = v, + (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(); + } + } + (FieldId::RenameOption(k), Some(SsDialog::Form(f))) => { + f.rename[k] = v; + // A prefix needs its "Sheet title" option. + if !f.rename[0] { + f.rename[1] = false; + } + if !f.rename[2] { + f.rename[3] = false; + } + follow_rename(f); + } + (FieldId::CategoryBlock(k), Some(SsDialog::Category(c))) => { + if let Some(b) = c.blocks.get_mut(k) { + b.2 = v; + } + } + (FieldId::Row(r), Some(SsDialog::Properties(p))) => { + if let Some(row) = p.rows.get_mut(r) { + row.value = if v { "Publish by Sheet 'Include for Publish' Setting" } else { "Do Not Publish Sheets" }.into(); + } + } + _ => {} } } SheetSetMsg::Choose(field, k) => match (field, self.sheet_set.dialog.as_mut()) { - (FieldId::Layout, Some(SsDialog::Form(f))) => f.layout = k, + (FieldId::Layout, Some(SsDialog::Template(t))) => t.layout = k, (FieldId::CustomOwner, Some(SsDialog::Properties(p))) => { if let Some(a) = p.adding.as_mut() { a.2 = k == 1; @@ -552,23 +743,26 @@ impl OpenCADStudio { match action { MenuAction::Open => return self.open_sheet(&id), MenuAction::NewSheet => { - // Next number: the last sheet's number + 1 when it ends in digits. - let template = template_of(db, &id) - .map(|t| format!("{} ({})", t.name, t.file_name)) - .unwrap_or_else(|| crate::t!("(default new drawing)").into_owned()); - self.sheet_set.dialog = Some(SsDialog::Form(Form { + let template = template_of(db, &id); + let form = Form { kind: FormKind::NewSheet, set, component: id.clone(), - number: String::new(), - title: String::new(), - file_name: String::new(), folder: sheet_folder_of(db, &id), - drawing: template, - layouts: Vec::new(), - layout: 0, - error: None, - })); + drawing: template + .as_ref() + .map(|t| format!("{} ({})", t.name, t.file_name)) + .unwrap_or_else(|| crate::t!("(default new drawing)").into_owned()), + ..Form::default() + }; + // A subset that prompts for its template asks for it first. + if prompts_for_template(db, &id) { + let file = template.map(|t| t.file_name).unwrap_or_default(); + let layouts = crate::io::load_file(Path::new(&file)).map(|d| paper_layouts(&d).into_iter().map(|(n, _)| n).collect()).unwrap_or_default(); + self.sheet_set.dialog = Some(SsDialog::Template(TemplatePick { form, file, layouts, layout: 0 })); + } else { + self.sheet_set.dialog = Some(SsDialog::Form(form)); + } self.active_modal = Some(crate::app::ModalKind::SheetSet); } MenuAction::NewSubset => { @@ -583,14 +777,10 @@ impl OpenCADStudio { kind: FormKind::NewSubset, set, component: id.clone(), - number: String::new(), title: format!("New Subset ({n})"), - file_name: String::new(), folder: sheet_folder_of(db, &id), - drawing: String::new(), - layouts: Vec::new(), - layout: 0, - error: None, + publish: true, + ..Form::default() })); self.active_modal = Some(crate::app::ModalKind::SheetSet); } @@ -609,28 +799,23 @@ impl OpenCADStudio { ); } MenuAction::Rename => { - let el = db.find(&id); - self.sheet_set.dialog = Some(SsDialog::Form(Form { - kind: FormKind::Rename, - set, - component: id.clone(), - number: el.and_then(|e| e.prop("Number")).unwrap_or("").to_string(), - title: el.and_then(|e| e.prop("Title")).unwrap_or("").to_string(), - file_name: String::new(), - folder: String::new(), - drawing: String::new(), - layouts: Vec::new(), - layout: 0, - error: None, - })); + self.sheet_set.dialog = Some(SsDialog::Form(rename_form(db, set, &id))); self.active_modal = Some(crate::app::ModalKind::SheetSet); } MenuAction::Remove => { - if let Some(db) = self.sheet_set.db_mut() { - db.remove(&id); + // A sheet asks first; a subset can only be removed while empty. + if db.find(&id).and_then(ComponentKind::of) == Some(ComponentKind::Sheet) { + let sheet = db.find(&id).map(ss::number_and_title).unwrap_or_default(); + let question = crate::tf!("Are you sure that you want to remove {} Sheet from {} Sheet Set?", sheet, db.name()).into_owned(); + self.sheet_set.dialog = Some(SsDialog::Confirm(set, id.clone(), question)); + self.active_modal = Some(crate::app::ModalKind::SheetSet); + } else if db.find(&id).is_some_and(|el| el.children.iter().all(|c| ComponentKind::of(c).is_none())) { + if let Some(db) = self.sheet_set.db_mut() { + db.remove(&id); + } + self.sheet_set.selected = None; + self.save_current_sheet_set(); } - self.sheet_set.selected = None; - self.save_current_sheet_set(); } MenuAction::Properties => { let p = build_properties(db, &id, Some(set)); @@ -766,15 +951,33 @@ impl OpenCADStudio { } _ => {} }, + Some(SsDialog::Category(c)) => { + if field == FieldId::Name { + c.name = v; + } + } + Some(SsDialog::Confirm(..)) | Some(SsDialog::Template(_)) => {} Some(SsDialog::Form(f)) => { let file_follows = f.file_name == format!("{} {}", f.number, f.title).trim(); match field { FieldId::Number => f.number = v, - FieldId::Title => f.title = v, + FieldId::Title => { + // A folder hierarchy follows the subset name. + if f.kind == FormKind::NewSubset && f.hierarchy { + if let Some(parent) = Path::new(&f.folder).parent() { + f.folder = parent.join(v.trim()).to_string_lossy().to_string(); + } + } + f.title = v + } FieldId::FileName => f.file_name = v, FieldId::Folder => f.folder = v, + FieldId::LayoutName => f.layout_name = v, _ => {} } + if f.kind == FormKind::Rename { + follow_rename(f); + } // The file name follows "Number Title" until edited. if f.kind == FormKind::NewSheet && file_follows && field != FieldId::FileName { f.file_name = format!("{} {}", f.number, f.title).trim().to_string(); @@ -865,8 +1068,9 @@ impl OpenCADStudio { let (Some(set), Some(parent)) = (self.sheet_set.current, self.sheet_set_import_parent.take()) else { return; }; - let layouts = match crate::io::load_file(&path) { - Ok(doc) => paper_layouts(&doc), + let Some(db) = self.sheet_set.db() else { return }; + let rows = match import_rows(&path, db) { + Ok(rows) => rows, Err(e) => { self.command_line.push_error(&format!("{}: {e}", path.display())); return; @@ -876,20 +1080,255 @@ impl OpenCADStudio { kind: FormKind::ImportLayout, set, component: parent, - number: String::new(), - title: String::new(), - file_name: String::new(), - folder: String::new(), - drawing: path.to_string_lossy().to_string(), - layouts, - layout: 0, - error: None, + rows, + prefix: true, + ..Form::default() })); self.active_modal = Some(crate::app::ModalKind::SheetSet); } + /// Browse for Drawings of the open Import dialog: add their layouts. + fn import_add(&mut self, paths: Vec) { + let Some(SsDialog::Form(f)) = self.sheet_set.dialog.as_ref() else { return }; + let Some(db) = self.sheet_set.sets.get(f.set) else { return }; + let mut added = Vec::new(); + for p in paths { + match import_rows(&p, db) { + Ok(rows) => added.extend(rows), + Err(e) => self.command_line.push_error(&format!("{}: {e}", p.display())), + } + } + 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); + } + } + } + } + + /// Rename & Renumber: number and title into the set, and the layout and + /// drawing file renamed when they changed. + fn apply_rename(&mut self, f: &Form) -> Result<(), String> { + let Some(db) = self.sheet_set.sets.get(f.set) else { return Ok(()) }; + let old = db.layout_reference(&f.component, "Layout").unwrap_or_default(); + let new_layout = f.layout_name.trim().to_string(); + let new_file = if old.file_name.is_empty() || f.file_name.trim().is_empty() { + old.file_name.clone() + } else { + Path::new(&f.folder).join(f.file_name.trim()).to_string_lossy().to_string() + }; + let file_changed = !old.file_name.is_empty() && ss::path_key(&new_file) != ss::path_key(&old.file_name); + let layout_changed = !old.name.is_empty() && !new_layout.is_empty() && new_layout != old.name; + let open_tab = self.tab_showing(Path::new(&old.file_name)); + if file_changed { + if open_tab.is_some() { + return Err(crate::t!("The drawing is open; close it to rename its file.").into_owned()); + } + if Path::new(&new_file).exists() { + return Err(crate::tf!("{} already exists.", new_file).into_owned()); + } + } + if layout_changed { + match open_tab { + Some(k) => { + self.tabs[k].scene.rename_layout(&old.name, &new_layout); + if let Some(mut link) = self.tabs[k].scene.document.sheet_set_data() { + link.layout_name = new_layout.clone(); + self.tabs[k].scene.document.set_sheet_set_data(&link); + } + } + None => { + let mut scene = crate::scene::Scene::new(); + scene.document = crate::io::load_file(Path::new(&old.file_name)).map_err(|e| e.to_string())?; + scene.rename_layout(&old.name, &new_layout); + if let Some(mut link) = scene.document.sheet_set_data() { + link.layout_name = new_layout.clone(); + scene.document.set_sheet_set_data(&link); + } + crate::io::save(&scene.document, Path::new(&old.file_name)).map_err(|e| e.to_string())?; + } + } + } + if file_changed { + std::fs::rename(&old.file_name, &new_file).map_err(|e| e.to_string())?; + } + let Some(db) = self.sheet_set.sets.get_mut(f.set) else { return Ok(()) }; + if let Some(el) = db.find_mut(&f.component) { + el.set_prop("Number", f.number.trim()); + el.set_prop("Title", f.title.trim()); + } + if layout_changed || file_changed { + db.set_layout_reference( + &f.component, + "Layout", + &LayoutReference { + file_name: ss::native_path(&new_file), + name: if layout_changed { new_layout } else { old.name }, + handle: old.handle, + }, + ); + } + Ok(()) + } + + /// Rename & Renumber < Previous / Next >: apply, then the neighbouring sheet. + fn rename_step(&mut self, forward: bool) { + let Some(SsDialog::Form(f)) = self.sheet_set.dialog.take() else { return }; + if let Err(e) = self.apply_rename(&f) { + self.sheet_set.dialog = Some(SsDialog::Form(Form { error: Some(e), ..f })); + return; + } + self.sheet_set.current = Some(f.set); + self.save_current_sheet_set(); + let Some(db) = self.sheet_set.sets.get(f.set) else { return }; + let ids: Vec = db.sheets().iter().map(|s| s.id().to_string()).collect(); + let k = ids.iter().position(|i| *i == f.component).unwrap_or(0); + let next = if forward { (k + 1).min(ids.len().saturating_sub(1)) } else { k.saturating_sub(1) }; + let mut form = rename_form(db, f.set, &ids[next]); + form.rename = f.rename; + follow_rename(&mut form); + self.sheet_set.selected = Some(ids[next].clone()); + self.sheet_set.dialog = Some(SsDialog::Form(form)); + } + + /// The View Category dialog for a new (`None`) or existing category. + fn open_category(&mut self, id: Option) { + let (Some(set), Some(db)) = (self.sheet_set.current, self.sheet_set.db()) else { return }; + let used = id.as_deref().map(|i| db.category_blocks(i)); + let blocks = db + .callout_blocks() + .iter() + .map(|b| { + let label = format!("{} ({})", b.prop("Name").unwrap_or(""), db.resolve_file(b)); + let on = used.as_ref().is_none_or(|u| u.iter().any(|x| x == b.id())); + (b.id().to_string(), label, on) + }) + .collect(); + let name = id.as_deref().and_then(|i| db.find(i)).and_then(|c| c.prop("Name")).unwrap_or("").to_string(); + self.sheet_set.dialog = Some(SsDialog::Category(Category { set, id, name, blocks, error: None })); + self.active_modal = Some(crate::app::ModalKind::SheetSet); + } + + /// Add Blocks...: the named blocks of a drawing become callout blocks. + fn category_add_blocks(&mut self, path: PathBuf) { + let doc = match crate::io::load_file(&path) { + Ok(d) => d, + Err(e) => { + self.command_line.push_error(&format!("{}: {e}", path.display())); + return; + } + }; + let file = ss::native_path(&path.to_string_lossy()); + let Some(SsDialog::Category(c)) = self.sheet_set.dialog.as_mut() else { return }; + let mut names: Vec<(String, String)> = doc + .block_records + .iter() + .filter(|b| !b.name.starts_with('*') && !b.is_layout()) + .map(|b| (b.name.clone(), format!("{:X}", b.handle.value()))) + .collect(); + names.sort(); + for (name, handle) in names { + c.blocks.push((format!("new:{file}|{name}|{handle}"), format!("{name} ({file})"), true)); + } + } + + fn category_ok(&mut self, mut c: Category) { + let name = c.name.trim().to_string(); + if name.is_empty() { + c.error = Some(crate::t!("The category name cannot be empty.").into_owned()); + self.sheet_set.dialog = Some(SsDialog::Category(c)); + return; + } + self.close_active_modal(); + let Some(db) = self.sheet_set.sets.get_mut(c.set) else { return }; + let mut used = Vec::new(); + for (id, _, on) in &c.blocks { + let id = match id.strip_prefix("new:") { + Some(spec) => { + let mut p = spec.splitn(3, '|'); + let (file, block, handle) = (p.next().unwrap_or(""), p.next().unwrap_or(""), p.next().unwrap_or("")); + match db.add_callout_block(file, block, handle) { + Some(id) => id, + None => continue, + } + } + None => id.clone(), + }; + if *on { + used.push(id); + } + } + db.set_view_category(c.id.as_deref(), &name, &used); + self.sheet_set.current = Some(c.set); + self.save_current_sheet_set(); + } + + /// Model Views: each location folder of the current set and its drawings. + pub(in crate::app) fn refresh_locations(&mut self) { + let Some(db) = self.sheet_set.db() else { + self.sheet_set.locations.clear(); + return; + }; + self.sheet_set.locations = db + .sheet_set() + .named("Resources") + .map(|r| { + r.children + .iter() + .map(|c| { + let folder = db.resolve_file(c); + let mut files: Vec = std::fs::read_dir(&folder) + .map(|d| { + d.filter_map(|e| e.ok()) + .map(|e| e.file_name().to_string_lossy().to_string()) + .filter(|n| n.to_ascii_lowercase().ends_with(".dwg")) + .collect() + }) + .unwrap_or_default(); + files.sort_by_key(|n| n.to_lowercase()); + (c.id().to_string(), folder, files) + }) + .collect() + }) + .unwrap_or_default(); + } + + /// SSMSHEETSTATUS: a sheet whose drawing is missing, or open (its `.dwl` + /// lock file, or a tab of this application). + pub(in crate::app) fn refresh_sheet_status(&mut self) { + self.sheet_set.status_at = Some(std::time::Instant::now()); + self.sheet_set.status.clear(); + if self.sheet_set.settings.sheet_status == 0 { + return; + } + let Some(db) = self.sheet_set.db() else { return }; + let mut status = std::collections::HashMap::new(); + for sheet in db.sheets() { + let Some(r) = db.layout_reference(sheet.id(), "Layout") else { continue }; + 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() { + Some(SheetStatus::Locked) + } else { + None + }; + if let Some(st) = state { + status.insert(sheet.id().to_string(), st); + } + } + self.sheet_set.status = status; + } + fn properties_template(&mut self, path: PathBuf) { let layouts = crate::io::load_file(&path).map(|d| paper_layouts(&d)).unwrap_or_default(); + if let Some(SsDialog::Template(t)) = self.sheet_set.dialog.as_mut() { + t.file = path.to_string_lossy().to_string(); + t.layouts = layouts.into_iter().map(|(n, _)| n).collect(); + t.layout = 0; + return; + } let Some(SsDialog::Properties(p)) = self.sheet_set.dialog.as_mut() else { return; }; @@ -914,6 +1353,29 @@ impl OpenCADStudio { Task::none() } Some(SsDialog::Form(f)) => self.form_ok(f), + Some(SsDialog::Confirm(set, id, _)) => { + self.close_active_modal(); + if let Some(db) = self.sheet_set.sets.get_mut(set) { + db.remove(&id); + } + self.sheet_set.current = Some(set); + self.sheet_set.selected = None; + self.save_current_sheet_set(); + Task::none() + } + Some(SsDialog::Template(t)) => { + let mut form = t.form; + if let Some(layout) = t.layouts.get(t.layout) { + form.drawing = format!("{layout} ({})", t.file); + form.template = Some((t.file, layout.clone())); + } + self.sheet_set.dialog = Some(SsDialog::Form(form)); + Task::none() + } + Some(SsDialog::Category(c)) => { + self.category_ok(c); + Task::none() + } None => Task::none(), } } @@ -1004,9 +1466,10 @@ impl OpenCADStudio { }; match f.kind { FormKind::Rename => { - if let Some(el) = db.find_mut(&f.component) { - el.set_prop("Number", f.number.trim()); - el.set_prop("Title", f.title.trim()); + if let Err(e) = self.apply_rename(&f) { + f.error = Some(e); + self.sheet_set.dialog = Some(SsDialog::Form(f)); + return Task::none(); } } FormKind::NewSubset => { @@ -1016,32 +1479,43 @@ impl OpenCADStudio { self.sheet_set.dialog = Some(SsDialog::Form(f)); return Task::none(); } - if let Some(sub) = db.add_subset(&f.component, &name, "") { - if !f.folder.trim().is_empty() { - db.set_file_reference(&sub, "NewSheetLocation", f.folder.trim()); + let parent_folder = sheet_folder_of(db, &f.component); + if let Some(sub) = db.add_subset(&f.component, &name, "New subset added") { + // Folder hierarchy: the parent's location + the subset name, created now. + let folder = if f.hierarchy { + let folder = Path::new(&parent_folder).join(&name); + let _ = std::fs::create_dir_all(&folder); + folder.to_string_lossy().to_string() + } else { + f.folder.trim().to_string() + }; + if !folder.is_empty() { + db.set_file_reference(&sub, "NewSheetLocation", &folder); + } + if !f.publish { + if let Some(el) = db.find_mut(&sub) { + el.set_prop_vt("OverrideSheetPublish", 2, "-1"); + } } } } FormKind::ImportLayout => { - let Some((layout, handle)) = f.layouts.get(f.layout).cloned() else { + let chosen: Vec<&ImportRow> = f.rows.iter().filter(|r| r.on && r.owner.is_none()).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)); return Task::none(); - }; - if db.sheet_for(&f.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)) - }) { - f.error = Some(crate::t!("The layout is already a sheet of this sheet set.").into_owned()); - self.sheet_set.dialog = Some(SsDialog::Form(f)); - return Task::none(); } - let stem = Path::new(&f.drawing).file_stem().map(|s| s.to_string_lossy().to_string()).unwrap_or_default(); - if let Some(sheet) = db.add_sheet(&f.component, "", &format!("{stem} - {layout}"), "") { - db.set_layout_reference( - &sheet, - "Layout", - &LayoutReference { file_name: f.drawing.clone(), name: layout, handle }, - ); + for r in chosen { + let stem = Path::new(&r.drawing).file_stem().map(|s| s.to_string_lossy().to_string()).unwrap_or_default(); + let title = if f.prefix { format!("{stem} - {}", r.layout) } else { r.layout.clone() }; + if let Some(sheet) = db.add_sheet(&f.component, "", &title, "") { + db.set_layout_reference( + &sheet, + "Layout", + &LayoutReference { file_name: r.drawing.clone(), name: r.layout.clone(), handle: r.handle.clone() }, + ); + } } } FormKind::NewSheet => return self.create_sheet(f), @@ -1080,7 +1554,10 @@ impl OpenCADStudio { return fail(self, f, crate::tf!("{} already exists.", path.display()).into_owned()); } let layout_name = format!("{} {}", f.number.trim(), title).trim().to_string(); - let template = self.sheet_set.sets.get(f.set).and_then(|db| template_of(db, &f.component)); + let template = match f.template.clone() { + Some((file_name, name)) => Some(LayoutReference { file_name, name, handle: String::new() }), + None => self.sheet_set.sets.get(f.set).and_then(|db| template_of(db, &f.component)), + }; let mut scene = crate::scene::Scene::new(); let source_layout = match template.as_ref().and_then(|t| crate::io::load_file(Path::new(&t.file_name)).ok().map(|d| (d, t.name.clone()))) { Some((doc, name)) => { @@ -1134,11 +1611,14 @@ impl OpenCADStudio { if let Err(e) = crate::io::save(&scene.document, &path) { self.command_line.push_error(&format!("{file}: {e}")); } - // The new sheet is not opened ("Open in drawing editor" is off by default). + // "Open in drawing editor" (off by default) opens the new sheet. self.close_active_modal(); self.sheet_set.current = Some(f.set); - self.sheet_set.selected = Some(sheet); + self.sheet_set.selected = Some(sheet.clone()); self.sheet_sets_changed(); + if f.open_after { + return self.open_sheet(&sheet); + } Task::none() } } @@ -1218,7 +1698,12 @@ pub(in crate::app) fn build_properties(db: &SheetSetDatabase, id: &str, set: Opt ComponentKind::Subset => { title = format!("{} - {}", crate::t!("Subset Properties"), prop("Name")); rows.push(row("Subset", "Subset name", RowKey::Prop("Name"), prop("Name"))); - rows.push(row("Subset", "Description", RowKey::Prop("Desc"), prop("Desc"))); + let publish = if el.prop("OverrideSheetPublish").is_some_and(|v| v.trim() == "-1") { + "Do Not Publish Sheets" + } else { + "Publish by Sheet 'Include for Publish' Setting" + }; + rows.push(row("Subset", "Publish sheets in subset", RowKey::Publish, publish.to_string())); rows.push(row("Subset", "New sheet location", RowKey::Folder("NewSheetLocation"), named_file("NewSheetLocation"))); rows.push(row("Subset", "Sheet creation template", RowKey::Template, template())); rows.push(row("Subset", "Prompt for template", RowKey::PromptTemplate, prompt())); @@ -1284,11 +1769,22 @@ fn write_properties(db: &mut SheetSetDatabase, p: &Properties) { db.set_file_reference(&p.component, name, r.value.trim()); } RowKey::PromptTemplate => { + // The set keeps it as an int (1), a subset as a short (-1). if let Some(el) = db.find_mut(&p.component) { - if r.value == "Yes" { - el.set_prop_vt("PromptForDwt", 3, "1"); + el.remove_named("PromptForDwt"); + match (r.value == "Yes", p.kind) { + (true, ComponentKind::SheetSet) => el.set_prop_vt("PromptForDwt", 3, "1"), + (true, _) => el.set_prop_vt("PromptForDwt", 2, "-1"), + _ => {} + } + } + } + RowKey::Publish => { + if let Some(el) = db.find_mut(&p.component) { + if r.value == "Do Not Publish Sheets" { + el.set_prop_vt("OverrideSheetPublish", 2, "-1"); } else { - el.remove_named("PromptForDwt"); + el.remove_named("OverrideSheetPublish"); } } } diff --git a/src/app/control/actions.rs b/src/app/control/actions.rs index 20affcc04..6dfe48e1e 100644 --- a/src/app/control/actions.rs +++ b/src/app/control/actions.rs @@ -1135,6 +1135,21 @@ impl OpenCADStudio { "link_url" => F::HyperlinkUrl(v.into()), "plot_scale" => F::PlotScale(index(codec::fields::PLOT_SCALE_FORMATS.len())?), "ss_custom" => F::SsCustom(v.into()), + "ss_set" => F::SsSet(v.parse().map_err(|_| failure("invalid_value", "index"))?), + "ss_dst" => F::SsPicked(Some(std::path::PathBuf::from(v))), + "ss_node" => { + let state = self.field_dialog.as_ref().ok_or_else(|| failure("invalid_state", "No Field dialog"))?; + let db = state.ss_sets.get(state.ss_set).ok_or_else(|| failure("invalid_state", "No sheet set"))?; + let views = state.name == "SheetView"; + let id = fd::nav_nodes(db, views) + .into_iter() + .find(|(_, id, label, _)| label == v || id == v) + .map(|(_, id, _, _)| id) + .ok_or_else(|| failure("invalid_value", "No such node"))?; + F::SsNode(id) + } + "ss_prop" => F::SsProp(v.into()), + "ss_href" => F::SsHref(v == "1"), "ss_placeholder" => F::SsPlaceholder(index(fd::PLACEHOLDER_TYPES.len())?), "ss_scale" => F::SsScaleFormat(index(codec::fields::PLOT_SCALE_FORMATS.len())?), "placeholder" => F::PlaceholderProperty(index(codec::fields::BLOCK_PLACEHOLDER_PROPERTIES.len())?), @@ -1233,6 +1248,61 @@ impl OpenCADStudio { "title" => M::Input(F::Title, arg.into()), "file_name" => M::Input(F::FileName, arg.into()), "pick_layout" => M::Choose(F::Layout, arg.parse().map_err(|_| failure("invalid_value", "index"))?), + "refresh" => M::Refresh, + "open_after" => M::Toggle(F::OpenAfter, arg == "1"), + "row_toggle" => { + let (i, v) = index_value(arg)?; + M::Toggle(F::Row(i), v == "1") + } + "publish" => M::Toggle(F::Publish, arg == "1"), + "layout_name" => M::Input(F::LayoutName, arg.into()), + "rename_opt" => { + let (i, v) = index_value(arg)?; + M::Toggle(F::RenameOption(i.min(3)), v == "1") + } + "import_add" => M::ImportAdd(arg.split('|').map(std::path::PathBuf::from).collect()), + "import_row" => { + let (i, v) = index_value(arg)?; + M::Toggle(F::ImportRow(i), v == "1") + } + "prefix" => M::Toggle(F::ImportPrefix, arg == "1"), + "previous" => M::Previous, + "next" => M::Next, + "views_by_category" => M::ViewsByCategory(arg == "1"), + "new_category" => M::NewCategory, + "category" => { + let id = self + .sheet_set + .db() + .and_then(|db| db.view_categories().iter().find(|c| c.prop("Name") == Some(arg)).map(|c| c.id().to_string())) + .ok_or_else(|| failure("invalid_value", "No such view category"))?; + M::CategoryProperties(id) + } + "remove_category" => { + let id = self + .sheet_set + .db() + .and_then(|db| db.view_categories().iter().find(|c| c.prop("Name") == Some(arg)).map(|c| c.id().to_string())) + .ok_or_else(|| failure("invalid_value", "No such view category"))?; + M::CategoryRemove(id) + } + "category_block" => { + let (i, v) = index_value(arg)?; + M::Toggle(F::CategoryBlock(i), v == "1") + } + "add_blocks" => M::BlocksPicked(Some(std::path::PathBuf::from(arg))), + "add_location" => M::LocationPicked(Some(std::path::PathBuf::from(arg))), + "remove_location" => { + let id = self + .sheet_set + .locations + .iter() + .find(|(_, folder, _)| codec::sheet_set::path_key(folder) == codec::sheet_set::path_key(arg)) + .map(|(id, _, _)| id.clone()) + .ok_or_else(|| failure("invalid_value", "No such location"))?; + M::RemoveLocation(id) + } + "open_drawing" => M::OpenDrawing(arg.into()), "ok" => M::Ok, "cancel" => M::Cancel, _ => return Err(failure("invalid_value", "Unknown sheet set action")), diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index 5d1a51e6e..4a3fc1d4e 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -585,7 +585,14 @@ impl OpenCADStudio { let (w, h) = match self.sheet_set.dialog.as_ref()? { crate::ui::window::sheet_set::SsDialog::Wizard(_) => (720, 560), crate::ui::window::sheet_set::SsDialog::Properties(_) => (640, 700), - crate::ui::window::sheet_set::SsDialog::Form(_) => (560, 420), + crate::ui::window::sheet_set::SsDialog::Form(f) => match f.kind { + crate::ui::window::sheet_set::FormKind::Rename => (560, 470), + crate::ui::window::sheet_set::FormKind::ImportLayout => (620, 470), + _ => (560, 420), + }, + crate::ui::window::sheet_set::SsDialog::Confirm(..) => (460, 190), + crate::ui::window::sheet_set::SsDialog::Template(_) => (480, 400), + crate::ui::window::sheet_set::SsDialog::Category(_) => (480, 440), }; sized_flow(ex, w, h, |flow| crate::ui::window::sheet_set::dialog_view(&self.sheet_set, flow)) } diff --git a/src/io/mod.rs b/src/io/mod.rs index 3a4c9d52a..ffaeba33b 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -759,7 +759,8 @@ pub fn load_bytes(name: &str, bytes: Vec) -> Result { ext.as_str() }; match format { - "dwg" => { + // Templates (.dwt) and standards (.dws) are drawings in the DWG format. + "dwg" | "dwt" | "dws" => { let mut doc = DwgReader::from_stream(Cursor::new(bytes)) .read() .map_err(|e| e.to_string())?; @@ -1022,7 +1023,7 @@ fn read_file_attempt( }; match effective.as_str() { - "dwg" => read_dwg_path(path, progress, failsafe), + "dwg" | "dwt" | "dws" => read_dwg_path(path, progress, failsafe), "dxf" => read_dxf_path(path, failsafe), _ => Err(ReaderFailure::terminal(format!( "Unsupported file format: .{ext}" diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index 14452f731..bc088b692 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -50,6 +50,45 @@ 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")), + "missing" => Some(("view", "ss-status-missing")), + "open" => Some(("view", "ss-status-open")), + "View by category" => Some(("view", "ss-view-by-category")), + "View by sheet" => Some(("view", "ss-view-by-sheet")), + "Import Layouts as Sheets" => Some(("view", "ss-import-layouts-as-sheets")), + "Remove Sheets" => Some(("view", "ss-remove-sheets")), + "View Category" => Some(("view", "ss-view-category")), + "New View Category..." => Some(("view", "ss-new-view-category")), + "Add New Location..." => Some(("view", "ss-add-new-location")), + "Remove Location" => Some(("view", "ss-remove-location")), + "Publish by Sheet 'Include for Publish' Setting" => Some(("view", "ss-publish-by-sheet")), + "Do Not Publish Sheets" => Some(("view", "ss-do-not-publish-sheets")), + "Open in drawing editor" => Some(("view", "ss-open-in-drawing-editor")), + "Create folder hierarchy" => Some(("view", "ss-create-folder-hierarchy")), + "Publish sheets in subset" => Some(("view", "ss-publish-sheets-in-subset")), + "Rename options" => Some(("view", "ss-rename-options")), + "Rename layout to match:" => Some(("view", "ss-rename-layout-to-match")), + "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")), + "A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it." => Some(("view", "ss-a-layout-can-belong-to-only-one-sheet-set")), + "Prefix sheet titles with file name" => Some(("view", "ss-prefix-sheet-titles-with-file-name")), + "Import Checked" => Some(("view", "ss-import-checked")), + "Template" => Some(("view", "ss-template")), + "Drawing template file name" => Some(("view", "ss-drawing-template-file-name")), + "Select a layout to create new sheets" => Some(("view", "ss-select-a-layout-to-create-new-sheets")), + "Category name" => Some(("view", "ss-category-name")), + "Select the callout blocks to be used in this category" => Some(("view", "ss-select-the-callout-blocks")), + "Selected blocks will be available for selection when you insert a callout block from the view tab." => Some(("view", "ss-selected-blocks-will-be-available")), + "Add Blocks..." => Some(("view", "ss-add-blocks")), + "(The layout and the drawing file will not be deleted.)" => Some(("view", "ss-layout-and-drawing-not-deleted")), + "Are you sure that you want to remove {} Sheet from {} Sheet Set?" => Some(("view", "ss-remove-sheet-question")), + "The drawing is open; close it to rename its file." => Some(("view", "ss-drawing-open-cannot-rename")), + "The category name cannot be empty." => Some(("view", "ss-category-name-empty")), + "Associate hyperlink" => Some(("view", "ss-associate-hyperlink")), "Remove Sheet" => Some(("view", "ss-remove-sheet")), "Lets you specify one or more folders containing drawings. The layouts from these drawings can be automatically imported into the sheet set." => Some(("view", "ss-lets-you-specify-one-or-more-folders-containing-drawings-the")), "Creates a sheet set with no subsets or sheets." => Some(("view", "ss-creates-a-sheet-set-with-no-subsets-or-sheets")), diff --git a/src/ui/window/field_dialog.rs b/src/ui/window/field_dialog.rs index 6851c0612..3a5e8e966 100644 --- a/src/ui/window/field_dialog.rs +++ b/src/ui/window/field_dialog.rs @@ -90,6 +90,105 @@ fn sheet_set_code(name: &str, custom: &str) -> String { format!("\\AcSm{version} {target}") } +/// The navigation tree of a SheetSet (set, subsets, sheets) or SheetView +/// (set, view categories) field: depth, component id, label, is a sheet. +pub fn nav_nodes(db: &codec::sheet_set::SheetSetDatabase, views: bool) -> Vec<(u16, String, String, bool)> { + use codec::sheet_set::{number_and_title, ComponentKind, Element as SsElement}; + 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() { + out.push((1, c.id().to_string(), c.prop("Name").unwrap_or("").to_string(), false)); + } + return out; + } + fn go(el: &SsElement, depth: u16, out: &mut Vec<(u16, String, String, bool)>) { + for c in &el.children { + match ComponentKind::of(c) { + Some(ComponentKind::Subset) => { + out.push((depth, c.id().to_string(), c.prop("Name").unwrap_or("").to_string(), false)); + go(c, depth + 1, out); + } + Some(ComponentKind::Sheet) => out.push((depth, c.id().to_string(), number_and_title(c), true)), + _ => {} + } + } + } + go(set, 1, &mut out); + out +} + +/// The properties a navigation node offers: list label, code property, version. +pub fn nav_props(db: &codec::sheet_set::SheetSetDatabase, id: &str) -> Vec<(String, String, &'static str)> { + use codec::sheet_set::{custom_properties, ComponentKind, CUSTOM_SHEET_PROP, CUSTOM_SHEET_SET_PROP}; + let Some(el) = db.find(id) else { return Vec::new() }; + let fixed = |list: &[(&str, &str, &'static str)]| -> Vec<(String, String, &'static str)> { + list.iter().map(|(l, p, v)| (l.to_string(), p.to_string(), *v)).collect() + }; + let custom = |flag: i32| -> Vec<(String, String, &'static str)> { + custom_properties(db.sheet_set()).into_iter().filter(|(_, _, f)| f & flag != 0).map(|(n, _, _)| (n.clone(), n, "")).collect() + }; + match ComponentKind::of(el) { + Some(ComponentKind::SheetSet) => { + let mut v = fixed(&[ + ("SheetSetName", "Name", ""), + ("SheetSetDescription", "Description", ""), + ("ProjectNumber", "ProjectNumber", ".16.2"), + ("ProjectName", "ProjectName", ".16.2"), + ("ProjectPhase", "ProjectPhase", ".16.2"), + ("ProjectMilestone", "ProjectMilestone", ".16.2"), + ]); + v.extend(custom(CUSTOM_SHEET_SET_PROP)); + v + } + Some(ComponentKind::Subset) => fixed(&[("SheetSubsetName", "Name", "")]), + Some(ComponentKind::Sheet) => { + let mut v = fixed(&[ + ("SheetNumberAndTitle", "NumberAndTitle", ""), + ("SheetTitle", "Title", ""), + ("SheetNumber", "Number", ""), + ("SheetDescription", "Description", ""), + ("RevisionNumber", "RevisionNumber", ".16.2"), + ("RevisionDate", "RevisionDate", ".16.2"), + ("IssuePurpose", "IssuePurpose", ".16.2"), + ("Category", "Category", ".16.2"), + ]); + v.extend(custom(CUSTOM_SHEET_PROP)); + v + } + None => fixed(&[("ViewCategoryName", "Name", "")]), + } +} + +impl FieldDialogState { + /// `\AcSm Database("…").SheetSet("…")[.Component("…")].Property` of the + /// picked node, with the text case and, on a sheet, the hyperlink to it. + fn navigation_code(&self) -> String { + let Some(db) = self.ss_sets.get(self.ss_set) else { return "\\AcSm".into() }; + let Some((_, prop, version)) = nav_props(db, &self.ss_node).into_iter().find(|(l, _, _)| *l == self.ss_prop) else { + return "\\AcSm".into(); + }; + let set = db.sheet_set().id().to_string(); + let path = codec::sheet_set::native_path(db.path.as_deref().unwrap_or("")); + 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 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)); + } + } + code + } +} + /// SheetSetPlaceholder types: list label, the `?` target, field version. pub const PLACEHOLDER_TYPES: &[(&str, &str, &str)] = &[ ("SheetNumberAndTitle", "?Sheet.NumberAndTitle", ""), @@ -232,6 +331,13 @@ pub struct FieldDialogState { pub ss_scale_format: usize, /// The open sheet set's name (SheetSet / SheetView fields). pub ss_set_name: String, + /// SheetSet / SheetView: the sets offered (open ones and any picked with + /// "..."), the one shown, the node and property picked, associate hyperlink. + pub ss_sets: Vec, + pub ss_set: usize, + pub ss_node: String, + pub ss_prop: String, + pub ss_href: bool, /// Today in each of DATE_FORMATS, for the Examples list. pub examples: Vec, } @@ -270,6 +376,11 @@ impl FieldDialogState { ss_placeholder: 0, ss_scale_format: 6, ss_set_name: String::new(), + ss_sets: Vec::new(), + ss_set: 0, + ss_node: String::new(), + ss_prop: String::new(), + ss_href: true, examples: Vec::new(), } } @@ -324,7 +435,7 @@ impl FieldDialogState { vec![], ), FieldKind::SheetSet | FieldKind::SheetSetCustom => (case(sheet_set_code(self.name, &self.ss_custom)), vec![]), - FieldKind::SheetSetNavigation => ("\\AcSm".into(), vec![]), + FieldKind::SheetSetNavigation => (self.navigation_code(), vec![]), FieldKind::SheetSetPlaceholder => { let (label, target, version) = PLACEHOLDER_TYPES[self.ss_placeholder.min(PLACEHOLDER_TYPES.len() - 1)]; let target = if label == "Custom" { format!("{target}{}", self.ss_custom) } else { target.to_string() }; @@ -454,6 +565,12 @@ pub enum FieldDialogMsg { SsCustom(String), SsPlaceholder(usize), SsScaleFormat(usize), + SsSet(usize), + SsNode(String), + SsProp(String), + SsHref(bool), + SsBrowse, + SsPicked(Option), Help, Ok, } @@ -814,14 +931,53 @@ fn format_panel<'a>(state: &'a FieldDialogState) -> Element<'a, Message> { } col.into() } - FieldKind::SheetSetNavigation => column![ - text(t!("Sheet set")).size(12), - container(text(state.ss_set_name.clone()).size(12).style(muted_style)).padding([5, 8]).width(Fill), - text(t!("Sheet navigation tree")).size(12), - container(Space::new()).height(Length::Fixed(120.0)), - ] - .spacing(8) - .into(), + FieldKind::SheetSetNavigation => { + let picks: Vec = state.ss_sets.iter().enumerate().map(|(i, d)| Pick(i, d.name().to_string())).collect(); + let current = picks.get(state.ss_set).cloned(); + let set_row = row![ + pick_list(current, picks, |p: &Pick| p.1.clone()) + .on_select(|p: Pick| msg(FieldDialogMsg::SsSet(p.0))) + .text_size(12) + .padding([5, 8]) + .width(Fill), + button(text("...").size(12)).on_press(msg(FieldDialogMsg::SsBrowse)).style(button_style(false)).padding([5, 10]), + ] + .spacing(6); + let db = state.ss_sets.get(state.ss_set); + let nodes = db.map(|d| nav_nodes(d, state.name == "SheetView")).unwrap_or_default(); + let tree = list( + nodes + .iter() + .map(|(depth, id, label, _)| { + (format!("{}{label}", " ".repeat(usize::from(*depth))), *id == state.ss_node, FieldDialogMsg::SsNode(id.clone())) + }) + .collect(), + 118.0, + ); + let props = db.map(|d| nav_props(d, &state.ss_node)).unwrap_or_default(); + let prop_list = list( + 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 { + href = href.on_toggle(|v| msg(FieldDialogMsg::SsHref(v))); + } + column![ + text(t!("Sheet set")).size(12), + set_row, + text(t!("Sheet navigation tree")).size(12), + tree, + text(t!("Property")).size(12), + prop_list, + text(t!("Format")).size(12), + case_list(state), + href, + ] + .spacing(8) + .into() + } FieldKind::Object => column![ row![ text(t!("Object type")).size(12).width(Length::Fixed(96.0)), @@ -918,6 +1074,7 @@ pub fn view<'a>( .spacing(10), FieldKind::Hyperlink => column![card(t!("Hyperlink").into_owned(), format_panel(state)), preview].spacing(10), FieldKind::BlockPlaceholder => column![card(t!("Block placeholder").into_owned(), format_panel(state)), preview].spacing(10), + FieldKind::SheetSetNavigation => column![card(t!("Sheet set").into_owned(), format_panel(state)), preview].spacing(10), _ => column![card(t!("Format").into_owned(), format_panel(state)), preview].spacing(10), }; let (code, _) = state.code(); diff --git a/src/ui/window/sheet_set.rs b/src/ui/window/sheet_set.rs index a1c54c7dd..e4f0d3170 100644 --- a/src/ui/window/sheet_set.rs +++ b/src/ui/window/sheet_set.rs @@ -65,6 +65,20 @@ pub struct SheetSetManager { pub dialog: Option, /// Last seen modification time of each open `.dst` (by path key). pub seen: std::collections::HashMap, + /// Sheet status by sheet id (SSMSHEETSTATUS) and when it was taken. + pub status: std::collections::HashMap, + pub status_at: Option, + /// Sheet Views: by category (else by sheet). + pub by_category: bool, + /// Model Views: location id, folder and its drawings. + pub locations: Vec<(String, String, Vec)>, +} + +/// A sheet whose drawing is missing, or open (a `.dwl` lock file, or a tab here). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SheetStatus { + Missing, + Locked, } impl SheetSetManager { @@ -103,6 +117,24 @@ pub enum SheetSetMsg { Refresh, /// Once a second: reload sets whose `.dst` changed on disk. Poll, + /// Import: Browse for Drawings and the drawings picked. + BrowseDrawings, + ImportAdd(Vec), + /// Rename & Renumber: apply and go to the previous / next sheet. + Previous, + Next, + /// Sheet Views: by category or by sheet. + ViewsByCategory(bool), + NewCategory, + CategoryProperties(String), + CategoryRemove(String), + AddBlocks, + BlocksPicked(Option), + /// Model Views. + AddLocation, + LocationPicked(Option), + RemoveLocation(String), + OpenDrawing(String), /// A `.dst` picked by Open (or given by automation). OpenPicked(Option), /// A drawing picked by Import Layout as Sheet. @@ -161,6 +193,15 @@ pub enum FieldId { AddFolder, /// Prompt for template (Properties). PromptTemplate, + OpenAfter, + Publish, + LayoutName, + /// Rename option 0..4. + RenameOption(usize), + ImportRow(usize), + ImportPrefix, + /// View Category: a callout block's check box. + CategoryBlock(usize), } /// What a Properties row edits. @@ -175,6 +216,8 @@ pub enum RowKey { Template, /// Yes / No: prompt for template. PromptTemplate, + /// Publish Sheets in Subset (`OverrideSheetPublish`). + Publish, /// Shown only. ReadOnly, } @@ -236,15 +279,27 @@ pub struct Wizard { } /// New Sheet, New Subset, Rename & Renumber and Import Layout as Sheet. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum FormKind { + #[default] NewSheet, NewSubset, Rename, ImportLayout, } +/// One layout of Import Layouts as Sheets. #[derive(Debug, Clone)] +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, + pub on: bool, +} + +#[derive(Debug, Clone, Default)] pub struct Form { pub kind: FormKind, pub set: usize, @@ -254,10 +309,42 @@ pub struct Form { pub title: String, pub file_name: String, pub folder: String, - /// Import: the drawing and its layouts (name, handle). + /// The sheet template picked when the subset prompts for one (file, layout). pub drawing: String, - pub layouts: Vec<(String, String)>, + pub template: Option<(String, String)>, + /// New Sheet: open the new sheet afterwards. + pub open_after: bool, + /// New Subset: folder hierarchy; publish sheets by their own setting. + pub hierarchy: bool, + pub publish: bool, + /// Rename & Renumber: layout name and the four rename options + /// (layout = title, layout prefix, file = title, file prefix). + pub layout_name: String, + pub rename: [bool; 4], + /// Import: the layouts found and "prefix sheet titles with file name". + pub rows: Vec, + pub prefix: bool, + pub error: Option, +} + +/// Select Layout as Sheet Template (a subset that prompts for its template). +#[derive(Debug, Clone)] +pub struct TemplatePick { + pub form: Form, + pub file: String, + pub layouts: Vec, pub layout: usize, +} + +/// The View Category dialog. +#[derive(Debug, Clone)] +pub struct Category { + pub set: usize, + /// `None` creates a category. + pub id: Option, + pub name: String, + /// Callout block id, label, used. + pub blocks: Vec<(String, String, bool)>, pub error: Option, } @@ -266,6 +353,10 @@ pub enum SsDialog { Wizard(Wizard), Properties(Properties), Form(Form), + /// Remove Sheets confirmation: set, sheet id, question. + Confirm(usize, String, String), + Template(TemplatePick), + Category(Category), } impl SsDialog { @@ -277,8 +368,11 @@ impl SsDialog { FormKind::NewSheet => t!("New Sheet").into_owned(), FormKind::NewSubset => t!("Subset Properties").into_owned(), FormKind::Rename => t!("Rename & Renumber Sheet").into_owned(), - FormKind::ImportLayout => t!("Import Layout as Sheet").into_owned(), + FormKind::ImportLayout => t!("Import Layouts as Sheets").into_owned(), }, + SsDialog::Confirm(..) => t!("Remove Sheets").into_owned(), + SsDialog::Template(_) => t!("Select Layout as Sheet Template").into_owned(), + SsDialog::Category(_) => t!("View Category").into_owned(), } } } @@ -369,6 +463,63 @@ fn row_menu(id: String, kind: ComponentKind, empty: bool) -> Element<'static, Me .into() } +/// A small status badge after a sheet name. +fn badge<'a>(prefix: &str, label: &str, warn: bool) -> Element<'a, Message> { + container(text(format!("{prefix}{}", t!(label))).size(10)) + .padding([0, 5]) + .style(move |theme: &Theme| { + let p = theme.palette(); + let c = if warn { p.warning.base } else { p.secondary.base }; + container::Style { + background: Some(Background::Color(c.color)), + text_color: Some(c.text), + border: Border { radius: 7.0.into(), ..Default::default() }, + ..Default::default() + } + }) + .into() +} + +/// A tree row of the Sheet Views / Model Views pages with its own menu. +fn plain_row<'a>( + depth: u16, + icon: &'static [u8], + label: String, + double: Option, + menu: Vec<(&'static str, Option)>, +) -> Element<'a, Message> { + let cells = row![ + Space::new().width(Length::Fixed(f32::from(depth) * 16.0 + 14.0)), + crate::ui::icons::semantic(icon, 14.0), + text(label).size(12).width(Fill), + ] + .spacing(5) + .align_y(iced::Center); + let mut area = mouse_area(container(cells).width(Fill).padding([3, 6])); + if let Some(m) = double { + area = area.on_double_click(m); + } + if menu.is_empty() { + return area.into(); + } + iced_aw::ContextMenu::new(area, move || { + let items: Vec> = menu + .iter() + .map(|(label, m)| if label.is_empty() { menu_separator() } else { menu_entry(t!(*label).into_owned(), m.clone()) }) + .collect(); + container(iced::widget::Column::with_children(items).spacing(1)) + .padding(4) + .width(Length::Fixed(220.0)) + .style(|theme: &Theme| container::Style { + background: Some(Background::Color(theme.palette().background.weak.color)), + border: Border { color: theme.palette().background.neutral.color, width: 1.0, radius: 4.0.into() }, + ..Default::default() + }) + .into() + }) + .into() +} + fn tree_row<'a>( state: &SheetSetManager, el: &SsElement, @@ -392,14 +543,23 @@ fn tree_row<'a>( None => Space::new().width(14).into(), }; let selected = state.selected.as_deref() == Some(id.as_str()); - let cells = row![ + let status = state.status.get(&id).copied(); + let mut cells = row![ Space::new().width(Length::Fixed(f32::from(depth) * 16.0)), arrow, crate::ui::icons::semantic(icon, 14.0), - text(label).size(12).width(Fill), ] .spacing(5) .align_y(iced::Center); + let name = text(label).size(12); + cells = match status { + // A missing drawing: grey with a "? missing" badge; an open one: a lock badge. + Some(SheetStatus::Missing) => { + cells.push(name.style(muted_style)).push(badge("? ", "missing", true)).push(Space::new().width(Fill)) + } + Some(SheetStatus::Locked) => cells.push(name).push(badge("", "open", false)).push(Space::new().width(Fill)), + None => cells.push(name.width(Fill)), + }; let area = mouse_area(container(cells).width(Fill).padding([3, 6]).style(move |theme: &Theme| container::Style { background: selected.then(|| Background::Color(theme.palette().primary.weak.color)), text_color: selected.then(|| theme.palette().primary.weak.text), @@ -434,31 +594,102 @@ fn push_tree<'a>( } } -/// Sheet views of the set: each sheet's `AcSmSheetView` children. -fn sheet_views(db: &SheetSetDatabase) -> Vec { - db.sheets() +/// A sheet's views (`AcSmSheetView`): label and the category it references. +fn views_of(sheet: &SsElement) -> Vec<(String, String)> { + sheet + .named("SheetViews") + .map(|v| v.children.iter().filter(|c| c.name == "AcSmSheetView").collect::>()) + .unwrap_or_default() .into_iter() - .flat_map(|sheet| { - sheet - .named("SheetViews") - .map(|v| v.children.iter().filter(|c| c.name == "AcSmSheetView").collect::>()) - .unwrap_or_default() - .into_iter() - .map(|v| { - let number = v.prop("Number").unwrap_or(""); - let title = v.prop("Title").unwrap_or(""); - if number.is_empty() { title.to_string() } else { format!("{number} - {title}") } - }) + .map(|v| { + let number = v.prop("Number").unwrap_or(""); + let title = v.prop("Title").unwrap_or(""); + let label = if number.is_empty() { title.to_string() } else { format!("{number} - {title}") }; + let category = v.named("Category").and_then(|c| c.prop("ReferencedObject")).unwrap_or("").to_string(); + (label, category) }) .collect() } -/// Model view locations (`Resources`) of the set. -fn model_views(db: &SheetSetDatabase) -> Vec { - db.sheet_set() - .named("Resources") - .map(|r| r.children.iter().map(|c| db.resolve_file(c)).collect()) - .unwrap_or_default() +/// The Sheet Views page: the set, then its view categories (or sheets) and their views. +fn sheet_views_rows<'a>(state: &SheetSetManager, db: &SheetSetDatabase) -> Vec> { + let mut rows = vec![plain_row( + 0, + SET_ICON, + db.name().to_string(), + None, + vec![("New View Category...", Some(msg(SheetSetMsg::NewCategory)))], + )]; + if state.by_category { + let views: Vec<(String, String)> = db.sheets().into_iter().flat_map(views_of).collect(); + for c in db.view_categories() { + let id = c.id().to_string(); + rows.push(plain_row( + 1, + crate::ui::icons::FOLDER_OPEN, + c.prop("Name").unwrap_or("").to_string(), + Some(msg(SheetSetMsg::CategoryProperties(id.clone()))), + vec![ + ("New View Category...", Some(msg(SheetSetMsg::NewCategory))), + ("Properties...", Some(msg(SheetSetMsg::CategoryProperties(id.clone())))), + ("Remove", Some(msg(SheetSetMsg::CategoryRemove(id.clone())))), + ], + )); + for (label, _) in views.iter().filter(|(_, cat)| *cat == id) { + rows.push(plain_row(2, crate::ui::icons::DOC, label.clone(), None, vec![])); + } + } + } else { + for sheet in db.sheets() { + let views = views_of(sheet); + if views.is_empty() { + continue; + } + rows.push(plain_row(1, crate::ui::icons::DOC, ss::number_and_title(sheet), None, vec![])); + for (label, _) in views { + rows.push(plain_row(2, crate::ui::icons::DOC, label, None, vec![])); + } + } + } + rows +} + +/// The Model Views page: each location folder and its drawings. +fn model_views_rows<'a>(state: &SheetSetManager) -> Vec> { + let mut rows = Vec::new(); + // A location shows relative to the set's folder when it can. + let base = state.db().and_then(|db| db.path.as_deref()).and_then(|p| std::path::Path::new(p).parent().map(|p| p.to_path_buf())); + for (id, folder, files) in &state.locations { + let label = base.as_deref().and_then(|b| ss::relative_path(b, std::path::Path::new(folder))).unwrap_or_else(|| folder.clone()); + rows.push(plain_row( + 0, + crate::ui::icons::FOLDER_OPEN, + label, + None, + vec![ + ("Add New Location...", Some(msg(SheetSetMsg::AddLocation))), + ("Remove Location", Some(msg(SheetSetMsg::RemoveLocation(id.clone())))), + ], + )); + for file in files { + let path = std::path::Path::new(folder).join(file).to_string_lossy().to_string(); + rows.push(plain_row( + 1, + crate::ui::icons::DOC, + file.clone(), + Some(msg(SheetSetMsg::OpenDrawing(path.clone()))), + vec![("Open", Some(msg(SheetSetMsg::OpenDrawing(path))))], + )); + } + } + rows.push( + button(text(format!("+ {}", t!("Add New Location..."))).size(12).style(muted_style)) + .on_press(msg(SheetSetMsg::AddLocation)) + .style(button::text) + .padding([3, 6]) + .into(), + ); + rows } fn heading(label: &str) -> Element<'static, Message> { @@ -522,24 +753,38 @@ pub fn view<'a>(state: &'a SheetSetManager, width: f32, auto_collapse: bool) -> .into() } (Some(db), SsmTab::SheetViews) => { - let views = sheet_views(db); - let list: Element<'a, Message> = if views.is_empty() { - text(t!("No sheet views.").into_owned()).size(12).style(muted_style).into() - } else { - iced::widget::Column::with_children(views.into_iter().map(|v| text(v).size(12).into())).spacing(3).into() - }; - column![heading("Views by sheet"), list].spacing(6).into() + let by = state.by_category; + let header = row![ + heading(if by { "View by category" } else { "View by sheet" }), + button(text(format!("{} ⇄", t!(if by { "View by sheet" } else { "View by category" }))).size(11)) + .on_press(msg(SheetSetMsg::ViewsByCategory(!by))) + .style(button::subtle) + .padding([2, 4]), + button(crate::ui::icons::semantic(crate::ui::icons::PLUS, 12.0)) + .on_press(msg(SheetSetMsg::NewCategory)) + .style(button::subtle) + .padding([2, 4]), + ] + .spacing(4) + .align_y(iced::Center); + column![header, scrollable(iced::widget::Column::with_children(sheet_views_rows(state, db)).spacing(1)).height(Fill)] + .spacing(6) + .into() } - (Some(db), SsmTab::ModelViews) => { - let locations = model_views(db); - let list: Element<'a, Message> = if locations.is_empty() { - text(t!("No locations.").into_owned()).size(12).style(muted_style).into() - } else { - iced::widget::Column::with_children(locations.into_iter().map(|v| text(v).size(12).into())) - .spacing(3) - .into() - }; - column![heading("Locations"), list].spacing(6).into() + (Some(_), SsmTab::ModelViews) => { + let header = row![ + heading("Locations"), + button(text("↻").size(13)).on_press(msg(SheetSetMsg::Refresh)).style(button::subtle).padding([2, 4]), + button(crate::ui::icons::semantic(crate::ui::icons::PLUS, 12.0)) + .on_press(msg(SheetSetMsg::AddLocation)) + .style(button::subtle) + .padding([2, 4]), + ] + .spacing(4) + .align_y(iced::Center); + column![header, scrollable(iced::widget::Column::with_children(model_views_rows(state)).spacing(1)).height(Fill)] + .spacing(6) + .into() } }; let card = container(body).padding(6).width(Fill).height(Fill).style(|theme: &Theme| container::Style { @@ -813,6 +1058,12 @@ fn properties_view<'a>(p: &'a Properties) -> Element<'a, Message> { .width(Fill) .into() } + RowKey::Publish => yes_no( + r.value != "Do Not Publish Sheets", + FieldId::Row(i), + "Publish by Sheet 'Include for Publish' Setting", + "Do Not Publish Sheets", + ), RowKey::Prop(_) => input(&r.value, FieldId::Row(i)), }; rows.push(labeled(r.label, control)); @@ -906,71 +1157,264 @@ fn properties_view<'a>(p: &'a Properties) -> Element<'a, Message> { .into() } -fn form_view<'a>(f: &'a Form) -> Element<'a, Message> { - let body: Element<'a, Message> = match f.kind { - FormKind::NewSheet => column![ - labeled("Number", input(&f.number, FieldId::Number)), - labeled("Sheet title", input(&f.title, FieldId::Title)), - labeled("File name", input(&f.file_name, FieldId::FileName)), - labeled("Folder path", container(text(f.folder.clone()).size(12).style(muted_style)).padding([5, 8])), - labeled("Sheet template", container(text(f.drawing.clone()).size(12).style(muted_style)).padding([5, 8])), - ] - .spacing(8) - .into(), - FormKind::NewSubset => column![ - labeled("Subset name", input(&f.title, FieldId::Title)), - labeled( - "New sheet location", - row![input(&f.folder, FieldId::Folder), browse(SheetSetMsg::Browse(FieldId::Folder))].spacing(6) - ), - ] - .spacing(8) - .into(), - FormKind::Rename => column![ - labeled("Number", input(&f.number, FieldId::Number)), - labeled("Sheet title", input(&f.title, FieldId::Title)), - ] - .spacing(8) - .into(), - FormKind::ImportLayout => { - let rows = f.layouts.iter().enumerate().map(|(i, (name, _))| { - button(text(name.clone()).size(12)) - .on_press(msg(SheetSetMsg::Choose(FieldId::Layout, i))) - .style(button_style(f.layout == i)) - .padding([3, 8]) - .width(Fill) - .into() - }); - column![ - labeled("Drawing", container(text(f.drawing.clone()).size(12).style(muted_style)).padding([5, 8])), - text(t!("Layouts").into_owned()).size(12), - container(scrollable(iced::widget::Column::with_children(rows.collect::>()).spacing(1))) - .height(Length::Fixed(160.0)), - ] - .spacing(8) - .into() - } - }; - let mut col = column![card( - match f.kind { - FormKind::NewSheet => "Sheet", - FormKind::NewSubset => "Subset", - FormKind::Rename => "Sheet", - FormKind::ImportLayout => "Import Layout as Sheet", - }, - body - )] - .spacing(10); - if let Some(e) = error_band(&f.error) { - col = col.push(e); +fn check<'a>(on: bool, label: &str, field: FieldId, enabled: bool) -> Element<'a, Message> { + let c = checkbox(on).label(t!(label).into_owned()).text_size(12).size(14); + if enabled { + c.on_toggle(move |v| msg(SheetSetMsg::Toggle(field, v))).into() + } else { + c.into() } - col.push(footer( +} + +fn read_only<'a>(value: &str) -> Element<'a, Message> { + container(text(value.to_string()).size(12).style(muted_style)).padding([5, 8]).width(Fill).into() +} + +fn ok_cancel<'a>(ok: &str) -> Element<'a, Message> { + footer( vec![], vec![ dialog_button(t!("Cancel"), msg(SheetSetMsg::Cancel), false).into(), - dialog_button(t!("OK"), msg(SheetSetMsg::Ok), true).into(), + dialog_button(t!(ok), msg(SheetSetMsg::Ok), true).into(), ], - )) + ) +} + +fn yes_no<'a>(yes: bool, field: FieldId, a: &str, b: &str) -> Element<'a, Message> { + let opts = vec![t!(a).into_owned(), t!(b).into_owned()]; + let first = opts[0].clone(); + pick_list(Some(opts[if yes { 0 } else { 1 }].clone()), opts, |s: &String| s.clone()) + .on_select(move |s: String| msg(SheetSetMsg::Toggle(field, s == first))) + .text_size(12) + .padding([5, 8]) + .width(Fill) + .into() +} + +fn form_view<'a>(f: &'a Form) -> Element<'a, Message> { + let number_title = row![ + text(t!("Number").into_owned()).size(12).width(Length::Fixed(96.0)), + container(input(&f.number, FieldId::Number)).width(Length::Fixed(110.0)), + text(t!("Sheet title").into_owned()).size(12), + input(&f.title, FieldId::Title), + ] + .spacing(8) + .align_y(iced::Center); + let short = |label: &str, control: Element<'a, Message>| -> Element<'a, Message> { + row![text(t!(label).into_owned()).size(12).width(Length::Fixed(96.0)), control].spacing(8).align_y(iced::Center).into() + }; + let cards: Vec> = match f.kind { + FormKind::NewSheet => vec![card( + "Sheet", + column![ + number_title, + short("File name", input(&f.file_name, FieldId::FileName)), + short("Folder path", read_only(&f.folder)), + short("Sheet template", read_only(&f.drawing)), + row![Space::new().width(Length::Fixed(104.0)), check(f.open_after, "Open in drawing editor", FieldId::OpenAfter, true)], + ] + .spacing(8), + )], + FormKind::NewSubset => vec![card( + "Subset", + column![ + labeled("Subset name", input(&f.title, FieldId::Title)), + labeled("Create folder hierarchy", yes_no(f.hierarchy, FieldId::Hierarchy, "Yes", "No")), + labeled( + "Publish sheets in subset", + yes_no(f.publish, FieldId::Publish, "Publish by Sheet 'Include for Publish' Setting", "Do Not Publish Sheets") + ), + labeled( + "New sheet location", + if f.hierarchy { + read_only(&f.folder) + } else { + row![input(&f.folder, FieldId::Folder), browse(SheetSetMsg::Browse(FieldId::Folder))].spacing(6).into() + } + ), + ] + .spacing(8), + )], + FormKind::Rename => { + let layout: Element<'a, Message> = if f.rename[0] { read_only(&f.layout_name) } else { input(&f.layout_name, FieldId::LayoutName) }; + let file: Element<'a, Message> = if f.rename[2] { read_only(&f.file_name) } else { input(&f.file_name, FieldId::FileName) }; + vec![ + card( + "Sheet", + column![number_title, short("Layout name", layout), short("File name", file), short("Folder path", read_only(&f.folder))] + .spacing(8), + ), + card( + "Rename options", + row![ + column![ + text(t!("Rename layout to match:").into_owned()).size(12), + check(f.rename[0], "Sheet title", FieldId::RenameOption(0), true), + row![Space::new().width(22), check(f.rename[1], "Prefix with sheet number", FieldId::RenameOption(1), f.rename[0])], + ] + .spacing(6) + .width(Fill), + column![ + text(t!("Rename drawing file to match:").into_owned()).size(12), + check(f.rename[2], "Sheet title", FieldId::RenameOption(2), true), + row![Space::new().width(22), check(f.rename[3], "Prefix with sheet number", FieldId::RenameOption(3), f.rename[2])], + ] + .spacing(6) + .width(Fill), + ] + .spacing(16), + ), + ] + } + FormKind::ImportLayout => { + let head = row![ + Space::new().width(22), + text(t!("Layout name").into_owned()).size(11).width(Fill), + text(t!("Drawing").into_owned()).size(11).width(Fill), + text(t!("Status").into_owned()).size(11).width(Fill), + ] + .spacing(6); + 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 enabled = r.owner.is_none(); + 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); + let file = text(file).size(12).width(Fill); + list.push( + row![cb, if enabled { name } else { name.style(muted_style) }, if enabled { file } else { file.style(muted_style) }, container(status).width(Fill)] + .spacing(6) + .align_y(iced::Center) + .into(), + ); + } + vec![card( + "Drawings", + column![ + row![ + text(t!("Select drawing files containing layouts").into_owned()).size(12).width(Fill), + button(text(t!("Browse for Drawings...").into_owned()).size(12)) + .on_press(msg(SheetSetMsg::BrowseDrawings)) + .style(button_style(false)) + .padding([5, 12]), + ] + .align_y(iced::Center), + text(t!("A layout can belong to only one sheet set. If a layout already belongs to a sheet set, you must create a copy of the layout to import it.").into_owned()) + .size(11) + .style(muted_style), + container(scrollable(iced::widget::Column::with_children(list).spacing(4))) + .padding(6) + .height(Length::Fixed(150.0)) + .style(card_style), + check(f.prefix, "Prefix sheet titles with file name", FieldId::ImportPrefix, true), + ] + .spacing(8), + )] + } + }; + let mut col = iced::widget::Column::with_children(cards).spacing(10); + if let Some(e) = error_band(&f.error) { + col = col.push(e); + } + let foot = match f.kind { + FormKind::Rename => footer( + vec![ + button(text(format!("‹ {}", t!("Previous"))).size(12)).on_press(msg(SheetSetMsg::Previous)).style(button_style(false)).padding([5, 12]).into(), + button(text(format!("{} ›", t!("Next"))).size(12)).on_press(msg(SheetSetMsg::Next)).style(button_style(false)).padding([5, 12]).into(), + ], + vec![ + dialog_button(t!("Cancel"), msg(SheetSetMsg::Cancel), false).into(), + dialog_button(t!("OK"), msg(SheetSetMsg::Ok), true).into(), + ], + ), + FormKind::ImportLayout => ok_cancel("Import Checked"), + _ => ok_cancel("OK"), + }; + col.push(foot).into() +} + +fn template_view<'a>(t: &'a TemplatePick) -> Element<'a, Message> { + let rows = t.layouts.iter().enumerate().map(|(i, name)| { + button(text(name.clone()).size(12)) + .on_press(msg(SheetSetMsg::Choose(FieldId::Layout, i))) + .style(button_style(t.layout == i)) + .padding([3, 8]) + .width(Fill) + .into() + }); + column![ + card( + "Template", + column![ + text(t!("Drawing template file name").into_owned()).size(12), + row![read_only(&t.file), browse(SheetSetMsg::BrowseTemplate)].spacing(6).align_y(iced::Center), + text(t!("Select a layout to create new sheets").into_owned()).size(12), + container(scrollable(iced::widget::Column::with_children(rows.collect::>()).spacing(1))) + .padding(4) + .height(Length::Fixed(130.0)) + .style(card_style), + ] + .spacing(8) + ), + ok_cancel("OK"), + ] + .spacing(10) + .into() +} + +fn category_view<'a>(c: &'a Category) -> Element<'a, Message> { + let blocks = c.blocks.iter().enumerate().map(|(i, (_, label, on))| check(*on, label, FieldId::CategoryBlock(i), true)); + let mut col = column![card( + "Category", + column![ + labeled("Category name", input(&c.name, FieldId::Name)), + text(t!("Select the callout blocks to be used in this category").into_owned()).size(12), + container(scrollable(iced::widget::Column::with_children(blocks.collect::>()).spacing(4))) + .padding(6) + .height(Length::Fixed(90.0)) + .width(Fill) + .style(card_style), + text(t!("Selected blocks will be available for selection when you insert a callout block from the view tab.").into_owned()) + .size(11) + .style(muted_style), + button(text(t!("Add Blocks...").into_owned()).size(12)) + .on_press(msg(SheetSetMsg::AddBlocks)) + .style(button_style(false)) + .padding([5, 12]), + ] + .spacing(8) + )] + .spacing(10); + if let Some(e) = error_band(&c.error) { + col = col.push(e); + } + col.push(ok_cancel("OK")).into() +} + +fn confirm_view<'a>(question: &'a str) -> Element<'a, Message> { + column![ + row![ + text("?").size(22).style(accent_text), + column![ + text(question.to_string()).size(12), + text(t!("(The layout and the drawing file will not be deleted.)").into_owned()).size(12).style(muted_style), + ] + .spacing(4), + ] + .spacing(14), + row![ + Space::new().width(Fill), + dialog_button(t!("Cancel"), msg(SheetSetMsg::Cancel), false), + dialog_button(t!("Remove"), msg(SheetSetMsg::Ok), true), + ] + .spacing(6), + ] + .spacing(14) .into() } @@ -979,6 +1423,9 @@ pub fn dialog_view<'a>(state: &'a SheetSetManager, sizing: crate::ui::modal::Mod Some(SsDialog::Wizard(w)) => wizard_view(w), Some(SsDialog::Properties(p)) => properties_view(p), Some(SsDialog::Form(f)) => form_view(f), + Some(SsDialog::Confirm(_, _, q)) => confirm_view(q), + Some(SsDialog::Template(t)) => template_view(t), + Some(SsDialog::Category(c)) => category_view(c), None => Space::new().into(), }; container(body).padding([10, 12]).width(sizing.width).into()