diff --git a/Cad/TemplateDrawingService.cs b/Cad/TemplateDrawingService.cs index edff0c7..a61b441 100644 --- a/Cad/TemplateDrawingService.cs +++ b/Cad/TemplateDrawingService.cs @@ -329,6 +329,100 @@ namespace CadParamPluging.Cad return null; } + private sealed class FeatureCategoryData + { + public string OriginalTemplate; + public string GroupId; + public string Role; + } + + private const string FeatureCategoryDictName = "CadParamPluging_FeatureCategory"; + + private static void SetFeatureCategoryData(Transaction tr, ObjectId id, string originalTemplate, string groupId, string role) + { + var ent = tr.GetObject(id, OpenMode.ForWrite) as Entity; + if (ent == null) return; + + if (ent.ExtensionDictionary.IsNull) + { + ent.CreateExtensionDictionary(); + } + + var dict = (DBDictionary)tr.GetObject(ent.ExtensionDictionary, OpenMode.ForWrite); + + var rb = new ResultBuffer( + new TypedValue((int)DxfCode.Text, originalTemplate ?? string.Empty), + new TypedValue((int)DxfCode.Text, groupId ?? string.Empty), + new TypedValue((int)DxfCode.Text, role ?? string.Empty) + ); + var xrec = new Xrecord { Data = rb }; + + if (dict.Contains(FeatureCategoryDictName)) + { + var oldId = dict.GetAt(FeatureCategoryDictName); + var oldXrec = (Xrecord)tr.GetObject(oldId, OpenMode.ForWrite); + oldXrec.Data = rb; + } + else + { + dict.SetAt(FeatureCategoryDictName, xrec); + tr.AddNewlyCreatedDBObject(xrec, true); + } + } + + private static FeatureCategoryData GetFeatureCategoryData(Transaction tr, ObjectId id) + { + var ent = tr.GetObject(id, OpenMode.ForRead) as Entity; + if (ent == null || ent.ExtensionDictionary.IsNull) return null; + + var dict = (DBDictionary)tr.GetObject(ent.ExtensionDictionary, OpenMode.ForRead); + if (!dict.Contains(FeatureCategoryDictName)) return null; + + var xrec = (Xrecord)tr.GetObject(dict.GetAt(FeatureCategoryDictName), OpenMode.ForRead); + var rb = xrec.Data; + if (rb == null) return null; + + var texts = new List(); + foreach (var tv in rb) + { + if (tv.TypeCode == (int)DxfCode.Text) + { + texts.Add(tv.Value as string); + } + } + + if (texts.Count == 0) return null; + + return new FeatureCategoryData + { + OriginalTemplate = texts.Count >= 1 ? texts[0] : null, + GroupId = texts.Count >= 2 ? texts[1] : null, + Role = texts.Count >= 3 ? texts[2] : null + }; + } + + private static string ExtractMTextPlainText(string contents) + { + if (string.IsNullOrEmpty(contents)) return string.Empty; + + var s = contents; + // Common pattern we generate: {\fSimSun|...;TEXT} + var semi = s.LastIndexOf(';'); + if (semi >= 0) + { + s = s.Substring(semi + 1); + } + + s = s.Trim(); + if (s.EndsWith("}", StringComparison.Ordinal)) + { + s = s.Substring(0, s.Length - 1); + } + + s = s.Replace("\\P", "\n"); + return s.Trim(); + } + public static NoteApplyResult ApplyNoteTemplate(CadContext ctx, string layoutName, bool scanModelSpace, TemplateSchemaDefinition schema, ParamBag bag) { if (ctx == null) @@ -1010,25 +1104,109 @@ namespace CadParamPluging.Cad if (space == null) return 0; + var ids = space.Cast().ToArray(); + int count = 0; - foreach (ObjectId id in space) + + // 0) If we previously created fallback feature-category texts, update or remove them (prevents duplication). + var fallbackGroupIdsToRemove = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var id in ids) + { + FeatureCategoryData fcData = null; + try { fcData = GetFeatureCategoryData(tr, id); } catch { } + if (fcData == null || string.IsNullOrWhiteSpace(fcData.Role)) + { + continue; + } + + if (string.Equals(fcData.Role, "FallbackText", StringComparison.OrdinalIgnoreCase)) + { + var ent = tr.GetObject(id, OpenMode.ForWrite, false) as Entity; + if (ent == null) continue; + + if (string.IsNullOrWhiteSpace(textToShow)) + { + if (!ent.IsErased) ent.Erase(); + if (!string.IsNullOrWhiteSpace(fcData.GroupId)) fallbackGroupIdsToRemove.Add(fcData.GroupId); + continue; + } + + if (ent is MText mt) + { + mt.Contents = @"{\fSimSun|b0|i0|c134|p2;" + ToMTextContents(textToShow) + "}"; + count++; + } + } + else if (string.Equals(fcData.Role, "FallbackBox", StringComparison.OrdinalIgnoreCase)) + { + if (string.IsNullOrWhiteSpace(textToShow) && !string.IsNullOrWhiteSpace(fcData.GroupId)) + { + fallbackGroupIdsToRemove.Add(fcData.GroupId); + } + } + + } + + if (fallbackGroupIdsToRemove.Count > 0) + { + foreach (var id in ids) + { + FeatureCategoryData fcData = null; + try { fcData = GetFeatureCategoryData(tr, id); } catch { } + if (fcData == null || string.IsNullOrWhiteSpace(fcData.GroupId)) continue; + if (!fallbackGroupIdsToRemove.Contains(fcData.GroupId)) continue; + + var ent = tr.GetObject(id, OpenMode.ForWrite, false) as Entity; + if (ent == null) continue; + if (!ent.IsErased) ent.Erase(); + } + } + + // 1) Update previously replaced placeholder entities using stored original template. + foreach (var id in ids) { var ent = tr.GetObject(id, OpenMode.ForWrite, false) as Entity; if (ent == null) continue; if (ent is DBText t) { - if (t.TextString.Contains("******")) + FeatureCategoryData fcData = null; + try { fcData = GetFeatureCategoryData(tr, id); } catch { } + + if (fcData != null && string.Equals(fcData.Role, "Placeholder", StringComparison.OrdinalIgnoreCase)) { - t.TextString = t.TextString.Replace("******", textToShow); + var original = fcData.OriginalTemplate ?? string.Empty; + t.TextString = original.Contains("******") ? original.Replace("******", textToShow) : textToShow; + count++; + continue; + } + + if (t.TextString != null && t.TextString.Contains("******")) + { + var original = t.TextString; + t.TextString = original.Replace("******", textToShow); + try { SetFeatureCategoryData(tr, id, original, null, "Placeholder"); } catch { } count++; } } else if (ent is MText mt) { - if (mt.Contents.Contains("******")) + FeatureCategoryData fcData = null; + try { fcData = GetFeatureCategoryData(tr, id); } catch { } + + if (fcData != null && string.Equals(fcData.Role, "Placeholder", StringComparison.OrdinalIgnoreCase)) { - mt.Contents = mt.Contents.Replace("******", textToShow); + var original = fcData.OriginalTemplate ?? string.Empty; + mt.Contents = original.Contains("******") ? original.Replace("******", textToShow) : textToShow; + count++; + continue; + } + + if (mt.Contents != null && mt.Contents.Contains("******")) + { + var original = mt.Contents; + mt.Contents = original.Replace("******", textToShow); + try { SetFeatureCategoryData(tr, id, original, null, "Placeholder"); } catch { } count++; } } @@ -1037,15 +1215,124 @@ namespace CadParamPluging.Cad foreach (ObjectId attId in br.AttributeCollection) { var att = tr.GetObject(attId, OpenMode.ForWrite, false) as AttributeReference; - if (att != null && att.TextString.Contains("******")) + if (att == null) continue; + + FeatureCategoryData fcData = null; + try { fcData = GetFeatureCategoryData(tr, attId); } catch { } + + if (fcData != null && string.Equals(fcData.Role, "Placeholder", StringComparison.OrdinalIgnoreCase)) { - att.TextString = att.TextString.Replace("******", textToShow); + var original = fcData.OriginalTemplate ?? string.Empty; + att.TextString = original.Contains("******") ? original.Replace("******", textToShow) : textToShow; + count++; + continue; + } + + if (att.TextString != null && att.TextString.Contains("******")) + { + var original = att.TextString; + att.TextString = original.Replace("******", textToShow); + try { SetFeatureCategoryData(tr, attId, original, null, "Placeholder"); } catch { } count++; } } } } + // If the category is blank ("一般件"), also try to remove previously inserted bottom-right category texts/boxes (legacy, unmarked). + if (string.IsNullOrWhiteSpace(textToShow)) + { + try + { + var spaceEntList = ids + .Select(x => + { + try { return tr.GetObject(x, OpenMode.ForRead, false) as Entity; } + catch { return null; } + }) + .Where(e => e != null && !e.IsErased) + .ToList(); + + var frame = ComputeWhiteFrameExtentsFromEntities(tr, spaceEntList); + if (frame.HasValue) + { + var f = frame.Value; + var known = new HashSet(StringComparer.OrdinalIgnoreCase) { "关键件", "重要件" }; + var regionMinX = f.MaxPoint.X - 220.0; + var regionMaxX = f.MaxPoint.X + 5.0; + var regionMinY = f.MinPoint.Y - 5.0; + var regionMaxY = f.MinPoint.Y + 140.0; + + var candidates = new List>(); + foreach (var e in spaceEntList) + { + try + { + var ex = e.GeometricExtents; + if (ex.MaxPoint.X < regionMinX || ex.MinPoint.X > regionMaxX || ex.MaxPoint.Y < regionMinY || ex.MinPoint.Y > regionMaxY) + { + continue; + } + + if (e is DBText dt) + { + var txt = (dt.TextString ?? string.Empty).Trim(); + if (known.Contains(txt)) candidates.Add(Tuple.Create(e.ObjectId, ex)); + } + else if (e is MText mtt) + { + var txt = ExtractMTextPlainText(mtt.Contents); + if (known.Contains(txt)) candidates.Add(Tuple.Create(e.ObjectId, ex)); + } + } + catch { } + } + + if (candidates.Count > 0) + { + foreach (var c in candidates) + { + try + { + var w = tr.GetObject(c.Item1, OpenMode.ForWrite, false) as Entity; + if (w != null && !w.IsErased) w.Erase(); + } + catch { } + } + + foreach (var e in spaceEntList) + { + if (e is Polyline pl) + { + try + { + if (!pl.Closed) continue; + var ex = pl.GeometricExtents; + var matchesAny = candidates.Any(c => + ex.MinPoint.X <= c.Item2.MinPoint.X - 0.1 + && ex.MaxPoint.X >= c.Item2.MaxPoint.X + 0.1 + && ex.MinPoint.Y <= c.Item2.MinPoint.Y - 0.1 + && ex.MaxPoint.Y >= c.Item2.MaxPoint.Y + 0.1); + if (matchesAny) + { + var w = tr.GetObject(pl.ObjectId, OpenMode.ForWrite, false) as Entity; + if (w != null && !w.IsErased) w.Erase(); + } + } + catch { } + } + } + } + } + } + catch + { + // ignore + } + + return count; + } + // Fallback: If no placeholder found AND we have text to show (e.g. "关键件"), create it manually. if (count == 0 && !string.IsNullOrWhiteSpace(textToShow)) { @@ -1063,6 +1350,134 @@ namespace CadParamPluging.Cad if (frame.HasValue) { var f = frame.Value; + + // 1) Heuristic: update/cleanup existing (older) feature-category texts in the bottom-right area + var known = new HashSet(StringComparer.OrdinalIgnoreCase) { "关键件", "重要件" }; + var regionMinX = f.MaxPoint.X - 220.0; + var regionMaxX = f.MaxPoint.X + 5.0; + var regionMinY = f.MinPoint.Y - 5.0; + var regionMaxY = f.MinPoint.Y + 140.0; + + var candidates = new List>(); + foreach (var ent in spaceEntList) + { + if (ent == null) continue; + try + { + var ex = ent.GeometricExtents; + if (ex.MaxPoint.X < regionMinX || ex.MinPoint.X > regionMaxX || ex.MaxPoint.Y < regionMinY || ex.MinPoint.Y > regionMaxY) + { + continue; + } + + if (ent is DBText dt) + { + var txt = (dt.TextString ?? string.Empty).Trim(); + if (known.Contains(txt)) + { + candidates.Add(Tuple.Create(ent.ObjectId, ex, txt)); + } + } + else if (ent is MText mtt) + { + var txt = ExtractMTextPlainText(mtt.Contents); + if (known.Contains(txt)) + { + candidates.Add(Tuple.Create(ent.ObjectId, ex, txt)); + } + } + } + catch + { + // ignore + } + } + + if (candidates.Count > 0) + { + // Keep the right-most (then top-most) one, erase the rest. + var keep = candidates + .OrderByDescending(c => c.Item2.MaxPoint.X) + .ThenByDescending(c => c.Item2.MaxPoint.Y) + .First(); + + var keepId = keep.Item1; + var keepExt = keep.Item2; + + foreach (var c in candidates) + { + if (c.Item1 == keepId) continue; + try + { + var e = tr.GetObject(c.Item1, OpenMode.ForWrite, false) as Entity; + if (e != null && !e.IsErased) e.Erase(); + } + catch { } + } + + // Remove boxes that belong to removed texts (keep the one containing kept text) + foreach (var ent in spaceEntList) + { + if (ent is Polyline pl) + { + try + { + if (!pl.Closed) continue; + var ex = pl.GeometricExtents; + + var containsKept = ex.MinPoint.X <= keepExt.MinPoint.X - 0.1 + && ex.MaxPoint.X >= keepExt.MaxPoint.X + 0.1 + && ex.MinPoint.Y <= keepExt.MinPoint.Y - 0.1 + && ex.MaxPoint.Y >= keepExt.MaxPoint.Y + 0.1; + if (containsKept) continue; + + var matchesAnyRemoved = candidates.Any(c => c.Item1 != keepId + && ex.MinPoint.X <= c.Item2.MinPoint.X - 0.1 + && ex.MaxPoint.X >= c.Item2.MaxPoint.X + 0.1 + && ex.MinPoint.Y <= c.Item2.MinPoint.Y - 0.1 + && ex.MaxPoint.Y >= c.Item2.MaxPoint.Y + 0.1); + + if (matchesAnyRemoved) + { + var w = tr.GetObject(pl.ObjectId, OpenMode.ForWrite, false) as Entity; + if (w != null && !w.IsErased) w.Erase(); + } + } + catch { } + } + } + + // Update kept text and mark it so next run won't fall back. + try + { + var e = tr.GetObject(keepId, OpenMode.ForWrite, false) as Entity; + if (e is DBText dt) + { + dt.TextString = textToShow; + SetFeatureCategoryData(tr, keepId, "******", null, "Placeholder"); + return 1; + } + if (e is MText mtt) + { + // Preserve its formatting if any; otherwise use our default. + var plain = ExtractMTextPlainText(mtt.Contents); + if (string.Equals(plain, mtt.Contents, StringComparison.Ordinal)) + { + mtt.Contents = @"{\fSimSun|b0|i0|c134|p2;" + ToMTextContents(textToShow) + "}"; + } + else + { + mtt.Contents = mtt.Contents.Replace(plain, textToShow); + } + SetFeatureCategoryData(tr, keepId, "******", null, "Placeholder"); + return 1; + } + } + catch + { + // ignore + } + } // Strategy: // 1. Align Right: Use f.MaxX as the right boundary anchor. @@ -1121,6 +1536,8 @@ namespace CadParamPluging.Cad var insertY = tableTopY + 2.0; var insertPoint = new Point3d(insertX, insertY, 0); + var groupId = Guid.NewGuid().ToString("N"); + var textHeight = 3.2; // 9号字 (interpreted as 9pt approx 3.2mm) var mt = new MText(); // Set font to SimSun (宋体) @@ -1171,11 +1588,13 @@ namespace CadParamPluging.Cad space.AppendEntity(poly); tr.AddNewlyCreatedDBObject(poly, true); + try { SetFeatureCategoryData(tr, poly.ObjectId, string.Empty, groupId, "FallbackBox"); } catch { } space.AppendEntity(mt); tr.AddNewlyCreatedDBObject(mt, true); + try { SetFeatureCategoryData(tr, mt.ObjectId, string.Empty, groupId, "FallbackText"); } catch { } count++; } } diff --git a/Common/DropdownOptions.cs b/Common/DropdownOptions.cs index f38200e..bb8cb4d 100644 --- a/Common/DropdownOptions.cs +++ b/Common/DropdownOptions.cs @@ -9,7 +9,7 @@ namespace CadParamPluging.Common public List ProcessMethods { get; set; } public List StructuralFeatures { get; set; } public List SpecialConditions { get; set; } - public string DefaultTemplatePath { get; set; } + public DropdownOptions() { diff --git a/UI/ParamDrawingPanel.cs b/UI/ParamDrawingPanel.cs index 35d18e4..f1e7482 100644 --- a/UI/ParamDrawingPanel.cs +++ b/UI/ParamDrawingPanel.cs @@ -21,18 +21,7 @@ namespace CadParamPluging.UI private readonly ComboBox _cbDrawingType; private readonly ComboBox _cbSheetSize; private readonly ComboBox _cbScale; - private ComboBox _cbTemplateList; - private Button _btnRefreshTemplates; - private TextBox _txtLog; - private TextBox _txtTemplatePath; - - private string _selectedFolderPath; - private string[] _cadFilePaths = new string[0]; - - private TemplateInfo _selectedTemplate; - private Extents3d? _selectedModelWindow; - private string _selectedSheetName; public ParamDrawingPanel() @@ -58,16 +47,6 @@ namespace CadParamPluging.UI // Initialize template controls here in constructor to follow readonly/non-readonly best practices, // though they are no longer readonly. - _btnRefreshTemplates = new Button { Text = "刷新列表", AutoSize = true }; - _btnRefreshTemplates.Click += (_, __) => RefreshTemplateList(); - - _cbTemplateList = new ComboBox - { - DropDownStyle = ComboBoxStyle.DropDownList, - Dock = DockStyle.Fill - }; - _cbTemplateList.SelectedIndexChanged += OnTemplateSelectionChanged; - var templateGroup = BuildTemplateGroup(); var drawingGroup = BuildDrawingGroup(); var actionPanel = BuildActionPanel(); @@ -79,24 +58,6 @@ namespace CadParamPluging.UI layout.Controls.Add(logGroup, 0, 3); Controls.Add(layout); - - if (!string.IsNullOrWhiteSpace(dropdownOptions.DefaultTemplatePath) - && Directory.Exists(dropdownOptions.DefaultTemplatePath)) - { - _selectedFolderPath = dropdownOptions.DefaultTemplatePath; - _txtTemplatePath.Text = dropdownOptions.DefaultTemplatePath; - _cadFilePaths = LoadCadFilesFromFolder(_selectedFolderPath); - - // Use BeginInvoke to ensure UI is ready - var timer = new Timer { Interval = 100 }; - timer.Tick += (s, e) => - { - timer.Stop(); - timer.Dispose(); - RefreshTemplateList(); - }; - timer.Start(); - } } private GroupBox BuildTemplateGroup() @@ -113,7 +74,7 @@ namespace CadParamPluging.UI { Dock = DockStyle.Fill, ColumnCount = 2, - RowCount = 6, + RowCount = 4, AutoSize = true }; @@ -129,17 +90,6 @@ namespace CadParamPluging.UI grid.Controls.Add(new Label { Text = "特殊条件", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 3); grid.Controls.Add(_cbScale, 1, 3); - var btnSelect = new Button { Text = "选择路径", AutoSize = true }; - btnSelect.Click += (_, __) => OnSelectTemplate(); - - _txtTemplatePath = new TextBox { Width = 160, ReadOnly = true }; - - grid.Controls.Add(btnSelect, 0, 4); - grid.Controls.Add(_txtTemplatePath, 1, 4); - - grid.Controls.Add(_btnRefreshTemplates, 0, 5); - grid.Controls.Add(_cbTemplateList, 1, 5); - group.Controls.Add(grid); return group; } @@ -217,284 +167,6 @@ namespace CadParamPluging.UI return group; } - private void RefreshTemplateList() - { - try - { - _cbTemplateList.Items.Clear(); - _selectedTemplate = null; - _selectedModelWindow = null; - _selectedSheetName = null; - - if (_cadFilePaths == null || _cadFilePaths.Length == 0) - { - AppendLog("未加载CAD文件,请先选择路径。"); - return; - } - - AppendLog("开始扫描模板..."); - var items = new System.Collections.Generic.List(); - - foreach (var cadPath in _cadFilePaths) - { - if (string.IsNullOrWhiteSpace(cadPath) || !File.Exists(cadPath)) continue; - - try - { - // 1) Layouts - var layouts = TemplateLayoutAnnotationExtractor.ExtractPerLayout(cadPath); - foreach (var l in layouts) - { - items.Add(new TemplateSelectionItem(new TemplateInfo - { - Name = Path.GetFileName(cadPath), - FilePath = cadPath, - LayoutName = l.LayoutName - })); - } - - // 2) Model Space Windows - var models = TemplateModelSheetExtractor.ExtractCandidates(cadPath); - foreach (var m in models) - { - var item = new TemplateSelectionItem(new TemplateInfo - { - Name = Path.GetFileName(cadPath), - FilePath = cadPath, - LayoutName = null // Model space - }); - item.ModelWindow = m.Window; - // Hint: TemplateInfo doesn't hold the 'Name' of the sheet if it's ModelSpace named candidate - // But usually DisplayName handles it. We can store the sheet name in Info.LayoutName or just use ToString override - // Actually TemplateInfo.LayoutName is used for opening. - // For model space candidates, we usually matched them by 'Name' in auto-match. - // Here we just list them. - items.Add(item); - } - - // Fallback: if no layouts/model candidates found, add the file as a generic "Model Space" entry - if (layouts.Count == 0 && models.Count == 0) - { - items.Add(new TemplateSelectionItem(new TemplateInfo - { - Name = Path.GetFileName(cadPath), - FilePath = cadPath, - LayoutName = null // Default to Model space - })); - } - } - catch (Exception ex) - { - Console.WriteLine(ex); // silent fail for single file - } - } - - if (items.Count == 0) - { - AppendLog("未在路径下找到有效的模板/布局。"); - return; - } - - _cbTemplateList.Items.AddRange(items.Cast().ToArray()); - AppendLog($"已加载 {items.Count} 个模板选项。"); - - if (_cbTemplateList.Items.Count > 0) - { - _cbTemplateList.SelectedIndex = 0; - } - } - catch (Exception ex) - { - AppendLog($"刷新模板列表失败: {ex.Message}"); - Logger.Error("RefreshTemplateList", ex); - } - } - - private void OnTemplateSelectionChanged(object sender, EventArgs e) - { - var item = _cbTemplateList.SelectedItem as TemplateSelectionItem; - if (item == null) - { - _selectedTemplate = null; - _selectedModelWindow = null; - _selectedSheetName = null; - return; - } - - _selectedTemplate = item.Info; - _selectedModelWindow = item.ModelWindow; - _selectedSheetName = item.ToString(); // Use the display string as sheet name reference - - // Log if needed - // AppendLog($"已选择: {_selectedSheetName}"); - } - - private void OnSelectTemplate() - { - try - { - using (var dialog = new FolderBrowserDialog()) - { - dialog.Description = "选择包含 CAD 文件的文件夹"; - - if (!string.IsNullOrWhiteSpace(_selectedFolderPath) && Directory.Exists(_selectedFolderPath)) - { - dialog.SelectedPath = _selectedFolderPath; - } - - if (dialog.ShowDialog(this) == DialogResult.OK) - { - var folderPath = dialog.SelectedPath; - if (string.IsNullOrWhiteSpace(folderPath) || !Directory.Exists(folderPath)) - { - AppendLog("选择路径无效。"); - return; - } - - _selectedFolderPath = folderPath; - _txtTemplatePath.Text = folderPath; - - _cadFilePaths = LoadCadFilesFromFolder(folderPath); - - AppendLog($"已选择路径: {folderPath}"); - AppendLog($"检测到 CAD 文件数量: {_cadFilePaths.Length}"); - - foreach (var p in _cadFilePaths.Take(10)) - { - AppendLog($"- {Path.GetFileName(p)}"); - } - - if (_cadFilePaths.Length > 10) - { - AppendLog($"... 还有 {_cadFilePaths.Length - 10} 个文件"); - } - - RefreshTemplateList(); - } - } - } - catch (Exception ex) - { - AppendLog($"选择路径失败: {ex.Message}"); - Logger.Error("SelectCadFolder", ex); - } - } - - private static string[] LoadCadFilesFromFolder(string folderPath) - { - if (string.IsNullOrWhiteSpace(folderPath) || !Directory.Exists(folderPath)) - { - return new string[0]; - } - - var patterns = new[] { "*.dwg", "*.dwt", "*.dxf" }; - - return patterns - .SelectMany(p => Directory.EnumerateFiles(folderPath, p, SearchOption.TopDirectoryOnly)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(p => p, StringComparer.OrdinalIgnoreCase) - .ToArray(); - } - - private void TryReplaceDropdownOptionsFromTemplate(string templatePath) - { - try - { - var result = TemplateAnnotationOptionsExtractor.Extract(templatePath); - var options = result?.Options; - - if (options == null || !HasAnyOptions(options)) - { - AppendLog("未从模板标注解析到参数,下拉列表不变。"); - return; - } - - AppendLog("已从模板标注解析到参数,询问是否替换下拉设置。"); - - var preview = BuildOptionsPreview(options, 5); - var confirm = MessageBox.Show( - this, - $"检测到模板参数(去重后):\n\n{preview}\n\n是否用模板参数替换当前下拉列表并保存?\n(特殊条件不从模板解析,将保持当前设置)", - "模板参数", - MessageBoxButtons.YesNo, - MessageBoxIcon.Question); - - if (confirm != DialogResult.Yes) - { - AppendLog("用户取消替换下拉设置。"); - return; - } - - var current = DropdownOptionsStore.Load(); - var updated = (current ?? DropdownOptions.CreateDefault()).Clone(); - - if (options.DeliveryStatuses != null && options.DeliveryStatuses.Count > 0) - { - updated.DeliveryStatuses = options.DeliveryStatuses.ToList(); - } - - if (options.ProcessMethods != null && options.ProcessMethods.Count > 0) - { - updated.ProcessMethods = options.ProcessMethods.ToList(); - } - - if (options.StructuralFeatures != null && options.StructuralFeatures.Count > 0) - { - updated.StructuralFeatures = options.StructuralFeatures.ToList(); - } - - updated.Normalize(); - DropdownOptionsStore.Save(updated); - ReloadDropdownOptions(updated); - AppendLog("已从模板替换下拉设置并保存。"); - } - catch (Exception ex) - { - AppendLog($"模板解析/替换失败: {ex.Message}"); - Logger.Error("TemplateDropdownOptions", ex); - } - } - - private static bool HasAnyOptions(DropdownOptions options) - { - return options != null - && ((options.DeliveryStatuses != null && options.DeliveryStatuses.Count > 0) - || (options.ProcessMethods != null && options.ProcessMethods.Count > 0) - || (options.StructuralFeatures != null && options.StructuralFeatures.Count > 0)); - } - - private static string BuildOptionsPreview(DropdownOptions options, int maxPerField) - { - return string.Join( - Environment.NewLine, - new[] - { - $"交付状态: {PreviewValues(options?.DeliveryStatuses, maxPerField)}", - $"工艺方法: {PreviewValues(options?.ProcessMethods, maxPerField)}", - $"结构特征: {PreviewValues(options?.StructuralFeatures, maxPerField)}" - }); - } - - private static string PreviewValues(System.Collections.Generic.IEnumerable values, int max) - { - if (values == null) - { - return "(无)"; - } - - var arr = values.Where(v => !string.IsNullOrWhiteSpace(v)).Select(v => v.Trim()).ToArray(); - if (arr.Length == 0) - { - return "(无)"; - } - - if (arr.Length <= max) - { - return string.Join(", ", arr); - } - - return string.Join(", ", arr.Take(max)) + $" ... (共 {arr.Length} 项)"; - } private static double ParseScaleFactor(string raw) { @@ -543,12 +215,6 @@ namespace CadParamPluging.UI { var tplParams = CollectTemplateParams(); - if (_selectedTemplate == null || string.IsNullOrWhiteSpace(_selectedTemplate.FilePath)) - { - AppendLog("请先选择并确认模板(下拉框)。"); - return; - } - var templateKey = TemplateKeyBuilder.Build(tplParams); if (string.IsNullOrWhiteSpace(templateKey)) { @@ -567,7 +233,8 @@ namespace CadParamPluging.UI var catalog = ParamCatalogStore.Load(); CadParamPluging.Common.ParamBag bag; - using (var f = new DrawingParamsForm(catalog, schema)) + // 若用户点击了【读取参数】,则将读取到的参数作为本次弹窗的默认值回填 + using (var f = new DrawingParamsForm(catalog, schema, _lastLoadedBag)) { var r = f.ShowDialog(this); if (r != DialogResult.OK) @@ -584,25 +251,26 @@ namespace CadParamPluging.UI DomainFacade.ValidateParameters(tplParams, drawingParams); - var doc = TemplateDrawingService.OpenTemplateDrawing(_selectedTemplate); + var doc = AcadApp.DocumentManager.MdiActiveDocument; + if (doc == null) + { + AppendLog("未检测到活动图纸,请先打开 CAD 图纸。"); + return; + } - if (_selectedModelWindow.HasValue) - { - TemplateDrawingService.KeepOnlyModelWindow(doc, _selectedModelWindow.Value); - } - else - { - TemplateDrawingService.KeepOnlyLayout(doc, _selectedTemplate.LayoutName); - } - AppendLog($"已生成图纸窗口,并仅保留图纸: {_selectedSheetName ?? _selectedTemplate.LayoutName}"); + string layoutName = LayoutManager.Current.CurrentLayout; + // 判断是否模型空间。注意:LayoutName为"Model"通常表示模型空间 + bool isModelSpace = string.Equals(layoutName, "Model", StringComparison.OrdinalIgnoreCase); + + AppendLog($"正在当前图纸生成: {doc.Name} ({layoutName})"); using (doc.LockDocument()) using (var ctx = new CadContext(doc)) { var removed = TemplateDrawingService.RemoveMatchParameterAnnotations( ctx, - _selectedTemplate.LayoutName, - _selectedModelWindow.HasValue); + layoutName, + isModelSpace); if (removed > 0) { AppendLog($"已移除模板匹配参数标注文本: {removed} 处"); @@ -610,8 +278,8 @@ namespace CadParamPluging.UI var featureCategoryCount = TemplateDrawingService.ApplyFeatureCategory( ctx, - _selectedTemplate.LayoutName, - _selectedModelWindow.HasValue, + layoutName, + isModelSpace, bag); if (featureCategoryCount > 0) { @@ -620,8 +288,8 @@ namespace CadParamPluging.UI var noteResult = TemplateDrawingService.ApplyNoteTemplate( ctx, - _selectedTemplate.LayoutName, - _selectedModelWindow.HasValue, + layoutName, + isModelSpace, schema, bag); if (noteResult != null) @@ -760,8 +428,8 @@ namespace CadParamPluging.UI // 更新标题栏中的比例属性 var scaleUpdated = TemplateDrawingService.UpdateScaleAttribute( ctx, - _selectedTemplate.LayoutName, - _selectedModelWindow.HasValue, + layoutName, + isModelSpace, scaleFactor ); if (scaleUpdated) @@ -779,8 +447,8 @@ namespace CadParamPluging.UI { var categoryUpdated = TemplateDrawingService.UpdateInspectionCategoryAttribute( ctx, - _selectedTemplate.LayoutName, - _selectedModelWindow.HasValue, + layoutName, + isModelSpace, inspectionCategory ); if (categoryUpdated) @@ -802,12 +470,7 @@ namespace CadParamPluging.UI { var bagToSave = bag; - // 保存模板信息以便后续“读取参数”功能可以自动填充UI - if (_selectedTemplate != null) - { - bagToSave.Set("_TemplatePath", _selectedTemplate.FilePath); - bagToSave.Set("_LayoutName", _selectedTemplate.LayoutName); - } + // 保存面板选项 bagToSave.Set("ProjectType", tplParams.ProjectType); bagToSave.Set("DrawingType", tplParams.DrawingType); @@ -913,46 +576,7 @@ namespace CadParamPluging.UI return; } - // 1. 尝试根据 path 恢复模板选择 - var tplPath = bag.GetString("_TemplatePath"); - if (!string.IsNullOrWhiteSpace(tplPath)) - { - // 在 _cbTemplateList 中查找 - int foundIndex = -1; - for (int i = 0; i < _cbTemplateList.Items.Count; i++) - { - var item = _cbTemplateList.Items[i] as TemplateSelectionItem; - if (item != null && string.Equals(item.Info.FilePath, tplPath, StringComparison.OrdinalIgnoreCase)) - { - // 如果原来的选择也是布局,尝试匹配布局名 - var originalLayout = bag.GetString("_LayoutName"); - if (!string.IsNullOrWhiteSpace(originalLayout)) - { - if (string.Equals(item.Info.LayoutName, originalLayout, StringComparison.OrdinalIgnoreCase)) - { - foundIndex = i; - break; - } - } - else - { - // 只有 Model Space 情况,或者不精确匹配布局 - foundIndex = i; - break; - } - } - } - if (foundIndex >= 0) - { - _cbTemplateList.SelectedIndex = foundIndex; - AppendLog($"已自动选中模板: {_cbTemplateList.Items[foundIndex]}"); - } - else - { - AppendLog($"注意: 未在列表中找到原模板文件: {Path.GetFileName(tplPath)},可能文件已移动或未加载路径。"); - } - } // 2. 恢复下拉框选项 // ProjectType diff --git a/UI/SettingsForm.cs b/UI/SettingsForm.cs index 3a5bb14..873d731 100644 --- a/UI/SettingsForm.cs +++ b/UI/SettingsForm.cs @@ -13,7 +13,7 @@ namespace CadParamPluging.UI private readonly ListBox _lbProcessMethods; private readonly ListBox _lbStructuralFeatures; private readonly ListBox _lbSpecialConditions; - private readonly TextBox _txtDefaultTemplatePath; + public DropdownOptions Result { get; private set; } @@ -32,10 +32,9 @@ namespace CadParamPluging.UI { Dock = DockStyle.Fill, Padding = new Padding(12), - RowCount = 3, + RowCount = 2, ColumnCount = 1 }; - mainLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Default Path mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 100f)); // Tabs mainLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); // Buttons @@ -43,48 +42,16 @@ namespace CadParamPluging.UI _lbProcessMethods = new ListBox(); _lbStructuralFeatures = new ListBox(); _lbSpecialConditions = new ListBox(); - _txtDefaultTemplatePath = new TextBox { Dock = DockStyle.Fill, ReadOnly = true }; var options = (current ?? DropdownOptions.CreateDefault()).Clone(); options.Normalize(); - _txtDefaultTemplatePath.Text = options.DefaultTemplatePath ?? string.Empty; FillList(_lbDeliveryStatuses, options.DeliveryStatuses); FillList(_lbProcessMethods, options.ProcessMethods); FillList(_lbStructuralFeatures, options.StructuralFeatures); FillList(_lbSpecialConditions, options.SpecialConditions); - // 1. Default Path Section - var pathGroup = new GroupBox - { - Text = "默认模板路径", - Dock = DockStyle.Fill, - AutoSize = true, - Padding = new Padding(6) - }; - var pathGrid = new TableLayoutPanel - { - Dock = DockStyle.Top, - AutoSize = true, - ColumnCount = 2, - RowCount = 1, - Padding = new Padding(4) - }; - pathGrid.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f)); - pathGrid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); - - var btnBrowsePath = new Button - { - Text = "浏览...", - AutoSize = true, - Height = _txtDefaultTemplatePath.Height, // visually match - Anchor = AnchorStyles.Left | AnchorStyles.Right - }; - btnBrowsePath.Click += (_, __) => OnBrowseDefaultPath(); - pathGrid.Controls.Add(_txtDefaultTemplatePath, 0, 0); - pathGrid.Controls.Add(btnBrowsePath, 1, 0); - pathGroup.Controls.Add(pathGrid); // 2. Tabs Section var tabs = new TabControl { Dock = DockStyle.Fill }; @@ -139,9 +106,8 @@ namespace CadParamPluging.UI bottomGrid.Controls.Add(rightActions, 1, 0); // Assemble Main - mainLayout.Controls.Add(pathGroup, 0, 0); - mainLayout.Controls.Add(tabs, 0, 1); - mainLayout.Controls.Add(bottomGrid, 0, 2); + mainLayout.Controls.Add(tabs, 0, 0); + mainLayout.Controls.Add(bottomGrid, 0, 1); Controls.Add(mainLayout); @@ -149,22 +115,7 @@ namespace CadParamPluging.UI CancelButton = btnCancel; } - private void OnBrowseDefaultPath() - { - using (var dialog = new FolderBrowserDialog()) - { - dialog.Description = "选择默认模板路径"; - if (System.IO.Directory.Exists(_txtDefaultTemplatePath.Text)) - { - dialog.SelectedPath = _txtDefaultTemplatePath.Text; - } - if (dialog.ShowDialog(this) == DialogResult.OK) - { - _txtDefaultTemplatePath.Text = dialog.SelectedPath; - } - } - } private void OnOpenParamCatalog() { @@ -351,8 +302,7 @@ namespace CadParamPluging.UI DeliveryStatuses = _lbDeliveryStatuses.Items.Cast().Select(o => (o as string) ?? string.Empty).ToList(), ProcessMethods = _lbProcessMethods.Items.Cast().Select(o => (o as string) ?? string.Empty).ToList(), StructuralFeatures = _lbStructuralFeatures.Items.Cast().Select(o => (o as string) ?? string.Empty).ToList(), - SpecialConditions = _lbSpecialConditions.Items.Cast().Select(o => (o as string) ?? string.Empty).ToList(), - DefaultTemplatePath = _txtDefaultTemplatePath.Text.Trim() + SpecialConditions = _lbSpecialConditions.Items.Cast().Select(o => (o as string) ?? string.Empty).ToList() }; result.Normalize();