diff --git a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db index e5dbd6f..5c06287 100644 Binary files a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db and b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db differ diff --git a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db index e5a4d31..ae61eab 100644 Binary files a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db and b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db differ diff --git a/WordAddIn/.vs/WordAddIn/FileContentIndex/cc724fb2-aece-4705-bab6-f396d457e01f.vsidx b/WordAddIn/.vs/WordAddIn/FileContentIndex/cc724fb2-aece-4705-bab6-f396d457e01f.vsidx deleted file mode 100644 index 9b46f80..0000000 Binary files a/WordAddIn/.vs/WordAddIn/FileContentIndex/cc724fb2-aece-4705-bab6-f396d457e01f.vsidx and /dev/null differ diff --git a/WordAddIn/.vs/WordAddIn/v17/.suo b/WordAddIn/.vs/WordAddIn/v17/.suo index 2722bcd..09748e3 100644 Binary files a/WordAddIn/.vs/WordAddIn/v17/.suo and b/WordAddIn/.vs/WordAddIn/v17/.suo differ diff --git a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json index f2bc6b6..ac31668 100644 --- a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json +++ b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json @@ -3,8 +3,20 @@ "WorkspaceRootPath": "D:\\dsWork\\aiData\\WordAddIn\\", "Documents": [ { - "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\modeldefinitions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\modeldefinitions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\outlineresultform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\outlineresultform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\loginform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\loginform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\imageoptionsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\imageoptionsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\createoutlineform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\createoutlineform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" } ], "DocumentGroupContainers": [ @@ -23,15 +35,47 @@ { "$type": "Document", "DocumentIndex": 0, - "Title": "ModelDefinitions.cs", - "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ModelDefinitions.cs", - "RelativeDocumentMoniker": "WordAddIn\\ModelDefinitions.cs", - "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ModelDefinitions.cs", - "RelativeToolTip": "WordAddIn\\ModelDefinitions.cs", - "ViewState": "AgIAAAkAAAAAAAAAAAAuwBIAAAAoAAAAAAAAAA==", + "Title": "OutlineResultForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineResultForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\OutlineResultForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineResultForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\OutlineResultForm.cs [\u8BBE\u8BA1]", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-01-28T13:21:19.787Z", - "EditorCaption": "" + "WhenOpened": "2026-01-28T23:14:29.889Z", + "EditorCaption": " [\u8BBE\u8BA1]" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "LoginForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\LoginForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\LoginForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\LoginForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\LoginForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-28T23:14:26.027Z" + }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "ImageOptionsForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ImageOptionsForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\ImageOptionsForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ImageOptionsForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\ImageOptionsForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-28T23:14:18.293Z" + }, + { + "$type": "Document", + "DocumentIndex": 3, + "Title": "CreateOutlineForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\CreateOutlineForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\CreateOutlineForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\CreateOutlineForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\CreateOutlineForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-28T23:14:17.315Z" } ] } diff --git a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json index f2bc6b6..ac31668 100644 --- a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json +++ b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json @@ -3,8 +3,20 @@ "WorkspaceRootPath": "D:\\dsWork\\aiData\\WordAddIn\\", "Documents": [ { - "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\modeldefinitions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\modeldefinitions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\outlineresultform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\outlineresultform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\loginform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\loginform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\imageoptionsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\imageoptionsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\createoutlineform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\createoutlineform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" } ], "DocumentGroupContainers": [ @@ -23,15 +35,47 @@ { "$type": "Document", "DocumentIndex": 0, - "Title": "ModelDefinitions.cs", - "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ModelDefinitions.cs", - "RelativeDocumentMoniker": "WordAddIn\\ModelDefinitions.cs", - "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ModelDefinitions.cs", - "RelativeToolTip": "WordAddIn\\ModelDefinitions.cs", - "ViewState": "AgIAAAkAAAAAAAAAAAAuwBIAAAAoAAAAAAAAAA==", + "Title": "OutlineResultForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineResultForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\OutlineResultForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineResultForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\OutlineResultForm.cs [\u8BBE\u8BA1]", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-01-28T13:21:19.787Z", - "EditorCaption": "" + "WhenOpened": "2026-01-28T23:14:29.889Z", + "EditorCaption": " [\u8BBE\u8BA1]" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "LoginForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\LoginForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\LoginForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\LoginForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\LoginForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-28T23:14:26.027Z" + }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "ImageOptionsForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ImageOptionsForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\ImageOptionsForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ImageOptionsForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\ImageOptionsForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-28T23:14:18.293Z" + }, + { + "$type": "Document", + "DocumentIndex": 3, + "Title": "CreateOutlineForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\CreateOutlineForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\CreateOutlineForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\CreateOutlineForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\CreateOutlineForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-28T23:14:17.315Z" } ] } diff --git a/WordAddIn/WordAddIn/AiRibbon.cs b/WordAddIn/WordAddIn/AiRibbon.cs index 80badce..dc956bb 100644 --- a/WordAddIn/WordAddIn/AiRibbon.cs +++ b/WordAddIn/WordAddIn/AiRibbon.cs @@ -32,14 +32,69 @@ namespace WordAddIn private AiService _aiService = new AiService(); // 登录状态标识 (静态变量,确保在插件生命周期内保持状态) - private static bool _isLoggedIn = false; + private static bool _isLoggedIn = false; + // 登录凭证存储路径 + private string _tokenFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WordAddIn", "login.cookie"); + + /// + /// 保存登录状态到本地文件 + /// + private void SaveLoginState() + { + try + { + string dir = Path.GetDirectoryName(_tokenFilePath); + if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); + File.WriteAllText(_tokenFilePath, "logged_in|" + DateTime.Now.Ticks); + } + catch (Exception ex) + { + try { File.AppendAllText(@"C:\Users\Public\WordAddInLog.txt", $"{DateTime.Now} SaveLoginState Error: {ex.Message}\n"); } catch { } + } + } + + /// + /// 加载本地登录状态 + /// + private void LoadLoginState() + { + try + { + if (File.Exists(_tokenFilePath)) + { + // 简单验证:文件存在即视为已登录。实际可校验内容有效期。 + _isLoggedIn = true; + } + } + catch (Exception ex) + { + try { File.AppendAllText(@"C:\Users\Public\WordAddInLog.txt", $"{DateTime.Now} LoadLoginState Error: {ex.Message}\n"); } catch { } + } + } + // 存储文档的全文分析上下文 (通过"通读全文"功能生成),用于辅助 AI 理解文档背景 private string _documentContext = null; // 存储外部参考文档内容 (通过"补充背景"功能导入),用于辅助 AI 进行写作 private string _referenceContext = null; + // 样式刷状态常量 + private const int BRUSH_NONE = -1; + private const int BRUSH_MAIN_TITLE = 0; + private const int BRUSH_HEADING1 = 1; + private const int BRUSH_HEADING2 = 2; + private const int BRUSH_HEADING3 = 3; + private const int BRUSH_BODY = 10; + + // 当前激活的样式刷等级 + private int _currentBrushLevel = BRUSH_NONE; + + // 样式刷配置缓存与状态控制 + private OutlineStyleConfig _cachedStyleConfig = null; + private long _lastBrushClickTicks = 0; + private bool _isBrushSticky = false; + /// /// 默认构造函数 /// @@ -98,6 +153,596 @@ namespace WordAddIn } catch {} this.ribbon = ribbonUI; + + // 加载登录状态 + LoadLoginState(); + + try + { + if (AddInContext.CurrentApplication != null) + { + AddInContext.CurrentApplication.WindowSelectionChange += CurrentApplication_WindowSelectionChange; + } + } + catch { } + } + + /// + /// 控制 Ribbon 按钮的可用状态 (Enabled)。 + /// 未登录时返回 false (灰色不可用),登录后返回 true。 + /// + /// 控件 + /// 是否可用 + public bool GetControlEnabled(Office.IRibbonControl control) + { + return _isLoggedIn; + } + + /// + /// 【登录】按钮点击事件处理。 + /// + /// 控件 + public void OnLoginClick(Office.IRibbonControl control) + { + using (LoginForm login = new LoginForm()) + { + if (login.ShowDialog() == DialogResult.OK) + { + _isLoggedIn = true; + SaveLoginState(); + + // 刷新 Ribbon 状态,使按钮变亮 + if (ribbon != null) + { + ribbon.Invalidate(); + } + } + } + } + + public void OnStyleBrushToggle(Office.IRibbonControl control, bool pressed) + { + int targetLevel = BRUSH_NONE; + switch (control.Id) + { + case "tbtnStyleMainTitle": targetLevel = BRUSH_MAIN_TITLE; break; + case "tbtnStyleLevel1": targetLevel = BRUSH_HEADING1; break; + case "tbtnStyleLevel2": targetLevel = BRUSH_HEADING2; break; + case "tbtnStyleLevel3": targetLevel = BRUSH_HEADING3; break; + case "tbtnStyleBody": targetLevel = BRUSH_BODY; break; + } + + if (pressed) + { + // 激活样式刷 + _currentBrushLevel = targetLevel; + _lastBrushClickTicks = DateTime.Now.Ticks; + _isBrushSticky = false; // 默认为单次使用模式 + + // 缓存配置,避免每次选区变化都反序列化,提高性能 + try + { + string styleJson = Settings.Default.OutlineStyles; + _cachedStyleConfig = string.IsNullOrEmpty(styleJson) ? new OutlineStyleConfig() : JsonConvert.DeserializeObject(styleJson); + } + catch { _cachedStyleConfig = new OutlineStyleConfig(); } + + // 反向模式:如果已经选中了文字,直接应用样式并退出刷子模式 + bool appliedImmediate = false; + try + { + if (AddInContext.CurrentApplication != null) + { + var sel = AddInContext.CurrentApplication.Selection; + // wdSelectionNormal = 2 (普通文本选区) + if (sel != null && sel.Type == Word.WdSelectionType.wdSelectionNormal && sel.Range.End > sel.Range.Start) + { + foreach (Word.Paragraph p in sel.Range.Paragraphs) + { + ApplyStyleToParagraph(p, targetLevel, _cachedStyleConfig); + } + appliedImmediate = true; + } + } + } + catch (Exception ex) + { + try { File.AppendAllText(@"C:\Users\Public\WordAddInLog.txt", $"{DateTime.Now} ReverseApply Error: {ex.Message}\n"); } catch { } + } + + if (appliedImmediate) + { + // 如果已立即应用,则重置刷子状态 + _currentBrushLevel = BRUSH_NONE; + _cachedStyleConfig = null; + + // 刷新控件状态,使按钮弹起 + if (ribbon != null) + { + ribbon.InvalidateControl(control.Id); + // 确保其他按钮状态也正确 + ribbon.InvalidateControl("tbtnStyleMainTitle"); + ribbon.InvalidateControl("tbtnStyleLevel1"); + ribbon.InvalidateControl("tbtnStyleLevel2"); + ribbon.InvalidateControl("tbtnStyleLevel3"); + ribbon.InvalidateControl("tbtnStyleBody"); + } + return; + } + } + else + { + // 尝试关闭样式刷 + long elapsedTicks = DateTime.Now.Ticks - _lastBrushClickTicks; + TimeSpan elapsed = new TimeSpan(elapsedTicks); + + // 如果点击间隔很短 (例如 500ms 内),视为双击,进入"连续使用"模式 + // 此时保持激活状态,并设为粘滞模式 + if (elapsed.TotalMilliseconds < 500 && _currentBrushLevel == targetLevel) + { + _isBrushSticky = true; + // 强制刷新控件状态,使其保持按下 + ribbon.InvalidateControl(control.Id); + return; + } + + // 正常关闭 + _currentBrushLevel = BRUSH_NONE; + _cachedStyleConfig = null; + _isBrushSticky = false; + } + + // 刷新所有相关按钮状态 + if (ribbon != null) + { + ribbon.InvalidateControl("tbtnStyleMainTitle"); + ribbon.InvalidateControl("tbtnStyleLevel1"); + ribbon.InvalidateControl("tbtnStyleLevel2"); + ribbon.InvalidateControl("tbtnStyleLevel3"); + ribbon.InvalidateControl("tbtnStyleBody"); + } + } + + public bool GetStyleBrushPressed(Office.IRibbonControl control) + { + switch (control.Id) + { + case "tbtnStyleMainTitle": return _currentBrushLevel == BRUSH_MAIN_TITLE; + case "tbtnStyleLevel1": return _currentBrushLevel == BRUSH_HEADING1; + case "tbtnStyleLevel2": return _currentBrushLevel == BRUSH_HEADING2; + case "tbtnStyleLevel3": return _currentBrushLevel == BRUSH_HEADING3; + case "tbtnStyleBody": return _currentBrushLevel == BRUSH_BODY; + default: return false; + } + } + + private void CurrentApplication_WindowSelectionChange(Word.Selection Sel) + { + if (_currentBrushLevel == BRUSH_NONE) return; + if (_cachedStyleConfig == null) return; + + try + { + foreach (Word.Paragraph p in Sel.Paragraphs) + { + ApplyStyleToParagraph(p, _currentBrushLevel, _cachedStyleConfig); + } + + // 如果不是粘滞模式 (单次使用),应用一次后立即关闭 + if (!_isBrushSticky) + { + _currentBrushLevel = BRUSH_NONE; + _cachedStyleConfig = null; + + if (ribbon != null) + { + ribbon.InvalidateControl("tbtnStyleMainTitle"); + ribbon.InvalidateControl("tbtnStyleLevel1"); + ribbon.InvalidateControl("tbtnStyleLevel2"); + ribbon.InvalidateControl("tbtnStyleLevel3"); + ribbon.InvalidateControl("tbtnStyleBody"); + } + } + } + catch (Exception ex) + { + try { File.AppendAllText(@"C:\Users\Public\WordAddInLog.txt", $"{DateTime.Now} Brush Error: {ex.Message}\n"); } catch { } + } + } + + private void ApplyStyleToParagraph(Word.Paragraph p, int level, OutlineStyleConfig styleConfig) + { + TextStyle ts = null; + try + { + switch (level) + { + case 0: // 主标题 + ts = styleConfig.MainTitle; + p.set_Style(Word.WdBuiltinStyle.wdStyleTitle); + p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevelBodyText; + p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevelBodyText; + break; + case 1: // 一级标题 + ts = styleConfig.Heading1; + p.set_Style(Word.WdBuiltinStyle.wdStyleHeading1); + p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel1; + p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel1; + break; + case 2: // 二级标题 + ts = styleConfig.Heading2; + p.set_Style(Word.WdBuiltinStyle.wdStyleHeading2); + p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel2; + p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel2; + break; + case 3: // 三级标题 + ts = styleConfig.Heading3; + p.set_Style(Word.WdBuiltinStyle.wdStyleHeading3); + p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel3; + p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel3; + break; + case 10: // 正文 + p.set_Style(Word.WdBuiltinStyle.wdStyleNormal); + p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevelBodyText; + p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevelBodyText; + p.Format.CharacterUnitFirstLineIndent = 2; // 首行缩进2字符 + return; + default: + if (level >= 1 && level <= 5) { + ts = styleConfig.Heading3; + p.set_Style(Word.WdBuiltinStyle.wdStyleHeading3); + } + break; + } + } + catch + { + try + { + if (level == 0) p.set_Style("标题"); + else if (level == 10) p.set_Style("正文"); + else p.set_Style("标题 " + level); + } + catch { } + } + + if (ts != null) + { + try { p.Range.Font.Name = ts.FontName; } catch { } + try { p.Range.Font.NameFarEast = ts.FontName; } catch { } + try { p.Range.Font.Size = ts.FontSize; } catch { } + try { p.Range.Font.Bold = ts.IsBold ? 1 : 0; } catch { } + try { p.Range.Font.Color = (Word.WdColor)ts.Color; } catch { } + + if (level == 0) + { + p.Format.Alignment = (Word.WdParagraphAlignment)ts.Alignment; + } + + p.Format.CharacterUnitFirstLineIndent = 0; + p.Format.FirstLineIndent = 0; + } + + // 智能序号填充 (Smart Numbering) + // 仅对 Level 1-3 生效,尝试探测上一级或同级序号并自动填充 + if (level >= 1 && level <= 3) + { + SmartNumbering.Apply(p, level); + } + } + + /// + /// 智能序号处理辅助类 + /// + private static class SmartNumbering + { + // 常见序号正则 + // 1. 中文序号: 一、 (支持 一、到 九十九、) + private static readonly Regex RegexChinese = new Regex(@"^([一二三四五六七八九十]+)、"); + // 2. 章节序号: 第一章 (支持 第一章 到 第九十九章) + private static readonly Regex RegexChapter = new Regex(@"^第([0-9一二三四五六七八九十]+)章"); + // 3. 数字点序号: 1. (支持 1. 到 99.) + private static readonly Regex RegexNumericDot = new Regex(@"^(\d+)\."); + // 4. 层级数字序号: 1.1 或 1.1.1 + private static readonly Regex RegexHierarchical = new Regex(@"^(\d+(\.\d+)+)"); + // 5. 括号数字: (1) 或 (一) - 暂不作为主要探测目标,避免误判 + + public static void Apply(Word.Paragraph p, int level) + { + try + { + // 1. 寻找前一个同级标题 (Sibling) + var siblingInfo = FindPreviousSibling(p, level); + + if (siblingInfo.Found) + { + // 发现同级,延续序号 + string newPrefix = IncrementNumbering(siblingInfo.Text, siblingInfo.Type); + if (!string.IsNullOrEmpty(newPrefix)) + { + ReplacePrefix(p, newPrefix); + } + } + else + { + // 未发现同级,寻找父级 (Parent) 以决定起始序号 + // 例如: Level 2 未找到同级,但找到 Level 1 "第一章",则可能开始 "1.1" 或 "一、" + // 策略:如果找不到同级,尝试根据父级重置为 "1" 或 "1.1" + var parentInfo = FindParent(p, level); + if (parentInfo.Found) + { + string startPrefix = GenerateStartNumbering(parentInfo.Text, level); + if (!string.IsNullOrEmpty(startPrefix)) + { + ReplacePrefix(p, startPrefix); + } + } + else + { + // 既无同级也无父级 (可能是文档开头),尝试默认初始化 + // 如果是 Level 1 且无前文,默认 "一、" + if (level == 1) + { + ReplacePrefix(p, "一、"); + } + } + } + } + catch (Exception ex) + { + try { File.AppendAllText(@"C:\Users\Public\WordAddInLog.txt", $"{DateTime.Now} SmartNumbering Error: {ex.Message}\n"); } catch { } + } + } + + private struct NumberingInfo + { + public bool Found; + public string Text; + public NumberingType Type; + } + + private enum NumberingType + { + None, + Chinese, // 一、 + Chapter, // 第一章 + NumericDot, // 1. + Hierarchical // 1.1 + } + + private static NumberingInfo FindPreviousSibling(Word.Paragraph p, int level) + { + // 向前搜索最多 100 段,避免性能问题 + int maxLookBack = 100; + Word.Paragraph curr = p; + + for (int i = 0; i < maxLookBack; i++) + { + curr = curr.Previous(); + if (curr == null) break; + + int currLevel = (int)curr.Format.OutlineLevel; // wdOutlineLevel1 = 1 + + if (currLevel == level) + { + // 找到同级 + string text = curr.Range.Text.Trim(); + var type = DetectType(text); + if (type != NumberingType.None) + { + return new NumberingInfo { Found = true, Text = text, Type = type }; + } + } + else if (currLevel < level && currLevel >= 1) // wdOutlineLevel1=1 < wdOutlineLevel2=2 + { + // 遇到父级 (OutlineLevel 值越小级别越高) + // 停止搜索,说明是该章节的第一项 + return new NumberingInfo { Found = false }; + } + } + return new NumberingInfo { Found = false }; + } + + private static NumberingInfo FindParent(Word.Paragraph p, int level) + { + int maxLookBack = 200; + Word.Paragraph curr = p; + + for (int i = 0; i < maxLookBack; i++) + { + curr = curr.Previous(); + if (curr == null) break; + + int currLevel = (int)curr.Format.OutlineLevel; + if (currLevel < level && currLevel >= 1) // 找到父级 + { + string text = curr.Range.Text.Trim(); + return new NumberingInfo { Found = true, Text = text, Type = DetectType(text) }; + } + } + return new NumberingInfo { Found = false }; + } + + private static NumberingType DetectType(string text) + { + if (RegexHierarchical.IsMatch(text)) return NumberingType.Hierarchical; + if (RegexChapter.IsMatch(text)) return NumberingType.Chapter; + if (RegexChinese.IsMatch(text)) return NumberingType.Chinese; + if (RegexNumericDot.IsMatch(text)) return NumberingType.NumericDot; + return NumberingType.None; + } + + private static string IncrementNumbering(string text, NumberingType type) + { + try + { + if (type == NumberingType.Chinese) + { + var match = RegexChinese.Match(text); + string numStr = match.Groups[1].Value; + int num = ChineseToInt(numStr); + return IntToChinese(num + 1) + "、"; + } + else if (type == NumberingType.Chapter) + { + var match = RegexChapter.Match(text); + string numStr = match.Groups[1].Value; + // 可能是中文也可能是数字 + int num = 0; + if (int.TryParse(numStr, out int n)) num = n; + else num = ChineseToInt(numStr); + + // 保持原格式 (中文或数字) + string newNumStr = int.TryParse(numStr, out _) ? (num + 1).ToString() : IntToChinese(num + 1); + return "第" + newNumStr + "章 "; // 增加一个空格 + } + else if (type == NumberingType.NumericDot) + { + var match = RegexNumericDot.Match(text); + int num = int.Parse(match.Groups[1].Value); + return (num + 1) + ". "; + } + else if (type == NumberingType.Hierarchical) + { + var match = RegexHierarchical.Match(text); + string fullNum = match.Groups[1].Value; // "1.1" + string[] parts = fullNum.Split('.'); + if (parts.Length > 0) + { + int last = int.Parse(parts[parts.Length - 1]); + parts[parts.Length - 1] = (last + 1).ToString(); + return string.Join(".", parts) + " "; + } + } + } + catch { } + return null; + } + + private static string GenerateStartNumbering(string parentText, int currentLevel) + { + // 根据父级生成子级其实序号 + // 父: 第1章 -> 子: 1.1 + // 父: 1. -> 子: 1.1 + // 父: 1.1 -> 子: 1.1.1 + // 父: 一、 -> 子: 1. (或其他) + + try + { + var parentType = DetectType(parentText); + string parentNum = "1"; + + if (parentType == NumberingType.Chapter) + { + var m = RegexChapter.Match(parentText); + string s = m.Groups[1].Value; + if (int.TryParse(s, out int n)) parentNum = n.ToString(); + else parentNum = ChineseToInt(s).ToString(); + } + else if (parentType == NumberingType.NumericDot) + { + var m = RegexNumericDot.Match(parentText); + parentNum = m.Groups[1].Value; + } + else if (parentType == NumberingType.Hierarchical) + { + var m = RegexHierarchical.Match(parentText); + parentNum = m.Groups[1].Value; + } + // Chinese "一、" usually maps to child "1" not "1.1" depending on style + + // 构造 + // 如果父级是 "1",当前是 Level 2,则 "1.1" + // 如果父级是 "1.1",当前是 Level 3,则 "1.1.1" + return parentNum + ".1 "; + } + catch { } + + return "1. "; // Fallback + } + + private static void ReplacePrefix(Word.Paragraph p, string newPrefix) + { + string text = p.Range.Text; + // 移除旧的前缀 + string cleanText = text; + if (RegexHierarchical.IsMatch(text)) cleanText = RegexHierarchical.Replace(text, "", 1); + else if (RegexChapter.IsMatch(text)) cleanText = RegexChapter.Replace(text, "", 1); + else if (RegexChinese.IsMatch(text)) cleanText = RegexChinese.Replace(text, "", 1); + else if (RegexNumericDot.IsMatch(text)) cleanText = RegexNumericDot.Replace(text, "", 1); + + cleanText = cleanText.TrimStart(); // 去除可能的空格 + + // 写入新文本 (注意保留段落标记) + // 直接设置 Range.Text 会覆盖格式,需小心 + // 策略:Range.InsertBefore? 不,这会保留旧文本 + // 策略:定位到 Start,删除旧前缀长度,插入新前缀 + + // 简单实现:覆盖 Text,重置 Style (因为 Text 赋值会清除部分格式,但我们是在 ApplyStyle 中,Style 刚被设置) + // 更好:只操作 Range 的 Start 到 Match End + + // 正则匹配旧前缀的长度 + int oldLength = 0; + if (RegexHierarchical.IsMatch(text)) oldLength = RegexHierarchical.Match(text).Length; + else if (RegexChapter.IsMatch(text)) oldLength = RegexChapter.Match(text).Length; + else if (RegexChinese.IsMatch(text)) oldLength = RegexChinese.Match(text).Length; + else if (RegexNumericDot.IsMatch(text)) oldLength = RegexNumericDot.Match(text).Length; + + // 删除旧前缀 + if (oldLength > 0) + { + var range = p.Range; + range.End = range.Start + oldLength; + range.Delete(); + } + + // 插入新前缀 + p.Range.InsertBefore(newPrefix); + } + + // 简易中文数字转换 (支持 1-99) + private static readonly string[] CN_Nums = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九" }; + private static readonly string[] CN_Units = { "", "十", "百" }; + + private static int ChineseToInt(string s) + { + if (string.IsNullOrEmpty(s)) return 0; + // 简单映射 + int result = 0; + int temp = 0; // 当前位数值 + + // 仅处理 一到九十九 + // 十 -> 10, 十一 -> 11, 二十 -> 20 + if (s.StartsWith("十")) result += 10; + + foreach (char c in s) + { + string cs = c.ToString(); + int idx = Array.IndexOf(CN_Nums, cs); + if (idx > 0) + { + temp = idx; + if (s.EndsWith(cs)) result += temp; // 个位 + } + else if (cs == "十") + { + if (temp == 0) temp = 1; // "十" 前没数视为1 + result += temp * 10; + temp = 0; + } + } + if (s == "十") return 10; + return result > 0 ? result : 0; + } + + private static string IntToChinese(int n) + { + if (n <= 0) return n.ToString(); + if (n <= 10) return CN_Nums[n]; + if (n < 20) return "十" + CN_Nums[n % 10]; + if (n < 100) return CN_Nums[n / 10] + "十" + (n % 10 == 0 ? "" : CN_Nums[n % 10]); + return n.ToString(); // 超过99暂返数字 + } } /// @@ -722,6 +1367,25 @@ namespace WordAddIn headings = new List(); } + // 加载大纲样式配置 + string styleJson = Settings.Default.OutlineStyles; + OutlineStyleConfig styleConfig = null; + try + { + styleConfig = string.IsNullOrEmpty(styleJson) + ? new OutlineStyleConfig() + : JsonConvert.DeserializeObject(styleJson); + } + catch + { + styleConfig = new OutlineStyleConfig(); + } + if (styleConfig == null) styleConfig = new OutlineStyleConfig(); + var defaultStyleConfig = new OutlineStyleConfig(); + if (styleConfig.Heading1 == null) styleConfig.Heading1 = defaultStyleConfig.Heading1; + if (styleConfig.Heading2 == null) styleConfig.Heading2 = defaultStyleConfig.Heading2; + if (styleConfig.Heading3 == null) styleConfig.Heading3 = defaultStyleConfig.Heading3; + // 4. 应用样式 (在 UI 线程操作 Word 对象) // 遍历所有段落,性能可能受文档长度影响,但对于一般文档可接受 int changeCount = 0; @@ -737,31 +1401,14 @@ namespace WordAddIn if (match != null) { - // 是标题:设置对应的 Word 内置标题样式 - Word.WdBuiltinStyle styleEnum = Word.WdBuiltinStyle.wdStyleNormal; - switch (match.level) - { - case 1: styleEnum = Word.WdBuiltinStyle.wdStyleHeading1; break; - case 2: styleEnum = Word.WdBuiltinStyle.wdStyleHeading2; break; - case 3: styleEnum = Word.WdBuiltinStyle.wdStyleHeading3; break; - case 4: styleEnum = Word.WdBuiltinStyle.wdStyleHeading4; break; - case 5: styleEnum = Word.WdBuiltinStyle.wdStyleHeading5; break; - default: styleEnum = Word.WdBuiltinStyle.wdStyleHeading1; break; - } - - p.set_Style(styleEnum); - - // 标题通常不需要首行缩进,强制清除 - p.Format.CharacterUnitFirstLineIndent = 0; - p.Format.FirstLineIndent = 0; - + // 使用统一的样式应用方法 + ApplyStyleToParagraph(p, match.level, styleConfig); changeCount++; } else { - // 6. 非标题(正文):统一设置首行缩进 2 字符 - // 让文档看起来更规范 - p.Format.CharacterUnitFirstLineIndent = 2; + // 6. 非标题(正文):应用正文样式(包含首行缩进) + ApplyStyleToParagraph(p, 10, styleConfig); indentCount++; } } @@ -769,7 +1416,7 @@ namespace WordAddIn string msg = $"格式美化完成!\n- 已调整 {changeCount} 处标题格式。"; if (indentCount > 0) { - msg += $"\n- 已对 {indentCount} 个段落应用首行缩进。"; + msg += $"\n- 已对 {indentCount} 个段落应用正文样式(首行缩进)。"; } MessageBox.Show(msg); } @@ -812,41 +1459,29 @@ namespace WordAddIn prompt = prompt.Trim(); } - // 2. 如果选区为空(或只包含空白字符),尝试获取剪贴板文本 - if (string.IsNullOrWhiteSpace(prompt)) - { - try - { - if (Clipboard.ContainsText()) - { - prompt = Clipboard.GetText().Trim(); - } - } - catch (Exception ex) - { - // 忽略剪贴板访问错误(如被其他进程占用) - Console.WriteLine("Clipboard access failed: " + ex.Message); - } - } - - // 3. 如果依然为空,提示用户 - if (string.IsNullOrWhiteSpace(prompt)) - { - MessageBox.Show("未能获取生图描述。\n请选择文档中的文字,或确保剪贴板中有文本内容。"); - return; - } - // 获取全文背景和参考资料上下文,合并到 Prompt 中供用户预览和编辑 string context = GetCombinedContext(); if (!string.IsNullOrWhiteSpace(context)) { + // 注意:这里我们只把 Selection 作为 SourceText 传给 Form + // Context 可以作为补充,但目前的 Form 逻辑主要是处理 SourceText + // 如果需要 Context,可能需要 Form 支持额外参数,或者直接 append 到 prompt + // 为了保持简单,我们暂时只传递选区文本,Context 在这里 append 到 prompt 会导致 + // SourceText 包含 Context,这可能不是用户想要的(如果他们只选了一个词) + // 但之前的逻辑是 append,所以我们保持一致,或者让 Form 决定 + + // 之前的逻辑: prompt += context + // 现在的逻辑: prompt 传给 Form.SourceText + // 如果 prompt 包含 context,那么 Form 的 Input Box 也会包含 context + // 这对于“选区”来说可能有点多,但对于“生成”来说是必要的 + // 让我们暂时保持原样,把 context 加进去,用户可以在 Input Box 里删掉 prompt += $"\n\n{context}"; } // 弹出选项窗体 using (var form = new ImageOptionsForm()) { - // 传入源文本 + // 传入源文本 (可能是选区,也可能是空) form.SourceText = prompt; if (form.ShowDialog() != DialogResult.OK) @@ -893,12 +1528,15 @@ namespace WordAddIn /// /// 【关于】按钮点击事件处理。 - /// 显示插件版本和作者信息。 + /// 显示插件版本和作者信息,以及功能使用说明。 /// /// 触发事件的 Ribbon 控件 public void OnAboutClick(Office.IRibbonControl control) { - MessageBox.Show("Version 0.2\n作者:黄海", "关于 AI 助手"); + using (var form = new AboutForm()) + { + form.ShowDialog(); + } } /// @@ -986,71 +1624,14 @@ namespace WordAddIn p.Range.Text = item.Title.Trim() + "\r"; // 7. 根据层级应用对应的样式 (字体、大小、加粗、对齐) - TextStyle ts = null; try { File.AppendAllText(@"C:\Users\Public\WordAddInLog.txt", $"{DateTime.Now} Processing Item: {item.Title}, Level: {item.Level}\n"); - - switch (item.Level) - { - case 0: // 主标题 - ts = styleConfig.MainTitle; - p.set_Style(Word.WdBuiltinStyle.wdStyleTitle); - p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevelBodyText; - p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevelBodyText; - break; - case 1: // 一级标题 - ts = styleConfig.Heading1; - p.set_Style(Word.WdBuiltinStyle.wdStyleHeading1); - p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel1; - p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel1; - break; - case 2: // 二级标题 - ts = styleConfig.Heading2; - p.set_Style(Word.WdBuiltinStyle.wdStyleHeading2); - p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel2; - p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel2; - break; - case 3: // 三级标题 - ts = styleConfig.Heading3; - p.set_Style(Word.WdBuiltinStyle.wdStyleHeading3); - p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel3; - p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel3; - break; - default: - ts = styleConfig.Heading3; - // 默认也应用三级标题样式,确保出现在大纲中 - p.set_Style(Word.WdBuiltinStyle.wdStyleHeading3); - p.Format.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel3; - p.Range.ParagraphFormat.OutlineLevel = Word.WdOutlineLevel.wdOutlineLevel3; - break; - } + ApplyStyleToParagraph(p, item.Level, styleConfig); } catch (Exception ex) { File.AppendAllText(@"C:\Users\Public\WordAddInLog.txt", $"{DateTime.Now} Error setting style: {ex.Message}\n"); - // Fallback: try setting style by name if object fails - try - { - if (item.Level == 0) p.set_Style("标题"); - else p.set_Style("标题 " + item.Level); - } - catch { } - } - - // 应用自定义样式属性 - if (ts != null) - { - p.Range.Font.Name = ts.FontName; - try { p.Range.Font.NameFarEast = ts.FontName; } catch { } - p.Range.Font.Size = ts.FontSize; - p.Range.Font.Bold = ts.IsBold ? 1 : 0; - p.Range.Font.Color = Word.WdColor.wdColorBlack; // 强制设为黑色,避免出现默认蓝色的情况 - - if (item.Level == 0) // 仅主标题应用自定义对齐方式 - { - p.Format.Alignment = (Word.WdParagraphAlignment)ts.Alignment; - } } // 更新插入位置到当前段落之后,准备插入下一行 @@ -1128,7 +1709,26 @@ namespace WordAddIn return false; } - private static void ApplyHeadingStylesToInsertedRange(Word.Document doc, int start, int end, OutlineStyleConfig styleConfig) + private static int GuessHeadingLevel(string text) + { + if (string.IsNullOrWhiteSpace(text)) return 10; + string t = text.Trim(); + + // Level 3: 1.1.1 + if (Regex.IsMatch(t, @"^\d+\.\d+\.\d+")) return 3; + + // Level 2: 1.1 + if (Regex.IsMatch(t, @"^\d+\.\d+")) return 2; + + // Level 1: 1. or 1、 or 第x章 or 一、 + if (Regex.IsMatch(t, @"^第[一二三四五六七八九十0-9]+章")) return 1; + if (Regex.IsMatch(t, @"^[一二三四五六七八九十]+、")) return 1; + if (Regex.IsMatch(t, @"^\d+(\.|、)")) return 1; + + return 10; // Body + } + + private void ApplyHeadingStylesToInsertedRange(Word.Document doc, int start, int end, OutlineStyleConfig styleConfig) { if (doc == null) return; if (end <= start) return; @@ -1155,74 +1755,7 @@ namespace WordAddIn level = GuessHeadingLevel(text); } - ApplyParagraphStyle(p, level, styleConfig); - } - } - - private static int GuessHeadingLevel(string text) - { - var t = (text ?? "").Trim(); - if (string.IsNullOrWhiteSpace(t)) return 1; - - if (Regex.IsMatch(t, @"^第[一二三四五六七八九十0-9]+章")) return 1; - if (Regex.IsMatch(t, @"^(附录|结语|结论|参考文献|致谢)\b")) return 1; - if (Regex.IsMatch(t, @"^\d+\.\d+\.\d+")) return 3; - if (Regex.IsMatch(t, @"^\d+\.\d+")) return 2; - if (Regex.IsMatch(t, @"^\d+\s")) return 1; - if (Regex.IsMatch(t, @"^[一二三四五六七八九十]+、")) return 2; - return 1; - } - - private static void ApplyParagraphStyle(Word.Paragraph p, int level, OutlineStyleConfig styleConfig) - { - if (p == null) return; - if (styleConfig == null) styleConfig = new OutlineStyleConfig(); - - if (level < 0) level = 0; - if (level > 3) level = 3; - - TextStyle ts; - Word.WdOutlineLevel outlineLevel; - - switch (level) - { - case 0: - ts = styleConfig.MainTitle; - p.set_Style(Word.WdBuiltinStyle.wdStyleTitle); - outlineLevel = Word.WdOutlineLevel.wdOutlineLevel1; - break; - case 1: - ts = styleConfig.Heading1; - p.set_Style(Word.WdBuiltinStyle.wdStyleHeading1); - outlineLevel = Word.WdOutlineLevel.wdOutlineLevel1; - break; - case 2: - ts = styleConfig.Heading2; - p.set_Style(Word.WdBuiltinStyle.wdStyleHeading2); - outlineLevel = Word.WdOutlineLevel.wdOutlineLevel2; - break; - default: - ts = styleConfig.Heading3; - p.set_Style(Word.WdBuiltinStyle.wdStyleHeading3); - outlineLevel = Word.WdOutlineLevel.wdOutlineLevel3; - break; - } - - try { p.Format.OutlineLevel = outlineLevel; } catch { } - try { p.Range.ParagraphFormat.OutlineLevel = outlineLevel; } catch { } - - if (ts != null) - { - try { p.Range.Font.Name = ts.FontName; } catch { } - try { p.Range.Font.NameFarEast = ts.FontName; } catch { } - try { p.Range.Font.Size = ts.FontSize; } catch { } - try { p.Range.Font.Bold = ts.IsBold ? 1 : 0; } catch { } - try { p.Range.Font.Color = Word.WdColor.wdColorBlack; } catch { } - - if (level == 0) - { - try { p.Format.Alignment = (Word.WdParagraphAlignment)ts.Alignment; } catch { } - } + ApplyStyleToParagraph(p, level, styleConfig); } } diff --git a/WordAddIn/WordAddIn/AiRibbon.xml b/WordAddIn/WordAddIn/AiRibbon.xml index 8c16637..65355db 100644 --- a/WordAddIn/WordAddIn/AiRibbon.xml +++ b/WordAddIn/WordAddIn/AiRibbon.xml @@ -2,21 +2,33 @@ + + public int Alignment { get; set; } = 0; + + /// + /// 字体颜色 (Word WdColor 枚举值) + /// 默认值为黑色 (wdColorBlack = -16777216, but usually 0 or auto) + /// 在 Word Interop 中,wdColorBlack = 0 (0x000000) + /// wdColorAutomatic = -16777216 (0xFF000000) + /// 我们存储 int 值。 + /// + public int Color { get; set; } = 0; // 0 = Black } } diff --git a/WordAddIn/WordAddIn/OutlineResultForm.cs b/WordAddIn/WordAddIn/OutlineResultForm.cs index b0c4ba7..58ea7de 100644 --- a/WordAddIn/WordAddIn/OutlineResultForm.cs +++ b/WordAddIn/WordAddIn/OutlineResultForm.cs @@ -79,6 +79,7 @@ namespace WordAddIn // OutlineResultForm // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Font = new System.Drawing.Font("Microsoft YaHei UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.ClientSize = new System.Drawing.Size(584, 461); this.Controls.Add(this.btnClose); this.Controls.Add(this.btnCopy); diff --git a/WordAddIn/WordAddIn/OutlineSettingsForm.cs b/WordAddIn/WordAddIn/OutlineSettingsForm.cs index e098c80..e849016 100644 --- a/WordAddIn/WordAddIn/OutlineSettingsForm.cs +++ b/WordAddIn/WordAddIn/OutlineSettingsForm.cs @@ -81,6 +81,7 @@ namespace WordAddIn this.AcceptButton = this.btnSave; // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Font = new System.Drawing.Font("Microsoft YaHei UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(450, 360); this.MinimumSize = new System.Drawing.Size(350, 300); diff --git a/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs b/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs index 1c76e26..27bb6aa 100644 --- a/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs +++ b/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs @@ -82,7 +82,7 @@ namespace WordAddIn | System.Windows.Forms.AnchorStyles.Right))); this.tabControl.Controls.Add(this.tabFile); this.tabControl.Controls.Add(this.tabText); - this.tabControl.Font = new System.Drawing.Font("微软雅黑", 11F); + this.tabControl.Font = new System.Drawing.Font("微软雅黑", 13F); this.tabControl.Location = new System.Drawing.Point(25, 25); this.tabControl.Name = "tabControl"; this.tabControl.SelectedIndex = 0; @@ -105,7 +105,7 @@ namespace WordAddIn // lblFileTip // this.lblFileTip.AutoSize = true; - this.lblFileTip.Font = new System.Drawing.Font("微软雅黑", 12F); + this.lblFileTip.Font = new System.Drawing.Font("微软雅黑", 14F); this.lblFileTip.Location = new System.Drawing.Point(30, 60); this.lblFileTip.Name = "lblFileTip"; this.lblFileTip.Size = new System.Drawing.Size(255, 21); @@ -116,7 +116,7 @@ namespace WordAddIn // this.txtFilePath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.txtFilePath.Font = new System.Drawing.Font("微软雅黑", 11F); + this.txtFilePath.Font = new System.Drawing.Font("微软雅黑", 13F); this.txtFilePath.Location = new System.Drawing.Point(30, 100); this.txtFilePath.Name = "txtFilePath"; this.txtFilePath.ReadOnly = true; @@ -127,7 +127,7 @@ namespace WordAddIn // this.btnBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnBrowse.Cursor = System.Windows.Forms.Cursors.Hand; - this.btnBrowse.Font = new System.Drawing.Font("微软雅黑", 10F); + this.btnBrowse.Font = new System.Drawing.Font("微软雅黑", 12F); this.btnBrowse.Location = new System.Drawing.Point(440, 98); this.btnBrowse.Name = "btnBrowse"; this.btnBrowse.Size = new System.Drawing.Size(100, 35); @@ -152,7 +152,7 @@ namespace WordAddIn this.txtReferenceInput.BackColor = System.Drawing.Color.WhiteSmoke; this.txtReferenceInput.BorderStyle = System.Windows.Forms.BorderStyle.None; this.txtReferenceInput.Dock = System.Windows.Forms.DockStyle.Fill; - this.txtReferenceInput.Font = new System.Drawing.Font("微软雅黑", 11F); + this.txtReferenceInput.Font = new System.Drawing.Font("微软雅黑", 13F); this.txtReferenceInput.Location = new System.Drawing.Point(10, 10); this.txtReferenceInput.Name = "txtReferenceInput"; this.txtReferenceInput.Size = new System.Drawing.Size(557, 247); @@ -163,7 +163,7 @@ namespace WordAddIn // this.lblWordCount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.lblWordCount.AutoSize = true; - this.lblWordCount.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold); + this.lblWordCount.Font = new System.Drawing.Font("微软雅黑", 14F, System.Drawing.FontStyle.Bold); this.lblWordCount.Location = new System.Drawing.Point(30, 355); this.lblWordCount.Name = "lblWordCount"; this.lblWordCount.Size = new System.Drawing.Size(117, 22); @@ -173,7 +173,7 @@ namespace WordAddIn // numWordCount // this.numWordCount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.numWordCount.Font = new System.Drawing.Font("微软雅黑", 14F); + this.numWordCount.Font = new System.Drawing.Font("微软雅黑", 16F); this.numWordCount.Increment = new decimal(new int[] { 100, 0, @@ -246,6 +246,7 @@ namespace WordAddIn this.AcceptButton = this.btnGenerate; // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.Font = new System.Drawing.Font("Microsoft YaHei UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.BackColor = System.Drawing.Color.White; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(650, 520); diff --git a/WordAddIn/WordAddIn/StyleEditorControl.cs b/WordAddIn/WordAddIn/StyleEditorControl.cs index 220573f..0ed74cb 100644 --- a/WordAddIn/WordAddIn/StyleEditorControl.cs +++ b/WordAddIn/WordAddIn/StyleEditorControl.cs @@ -18,6 +18,10 @@ namespace WordAddIn private NumericUpDown numSize; private CheckBox chkBold; private ComboBox cmbAlign; + private Button btnColor; // 颜色选择按钮 + private Panel pnlColorPreview; // 颜色预览面板 + private int _selectedColor = 0; // 存储选中的颜色值 (Word WdColor) + private bool _showAlign; /// @@ -70,6 +74,17 @@ namespace WordAddIn this.Controls.Add(numSize); this.Controls.Add(chkBold); + // 颜色选择 + Label lblColor = new Label { Text = "颜色:", Location = new Point(180, 67), AutoSize = true }; + btnColor = new Button { Text = "选择...", Location = new Point(220, 63), Width = 60, Height = 25 }; + pnlColorPreview = new Panel { Location = new Point(290, 65), Width = 20, Height = 20, BorderStyle = BorderStyle.FixedSingle, BackColor = Color.Black }; + + btnColor.Click += BtnColor_Click; + + this.Controls.Add(lblColor); + this.Controls.Add(btnColor); + this.Controls.Add(pnlColorPreview); + // 对齐方式 (可选) if (_showAlign) { @@ -83,6 +98,35 @@ namespace WordAddIn } } + private void BtnColor_Click(object sender, EventArgs e) + { + using (ColorDialog colorDialog = new ColorDialog()) + { + // Set initial color based on current selection + colorDialog.Color = pnlColorPreview.BackColor; + + if (colorDialog.ShowDialog() == DialogResult.OK) + { + pnlColorPreview.BackColor = colorDialog.Color; + + // Convert .NET Color to Word WdColor (int) + // Word stores colors as BGR (0xBBGGRR) in lower 24 bits + // But .NET Color.ToArgb() returns ARGB (0xAARRGGBB) + // WdColor is weird. + // wdColorBlack = 0 + // wdColorRed = 255 (0x0000FF) + // It seems to be standard RGB or BGR integer value. + // Let's use ColorTranslator or manual conversion. + // Word Interop usually expects BGR int for some properties, or standard OLE color. + // Actually, WdColor enum has values like wdColorRed = 0xFF. + // The safest way is to cast the RGB value. + // However, ColorTranslator.ToOle(c) returns BGR, which is what Office usually expects. + + _selectedColor = ColorTranslator.ToOle(colorDialog.Color); + } + } + } + /// /// 将 TextStyle 对象应用到界面控件。 /// @@ -98,6 +142,17 @@ namespace WordAddIn numSize.Value = (decimal)style.FontSize; chkBold.Checked = style.IsBold; + + _selectedColor = style.Color; + // Convert OLE Color back to .NET Color for preview + try + { + pnlColorPreview.BackColor = ColorTranslator.FromOle(_selectedColor); + } + catch + { + pnlColorPreview.BackColor = Color.Black; + } if (_showAlign && cmbAlign != null) { @@ -116,6 +171,8 @@ namespace WordAddIn style.FontName = cmbFont.Text; style.FontSize = (float)numSize.Value; style.IsBold = chkBold.Checked; + style.Color = _selectedColor; + if (_showAlign && cmbAlign != null) { style.Alignment = cmbAlign.SelectedIndex; diff --git a/WordAddIn/WordAddIn/WordAddIn.csproj b/WordAddIn/WordAddIn/WordAddIn.csproj index b79b50c..a9504ab 100644 --- a/WordAddIn/WordAddIn/WordAddIn.csproj +++ b/WordAddIn/WordAddIn/WordAddIn.csproj @@ -288,6 +288,9 @@ UserControl + + Form + ThisAddIn.cs diff --git a/WordAddIn/WordAddIn/bin/Release/WordAddIn.dll b/WordAddIn/WordAddIn/bin/Release/WordAddIn.dll index a3d5ec7..4fc2a5a 100644 Binary files a/WordAddIn/WordAddIn/bin/Release/WordAddIn.dll and b/WordAddIn/WordAddIn/bin/Release/WordAddIn.dll differ diff --git a/WordAddIn/WordAddIn/bin/Release/WordAddIn.dll.manifest b/WordAddIn/WordAddIn/bin/Release/WordAddIn.dll.manifest index 710aabf..b4cafde 100644 --- a/WordAddIn/WordAddIn/bin/Release/WordAddIn.dll.manifest +++ b/WordAddIn/WordAddIn/bin/Release/WordAddIn.dll.manifest @@ -166,14 +166,14 @@ - + - b7Cc32gOO4NDdlCmmAwOKVKIxiGSuuKv6qdRFKUoZo8= + Sv+qgHZFDhjy9IFwv7kNOHU+fY7HZYjsKfxAAPn7OKk= @@ -200,4 +200,4 @@ -UhpvvvhZ0JKx4s3N602bgk5DyV5yFn1sFdOJrcd9Vjg=GZa7zL6wJ+jKnaAHu66eweHxTbmrpypWRicD0zClj3D28NdnnNe1IO51tYdaVglSZ9frQUYeS0mDj2FrUXr26NET0eFZTrfjXBwXtTsiiVnD53Zfd/MGpLF1LlXran/i7/Q8CknNtNDechzMVjs7uDS2Gx+PsycmEu03QrNpC1Y=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\AdministratorvkwRm4lIKJ57QGSD+RJm0qwloH1cmITo8KKjBN5VXXY=Sk6rRKpxCaPMi39cj35dPg3SXlS0etffN9z9NtdtGDm2TdptcSY9ljLa1p8fhfNBSTQn2T8wpTC+BYYgLfBI0ymRfH38uKAFdfXvTkOTfM0LPWguRxvu/GLrXsh9K3SrxJnuLk3Dc1Rdh5/BG91NPAtojdkcVvXhnB4fdPZlboM=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q== \ No newline at end of file +ZOkNjAKYNyMSBNhvZ8gVr+wBPStzS85tNEApEJeuU10=m54F1LbgqoSzKzsz7VyEaU5L5VPSErBFaVB+4TDGDHYawHVbgmm+xC3AcJZeVCYlrEobHTKRQrug9hgt0u3q6pK+tYcye6oXQEkxZPWvSR+HCMd642w1HnEXe0OYgQrgr3BCfGgCbbqjGgSCyI3JsakstMSgAudffFmbmK9rbLI=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\Administratort0BRfVoNqhoaPOc2UKbHbTHoevSy5xe2FpaMu7/cbao=yhN59Dtne4w1PdCUP45PNv5AM8RCDXSK1L/BP2VtKtHrZuR58L+mJcST1vBX4HkDilbegq8WX2Ox7Hf+kqLSuS6LMP4teKrzuzF9ROH0TFnLKUOPuWrJjtBoZAmInun8Vfqxq2fCpOCDd/wRrDqRjm2YQHn/qHwHQkXNzlzfjuA=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q== \ No newline at end of file diff --git a/WordAddIn/WordAddIn/bin/Release/WordAddIn.pdb b/WordAddIn/WordAddIn/bin/Release/WordAddIn.pdb index 9c663fb..aa50c45 100644 Binary files a/WordAddIn/WordAddIn/bin/Release/WordAddIn.pdb and b/WordAddIn/WordAddIn/bin/Release/WordAddIn.pdb differ diff --git a/WordAddIn/WordAddIn/bin/Release/WordAddIn.vsto b/WordAddIn/WordAddIn/bin/Release/WordAddIn.vsto index 4e55349..da6b110 100644 --- a/WordAddIn/WordAddIn/bin/Release/WordAddIn.vsto +++ b/WordAddIn/WordAddIn/bin/Release/WordAddIn.vsto @@ -14,8 +14,8 @@ - obQbc7h6zkA0TnoIduOuBc41vNKQMHpPh/AfJA2YISk= + 9reCGw0n5BNGpv4nRAlMEl+9KA74gqf2Bi31YDIuVSE= -ZwzyY1yEsVCU4BhxJzLlO8ZQHNeXqVPqlmSJisLn3tY=crILPNEfy0A7RZ296BpImLzFIcUZ0g/jODUjoCsDq5DmdI/7irDKROQteazqexi045TvacD9fYe6amgHBgdxM4ET4/rIoivBaRBjMdzsUKvowukrNf9VhMJC3RpRd8HWkugN8+7/JfKjXTsCdZyVXNNOzKr0eYJeuYe3ghBj33E=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\Administrator2aT8MYvx/mBGrPRVOvz9piTveMU6NJjxI8LltLyjLWw=hqq7e3z1TzCr+rHGXu1pVw078sk9+Ur7YDZf2YI3Pw1Y5+78F0ize4FhdGdcVjmBmq7f0/5qrA3jqbCcf0hUwd+4AiQSlEeAV50B9SlrvcoRwVjXl6CjkSnKyUEOChNJgA7rgr931hNUXsdIUQ6vtEqdHMLf4WT1bSDiqqvfxYI=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q== \ No newline at end of file +SUC6Yjv6NvxmVHgsXQGLba0wVDBY01J0NUXy81ARtjM=rJF3Cswyo7dSJZ0ZBkoYjEc/BDekJOQ+kDoxvMOf7UWBYec+2EOUBXb+XZwVF7uVLXOJsDTJncnHBf9hcrcwnq8fF7HWuDw9lOMkzvDs5kPvkp9aTIFvPuu8BotBAYV3sn1jHnrtOAf2mkmD/1LU2hyhiH5ymyyI0w17Rwi+f0k=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\AdministratorBmlXdT7EP/tEd4mBeMHh2OJ8nURtQ9BfdX3rIi01DPA=eRIoPFOTfeko5ygKR1m63sb+yJ8qFp9CZniS9T8sMW1WmMAZozZJXiMH+bYIHO1P6Z+47rJHoSfmvUSESLpHIzk0s+WYSd/w4NK/aGE+vIwFA/SeHCWeqfhqC65ZjS0hVbQJ7ScoJfiBTLjNBEPmD/tQhQM/m8/jLv6cRZ8ToQA=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q== \ No newline at end of file diff --git a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.csproj.FileListAbsolute.txt b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.csproj.FileListAbsolute.txt index dc1abd6..2ee1712 100644 --- a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.csproj.FileListAbsolute.txt +++ b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.csproj.FileListAbsolute.txt @@ -12,3 +12,17 @@ D:\dsWork\aiData\addOne\WordAddIn\WordAddIn\obj\Debug\WordAddIn.csproj.CoreCompi D:\dsWork\aiData\addOne\WordAddIn\WordAddIn\obj\Debug\WordAddIn.csproj.Up2Date D:\dsWork\aiData\addOne\WordAddIn\WordAddIn\obj\Debug\WordAddIn.dll D:\dsWork\aiData\addOne\WordAddIn\WordAddIn\obj\Debug\WordAddIn.pdb +d:\dsWork\aiData\WordAddIn\WordAddIn\bin\Debug\WordAddIn.dll +d:\dsWork\aiData\WordAddIn\WordAddIn\bin\Debug\WordAddIn.pdb +d:\dsWork\aiData\WordAddIn\WordAddIn\bin\Debug\WordAddIn.dll.manifest +d:\dsWork\aiData\WordAddIn\WordAddIn\bin\Debug\WordAddIn.vsto +d:\dsWork\aiData\WordAddIn\WordAddIn\bin\Debug\Microsoft.Office.Tools.Common.v4.0.Utilities.dll +d:\dsWork\aiData\WordAddIn\WordAddIn\bin\Debug\Newtonsoft.Json.dll +d:\dsWork\aiData\WordAddIn\WordAddIn\bin\Debug\Newtonsoft.Json.xml +d:\dsWork\aiData\WordAddIn\WordAddIn\obj\Debug\WordAddIn.csproj.AssemblyReference.cache +d:\dsWork\aiData\WordAddIn\WordAddIn\obj\Debug\WordAddIn.Properties.Resources.resources +d:\dsWork\aiData\WordAddIn\WordAddIn\obj\Debug\WordAddIn.csproj.GenerateResource.cache +d:\dsWork\aiData\WordAddIn\WordAddIn\obj\Debug\WordAddIn.csproj.CoreCompileInputs.cache +d:\dsWork\aiData\WordAddIn\WordAddIn\obj\Debug\WordAddIn.csproj.Up2Date +d:\dsWork\aiData\WordAddIn\WordAddIn\obj\Debug\WordAddIn.dll +d:\dsWork\aiData\WordAddIn\WordAddIn\obj\Debug\WordAddIn.pdb diff --git a/WordAddIn/WordAddIn/obj/Release/WordAddIn.csproj.CoreCompileInputs.cache b/WordAddIn/WordAddIn/obj/Release/WordAddIn.csproj.CoreCompileInputs.cache index e58e509..bcfb20e 100644 --- a/WordAddIn/WordAddIn/obj/Release/WordAddIn.csproj.CoreCompileInputs.cache +++ b/WordAddIn/WordAddIn/obj/Release/WordAddIn.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -e154ea9669b13c0b299747f4b6bfc75e5ab40e13fd7d98491977bb58e397c4dc +a8dbfbd53c9d2d6a82eb262741605b35dd6e229dda7031b84eeac3678ff78031 diff --git a/WordAddIn/WordAddIn/obj/Release/WordAddIn.dll b/WordAddIn/WordAddIn/obj/Release/WordAddIn.dll index a3d5ec7..4fc2a5a 100644 Binary files a/WordAddIn/WordAddIn/obj/Release/WordAddIn.dll and b/WordAddIn/WordAddIn/obj/Release/WordAddIn.dll differ diff --git a/WordAddIn/WordAddIn/obj/Release/WordAddIn.pdb b/WordAddIn/WordAddIn/obj/Release/WordAddIn.pdb index 9c663fb..aa50c45 100644 Binary files a/WordAddIn/WordAddIn/obj/Release/WordAddIn.pdb and b/WordAddIn/WordAddIn/obj/Release/WordAddIn.pdb differ diff --git a/WordAddIn/WpsInstaller/Program.cs b/WordAddIn/WpsInstaller/Program.cs index 09dd199..06e6c79 100644 --- a/WordAddIn/WpsInstaller/Program.cs +++ b/WordAddIn/WpsInstaller/Program.cs @@ -121,6 +121,102 @@ namespace WpsInstaller Pause(); } + static void UninstallOldVersions() + { + Console.WriteLine("Checking for existing versions to uninstall..."); + + // 1. Cleanup VSTO Keys + CleanupVstoKeys(); + + // 2. Cleanup WPS Keys + CleanupWpsKeys(); + + // 3. Cleanup COM Registration (WPS) + UnregisterCom("{D8E52004-9D68-4509-8A11-66774D4F0523}", "WordAddIn.WpsAddIn"); + } + + static void UnregisterCom(string guid, string progId) + { + try + { + // Try to find CodeBase from HKCR\CLSID\{GUID}\InprocServer32 + string clsidKeyPath = @"CLSID\" + guid; + string inprocKeyPath = clsidKeyPath + @"\InprocServer32"; + string oldDllPath = null; + + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(inprocKeyPath)) + { + if (key != null) + { + oldDllPath = key.GetValue("CodeBase") as string; + if (string.IsNullOrEmpty(oldDllPath)) + { + oldDllPath = key.GetValue("") as string; // Default value + } + } + } + + if (!string.IsNullOrEmpty(oldDllPath)) + { + // Fix file:/// prefix if present + if (oldDllPath.StartsWith("file:///", StringComparison.OrdinalIgnoreCase)) + { + oldDllPath = oldDllPath.Substring(8).Replace('/', '\\'); + } + + if (File.Exists(oldDllPath)) + { + Console.WriteLine(" Found old COM registration pointing to: " + oldDllPath); + Console.WriteLine(" Unregistering assembly..."); + + string regAsmPath = GetRegAsmPath(); + if (!string.IsNullOrEmpty(regAsmPath)) + { + string args = string.Format("\"{0}\" /u /tlb", oldDllPath); + ProcessStartInfo psi = new ProcessStartInfo(regAsmPath, args); + psi.UseShellExecute = false; + psi.RedirectStandardOutput = true; + psi.CreateNoWindow = true; + + using (Process p = Process.Start(psi)) + { + string output = p.StandardOutput.ReadToEnd(); + p.WaitForExit(); + // Console.WriteLine(output); // Optional: show output + } + Console.WriteLine(" [OK] RegAsm /u completed."); + } + } + else + { + Console.WriteLine(" Old COM DLL not found at: " + oldDllPath + ". Skipping RegAsm /u."); + } + } + + // Force delete registry keys to be sure + try + { + Registry.ClassesRoot.DeleteSubKeyTree(clsidKeyPath, false); + Console.WriteLine(" [OK] Deleted HKCR\\" + clsidKeyPath); + } + catch {} + + try + { + if (!string.IsNullOrEmpty(progId)) + { + Registry.ClassesRoot.DeleteSubKeyTree(progId, false); + Console.WriteLine(" [OK] Deleted HKCR\\" + progId); + } + } + catch {} + } + catch (Exception ex) + { + Console.WriteLine(" Warning during COM unregistration: " + ex.Message); + } + } + static void Pause() { Console.WriteLine("\nPress any key to exit..."); diff --git a/WordAddIn/WpsInstaller/bin/Debug/WpsInstaller.exe b/WordAddIn/WpsInstaller/bin/Debug/WpsInstaller.exe index 32a7def..07080cf 100644 Binary files a/WordAddIn/WpsInstaller/bin/Debug/WpsInstaller.exe and b/WordAddIn/WpsInstaller/bin/Debug/WpsInstaller.exe differ diff --git a/WordAddIn/WpsInstaller/bin/Debug/WpsInstaller.pdb b/WordAddIn/WpsInstaller/bin/Debug/WpsInstaller.pdb index d12ce85..2b6bb43 100644 Binary files a/WordAddIn/WpsInstaller/bin/Debug/WpsInstaller.pdb and b/WordAddIn/WpsInstaller/bin/Debug/WpsInstaller.pdb differ diff --git a/WordAddIn/WpsInstaller/bin/Release/WpsInstaller.exe b/WordAddIn/WpsInstaller/bin/Release/WpsInstaller.exe index d9db986..371d1ac 100644 Binary files a/WordAddIn/WpsInstaller/bin/Release/WpsInstaller.exe and b/WordAddIn/WpsInstaller/bin/Release/WpsInstaller.exe differ diff --git a/WordAddIn/WpsInstaller/bin/Release/WpsInstaller.pdb b/WordAddIn/WpsInstaller/bin/Release/WpsInstaller.pdb index 7e6dbd8..84ac618 100644 Binary files a/WordAddIn/WpsInstaller/bin/Release/WpsInstaller.pdb and b/WordAddIn/WpsInstaller/bin/Release/WpsInstaller.pdb differ diff --git a/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.csproj.AssemblyReference.cache b/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.csproj.AssemblyReference.cache deleted file mode 100644 index c5dbbe8..0000000 Binary files a/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.csproj.AssemblyReference.cache and /dev/null differ diff --git a/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.csproj.FileListAbsolute.txt b/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.csproj.FileListAbsolute.txt index dce9807..560a125 100644 --- a/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.csproj.FileListAbsolute.txt +++ b/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.csproj.FileListAbsolute.txt @@ -1,6 +1,5 @@ D:\dsWork\aiData\WordAddIn\WpsInstaller\bin\Debug\WpsInstaller.exe D:\dsWork\aiData\WordAddIn\WpsInstaller\bin\Debug\WpsInstaller.pdb -D:\dsWork\aiData\WordAddIn\WpsInstaller\obj\Debug\WpsInstaller.csproj.AssemblyReference.cache -D:\dsWork\aiData\WordAddIn\WpsInstaller\obj\Debug\WpsInstaller.csproj.CoreCompileInputs.cache D:\dsWork\aiData\WordAddIn\WpsInstaller\obj\Debug\WpsInstaller.exe D:\dsWork\aiData\WordAddIn\WpsInstaller\obj\Debug\WpsInstaller.pdb +d:\dsWork\aiData\WordAddIn\WpsInstaller\obj\Debug\WpsInstaller.csprojResolveAssemblyReference.cache diff --git a/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.exe b/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.exe index 32a7def..07080cf 100644 Binary files a/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.exe and b/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.exe differ diff --git a/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.pdb b/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.pdb index d12ce85..2b6bb43 100644 Binary files a/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.pdb and b/WordAddIn/WpsInstaller/obj/Debug/WpsInstaller.pdb differ diff --git a/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.csproj.AssemblyReference.cache b/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.csproj.AssemblyReference.cache index c5dbbe8..442efad 100644 Binary files a/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.csproj.AssemblyReference.cache and b/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.csproj.AssemblyReference.cache differ diff --git a/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.exe b/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.exe index d9db986..371d1ac 100644 Binary files a/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.exe and b/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.exe differ diff --git a/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.pdb b/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.pdb index 7e6dbd8..84ac618 100644 Binary files a/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.pdb and b/WordAddIn/WpsInstaller/obj/Release/WpsInstaller.pdb differ