From 04b897656c69666349a5c336e1bd7d15acbd480d Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Fri, 7 Jun 2024 20:48:48 +0300 Subject: [PATCH] Initial commit --- VBAMake.txt | 41 ++ VERSION | 1 + distr/Word.docx | Bin 0 -> 86952 bytes distr/banned-words.txt | 125 ++++ script/manifest.txt | 87 +++ skeleton/CONCEPT.dotm | Bin 0 -> 31158 bytes src/BracketItem.cls | 14 + src/Declarations.bas | 36 ++ src/DevHelper.bas | 20 + src/DocumentEditor.cls | 221 ++++++++ src/LMUFunctions.bas | 26 + src/LMUItem.cls | 20 + src/LinkFunctions.bas | 60 ++ src/LinkMappingUnit.cls | 128 +++++ src/Main.bas | 354 ++++++++++++ src/MainImpl.bas | 247 ++++++++ src/PublicSubs.bas | 144 +++++ src/RulesAccess.bas | 137 +++++ src/RulesProcessor.cls | 1162 ++++++++++++++++++++++++++++++++++++++ src/s_RulesProcessor.cls | 209 +++++++ src/z_UIMessages.bas | 92 +++ src/z_UIRibbon.bas | 30 + ui/.rels | 2 + ui/customUI.xml | 101 ++++ ui/customizations.xml | 2 + 25 files changed, 3259 insertions(+) create mode 100644 VBAMake.txt create mode 100644 VERSION create mode 100644 distr/Word.docx create mode 100644 distr/banned-words.txt create mode 100644 script/manifest.txt create mode 100644 skeleton/CONCEPT.dotm create mode 100644 src/BracketItem.cls create mode 100644 src/Declarations.bas create mode 100644 src/DevHelper.bas create mode 100644 src/DocumentEditor.cls create mode 100644 src/LMUFunctions.bas create mode 100644 src/LMUItem.cls create mode 100644 src/LinkFunctions.bas create mode 100644 src/LinkMappingUnit.cls create mode 100644 src/Main.bas create mode 100644 src/MainImpl.bas create mode 100644 src/PublicSubs.bas create mode 100644 src/RulesAccess.bas create mode 100644 src/RulesProcessor.cls create mode 100644 src/s_RulesProcessor.cls create mode 100644 src/z_UIMessages.bas create mode 100644 src/z_UIRibbon.bas create mode 100644 ui/.rels create mode 100644 ui/customUI.xml create mode 100644 ui/customizations.xml diff --git a/VBAMake.txt b/VBAMake.txt new file mode 100644 index 0000000..333bde6 --- /dev/null +++ b/VBAMake.txt @@ -0,0 +1,41 @@ +# == Properties Section == +# configuration properties +# use .ini format to define properties +# mandatory properties: name, artifact_home, source_home + +id = WordHelper +name = WordHelper +description = Надстройка КОНЦЕПТ для Word +artifact_home = Addins +source_home = WordAI +install_home = \\fs1.concept.ru\projects\04 Направления деятельности\60 КИВТ\21 Надстройки\WORD + +%% +# === Build section === +# Available commands: +# build LOCAL_MANIFEST +# copy LOCAL_SOURCE -> [LOCAL_ARTIFACT] +# save_as LOCAL_ARTIFACT -> LOCAL_ARTIFACT +# run LOCAL_SOURCE.bat + +build script\manifest.txt +copy distr\Word.docx +copy distr\banned-words.txt + +%% +# === Install section == +# Available commands: +# install LOCAL_ARTIFACT -> [INSTALL_PATH] +# add_template LOCAL_ARTIFACT -> [LOCAL_TEMPLATE] +# run LOCAL_ARTIFACT.bat <- [PARAMETERS] +# run APPLICATION <- [PARAMETERS] + +install CONCEPT.dotm +install CONCEPT.dotm -> \\fs1.concept.ru\Exchange\ConceptDistr\data\Add-ins\Word\CONCEPT.dotm + +install Word.docx +install Word.docx -> \\fs1.concept.ru\projects\10 Автоматизация деятельности\02 Офисная автоматизация\!Надстройки\Word.docx + +install banned-words.txt +install banned-words.txt -> \\fs1.concept.ru\Exchange\ConceptDistr\models\banned-words.txt +install banned-words.txt -> \\fs1.concept.ru\projects\10 Автоматизация деятельности\!Concept\models\banned-words.txt \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..0495c4a --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.2.3 diff --git a/distr/Word.docx b/distr/Word.docx new file mode 100644 index 0000000000000000000000000000000000000000..282ce863948876265e5eb64c7896665cf063ab89 GIT binary patch literal 86952 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 KAPr~|K>rW^GRj~8 literal 0 HcmV?d00001 diff --git a/distr/banned-words.txt b/distr/banned-words.txt new file mode 100644 index 0000000..9b07355 --- /dev/null +++ b/distr/banned-words.txt @@ -0,0 +1,125 @@ +никогда +нигде +эффективность +эффективности +эффективностью +минимальный +минимального +минимальная +минимальной +минимальному +минимальным +минимальные +минимальными +минимальному +максимальный +максимальная +максимальные +максимальным +максимальному +максимальной +максимальными +минимизация +минимизации +минимизацией +глобальный +глобальная +глобальные +глобальным +глобальной +глобальными +глобальному +беспрецедентный +беспрецедентная +беспрецедентные +беспрецедентным +беспрецедентному +беспрецедентными +беспрецедентной +предельный +предельная +предельные +предельным +предельному +предельной +предельными +уникальный +уникальная +уникальные +уникальным +уникальными +уникальному +уникальной +реальный +реальная +реальные +реальным +реальными +реальному +реальной +всегда +подавляющий +подавляющего +подавляющим +подавляющему +подавляющем +подавляющая +подавляющей +подавляющие +подавляющим +подавляющих +было +был +была +были +эффективный +эффективная +эффективное +эффективные +эффективным +эффективной +эффективными +эффективных +эффективному +эффективном +неэффективный +неэффективная +неэффективное +неэффективные +неэффективным +неэффективной +неэффективными +неэффективных +неэффективному +неэффективном +минимально +максимально +эффективно +недавно +существует +существуют +существовать +текущий +текущая +текущие +текущему +текущей +текущим +текущими +текущих +постоянно +очевидно +просто +сразу +любой бред +любого бреда +любому бреду +любым бредом +любом бреде +какой-то +какая-то +какие-то +какому-то +какой-то +каким-то +какими-то \ No newline at end of file diff --git a/script/manifest.txt b/script/manifest.txt new file mode 100644 index 0000000..6af5e96 --- /dev/null +++ b/script/manifest.txt @@ -0,0 +1,87 @@ +# == Properties Section == +# configuration properties +# use .ini format to define properties +# mandatory properties: name, artifact + +name = CONCEPT.dotm +artifact = CONCEPT.dotm + +%% +# === Imports Section === +# Hierarchy of folders and files +# Use Tabulator to mark next level in hierarchy +# All folders are nested into SharedHome path + +dev + DevTester.bas + +api + ex_WinAPI.bas + API_WordWrapper.cls + API_XLWrapper.cls + API_UserInteraction.cls + +word + ex_Word.bas + API_WordEditGuard.cls + +utility + ex_VBA.bas + ex_Version.bas + + API_DistrManifest.cls + API_Config.cls + API_JSON.cls + +ui + CSE_ProgressBar.frm + +%% +# === Source Code Section == +# Hierarchy of folders and files +# Use Tabulator to mark next level in hierarchy +# All folders are nested into SourceHome path + +src + DevHelper.bas + Declarations.bas + Main.bas + MainImpl.bas + z_UIMessages.bas + z_UIRibbon.bas + + RulesAccess.bas + DocumentEditor.cls + RulesProcessor.cls + BracketItem.cls + + LMUFunctions.bas + LinkFunctions.bas + LinkMappingUnit.cls + LMUItem.cls + + s_RulesProcessor.cls + +%% +# ===== UI Section ======= +# Pairs of path to UI elements, use " -> " delimiter +# First component is a path relative to SourceHome\ui folders +# Second component is internal path inside project file + +.rels -> _rels\.rels +customUI.xml -> customUI\customUI.xml +customizations.xml -> word\customizations.xml + +%% +# === References Section === +# List dependencies in one of the formats +# global : GLOBAL_NAME +# guid : {REGISTERED_GUID} +# file : PATH_TO_LIBRARY + +global : Shell32 +global : Scripting +global : Excel +global : MSForms +global : ADODB +global : IWshRuntimeLibrary \ No newline at end of file diff --git a/skeleton/CONCEPT.dotm b/skeleton/CONCEPT.dotm new file mode 100644 index 0000000000000000000000000000000000000000..2b97637ad6bb5b48ad66d6683f222db0ea7002d0 GIT binary patch literal 31158 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 0 + leftSide = Left(addr, InStr(addr, "../") - 1) + leftSide = Left(leftSide, InStrRev(leftSide, "\", Len(leftSide) - 1)) + addr = leftSide & Right(addr, Len(addr) - InStr(addr, "../") - 2) + Loop + + GetFullPath = Replace(addr, "/", "\") +End Function + +Public Function ConvertLinkToLocal(ByVal addr$) As String +' P:\ \\STRG1\... + ConvertLinkToLocal = addr + If Left(addr, 3) Like "P:\" Then _ + ConvertLinkToLocal = "\\STRG1\Projects\" & Right(addr, Len(addr) - 3) + If Left(addr, 3) Like "X:\" Then _ + ConvertLinkToLocal = "\\Server\Exchange\" & Right(addr, Len(addr) - 3) +End Function + +Public Function ConvertLinkToURL(ByVal addr$) As String +' \\STRG1\... P:\ \\server\... X:\ + ConvertLinkToURL = addr + If Left(addr, 17) Like "\\[Ss][Tt][Rr][Gg]1\[Pp]rojects\" Then _ + ConvertLinkToURL = "P:\" & Right(addr, Len(addr) - 17) + If Left(addr, 18) Like "\\[Ss][Ee][Rr][Vv][Ee][Rr]\[Ee]xchange\" Then _ + ConvertLinkToURL = "X:\" & Right(addr, Len(addr) - 18) +End Function + +Public Function SafeGetHLinkAddress(hLink As Hyperlink) As String +' . 4198 + On Error GoTo PICK_RANGE + SafeGetHLinkAddress = hLink.Address + + Exit Function +PICK_RANGE: + SafeGetHLinkAddress = hLink.Range.Text +End Function + +Public Function IsSamePath(ByVal hLinkText$, ByVal hLinkAddress$, docpath$) As Boolean + IsSamePath = False + hLinkText = UCase(ConvertLinkToURL(GetFullPath(hLinkText, docpath))) + hLinkAddress = UCase(ConvertLinkToURL(GetFullPath(hLinkAddress, docpath))) + If InStr(hLinkText, hLinkAddress) = 1 And Abs(Len(hLinkText) - Len(hLinkAddress)) <= 1 Then IsSamePath = True +End Function diff --git a/src/LinkMappingUnit.cls b/src/LinkMappingUnit.cls new file mode 100644 index 0000000..762ab2a --- /dev/null +++ b/src/LinkMappingUnit.cls @@ -0,0 +1,128 @@ +VERSION 1.0 CLASS +BEGIN + MultiUse = -1 'True +END +Attribute VB_Name = "LinkMappingUnit" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = False +Attribute VB_Exposed = False +' +Option Explicit + +Private m_ReplaceMap As Collection + +'---------- Public Interface ---------- +Public Function FixLink(ByRef adr$) As LMU_Return +' + Dim testAdr$ + If Not IsFSO(adr, "") Then + FixLink = RV_NOTAFSO + Exit Function + End If + + If IsValidAddress(adr) Then + testAdr = ConvertLinkToURL(adr) + If CheckFile(testAdr, testAdr) Then + FixLink = RV_LINK_OK + Exit Function + End If + End If + + testAdr = SwapStrgServer(adr) + If CheckFile(testAdr, testAdr) Then + FixLink = RV_LINK_FIXED + adr = ConvertLinkToLocal(testAdr) + Exit Function + End If + + Dim j&, k& + Dim rule As LMUItem + Dim result$ + For j = 1 To m_ReplaceMap.Count + Set rule = m_ReplaceMap.Item(j) + If InStr(adr, rule.repWhat) = 0 Then _ + GoTo NEXT_RULE + + For k = 1 To rule.repList.Count + result = Replace(testAdr, rule.repWhat, rule.repList(k)) + If CheckFile(result, "") Then + adr = ConvertLinkToLocal(result) + FixLink = RV_LINK_FIXED + Exit Function + End If + Next k +NEXT_RULE: + Next j + + FixLink = RV_LINK_NOFIX +End Function + +'---------- Event handlers and misc code ---------- +Private Sub Class_Initialize() + Set m_ReplaceMap = New Collection + Dim rule As LMUItem + Set rule = New LMUItem + rule.repWhat = "01 Income-" + rule.repList.Add ("00 \01 ") + Call m_ReplaceMap.Add(rule) + + Set rule = New LMUItem + rule.repWhat = "02 Outcome-" + rule.repList.Add ("00 \02 ") + Call m_ReplaceMap.Add(rule) + + Set rule = New LMUItem + rule.repWhat = "03 " + rule.repList.Add ("00 \03 ") + rule.repList.Add ("01 Income-") + rule.repList.Add ("02 Outcome-") + Call m_ReplaceMap.Add(rule) + + Set rule = New LMUItem + rule.repWhat = "031 " + rule.repList.Add ("00 \03 ") + rule.repList.Add ("03 ") + Call m_ReplaceMap.Add(rule) + + Set rule = New LMUItem + rule.repWhat = "05 \" + rule.repList.Add ("05 \04 ") + Call m_ReplaceMap.Add(rule) +End Sub + +Private Function IsValidAddress(ByVal addr$) As Boolean + IsValidAddress = True + If Left(addr, Len("\\server\Projects")) Like "\\[Ss][Ee][Rr][Vv][Ee][Rr]\[Pp]rojects" Then IsValidAddress = False + If Left(addr, Len("\\strg1\Exchange")) Like "\\[Ss][Tt][Rr][Gg]1\[Ee]xchange" Then IsValidAddress = False +End Function + +Private Function IsFSO(addr$, docpath$) As Boolean +' + addr = GetFullPath(addr, docpath) + If Left(addr, 3) Like "[PpXx]:\" Then IsFSO = True: Exit Function + If Left(addr, 16) Like "\\[Ss][Tt][Rr][Gg]1\[Pp]rojects" Then IsFSO = True: Exit Function + If Left(addr, 16) Like "\\[Ss][Tt][Rr][Gg]1\[Ee]xchange" Then IsFSO = True: Exit Function + If Left(addr, 17) Like "\\[Ss][Ee][Rr][Vv][Ee][Rr]\[Ee]xchange" Then IsFSO = True: Exit Function + If Left(addr, 17) Like "\\[Ss][Ee][Rr][Vv][Ee][Rr]\[Pp]rojects" Then IsFSO = True: Exit Function + + IsFSO = False +End Function + +Private Function CheckFile(ByVal addr$, currentPath$) As Boolean +' + If Left(addr, 1) = "." Or InStr(addr, "\") + InStr(addr, "/") = 0 Then _ + addr = currentPath & "\" & addr + + On Error Resume Next + CheckFile = Len(Dir(addr)) <> 0 Or Len(Dir(addr, vbDirectory)) <> 0 +End Function + +Private Function SwapStrgServer(ByVal addr$) + SwapStrgServer = addr + If Left(addr, Len("\\server\Projects")) Like "\\[Ss][Ee][Rr][Vv][Ee][Rr]\[Pp]rojects" Then _ + SwapStrgServer = "P:\" + Right(addr, Len(addr) - Len("\\server\Projects\")) + If Left(addr, Len("\\strg1\Exchange")) Like "\\[Ss][Tt][Rr][Gg]1\[Ee]xchange" Then _ + SwapStrgServer = "X:\" + Right(addr, Len(addr) - Len("\\strg1\Exchange\")) +End Function + diff --git a/src/Main.bas b/src/Main.bas new file mode 100644 index 0000000..d6d3667 --- /dev/null +++ b/src/Main.bas @@ -0,0 +1,354 @@ +Attribute VB_Name = "Main" +Option Explicit + +Public Sub CC_AddColumnBreak() + Dim selPar As Word.Range: Set selPar = ActiveDocument.ActiveWindow.Selection.Paragraphs.First.Range + Call selPar.Collapse(wdCollapseStart) + Call selPar.InsertBreak(wdColumnBreak) +End Sub + +Public Sub CC_NonBreakPar() + ActiveDocument.ActiveWindow.Selection.ParagraphFormat.KeepTogether = Not ActiveDocument.ActiveWindow.Selection.ParagraphFormat.KeepTogether +End Sub + +Public Sub CC_WithNext() + Selection.ParagraphFormat.KeepWithNext = Not Selection.ParagraphFormat.KeepWithNext +End Sub + +Public Sub CC_FieldLowerer() + Dim tmpRange As Word.Range: Set tmpRange = ActiveDocument.ActiveWindow.Selection.Range.Duplicate + tmpRange.Start = ActiveDocument.ActiveWindow.Selection.Words.First.Start + tmpRange.End = ActiveDocument.ActiveWindow.Selection.Words.Last.End + + On Error GoTo EXT_SUB + Dim execField As Word.Field + For Each execField In tmpRange.Fields + Call LowerReference(execField) + Next execField +EXT_SUB: +End Sub + +Public Sub CC_MakeUniqueBM() + Dim contextMap As Scripting.Dictionary: Set contextMap = RecreateBookmarks(ActiveDocument) + Call FixHyperlinksAfterBMReplace(ActiveDocument, contextMap) + Call UserInteraction.ShowMessage(IM_UNIQUE_BOOKMARKS_OK) +End Sub + +Public Sub CC_ConvertAllShapesToText() +' + Dim theDoc As Word.Document: Set theDoc = ActiveDocument + Dim tRange As Word.Range: Set tRange = theDoc.Range + + Dim nShape&: nShape = 1 + Dim theText$ + + Dim aShape As Word.Shape + Do While nShape <= theDoc.Shapes.Count ' + Set aShape = theDoc.Shapes(nShape) + If Not aShape.Anchor.InRange(tRange) Then _ + GoTo NEXT_SHAPE + If aShape.Type <> msoTextBox Then _ + GoTo NEXT_SHAPE + + theText = aShape.TextFrame.TextRange.Text + + aShape.TextFrame.TextRange.Copy + tRange.Paragraphs.Add + Call tRange.Paragraphs.Last.Range.Paste + +NEXT_SHAPE: + nShape = nShape + 1 + Loop + + Call UserInteraction.ShowMessage(IM_SHAPES_TO_TEXT_OK) +End Sub + +Public Sub CC_Convert1252_1251() +' + Dim theDoc As Word.Document: Set theDoc = ActiveDocument + theDoc.ActiveWindow.View.ReadingLayout = False + + Dim inputR As Word.Range: Set inputR = theDoc.Application.Selection.Range + + Dim wordUI As New API_WordWrapper: Call wordUI.SetDocument(theDoc) + Call wordUI.PauseUI + + Call CSE_ProgressBar.Init(" 1252 -> 1251", sHeader:=" ...", maxVal:=inputR.Characters.Count) + Call CSE_ProgressBar.ShowModeless + + Dim aChr As Word.Range: Set aChr = inputR.Characters.First + Do While aChr.End < inputR.End + Dim ChrW&: ChrW = VBA.AscW(aChr) + If ChrW < 256 And ChrW > 127 Then _ + aChr.Text = VBA.Chr(ChrW) + Set aChr = aChr.Next(wdCharacter, 1) + Call CSE_ProgressBar.IncrementA + Loop + + Call Unload(CSE_ProgressBar) + Call wordUI.ResumeUI + Call UserInteraction.ShowMessage(IM_CONVERT_CODEPAGE_OK) +End Sub + +Public Sub CC_ParFix() +' Modify spacing to make last line optimal length + Dim selRange As Word.Range: Set selRange = ActiveDocument.ActiveWindow.Selection.Range.Duplicate + Dim aPar As Word.Paragraph + For Each aPar In selRange.Paragraphs + Dim tRange As Word.Range: Set tRange = aPar.Range + If tRange.Tables.Count > 0 Then tRange.End = tRange.End - 1 + Call tRange.Select + Call TryFixingParagraph(tRange) + Next aPar +End Sub + +Public Sub CC_ExportComments() + ' Export comments in new document + Dim iSource As Word.Document: Set iSource = ActiveDocument + Dim iDestination As Word.Document: Set iDestination = Word.Application.Documents.Add + + Dim targetRange As Word.Range: Set targetRange = iDestination.Range + Dim aComment As Word.Comment + Dim nCount&: nCount = 0 + Dim nPage& + For Each aComment In iSource.Comments + nCount = nCount + 1 + Set targetRange = targetRange.Paragraphs.Add.Range + nPage = aComment.Reference.Information(wdActiveEndPageNumber) + targetRange.Text = Fmt(" {1}, {2}, {3}:" & vbNewLine & "{4}" & vbNewLine, nCount, nPage, aComment.Author, aComment.Range.Text) + Next aComment + + Call UserInteraction.ShowMessage(IM_COMMENT_EXPORT_OK, nCount) +End Sub + +Public Sub CC_LineUp() + Dim tRange As Word.Range: Set tRange = ActiveDocument.ActiveWindow.Selection.Paragraphs(1).Range + Dim iSpace As Double: iSpace = tRange.ParagraphFormat.SpaceBefore + tRange.ParagraphFormat.LineSpacing + tRange.ParagraphFormat.SpaceBefore = iSpace +End Sub + +Public Sub CC_LineUpA() + Dim tRange As Word.Range: Set tRange = ActiveDocument.ActiveWindow.Selection.Paragraphs(1).Range + Dim iSpace As Double: iSpace = tRange.ParagraphFormat.SpaceBefore - tRange.ParagraphFormat.LineSpacing + tRange.ParagraphFormat.SpaceBefore = IIf(iSpace >= 0, iSpace, 0) +End Sub + +Public Sub CC_LineDown() + Dim tRange As Word.Range: Set tRange = ActiveDocument.ActiveWindow.Selection.Paragraphs(1).Range + Dim iSpace As Double: iSpace = tRange.ParagraphFormat.SpaceAfter + tRange.ParagraphFormat.LineSpacing + tRange.ParagraphFormat.SpaceAfter = iSpace +End Sub + +Public Sub CC_LineDownA() + Dim tRange As Word.Range: Set tRange = ActiveDocument.ActiveWindow.Selection.Paragraphs(1).Range + Dim iSpace As Double: iSpace = tRange.ParagraphFormat.SpaceAfter - tRange.ParagraphFormat.LineSpacing + tRange.ParagraphFormat.SpaceAfter = IIf(iSpace >= 0, iSpace, 0) +End Sub + +Public Sub CC_CheckLinks() +' + Dim thisDoc As Word.Document: Set thisDoc = ActiveDocument + Dim linkCount&: linkCount = thisDoc.Hyperlinks.Count + If thisDoc.Hyperlinks.Count = 0 Then + Call UserInteraction.ShowMessage(EM_HYPERLINKS_MISSING) + Exit Sub + End If + + Dim result As LMU_LinksInfo: result = FixHyperlinksIn(ActiveDocument.Range) + Call UserInteraction.ShowMessage(IM_HYPERLINKS_FIX_OK, result.lnkCount_, result.errCount_, result.modCount_) +End Sub + +Public Sub CC_AddQuote() + ActiveDocument.ActiveWindow.Selection.Range.Text = Chr(171) & ActiveDocument.ActiveWindow.Selection.Text & Chr(187) +End Sub + +Public Sub CC_EditCut() +' + Dim aRange As Word.Range: Set aRange = ActiveDocument.ActiveWindow.Selection.Range + aRange.Start = aRange.Paragraphs.First.Range.Start + aRange.End = aRange.Paragraphs.Last.Range.End + + Call aRange.Cut + Call ActiveDocument.Paragraphs.Add + Call ActiveDocument.Paragraphs.Last.Range.Paste +End Sub + +Public Sub CC_EditCopy() +' + Dim aRange As Word.Range: Set aRange = ActiveDocument.ActiveWindow.Selection.Range + aRange.Start = aRange.Paragraphs.First.Range.Start + aRange.End = aRange.Paragraphs.Last.Range.End + + Call aRange.Copy + + Call ActiveDocument.Paragraphs.Add + Call ActiveDocument.Paragraphs.Last.Range.Paste + + aRange.HighlightColorIndex = wdYellow +End Sub + +Public Sub CC_CreateHLink() + Dim target As Word.Range: Set target = ActiveDocument.ActiveWindow.Selection.Range + + Dim bookmarkID$ + Dim aBM As Word.Bookmark + For Each aBM In target.Bookmarks + If aBM.Range.End = target.End And aBM.Range.Start = target.Start Then bookmarkID = aBM.Name + Next aBM + + If bookmarkID = vbNullString Then + On Error GoTo ERROR_MSG + bookmarkID = "AUTO_" & Format(Date, "yyyymmdd") & Replace(Time, ":", "") + Call target.Bookmarks.Add(bookmarkID) + End If + + Call AddToClipboard(target.Text) + DoEvents + Call AddToClipboard(ActiveDocument.FullName & "#" & bookmarkID) + Exit Sub + +ERROR_MSG: + Call UserInteraction.ShowMessage(EM_HYPERLINK_CREATION_FAIL) +End Sub + +Public Sub CC_RemoveExternalHyperlinks() + Dim target As Word.Range: Set target = ActiveDocument.ActiveWindow.Selection.Range.Duplicate + Dim aLink As Hyperlink + Dim nLink&: nLink = 1 + Dim delCount&: delCount = 0 + Do While target.Hyperlinks.Count >= nLink + Set aLink = target.Hyperlinks.Item(nLink) + If aLink.Address <> vbNullString Then + Call aLink.Range.Font.Reset + delCount = delCount + 1 + Call aLink.Delete + Else + nLink = nLink + 1 + End If + Loop + Call Word.Application.ScreenRefresh + Call UserInteraction.ShowMessage(IM_REMOVE_HYPERLINKS_OK, delCount) +End Sub + +Public Sub CC_RemoveAllHyperlinks() + Dim target As Word.Range: Set target = ActiveDocument.ActiveWindow.Selection.Range.Duplicate + Dim aLink As Hyperlink + Dim delCount&: delCount = target.Hyperlinks.Count + Do While target.Hyperlinks.Count > 0 + Set aLink = target.Hyperlinks.Item(1) + Call aLink.Range.Font.Reset + Call aLink.Delete + Loop + Call Word.Application.ScreenRefresh + Call UserInteraction.ShowMessage(IM_REMOVE_HYPERLINKS_OK, delCount) +End Sub + +Public Sub CC_RemoveImages() + Dim nDeleted&: nDeleted = RemoveImages(ActiveDocument.ActiveWindow.Selection.Range.Duplicate) + Call Word.Application.ScreenRefresh + Call UserInteraction.ShowMessage(IM_REMOVE_IMAGES_OK, nDeleted) +End Sub + +Public Sub CC_RemoveBookmarks() + Dim target As Word.Range: Set target = ActiveDocument.ActiveWindow.Selection.Range.Duplicate + Dim aBM As Word.Bookmark + Dim delCount&: delCount = target.Bookmarks.Count + Do While target.Bookmarks.Count > 0 + Set aBM = target.Bookmarks.Item(1) + Call aBM.Delete + Loop + Call Word.Application.ScreenRefresh + Call UserInteraction.ShowMessage(IM_REMOVE_BOOKMARKS_OK, delCount) +End Sub + +Public Sub CC_RemoveAccents() + Dim target As Word.Range: Set target = ActiveDocument.ActiveWindow.Selection.Range.Duplicate + If target.Start = target.End Then _ + Set target = ActiveDocument.Range + + Dim nCount&: nCount = RemoveAccentsIn(target) + Call Word.Application.ScreenRefresh + Call UserInteraction.ShowMessage(IM_REMOVE_ACCENTS_OK, nCount) +End Sub + +Public Sub CC_MasterText() + Dim sPath$: sPath = Word.ActiveDocument.FullName + If Word.ActiveDocument.Path = vbNullString Then + Call UserInteraction.ShowMessage(EM_DOCUMENT_NOT_SAVED) + Exit Sub + End If + + Dim xlWrap As New API_XLWrapper + Dim aDoc As Excel.Workbook: Set aDoc = xlWrap.OpenDocument(SERVER_MASTER_TEXT) + If aDoc Is Nothing Then _ + Exit Sub + + Call aDoc.Activate + aDoc.Names(CELL_MASTER_PATH).RefersToRange = sPath$ +End Sub + +Public Sub CC_UpdateConcept() + If Not UpdateConceptPowershell Then + Call UserInteraction.ShowMessage(EM_CORE_UPDATE_FAILED) + Exit Sub + End If + Call UserInteraction.ShowMessage(IM_CONCEPT_UPDATE_OK) +End Sub + +Public Sub CC_Checkup() + Call ExecuteAllRules(ActiveDocument, bApplyFix:=False) +End Sub + +Public Sub CC_FixErrors() + Call ExecuteAllRules(ActiveDocument, bApplyFix:=True) +End Sub + +Public Sub CC_MasterConfig() + Call OpenMasterConfig +End Sub + +Public Sub CC_IgnoreSelected() + ' + Dim target As Word.Range: Set target = ActiveDocument.Application.Selection.Range + If target.Start = target.End Then + Call UserInteraction.ShowMessage(EM_SELECTION_EMPTY) + Exit Sub + End If + + Dim iEditor As New API_WordEditGuard: Call iEditor.InitDoc(target.Document) + Dim sBookmark$: sBookmark = iEditor.BookmarkIgnored(target) + Call UserInteraction.ShowMessage(IM_BOOKMARK_IGNORED, sBookmark) +End Sub + +Public Sub CC_Help() + Dim wordWrap As New API_WordWrapper + Dim helpDoc As Word.Document: Set helpDoc = wordWrap.OpenDocument(SERVER_PATH_HELP, bReadOnly:=True) + If helpDoc Is Nothing Then _ + Exit Sub + + With helpDoc + .ActiveWindow.View.ReadingLayout = False + Call .Application.Activate + End With +End Sub + +' ============ +Private Function ExecuteAllRules(iDoc As Word.Document, Optional bApplyFix As Boolean = True) + Dim iConfig As API_Config: Set iConfig = LoadEditConfig + Dim iProcessor As New RulesProcessor: Call iProcessor.Init(iDoc, iConfig) + Dim iGuard As New API_WordEditGuard: Call iGuard.InitDoc(iDoc) + + Call iGuard.BeginEdit(iConfig.GetValue("TrackRevisions")) + + Call CSE_ProgressBar.Init(" ", sHeader:=" , ...", maxVal:=iProcessor.RuleCount, canInterrupt:=True) + Call CSE_ProgressBar.ShowModeless + + Call iProcessor.RunAll(CollectionToDictionary(iConfig.GetValue("ExcludeRules")), bApplyFix) + + Dim nMilliseconds&: nMilliseconds = Int(CSE_ProgressBar.Time) + CSE_ProgressBar.Description = " " + Call iGuard.EndEdit + + Call Unload(CSE_ProgressBar) + Call UserInteraction.ShowMessage(IM_RULES_COMPLETE, nMilliseconds, iProcessor.errors_, iProcessor.fixes_) +End Function diff --git a/src/MainImpl.bas b/src/MainImpl.bas new file mode 100644 index 0000000..335e7ad --- /dev/null +++ b/src/MainImpl.bas @@ -0,0 +1,247 @@ +Attribute VB_Name = "MainImpl" +Option Explicit + +Public Function RemoveImages(target As Word.Range) As Long + Dim iShp As Word.InlineShape + Dim nDeleted&: nDeleted = target.InlineShapes.Count + Do While target.InlineShapes.Count > 0 + Set iShp = target.InlineShapes.Item(1) + Dim pasteRange As Word.Range: Set pasteRange = iShp.Range.Duplicate + Call pasteRange.Collapse(wdCollapseStart) + Call iShp.Delete + pasteRange.Text = "[ ]" + pasteRange.Font.Bold = True + Loop + RemoveImages = nDeleted +End Function + +Public Function RecreateBookmarks(target As Word.Document) As Scripting.Dictionary + Dim contextMap As New Scripting.Dictionary + Dim pref$: pref = "t" & VBA.Format(Date, "yyyymmdd") & VBA.Replace(VBA.Time, ":", "") + Dim nBookmark& + For nBookmark = target.Bookmarks.Count To 1 Step -1 ' Note: reverse order for recreation + Dim aMark As Word.Bookmark: Set aMark = target.Bookmarks(nBookmark) + Dim bmRange As Word.Range: Set bmRange = aMark.Range + Dim sNewName$: sNewName = pref & "_" & aMark.Name + If VBA.Len(sNewName) > 40 Then _ + GoTo NEXT_BM + + Call contextMap.Add(aMark.Name, sNewName) + Call bmRange.Bookmarks.Add(pref & "_" & aMark.Name) + Call aMark.Delete +NEXT_BM: + Next nBookmark + + Set RecreateBookmarks = contextMap +End Function + +Public Function FixHyperlinksAfterBMReplace(target As Word.Document, iContext As Scripting.Dictionary) + Dim newAddr$ + Dim nLink& + For nLink = target.Hyperlinks.Count To 1 Step -1 + Dim aLink As Word.Hyperlink: Set aLink = target.Hyperlinks(nLink) + Dim linkRange As Word.Range: Set linkRange = target.Range(aLink.Range.Start, aLink.Range.End) + Dim sSub$: sSub = aLink.SubAddress + If Not aLink.Address = vbNullString Or sSub = vbNullString Then _ + GoTo NEXT_LINK + If Not iContext.Exists(sSub) Then _ + GoTo NEXT_LINK + + newAddr = iContext(sSub) + Call aLink.Delete + Call linkRange.Hyperlinks.Add(linkRange, "", newAddr) + +NEXT_LINK: + Next nLink +End Function + +Public Function FixHyperlinksIn(target As Word.Range) As LMU_LinksInfo + FixHyperlinksIn.lnkCount_ = target.Hyperlinks.Count + + Call CSE_ProgressBar.Init(" ", sHeader:="...", _ + maxVal:=FixHyperlinksIn.lnkCount_) + Call CSE_ProgressBar.ShowModeless + + FixHyperlinksIn.modCount_ = 0 + FixHyperlinksIn.errCount_ = 0 + + Dim aLink As Hyperlink + For Each aLink In target.Hyperlinks + Dim linkAdr$: linkAdr = GetFullPath(SafeGetHLinkAddress(aLink), target.Document.Path) + If linkAdr = vbNullString Then _ + GoTo NEXT_LINK + + Select Case LinkCheckResult(linkAdr) + Case RV_LINK_FIXED: + FixHyperlinksIn.modCount_ = FixHyperlinksIn.modCount_ + 1 + Dim linkRange As Word.Range: Set linkRange = aLink.Range + If linkRange.Font.ColorIndex = wdRed Then _ + linkRange.Font.ColorIndex = wdAuto + If linkRange.HighlightColorIndex = wdTurquoise Then _ + linkRange.HighlightColorIndex = wdAuto + + If IsSamePath(linkRange.Text, SafeGetHLinkAddress(aLink), target.Document.Path) Then + If Left(linkRange.Text, 2) = "\\" Then + linkRange.Text = ConvertLinkToLocal(linkAdr) + Else + linkRange.Text = ConvertLinkToURL(linkAdr) + End If + End If + + linkRange.Text = linkRange.Text + Call ActiveDocument.Hyperlinks.Add(linkRange, linkAdr) + + Case RV_LINK_NOFIX: + FixHyperlinksIn.errCount_ = FixHyperlinksIn.errCount_ + 1 + If aLink.Range.HighlightColorIndex <> wdTurquoise Then + aLink.Range.HighlightColorIndex = wdTurquoise + End If + End Select + +NEXT_LINK: + Call CSE_ProgressBar.IncrementA + Next aLink + + Call Unload(CSE_ProgressBar) +End Function + +Public Function TryFixingParagraph(ByRef target As Word.Range) + Dim nLine&: nLine = target.ComputeStatistics(wdStatisticLines) + Dim newLine& + Dim oldSpace As Double: oldSpace = target.Font.Spacing + Dim dSpace As Double: dSpace = oldSpace + + Do While dSpace >= -PAR_SCALE_MAX + On Error GoTo ERR_H + target.Font.Spacing = dSpace + GoTo ERR_NEXT +ERR_H: + oldSpace = 0 + dSpace = 0 + target.Font.Spacing = dSpace +ERR_NEXT: + + newLine = target.ComputeStatistics(wdStatisticLines) + If newLine < nLine Then _ + Exit Function + + dSpace = dSpace - PAR_SCALE_STEP + Loop + + target.Font.Spacing = oldSpace + + Dim colWid As Double, finC As Double + Dim finRng As Word.Range, curSel As Word.Range + + Set curSel = target.Application.Selection.Range.Duplicate + + Set finRng = target.Duplicate + Call finRng.MoveEnd(wdCharacter, -1) + Call finRng.Collapse(wdCollapseEnd) + Call finRng.Select + + ' , + On Error GoTo RET_SEL + colWid = target.PageSetup.TextColumns(1).Width + On Error GoTo 0 + + Do While dSpace <= PAR_SCALE_MAX + On Error GoTo ERR_V + target.Font.Spacing = dSpace + GoTo ERR_NEW +ERR_V: + oldSpace = 0 + dSpace = 0 + target.Font.Spacing = dSpace +ERR_NEW: + + newLine = target.ComputeStatistics(wdStatisticLines) + If newLine < nLine Then _ + GoTo RET_SEL + If newLine > nLine Then _ + Exit Do + + finC = target.Application.Selection.Information(wdHorizontalPositionRelativeToTextBoundary) + If finC / colWid >= 0.5 Then _ + GoTo RET_SEL + + dSpace = dSpace + PAR_SCALE_STEP + Loop + + target.Font.Spacing = oldSpace + +RET_SEL: + Call curSel.Select +End Function + +Public Function LowerReference(execField As Word.Field) + If execField.Type <> wdFieldRef Then _ + Exit Function + + With execField + Dim codeStr$: codeStr = .Code + Dim fSlash&: fSlash = InStr(codeStr, "\") + fSlash = IIf(fSlash = 0, Len(codeStr), fSlash) + codeStr = Left(codeStr, fSlash - 1) & "\* Lower \h }" + + .Code.Text = codeStr + .Update + + .result.Italic = True + .result.Bold = True + End With +End Function + +Public Function RemoveAccentsIn(target As Word.Range) As Long + Dim nCount&: nCount = 0 + Dim aWord As Word.Range + For Each aWord In target.Words + Dim sInitial$: sInitial = aWord.Text + Dim sFixed$: sFixed = VBA.Replace(sInitial, ChrW(769), "") + If VBA.Len(sInitial) > VBA.Len(sFixed) Then + nCount = nCount + VBA.Len(sInitial) - VBA.Len(sFixed) + aWord = sFixed + End If + Next aWord + RemoveAccentsIn = nCount +End Function + +Public Function UpdateConceptPowershell() As Boolean + UpdateConceptPowershell = False + + Dim fso As New Scripting.FileSystemObject + Dim sInstall$: sInstall = CP_TOOLS_SERVER & "\" & CONCEPT_INSTALLER + If Not fso.FileExists(sInstall) Then _ + Exit Function + + On Error GoTo RETURN_FALSE + Dim sExec$: sExec = "cmd.exe /c " & """" & sInstall & """" & " < nul" + Dim iShell As New WshShell + iShell.CurrentDirectory = CP_TOOLS_SERVER + UpdateConceptPowershell = iShell.Run(sExec, waitOnReturn:=True) = 0 + Exit Function + +RETURN_FALSE: + On Error GoTo 0 +End Function + +' ============= +Private Function LinkCheckResult(ByRef rawLnk$) As LMU_Return +' ( ) + Dim rulemap As LinkMappingUnit: Set rulemap = GetLMU + Dim sResult$: sResult = rawLnk + LinkCheckResult = rulemap.FixLink(sResult) + If LinkCheckResult = RV_LINK_FIXED Then _ + rawLnk = sResult +End Function + +Private Function TryProcessAddin(sLocal$, sServer$) As Boolean + TryProcessAddin = False + + Dim fso As New Scripting.FileSystemObject + If Not fso.FileExists(sServer) Then _ + Exit Function + + Call fso.CopyFile(sServer, sLocal) + TryProcessAddin = True +End Function diff --git a/src/PublicSubs.bas b/src/PublicSubs.bas new file mode 100644 index 0000000..76d80f2 --- /dev/null +++ b/src/PublicSubs.bas @@ -0,0 +1,144 @@ +Attribute VB_Name = "PublicSubs" +Option Explicit + +Public Sub CCM_AddColumnBreak() + Dim selPar As Word.Range: Set selPar = ActiveDocument.ActiveWindow.Selection.Paragraphs.First.Range + Call selPar.Collapse(wdCollapseStart) + Call selPar.InsertBreak(wdColumnBreak) +End Sub + +Public Sub CCM_NonBreakPar() + ActiveDocument.ActiveWindow.Selection.ParagraphFormat.KeepTogether = Not ActiveDocument.ActiveWindow.Selection.ParagraphFormat.KeepTogether +End Sub + +Public Sub CCM_WithNext() + Selection.ParagraphFormat.KeepWithNext = Not Selection.ParagraphFormat.KeepWithNext +End Sub + +Public Sub CCM_FieldLowerer() + Dim tmpRange As Word.Range: Set tmpRange = ActiveDocument.ActiveWindow.Selection.Range.Duplicate + tmpRange.Start = ActiveDocument.ActiveWindow.Selection.Words.First.Start + tmpRange.End = ActiveDocument.ActiveWindow.Selection.Words.Last.End + + On Error GoTo EXT_SUB + Dim execField As Word.Field + For Each execField In tmpRange.Fields + Call bilCrossRef(execField) + Next execField +EXT_SUB: +End Sub + +Public Sub CCM_MakeUniqueBM() +' + Dim contextMap As New Collection + + Dim pref$: pref = "t" & Format(Date, "yyyymmdd") & Replace(Time, ":", "") + Dim i& + For i = ActiveDocument.Bookmarks.Count To 1 Step -1 ' + Dim aMark As Word.BookMark: Set aMark = ActiveDocument.Bookmarks(i) + Dim bmRange As Word.Range: Set bmRange = aMark.Range + Dim newName$: newName = pref & "_" & aMark.Name + + ' + If Len(newName) > 40 Then _ + GoTo NEXT_BM + + Call contextMap.Add(newName, aMark.Name) + Call bmRange.Bookmarks.Add(pref & "_" & aMark.Name) + Call aMark.Delete +NEXT_BM: + Next i + + ' + Dim newAddr$ + For i = ActiveDocument.Hyperlinks.Count To 1 Step -1 + Dim aLink As Word.Hyperlink: Set aLink = ActiveDocument.Hyperlinks(i) + Dim linkRange As Word.Range: Set linkRange = ActiveDocument.Range(aLink.Range.Start, aLink.Range.End) + If Not aLink.Address = "" Or aLink.SubAddress = "" Then _ + GoTo NEXT_LINK + If Not InCollection(aLink.SubAddress, contextMap) Then _ + GoTo NEXT_LINK + + newAddr = contextMap(aLink.SubAddress) + Call aLink.Delete + Call linkRange.Hyperlinks.Add(linkRange, "", newAddr) + +NEXT_LINK: + Next i +End Sub + +Public Sub CCM_ConvertAllShapesToText() +' + Dim theDoc As Word.Document: Set theDoc = ActiveDocument + Dim tRange As Word.Range: Set tRange = theDoc.Range + + Dim nShape&: nShape = 1 + Dim theText$ + + Dim aShape As Word.Shape + Do While nShape <= theDoc.Shapes.Count ' + Set aShape = theDoc.Shapes(nShape) + If Not aShape.Anchor.InRange(tRange) Then _ + GoTo NEXT_SHAPE + If aShape.Type <> msoTextBox Then _ + GoTo NEXT_SHAPE + + theText = aShape.TextFrame.TextRange.Text + + aShape.TextFrame.TextRange.Copy + tRange.Paragraphs.Add + Call tRange.Paragraphs.Last.Range.Paste + +NEXT_SHAPE: + nShape = nShape + 1 + Loop + + Call UserInteraction.ShowMessage(IM_SHAPES_TO_TEXT_OK) +End Sub + +Public Sub CC_Convert1252_1251() +' + Dim theDoc As Word.Document: Set theDoc = ActiveDocument + theDoc.ActiveWindow.View.ReadingLayout = False + + Dim inputR As Word.Range: Set inputR = theDoc.Application.Selection.Range + + Dim wordUI As New API_WordWrapper: Call wordUI.SetDocument(theDoc) + Call wordUI.PauseUI + + Call CSE_ProgressBar.Init(" 1252 -> 1251", sHeader:=" ...", maxVal:=inputR.Characters.Count) + Call CSE_ProgressBar.ShowModeless + + Dim aChr As Word.Range: Set aChr = inputR.Characters.First + Do While aChr.End < inputR.End + Dim chrW&: chrW = VBA.AscW(aChr) + If chrW < 256 And chrW > 127 Then _ + aChr.Text = VBA.Chr(chrW) + Set aChr = aChr.Next(wdCharacter, 1) + Call CSE_ProgressBar.IncrementA + Loop + + Call Unload(CSE_ProgressBar) + Call wordUI.ResumeUI + Call UserInteraction.ShowMessage(IM_CONVERT_CODEPAGE_OK) +End Sub + +' ============ +Private Function bilCrossRef(execField As Word.Field) + If execField.Type <> wdFieldRef Then _ + Exit Function + + With execField + Dim codeStr$: codeStr = .code + Dim fSlash&: fSlash = InStr(codeStr, "\") + fSlash = IIf(fSlash = 0, Len(codeStr), fSlash) + codeStr = Left(codeStr, fSlash - 1) & "\* Lower \h }" + + .code.Text = codeStr + .Update + + .result.Italic = True + .result.Bold = True + End With +End Function + diff --git a/src/RulesAccess.bas b/src/RulesAccess.bas new file mode 100644 index 0000000..7ae9353 --- /dev/null +++ b/src/RulesAccess.bas @@ -0,0 +1,137 @@ +Attribute VB_Name = "RulesAccess" +' Grant access to document processing rules +Option Explicit + +Public Function AccessProcessor() As RulesProcessor + Static s_Processor As RulesProcessor + If s_Processor Is Nothing Then _ + Set s_Processor = New RulesProcessor + Set AccessProcessor = s_Processor +End Function + +Public Function InitProcessor(sDocument$, sConfigJSON$) As Boolean + On Error GoTo RETURN_FALSE + Dim iWrap As New API_WordWrapper + If iWrap.OpenDocument(sDocument) Is Nothing Then _ + GoTo RETURN_FALSE + + Dim iConfig As New API_Config: Call iConfig.LoadFromJSON(sConfigJSON) + Call AccessProcessor.Init(iWrap.Document, iConfig) + InitProcessor = True + Exit Function + +RETURN_FALSE: + InitProcessor = False +End Function + +Public Function RunRule(sRuleID$, Optional bApplyFix As Boolean = True) As Long() + Dim iResult() As Long + ReDim iResult(0 To 2) + + Dim iProcessor As RulesProcessor: Set iProcessor = AccessProcessor + + On Error GoTo RETURN_ERROR + Call iProcessor.ResetCounters + Call iProcessor.RunRule(sRuleID, bApplyFix) + On Error GoTo 0 + + iResult(0) = 0 + iResult(1) = iProcessor.errors_ + iResult(2) = iProcessor.fixes_ + RunRule = iResult + Exit Function + +RETURN_ERROR: + iResult(0) = Err.Number + RunRule = iResult +End Function + +Public Function FinalizeRulesProcessing() + Call AccessProcessor.FinalizeProcessing +End Function + +Public Function EmptyEditConfig() As API_Config + Set EmptyEditConfig = New API_Config + With EmptyEditConfig + Call .SetValue("HighlightErrors", False) + Call .SetValue("CommentUnresolved", True) + Call .SetValue("TrackRevisions", False) + Call .SetValue("ExcludeRules", New Collection) + Call .SetValue("BannedWords", New Collection) + End With +End Function + +Public Function DefaultEditConfig() As API_Config + Set DefaultEditConfig = New API_Config + With DefaultEditConfig + Call .SetValue("HighlightErrors", False) + Call .SetValue("CommentUnresolved", True) + Call .SetValue("TrackRevisions", True) + Call .SetValue("ExcludeRules", New Collection) + Call .SetValue("BannedWords", LoadBannedWords) + End With +End Function + +Public Function LoadEditConfig() As API_Config + Dim iConfig As API_Config + Dim fso As New Scripting.FileSystemObject + Dim sFile$: sFile = VBA.Environ$("USERPROFILE") & "\" & CONCEPT_LOCAL_HOME & "\" & CONCEPT_MASTER_CONFIG + If fso.FileExists(sFile) Then + Set iConfig = New API_Config + Call iConfig.LoadFromFile(sFile) + Else + Set iConfig = DefaultEditConfig + Call iConfig.RemoveKey("BannedWords") + Call iConfig.SaveToFile(sFile) + End If + Call iConfig.SetValue("BannedWords", LoadBannedWords) + Set LoadEditConfig = iConfig +End Function + +Public Function OpenMasterConfig() + Dim fso As New Scripting.FileSystemObject + Dim sFile$: sFile = VBA.Environ$("USERPROFILE") & "\" & CONCEPT_LOCAL_HOME & "\" & CONCEPT_MASTER_CONFIG + If Not fso.FileExists(sFile) Then + Dim iConfig As API_Config: Set iConfig = DefaultEditConfig + Call iConfig.RemoveKey("BannedWords") + Call iConfig.SaveToFile(sFile) + End If + Dim oShell As New Shell32.Shell + Call oShell.Open(sFile) +End Function + +' ======= +Private Function LoadBannedWords() As Collection + Dim result As New Collection + Dim sLocal$: sLocal = VBA.Environ$("USERPROFILE") & "\" & CONCEPT_LOCAL_HOME & "\models\" & CONCEPT_BANNED_WORDS + Dim fso As New Scripting.FileSystemObject + If Not fso.FileExists(sLocal) Then + If Not TryUpdateBannedWords() Then + Call UserInteraction.ShowMessage(EM_MISSING_FILE, sLocal) + GoTo RETURN_EMPTY + End If + End If + + Dim adoStream As New ADODB.Stream + adoStream.Charset = "utf-8" + Call adoStream.Open + Call adoStream.LoadFromFile(sLocal) + Dim sLines() As String: sLines = VBA.Split(adoStream.ReadText, vbCrLf) + Call adoStream.Close + + Dim nLine& + For nLine = LBound(sLines, 1) To UBound(sLines, 1) Step 1 + Dim sText$: sText = sLines(nLine) + If sText <> vbNullString Then _ + Call result.Add(sText) + Next nLine + +RETURN_EMPTY: + Set LoadBannedWords = result +End Function + +Private Function TryUpdateBannedWords() As Boolean + Dim sServer$: sServer = CONCEPT_SERVER_HOME & CONCEPT_BANNED_WORDS + Dim sLocal$: sLocal = VBA.Environ$("USERPROFILE") & "\" & CONCEPT_LOCAL_HOME & "\models\" & CONCEPT_BANNED_WORDS + TryUpdateBannedWords = CopyFileOrFolder(sSource:=sServer, sDestination:=sLocal) +End Function diff --git a/src/RulesProcessor.cls b/src/RulesProcessor.cls new file mode 100644 index 0000000..8e7b098 --- /dev/null +++ b/src/RulesProcessor.cls @@ -0,0 +1,1162 @@ +VERSION 1.0 CLASS +BEGIN + MultiUse = -1 'True +END +Attribute VB_Name = "RulesProcessor" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = False +Attribute VB_Exposed = False +Option Explicit + +Private Const SYMBOL_QUOTE$ = """" +Private Const SYMBOL_LETTER$ = "a-zA-Z--߸" +Private Const SYMBOL_DIGIT$ = "0-9" + +Private Const MASK_LETTER$ = "[" & SYMBOL_LETTER & "]" +Private Const MASK_DIGIT$ = "[" & SYMBOL_DIGIT & "]" +Private Const MASK_ALPHANUM = "[" & SYMBOL_LETTER & SYMBOL_DIGIT & "]" +Private Const MASK_ROMAN_DIGIT$ = "[ivxlcdmIVXLCDM]" + +Private Const MASK_QUOTE$ = "[" & SYMBOL_QUOTE & "]" + +Private Const NEW_PARAGRAPH = -1 + +' +Private Const AR1019_LINES_PER_PAR = 10 + +Private Const TEXT_SPELLING_STATS = "{1}" & vbNewLine & " : {2}" & vbNewLine & " : {3}" + +Private editor_ As DocumentEditor +Private config_ As API_Config +Private applyFix_ As Boolean + +Public errors_ As Long +Public fixes_ As Long + +Private Sub Class_Initialize() + Call ResetCounters +End Sub + +Public Property Get RuleCount() As Long + RuleCount = 30 +End Property + +Public Function Init(iDoc As Word.Document, iConfig As API_Config) + Set editor_ = New DocumentEditor + Call editor_.Init(iDoc, iConfig) + Set config_ = iConfig + Call ResetCounters +End Function + +Public Function ResetCounters() + errors_ = 0 + fixes_ = 0 +End Function + +Public Function RunAll(iExclude As Scripting.Dictionary, bApplyFix As Boolean) + applyFix_ = bApplyFix + Call RunRulesInternal(iExclude) + Call FinalizeProcessing +End Function + +Public Function RunRule(sRuleID$, bApplyFix As Boolean) + Dim sFunction$: sFunction = "Rule" & VBA.Right(sRuleID, VBA.Len(sRuleID) - 2) + applyFix_ = bApplyFix + Call CallByName(Me, sFunction, VbMethod) +End Function + +Public Function FinalizeProcessing() + Call editor_.FinalizeComments +End Function + +Public Function Rule1001() + Const RULE_DESCRIPTION$ = "AR1001: / " + + Dim rFind As Word.Range: Set rFind = editor_.SetupFindLiteral("^p ") + Do While rFind.Find.Execute + Call rFind.MoveEndWhile(CSet:=" " & Chr(160), Count:=wdForward) + + If Not applyFix_ Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION$) + Else + rFind.Start = rFind.Start + 1 + rFind.Text = vbNullString + End If + + Call rFind.Collapse(wdCollapseEnd) + Call Increment(applyFix_) + Loop + + Set rFind = editor_.SetupFindLiteral(" ^p") + Do While rFind.Find.Execute + Call rFind.MoveStartWhile(CSet:=" " & Chr(160), Count:=wdBackward) + If Not applyFix_ Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION$) + Else + rFind.End = rFind.End - 1 + rFind.Text = vbNullString + End If + + Call rFind.Collapse(wdCollapseEnd) + Call Increment(applyFix_) + Loop + + Set rFind = editor_.SetupFindLiteral("^t^p") + Do While rFind.Find.Execute + Call rFind.MoveStartWhile(CSet:=" " & Chr(160) & vbTab, Count:=wdBackward) + If Not applyFix_ Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION$) + Else + rFind.End = rFind.End - 1 + rFind.Text = vbNullString + End If + + Call rFind.Collapse(wdCollapseEnd) + Call Increment(applyFix_) + Loop +End Function + +Public Function Rule1002() + Const RULE_DESCRIPTION$ = "AR1002: " + + Dim rFind As Word.Range: Set rFind = editor_.SetupFindLiteral(" ") + Do While rFind.Find.Execute + Call rFind.MoveEndWhile(CSet:=" ", Count:=wdForward) + If Not applyFix_ Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION) + Else + rFind.Start = rFind.Start + 1 + rFind.Text = vbNullString + End If + + Call rFind.Collapse(wdCollapseEnd) + Call Increment(applyFix_) + Loop +End Function + +Public Function Rule1003() + Const RULE_DESCRIPTION$ = "AR1003: " + + Dim rFind As Word.Range: Set rFind = editor_.SetupFindLiteral("^p^p") + Do While rFind.Find.Execute + If Not applyFix_ Or rFind.End - rFind.Start <> 2 Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION) + Call IncrementErrors + Else + Call rFind.MoveEndWhile(CSet:=vbNewLine, Count:=wdForward) + rFind.End = rFind.End - 1 ' Note: if Start is adjusted, then will have problems with end of document + rFind.Text = vbNullString + Call Increment(doBoth:=True) + End If + + Call rFind.Collapse(wdCollapseEnd) + Loop +End Function + +Public Function Rule1004() + Const RULE_DESCRIPTION$ = "AR1004: -" + + Dim result&: result = 0 + result = result + editor_.ReplaceText("+/-", "", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceText("+ / -", "", applyFix_, RULE_DESCRIPTION) + Call Increment(applyFix_, result) +End Function + +Public Function Rule1005() + Const RULE_DESCRIPTION$ = "AR1005: " + + Call Increment(applyFix_, editor_.ReplaceCascade("[ ^0160][-^0150][ ^0160]", "[-^0150]", Chr(151), applyFix_, RULE_DESCRIPTION, bWildcard:=True)) +End Function + +Public Function Rule1006() + Const RULE_DESCRIPTION$ = "AR1006: " + + Dim result&: result = 0 + result = result + editor_.ReplaceCascade(MASK_LETTER & "^0151" & MASK_LETTER, "^0151", "-", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade(MASK_LETTER & "^0150" & MASK_LETTER, "^0150", "-", applyFix_, RULE_DESCRIPTION) + Call Increment(applyFix_, result) +End Function + +Public Function Rule1007() + Const RULE_DESCRIPTION$ = "AR1007: " + + Dim result&: result = 0 + result = result + editor_.ReplaceCascade(". [0-9]", " ", Chr(160), applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[]>. [0-9]", " ", Chr(160), applyFix_, RULE_DESCRIPTION) + Call Increment(applyFix_, result) +End Function + +Public Function Rule1013() + Const RULE_DESCRIPTION$ = "AR1013: " + + Dim rFind As Word.Range: Set rFind = editor_.SetupFindLiteral("=") + Do While rFind.Find.Execute(Replace:=False) + If rFind.OMaths.Count = 0 Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION) + Call IncrementErrors + End If + Call rFind.Collapse(wdCollapseEnd) + Loop +End Function + +Public Function Rule1014() + Const RULE_DESCRIPTION$ = "AR1014: " + + Dim result&: result = 0 + Dim sReplacement$: sReplacement = "^0160^0160" + + result = result + editor_.ReplaceWC(" [ ^0160]", sReplacement, applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceWC("[ ^0160] ", sReplacement, applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceWC("[ ^0160][ ^0160][""][""]", sReplacement, applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceWC("[ ^0160][ ^0160][""][""]", sReplacement, applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceWC("[ ^0160]" & MASK_QUOTE & "[ ^0160]" _ + & MASK_QUOTE & "" & MASK_QUOTE & MASK_QUOTE, sReplacement, applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceWC("[ ^0160]" & MASK_QUOTE & "[ ^0160]" _ + & MASK_QUOTE & "" & MASK_QUOTE & MASK_QUOTE, sReplacement, applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceWC("[ ^0160][ ^0160]һ", sReplacement, applyFix_, RULE_DESCRIPTION) + + Call Increment(applyFix_, result) +End Function + +Public Function Rule1016() + Const RULE_DESCRIPTION$ = "AR1016: . . " + +' TODO: Refactor + Dim newText$, leftC$, rightC$ + Dim leftWord As Word.Range, rightWord As Word.Range + Dim leftFlag As Boolean, rightFlag As Boolean + + Dim rFind As Word.Range + Set rFind = editor_.SetupFindWildcard("[-].[ ^0160][-].") + Do While rFind.Find.Execute + leftC = rFind.Previous(wdCharacter, 1).Text + rightC = rFind.Next(wdCharacter, 1).Text + + If leftC Like "[--0-9]" Then _ + GoTo FIND_NEXT + + ' + Set leftWord = rFind.Previous(wdWord, 1) + Set rightWord = rFind.Next(wdWord, 1) + Call rightWord.MoveEndWhile(CSet:=" ^t", Count:=wdBackward) ' + + leftFlag = False + rightFlag = False + If rightC Like "[-]" Then + If Not rFind.Next(wdCharacter, 2) Like "[-]" Then _ + GoTo FIND_NEXT + + Set rightWord = rFind.Next(wdCharacter, 3) + Do While rightWord.Text Like "[-]" + Set rightWord = rightWord.Next(wdCharacter, 1) ' + Loop + + rightFlag = True + rightWord.Start = rFind.End + 2 + rightWord.End = rightWord.End - 1 + Else + If Not leftC = Chr(160) And Not rightC = Chr(160) Then + leftFlag = RangeIsSurname(leftWord) + If rightC Like "[;,:?!.]" Then + rightFlag = False + Else + rightFlag = RangeIsSurname(rightWord) + End If + Else + If leftC = Chr(160) Then + Set leftWord = rFind.Previous(wdCharacter, 2) + Do While leftWord.Text Like "[-]" + Set leftWord = leftWord.Previous(wdCharacter, 1) ' + Loop + + leftFlag = leftWord.Start < rFind.Start - 2 And leftWord.Text Like "[-]" + leftWord.End = rFind.Start - 1 + End If + If rightC = Chr(160) And rFind.Next(wdCharacter, 2) Like "[-]" And _ + rFind.Next(wdCharacter, 3) Like "[-]" Then + Set rightWord = rFind.Next(wdCharacter, 4) + Do While rightWord.Text Like "[-]" + Set rightWord = rightWord.Next(wdCharacter, 1) ' + Loop + + ' + If rFind.Characters(3) = Chr(160) Then _ + GoTo FIND_NEXT + + rightFlag = True + rightWord.Start = rFind.End + 2 + rightWord.End = rightWord.End - 1 + End If + End If + End If + + If Not leftFlag And Not rightFlag Then _ + GoTo FIND_NEXT + + If Not applyFix_ Or (leftFlag And rightFlag) Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION) + Call IncrementErrors + GoTo FIND_NEXT + End If + + newText = rFind.Text + newText = Left(newText, 2) & Chr(160) & Right(newText, 2) & Chr(160) + If leftFlag Then ' .. + rFind.Start = leftWord.Start + + newText = newText & Trim(leftWord.Text) + rFind.Text = newText + Else ' .. + rFind.Start = rFind.Start + rFind.End = rightWord.End + + newText = newText & Trim(rightWord.Text) + End If + + If rFind.Fields.Count > 0 Then _ + GoTo FIND_NEXT + + rFind.Text = newText + Call Increment(doBoth:=True) + +FIND_NEXT: + Call rFind.Collapse(wdCollapseEnd) + Loop + + Set rFind = editor_.SetupFindWildcard("[-].[-].") + Do While rFind.Find.Execute + ' + leftC = rFind.Previous(wdCharacter, 1).Text + rightC = rFind.Next(wdCharacter, 1).Text + + If leftC Like "[--0-9]" Then _ + GoTo FIND_NEXT2 + + ' + Set leftWord = rFind.Previous(wdWord, 1) + Set rightWord = rFind.Next(wdWord, 1) + Call rightWord.MoveEndWhile(CSet:=" ^t", Count:=wdBackward) ' + + leftFlag = False + rightFlag = False + + If rightC Like "[-]" Then + If Not rFind.Next(wdCharacter, 2) Like "[-]" Then _ + GoTo FIND_NEXT2 + + Set rightWord = rFind.Next(wdCharacter, 3) + Do While rightWord.Text Like "[-]" + Set rightWord = rightWord.Next(wdCharacter, 1) ' + Loop + + rightFlag = True + rightWord.Start = rFind.End + rightWord.End = rightWord.End - 1 + Else + If Not leftC = Chr(160) And Not rightC = Chr(160) Then + leftFlag = RangeIsSurname(leftWord) + If rightC Like "[;,:?!.]" Then + rightFlag = False + Else + rightFlag = RangeIsSurname(rightWord) + End If + Else + If leftC = Chr(160) Then + Set leftWord = rFind.Previous(wdCharacter, 2) + Do While leftWord.Text Like "[-]" + Set leftWord = leftWord.Previous(wdCharacter, 1) ' + Loop + + leftFlag = leftWord.Start < rFind.Start - 2 And leftWord.Text Like "[-]" + leftWord.End = rFind.Start - 2 + End If + If rightC = Chr(160) And rFind.Next(wdCharacter, 2) Like "[-]" And _ + rFind.Next(wdCharacter, 3) Like "[-]" Then + Set rightWord = rFind.Next(wdCharacter, 4) + Do While rightWord.Text Like "[-]" + Set rightWord = rightWord.Next(wdCharacter, 1) ' + Loop + + rightFlag = True + rightWord.Start = rFind.End + 1 + rightWord.End = rightWord.End - 1 + End If + End If + End If + + If Not leftFlag And Not rightFlag Then _ + GoTo FIND_NEXT2 + + If Not applyFix_ Or (leftFlag And rightFlag) Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION) + Call IncrementErrors + GoTo FIND_NEXT2 + End If + + newText = rFind.Text + newText = Left(newText, 2) & Chr(160) & Right(newText, 2) & Chr(160) + If leftFlag Then ' .. + rFind.Start = leftWord.Start + + newText = newText & Trim(leftWord.Text) + rFind.Text = newText + Else ' .. + rFind.Start = rFind.Start + rFind.End = rightWord.End + + newText = newText & Trim(rightWord.Text) + End If + + If rFind.Fields.Count > 0 Then _ + GoTo FIND_NEXT2 + + rFind.Text = newText + Call Increment(doBoth:=True) + +FIND_NEXT2: + Call rFind.Collapse(wdCollapseEnd) + Loop +End Function + +Public Function Rule1017() + Const RULE_DESCRIPTION$ = "AR1017: " + + Dim rFind As Word.Range: Set rFind = editor_.SetupFindWildcard(MASK_QUOTE) + rFind.Find.Forward = False ' ! + Dim nLevel&: nLevel = 0 + Dim innerQuote As Boolean: innerQuote = False + Do While rFind.Find.Execute + Dim nextC$: nextC = rFind.Next(wdCharacter, 1).Text + Dim prevC$: prevC = rFind.Previous(wdCharacter, 1).Text + If nextC = "_" Or prevC = "_" Then _ + GoTo NEXT_FIND ' + + Dim quoteChar$ + If IsQuoteOpen(nextC, prevC, nLevel) Then + nLevel = nLevel - 1 + quoteChar = IIf(innerQuote, "", "") + innerQuote = False + If nextC Like "[]" Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION & vbNewLine & " ") + Call IncrementErrors + End If + Else + nLevel = nLevel + 1 + If innerQuote Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION & vbNewLine & " ") + Call IncrementErrors + End If + If nextC Like MASK_QUOTE Then + innerQuote = True + quoteChar = ChrW(8220) + Else + quoteChar = "" + End If + End If + + If nLevel < 0 Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION & vbNewLine & "- ") + Call IncrementErrors + nLevel = 0 + ElseIf nLevel > 3 Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION & vbNewLine & " > 3") + Call IncrementErrors + nLevel = nLevel - 1 + End If + + If rFind.Text <> quoteChar Then + If Not applyFix_ Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION & vbNewLine & " ") + Else + rFind.Text = quoteChar + End If + Call Increment(applyFix_) + End If +NEXT_FIND: + Loop + + If nLevel <> 0 Then + Call editor_.AddGlobalComment(RULE_DESCRIPTION & vbNewLine & " : " & nLevel) + Call IncrementErrors + End If +End Function + +Private Function IsQuoteOpen(sNextChar$, sPrevChar$, nLevel&) As Boolean + If sNextChar Like MASK_ALPHANUM Then + IsQuoteOpen = True + ElseIf sNextChar = Chr(13) Then + IsQuoteOpen = False + ElseIf sPrevChar = Chr(13) Then + IsQuoteOpen = True + ElseIf sPrevChar Like "[().;!\?" & Chr(133) & SYMBOL_DIGIT & SYMBOL_LETTER & SYMBOL_QUOTE & "]" Then + IsQuoteOpen = False + Else + IsQuoteOpen = nLevel > 0 + End If +End Function + +Public Function Rule1018() + Const RULE_DESCRIPTION$ = "AR1018: " + + Dim aCell As Word.Cell, aRow As Word.Row, aTable As Word.Table + Dim theWord As Word.Range + Dim tableRange As Word.Range + Dim prevWord$ + For Each aTable In editor_.Document.Tables + Set tableRange = aTable.Range + For Each aCell In tableRange.Cells + If aCell.Range.Words.Count <= 1 Then _ + GoTo NEXT_CELL + + Set theWord = aCell.Range.Words(aCell.Range.Words.Count - 1) + If theWord Is Nothing Then _ + GoTo NEXT_CELL + + Dim lastword$: lastword = theWord.Text + If Not lastword Like "*[\!\?;.," & Chr(13) & "]" Then _ + GoTo NEXT_CELL + + If lastword = "." Then + prevWord = theWord.Previous(wdWord, 1).Text + If prevWord = "" Or prevWord = "" Or prevWord = "" Or prevWord = "" _ + Or prevWord = "" Or prevWord = "" Or prevWord = "" Or prevWord = "" _ + Then GoTo NEXT_CELL + End If + + Call Increment(applyFix_) + Set theWord = theWord.Characters.Last + + If Not applyFix_ Then + Call editor_.MarkError(aCell.Range, RULE_DESCRIPTION) + GoTo NEXT_CELL + End If + + theWord.Text = vbNullString +NEXT_CELL: + Next aCell + Next aTable +End Function + +Public Function Rule1019() + Const RULE_DESCRIPTION$ = "AR1019: 10 " + + Dim tableOfCont As Word.Range + Dim checkToC As Boolean: checkToC = editor_.Document.TablesOfContents.Count > 0 + If checkToC Then + Set tableOfCont = editor_.Document.TablesOfContents(1).Range + Call tableOfCont.MoveEnd(wdParagraph, 1) + Else + Set tableOfCont = Nothing + End If + + Dim aPar As Word.Paragraph + Dim theRange As Word.Range + For Each aPar In editor_.Document.Paragraphs + Set theRange = aPar.Range + + If theRange.Font.Hidden Then _ + GoTo NEXT_PARAGRAPH + If theRange.Tables.Count > 0 Then _ + GoTo NEXT_PARAGRAPH + If checkToC Then _ + If theRange.InRange(tableOfCont) Then _ + GoTo NEXT_PARAGRAPH + + If theRange.Words.Count < 120 Then _ + GoTo NEXT_PARAGRAPH + If theRange.ComputeStatistics(wdStatisticLines) <= AR1019_LINES_PER_PAR Then _ + GoTo NEXT_PARAGRAPH + + Call editor_.MarkError(theRange, RULE_DESCRIPTION) + Call IncrementErrors +NEXT_PARAGRAPH: + Next aPar +End Function + +Public Function Rule1020() + Const RULE_DESCRIPTION$ = "AR1020: " + + Dim bannedList As Collection: Set bannedList = config_.GetValue("BannedWords") + Dim rFind As Word.Range + Dim sWord As Variant + For Each sWord In bannedList + Set rFind = editor_.Document.Range + With rFind.Find + .MatchWholeWord = True + .Text = CStr(sWord) + .Format = True + .Font.Hidden = False + End With + + Do While rFind.Find.Execute + Call editor_.MarkError(rFind, RULE_DESCRIPTION) + Call IncrementErrors + Call rFind.Collapse(wdCollapseEnd) + Loop + Next sWord +End Function + +Public Function Rule1021() + Const RULE_DESCRIPTION$ = "AR1021: " + + Dim theRange As Word.Range: Set theRange = editor_.Document.Paragraphs.Last.Range + Dim nextRange As Word.Range + + Dim level&, newLevel& + level = 0 + Do While Not theRange Is Nothing + Set nextRange = theRange.Previous(wdParagraph, 1) + + If theRange.Tables.Count > 0 Then _ + GoTo RESET_LEVEL + If Not theRange.ParagraphFormat.OutlineLevel = wdOutlineLevelBodyText Then _ + GoTo RESET_LEVEL + If theRange.Font.Hidden = True Then _ + GoTo RESET_LEVEL + If theRange.Tables.Count > 0 Then _ + GoTo RESET_LEVEL + + newLevel = theRange.ListFormat.ListLevelNumber + If theRange.ListFormat.ListValue = 0 And theRange.ListFormat.ListString = vbNullString Then _ + newLevel = 0 + + If level > newLevel And Not theRange.ParagraphFormat.KeepWithNext Then + If Not applyFix_ Then + Call editor_.MarkError(theRange, RULE_DESCRIPTION) + Else + theRange.ParagraphFormat.KeepWithNext = True + End If + Call Increment(applyFix_) + End If + + level = newLevel + GoTo SKIP_RESET +RESET_LEVEL: + level = 0 +SKIP_RESET: + Set theRange = nextRange + Loop +End Function + +Public Function Rule1022() + Const RULE_DESCRIPTION$ = "AR1022: " + + Dim numLists&: numLists = editor_.Document.ListParagraphs.Count + Dim lastChar$ + Dim theRange As Word.Range + Dim nextRange As Word.Range + Dim nItem& + For nItem = 1 To numLists Step 1 + ' + Set theRange = editor_.Document.ListParagraphs(nItem).Range + If Not theRange.ParagraphFormat.OutlineLevel = wdOutlineLevelBodyText Then _ + GoTo NEXT_LIST_ITEM + If theRange.Tables.Count > 0 Then _ + GoTo NEXT_LIST_ITEM + + Set nextRange = theRange.Next + Call theRange.MoveEndWhile(Chr(13) & Chr(7) & Chr(12) & Chr(14), wdBackward) + If theRange.Start = theRange.End Then _ + GoTo NEXT_LIST_ITEM + lastChar = theRange.Characters.Last + + If IsList(nextRange) Then + If lastChar Like "[.?!,:;]" Then _ + GoTo NEXT_LIST_ITEM + Call IncrementErrors + Call editor_.MarkError(theRange.Words.Last, RULE_DESCRIPTION & vbNewLine & " ") + GoTo NEXT_LIST_ITEM + End If + + If lastChar Like "[.?!]" Then _ + GoTo NEXT_LIST_ITEM + If lastChar Like "[;,]" And Left(nextRange.Text, 1) Like "[a-z-]" Then _ + GoTo NEXT_LIST_ITEM + + If applyFix_ Then + If lastChar Like "[;,]" Then + theRange.Characters.Last = "." + Else + Call theRange.InsertAfter(".") + End If + Else + Call editor_.MarkError(theRange, RULE_DESCRIPTION) + End If + Call Increment(applyFix_) +NEXT_LIST_ITEM: + Next nItem +End Function + +Public Function Rule1023() + Const RULE_DESCRIPTION$ = "AR1023: " + + Dim result&: result = 0 + result = result + editor_.ReplaceCascade("<[].^0032" & MASK_DIGIT, "^0032", "^0160", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[].^0032" & MASK_DIGIT, "^0032", "^0160", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[].^0032" & MASK_DIGIT, "^0032", "^0160", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[].^0032" & MASK_DIGIT, "^0032", "^0160", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[]." & MASK_DIGIT, ".", ".^0160", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[]." & MASK_DIGIT, ".", ".^0160", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[]." & MASK_DIGIT, ".", ".^0160", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[]." & MASK_DIGIT, ".", ".^0160", applyFix_, RULE_DESCRIPTION) + Call Increment(applyFix_, result) +End Function + +Public Function Rule1024() + Const RULE_DESCRIPTION$ = "AR1024: " +' TODO: refactor! + Dim brStack As New Collection + Dim stackItem As BracketItem + Dim openBr As Boolean + Dim brType As BracketType + + Dim i&, totalstack& + + Dim nStartPara&: nStartPara = NEW_PARAGRAPH + Dim firstPar As Word.Range + Dim firstWord$ + + Dim rFind As Word.Range + Set rFind = editor_.SetupFindWildcard("[\{\}\(\)\[\]]") + Do While rFind.Find.Execute + brType = GetBracketType(rFind.Text) 'get bracket type + + ' - ( ) + Set firstPar = rFind.Paragraphs.First.Range + If Not firstPar.Start = nStartPara Then + If firstPar.Words.Count < 2 Or brType <> BT_PAP Then _ + GoTo CONTINUE + If firstPar.Words(2).Start <> rFind.Start Then _ + GoTo CONTINUE + If Not rFind.Next(wdCharacter, 1) Like "[ " & Chr(9) & "]" Then _ + GoTo CONTINUE + + firstWord = firstPar.Words(1) + If firstWord Like MASK_ROMAN_DIGIT & "*" Or firstWord Like MASK_LETTER Or IsNumeric(firstWord) Then _ + GoTo NEXT_BRACKET + +CONTINUE: + If nStartPara = NEW_PARAGRAPH Then _ + GoTo SKIP_STACK_CLEAR + For i = 1 To brStack.Count + Call editor_.MarkError(brStack.Item(i).brRange, RULE_DESCRIPTION & vbNewLine & " ") + Call IncrementErrors + Next i + Set brStack = New Collection + nStartPara = NEW_PARAGRAPH + End If + +SKIP_STACK_CLEAR: + openBr = rFind.Text Like "[\(\{\[]" 'push + If openBr Then + Set stackItem = New BracketItem + Set stackItem.brRange = editor_.Document.Range(rFind.Start, rFind.End) + stackItem.brType = brType + stackItem.brMark = False + Call brStack.Add(stackItem) + + If nStartPara = -1 Then _ + nStartPara = rFind.Paragraphs.First.Range.Start + + GoTo NEXT_BRACKET + End If + + ' , + ' - ( ) + + totalstack = brStack.Count + If totalstack <> 0 Then + Do While Not brStack.Item(totalstack).brType = brType + brStack.Item(totalstack).brMark = True + totalstack = totalstack - 1 + If totalstack = 0 Then _ + Exit Do + Loop + End If + + ' ( ) + If totalstack = 0 Then + Call editor_.MarkError(rFind, RULE_DESCRIPTION & vbNewLine & " ") + Call IncrementErrors + GoTo NEXT_BRACKET + End If + + ' - + If brStack.Item(totalstack).brMark Then + Call editor_.MarkError(brStack.Item(totalstack).brRange, RULE_DESCRIPTION & vbNewLine & " ") + Call IncrementErrors + End If + + ' , ( ) + Call brStack.Remove(totalstack) +NEXT_BRACKET: + Loop + + ' + For i = 1 To brStack.Count + Call editor_.MarkError(brStack.Item(i).brRange, RULE_DESCRIPTION & vbNewLine & " ") + Call IncrementErrors + Next i +End Function + +Public Function Rule1025() + Const RULE_DESCRIPTION$ = "AR1025: " + + Dim theRange As Word.Range, prevRange As Word.Range + Dim firstPar As Word.Range: Set firstPar = editor_.Document.Paragraphs.First.Range + Dim theLvl&, prevLvl& + Dim para As Word.Paragraph + For Each para In editor_.Document.ListParagraphs + Set theRange = para.Range + theLvl = theRange.ListFormat.ListLevelNumber + If InStr(theRange, Chr(7)) Then _ + GoTo NEXT_PARA + + Set prevRange = theRange.Previous(wdParagraph) + On Error Resume Next + If Not theRange.ListFormat.ListString Like "*#*" Or IsRangeHeader(theRange) Then _ + GoTo NEXT_PARA + If prevRange Is Nothing Or IsRangeHeader(prevRange) Or _ + prevRange.ListFormat.List Is Nothing Or _ + theLvl > prevRange.ListFormat.ListLevelNumber Then + + If theRange.ListFormat.ListValue <> 1 Then + Call theRange.Collapse(wdCollapseStart) + Call editor_.MarkError(theRange, RULE_DESCRIPTION & vbNewLine & " ") + Call IncrementErrors + End If + End If + +NEXT_PARA: + Next para +End Function + +Public Function Rule1026() + Const RULE_DESCRIPTION$ = "AR1026: " + + Dim result&: result = 0 + result = result + editor_.ReplaceCascade("<[]." & "^0160" & ".", "." & "^0160" & ".", "" & "^0160" & "", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[]." & "^0032" & ".", "." & "^0032" & ".", "" & "^0160" & "", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[]..", "..", "" & "^0160" & "", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[].[].", ".", ".^0160", applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<[].^0032[].", "^0032", "^0160", applyFix_, RULE_DESCRIPTION) + Call Increment(applyFix_, result) +End Function + +Public Function Rule1027() + Const RULE_DESCRIPTION$ = "AR1027: " + + Dim tmpRef As Word.Field + Dim codeString$ + Dim startPos& + For Each tmpRef In editor_.Document.Fields + + If Not (tmpRef.Type = wdFieldRef And (tmpRef.result Like "[]*" Or tmpRef.result Like "[]*")) Then _ + GoTo SKIP_LOWERING + + If InStr(tmpRef.Code.Text, "\* Lower") = 0 Then + Call IncrementErrors + + If Not applyFix_ Then + Call editor_.MarkError(tmpRef.result, RULE_DESCRIPTION) + GoTo SKIP_LOWERING + End If + + codeString = tmpRef.Code.Text + startPos = InStr(codeString, "_Ref") + Do While Mid(codeString, startPos, 1) <> " " + startPos = startPos + 1 + Loop + codeString = VBA.Left(codeString, startPos) & "\* Lower " & VBA.Mid(codeString, startPos + 1) + tmpRef.Code.Text = codeString + tmpRef.Update + End If +SKIP_LOWERING: + Next tmpRef +End Function + +Public Function Rule1028() + Const RULE_DESCRIPTION$ = "AR1028: " + + Dim iFoot As Word.Footnote + Dim iEnd As Word.Endnote + Dim preRange As Word.Range + For Each iFoot In editor_.Document.Footnotes + Set preRange = iFoot.Reference.Previous(wdCharacter) + If Asc(preRange) = 32 Or Asc(preRange) = 160 Then + Call Increment(applyFix_) + If applyFix_ Then + Call preRange.MoveStartWhile(" " & Chr(160), wdBackward) + Call preRange.Delete + Else + Call editor_.MarkError(preRange, RULE_DESCRIPTION) + End If + End If + Next iFoot + + For Each iEnd In editor_.Document.Endnotes + Set preRange = iEnd.Reference.Previous(wdCharacter) + If Asc(preRange) = 32 Or Asc(preRange) = 160 Then + Call Increment(applyFix_) + If applyFix_ Then + Call preRange.MoveStartWhile(" " & Chr(160), wdBackward) + Call preRange.Delete + Else + Call editor_.MarkError(preRange, RULE_DESCRIPTION) + End If + End If + Next iEnd +End Function + +Public Function Rule1029() + Const RULE_DESCRIPTION$ = "AR1029: /" + + Dim result&: result = 0 + Dim badTail$: badTail = "[" & "^0160" & "^0032" & "]" + Dim repWith$: repWith = "^0160" & "()" + + result = result + editor_.ReplaceCascade("", "/" & badTail, repWith, applyFix_, RULE_DESCRIPTION, bWildcard:=True) + result = result + editor_.ReplaceCascade("<^0032/" & badTail & ">", " /" & badTail, repWith, applyFix_, RULE_DESCRIPTION, bWildcard:=True) + result = result + editor_.ReplaceCascade("", "/", repWith, applyFix_, RULE_DESCRIPTION) + result = result + editor_.ReplaceCascade("<^0032/>", " /", repWith, applyFix_, RULE_DESCRIPTION) + + Call Increment(applyFix_, result) +End Function + +Public Function Rule1098() + Const RULE_DESCRIPTION$ = "AR1098: Word" + + Dim nSpelling&: nSpelling = editor_.Document.Range.SpellingErrors.Count + Dim nGrammar&: nGrammar = editor_.Document.Range.GrammaticalErrors.Count + If nSpelling + nGrammar > 0 Then _ + Call editor_.AddGlobalComment(Fmt(TEXT_SPELLING_STATS, RULE_DESCRIPTION, nSpelling, nGrammar)) + + Call IncrementErrors(nSpelling + nGrammar) +End Function + +Public Function Rule1099() + Const RULE_DESCRIPTION$ = "AR1099: " + + Dim tmp As Boolean: tmp = editor_.Document.TrackRevisions + editor_.Document.TrackRevisions = False + + Dim aField As Word.Field + For Each aField In editor_.Document.Fields + If Not aField.Update Then + If Not aField.result.Font.Hidden Then + Call editor_.MarkError(aField.result, RULE_DESCRIPTION) + Call IncrementErrors + End If + End If + Next aField + + editor_.Document.TrackRevisions = tmp +End Function + +' =========== +Private Function Increment(doBoth As Boolean, Optional nInc& = 1) + errors_ = errors_ + nInc + If doBoth Then _ + fixes_ = fixes_ + nInc +End Function + +Private Function IncrementFixes(Optional nInc& = 1) + fixes_ = fixes_ + nInc +End Function + +Private Function IncrementErrors(Optional nInc& = 1) + errors_ = errors_ + nInc +End Function + +Private Function RangeIsSurname(subject As Word.Range) As Boolean + RangeIsSurname = False + + Dim sStr$: sStr = VBA.Trim(subject.Text) + If sStr Like "*[^0160.]" Then _ + Exit Function + If Not sStr Like "[-]*[-]" Then _ + Exit Function + + RangeIsSurname = True +End Function + +Private Function IsRangeHeader(theRange As Word.Range) As Boolean + If theRange Is Nothing Then + IsRangeHeader = False + Exit Function + End If + IsRangeHeader = theRange.ParagraphFormat.OutlineLevel <> wdOutlineLevelBodyText _ + Or theRange.Style Like "*[]*" +End Function + +Private Function IsList(aRange As Word.Range) As Boolean +' + If aRange Is Nothing Then + IsList = True + Exit Function + End If + IsList = aRange.ListFormat.ListValue = 0 And aRange.ListFormat.ListString = vbNullString And aRange.ListFormat.ListLevelNumber = 0 + IsList = Not IsList +End Function + +Private Function ReplaceAliasDig(targetText$, aText$) As Long +' + Dim replText$: replText = Chr(160) & targetText + Dim result& + result = editor_.ReplaceCascade("([0-9]{2;4})" & targetText, targetText, replText, applyFix_, aText) + result = result + editor_.ReplaceCascade("([0-9]{2;4}) " & targetText, " ", Chr(160), applyFix_, aText) + ReplaceAliasDig = result +End Function + +Private Function RunRulesInternal(iExclude As Scripting.Dictionary) + If Not iExclude.Exists("AR1001") Then Call Rule1001 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1002") Then Call Rule1002 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1003") Then Call Rule1003 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1004") Then Call Rule1004 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1005") Then Call Rule1005 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1006") Then Call Rule1006 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1007") Then Call Rule1007 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1008") Then Call Rule1008 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1009") Then Call Rule1009 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1010") Then Call Rule1010 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1011") Then Call Rule1011 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1012") Then Call Rule1012 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1013") Then Call Rule1013 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1014") Then Call Rule1014 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1016") Then Call Rule1016 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1017") Then Call Rule1017 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1018") Then Call Rule1018 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1019") Then Call Rule1019 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1020") Then Call Rule1020 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1021") Then Call Rule1021 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1022") Then Call Rule1022 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1023") Then Call Rule1023 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1024") Then Call Rule1024 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1025") Then Call Rule1025 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1026") Then Call Rule1026 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1027") Then Call Rule1027 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1028") Then Call Rule1028 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1029") Then Call Rule1029 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1098") Then Call Rule1098 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA + If Not iExclude.Exists("AR1099") Then Call Rule1099 + If CSE_ProgressBar.Visible Then Call CSE_ProgressBar.IncrementA +End Function diff --git a/src/s_RulesProcessor.cls b/src/s_RulesProcessor.cls new file mode 100644 index 0000000..9773d27 --- /dev/null +++ b/src/s_RulesProcessor.cls @@ -0,0 +1,209 @@ +VERSION 1.0 CLASS +BEGIN + MultiUse = -1 'True +END +Attribute VB_Name = "s_RulesProcessor" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = False +Attribute VB_Exposed = False +Option Explicit + +' TODO: test all missing rules + +Private processor_ As RulesProcessor +Private doc_ As Word.Document +Private ruleID_ As String + +Public Function Setup() + Set doc_ = ThisDocument + Set processor_ = New RulesProcessor + Call processor_.Init(doc_, EmptyEditConfig) +End Function + +Public Function Teardown() + Call doc_.Range.Delete +End Function + +Private Function TestText(sInitial$, sExpected$, Optional sMsg$ = vbNullString) + Dim nStart&: nStart = doc_.Range.End + Call doc_.Paragraphs.Add.Range.InsertAfter(sInitial) + + Call processor_.ResetCounters + Call processor_.RunRule(ruleID_, bApplyFix:=True) + + Dim iLastRange As Word.Range: Set iLastRange = doc_.Range.Duplicate + iLastRange.Start = nStart + iLastRange.End = iLastRange.End - 1 + Dim sTest$: sTest = iLastRange.Text + Call doc_.Range.Delete + + Call Dev_ExpectEQ(sExpected, sTest, sMsg) +End Function + +Private Function TestComment(sInitial$, sAnchor$, Optional sComment$ = vbNullString, Optional sClause$ = vbNullString) + If sClause <> "" Then _ + Call Dev_NewClause(sClause) + + Call doc_.Paragraphs.Add.Range.InsertAfter(sInitial) + + Call processor_.RunRule(ruleID_, bApplyFix:=True) + Call processor_.FinalizeProcessing + Call Dev_AssertEQ(1, doc_.Comments.Count, "Comments count") + + Dim iComment As Word.Comment: Set iComment = doc_.Comments(1) + + Dim sText$: sText = iComment.Range.Text + Call Dev_ExpectEQ(sAnchor, iComment.Scope, "Anchor text") + If sComment <> "" Then + Call Dev_ExpectLike(sText, "*" & sComment, "Comment text") + End If + + Call doc_.Range.Delete +End Function + +Public Function t_HiddenText() + On Error GoTo PROPAGATE_ERROR + ruleID_ = "AR1002" + + Dim sText$: sText = "Test1 Test2" + Dim nStart&: nStart = doc_.Range.End + Call doc_.Paragraphs.Add.Range.InsertAfter(sText) + Dim iLastRange As Word.Range: Set iLastRange = doc_.Range.Duplicate + iLastRange.Start = nStart + iLastRange.End = iLastRange.End - 1 + iLastRange.Font.Hidden = True + + Call processor_.RunRule(ruleID_, bApplyFix:=True) + + Set iLastRange = doc_.Range.Duplicate + iLastRange.Start = nStart + iLastRange.End = iLastRange.End - 1 + Dim sTest$: sTest = iLastRange.Text + + Call Dev_ExpectEQ(sText, sTest, "Do not apply rule to hidden text") + + iLastRange.Font.Hidden = False + Call processor_.RunRule(ruleID_, bApplyFix:=True) + + Set iLastRange = doc_.Range.Duplicate + iLastRange.Start = nStart + iLastRange.End = iLastRange.End - 1 + sTest = iLastRange.Text + Call Dev_ExpectNE(sText, sTest, "Apply valid rule to not hidden text") + + Exit Function +PROPAGATE_ERROR: + Call Dev_LogError(Err.Number, Err.Description) +End Function + +Public Function t_ParagraphWhitespace() + On Error GoTo PROPAGATE_ERROR + ruleID_ = "AR1001" + + Call Dev_NewCase("Space in front") + Call TestText(" Test", "Test", "Single space") + Call TestText(" Test", "Test", "Multiple spaces") + Call TestText(Chr(160) & "Test", "Test", "Unbreakable space") + Call TestText(Chr(160) & " " & Chr(160) & "Test", "Test", "Mixed spaces") + Call TestText(vbTab & "Test", vbTab & "Test", "Ignore tabulator") + Call TestText(" " & vbTab & "Test", vbTab & "Test", "Mixed tabulator") + + Call Dev_NewCase("Space in back") + Call TestText("Test ", "Test", "Single space") + Call TestText("Test ", "Test", "Multiple spaces") + Call TestText("Test" & Chr(160), "Test", "Unbreakable space") + Call TestText("Test" & Chr(160) & " " & Chr(160), "Test", "Mixed spaces") + Call TestText("Test" & vbTab, "Test", "Tabulator") + Call TestText("Test" & vbTab & vbTab, "Test", "Multiple tabulator") + Call TestText("Test" & " " & vbTab, "Test", "Mixed tabulator") + + Call Dev_NewCase("Combo spaces") + Call TestText(" Test " & vbTab, "Test") + + Exit Function +PROPAGATE_ERROR: + Call Dev_LogError(Err.Number, Err.Description) +End Function + +Public Function t_DoubleSpace() + On Error GoTo PROPAGATE_ERROR + ruleID_ = "AR1002" + + Call TestText("Test1 test2", "Test1 test2", "Double space") + Call TestText("Test1 " & Chr(160) & "test2", "Test1 test2", "Double unbreakable space") + Call TestText("Test1 test2", "Test1 test2", "Triple space") + Call TestText("Test1" & vbTab & vbTab & "test2", "Test1" & vbTab & vbTab & "test2", "Double tab") + + Call Dev_NewCase("Quadruple space") + Call TestText("Test1 test2", "Test1 test2", "Replace text") + Call Dev_ExpectEQ(1, processor_.errors_, "Error count") + Call Dev_ExpectEQ(1, processor_.errors_, "Fixes count") + + Exit Function +PROPAGATE_ERROR: + Call Dev_LogError(Err.Number, Err.Description) +End Function + +Public Function t_DoubleEndline() + On Error GoTo PROPAGATE_ERROR + ruleID_ = "AR1003" + + Call TestText(vbNewLine & "Test", "Test", "Double endline") + Call TestText("Test" & vbNewLine, "Test", "End of document") + + Call Dev_NewCase("Triple endline") + Call TestText(vbNewLine & vbNewLine & "Test", "Test", "Replace text") + Call Dev_ExpectEQ(1, processor_.errors_, "Error count") + Call Dev_ExpectEQ(1, processor_.errors_, "Fixes count") + + Call Dev_NewCase("Quadruple endline") + Call TestText(vbNewLine & vbNewLine & vbNewLine & "Test", "Test", "Replace text") + Call Dev_ExpectEQ(1, processor_.errors_, "Error count") + Call Dev_ExpectEQ(1, processor_.errors_, "Fixes count") + + Exit Function +PROPAGATE_ERROR: + Call Dev_LogError(Err.Number, Err.Description) +End Function + +Public Function t_PlusMinus() + On Error GoTo PROPAGATE_ERROR + ruleID_ = "AR1004" + + Call TestText("1 +/- 1", "1 1", "Spaces") + Call TestText("1+/-1", "11", "No spaces") + Call TestText("1 + / - 1", "1 1", "Inner spaces") + + Exit Function +PROPAGATE_ERROR: + Call Dev_LogError(Err.Number, Err.Description) +End Function + +Public Function t_Quotes() + On Error GoTo PROPAGATE_ERROR + ruleID_ = "AR1017" + + Call Dev_NewCase("Replace incorrect brackets") + Call TestText("Test", "Test") + Call TestText("""Test""", "Test") + Call TestText("Test", "Test") + Call TestText("Test", "Test") + Call TestText("""""", "") + Call TestText("", "") + + Call Dev_NewCase("Double brackets") + Call TestText(" һ", " ғ") + Call TestText(""" ҫ", " ғ") + Call TestText(" һ INC", " һ INC") + + Call Dev_NewCase("Invalid structure") + Call TestComment("Test", "", sClause:="Missing close bracket") + Call TestComment("Test 12", "", sClause:="Missing close bracket") + Call TestComment("Test", "", sClause:="Missing open bracket") + + Exit Function +PROPAGATE_ERROR: + Call Dev_LogError(Err.Number, Err.Description) +End Function + diff --git a/src/z_UIMessages.bas b/src/z_UIMessages.bas new file mode 100644 index 0000000..1b88709 --- /dev/null +++ b/src/z_UIMessages.bas @@ -0,0 +1,92 @@ +Attribute VB_Name = "z_UIMessages" +' Messaging module +Option Private Module +Option Explicit + +Public Enum MsgCode + EM_HYPERLINK_CREATION_FAIL + EM_HYPERLINKS_MISSING + EM_DOCUMENT_NOT_SAVED + EM_CORE_UPDATE_FAILED + EM_SELECTION_EMPTY + EM_MISSING_FILE + + IM_REMOVE_HYPERLINKS_OK + IM_HYPERLINKS_FIX_OK + IM_REMOVE_BOOKMARKS_OK + IM_REMOVE_IMAGES_OK + IM_SHAPES_TO_TEXT_OK + IM_CONVERT_CODEPAGE_OK + IM_UNIQUE_BOOKMARKS_OK + IM_CONCEPT_UPDATE_OK + IM_BOOKMARK_IGNORED + IM_RULES_COMPLETE + IM_REMOVE_ACCENTS_OK + IM_COMMENT_EXPORT_OK +End Enum + +Private g_UI As API_UserInteraction + +Public Function UserInteraction() As API_UserInteraction + If g_UI Is Nothing Then _ + Set g_UI = New API_UserInteraction + Set UserInteraction = g_UI +End Function + +Public Function SetUserInteraction(newUI As API_UserInteraction) + Set g_UI = newUI +End Function + +Public Function UIShowMessage(theCode As MsgCode, ParamArray params() As Variant) + Dim unwrapped As Variant: unwrapped = params + unwrapped = FixForwardedParams(unwrapped) + + Select Case theCode + Case EM_HYPERLINK_CREATION_FAIL: Call MsgBox(" . ", vbExclamation) + Case EM_HYPERLINKS_MISSING: Call MsgBox(" ", vbInformation) + Case EM_DOCUMENT_NOT_SAVED: Call MsgBox(" " & vbNewLine & " ", vbCritical) + Case EM_SELECTION_EMPTY: Call MsgBox(" . ", vbExclamation) + Case EM_MISSING_FILE: Call MsgBox(Fmt(" : {1}", unwrapped), vbExclamation) + Case EM_CORE_UPDATE_FAILED: Call MsgBox(" ." & vbNewLine _ + & " VPN, (\\fs1.concept.ru)" & vbNewLine _ + & " Excel dll", vbExclamation) + + Case IM_REMOVE_HYPERLINKS_OK: Call MsgBox(Fmt(" : {1}", unwrapped), vbInformation) + Case IM_REMOVE_BOOKMARKS_OK: Call MsgBox(Fmt(" : {1}", unwrapped), vbInformation) + Case IM_REMOVE_IMAGES_OK: Call MsgBox(Fmt(" : {1}", unwrapped), vbInformation) + Case IM_SHAPES_TO_TEXT_OK: Call MsgBox(" ", vbInformation) + Case IM_CONVERT_CODEPAGE_OK: Call MsgBox(" ", vbInformation) + Case IM_UNIQUE_BOOKMARKS_OK: Call MsgBox(" ", vbInformation) + Case IM_REMOVE_ACCENTS_OK: Call MsgBox(Fmt(" : {1}", unwrapped), vbInformation) + Case IM_CONCEPT_UPDATE_OK: Call MsgBox(" ", vbInformation) + Case IM_COMMENT_EXPORT_OK: Call MsgBox(Fmt(" : {1}", unwrapped), vbInformation) + Case IM_BOOKMARK_IGNORED: + Call MsgBox(Fmt(" " & vbNewLine & _ + " {1}", unwrapped), vbInformation) + Case IM_HYPERLINKS_FIX_OK: + Call MsgBox(Fmt(" : {1}" & vbNewLine & _ + " : {2}" & vbNewLine & _ + ": {3}", unwrapped), vbInformation) + Case IM_RULES_COMPLETE + Call MsgBox(Fmt(" : {1} ms" & vbNewLine & vbNewLine & _ + " : {2}" & vbNewLine & _ + " : {3}", unwrapped), vbInformation) + + Case Else: Call MsgBox(" ", vbCritical) + End Select +End Function + +Public Function UIAskQuestion(theCode As MsgCode, ParamArray params() As Variant) As Boolean + Dim unwrapped As Variant: unwrapped = params + unwrapped = FixForwardedParams(unwrapped) + + Dim answer&: answer = vbNo + Select Case theCode +' Case QM_CODE_DELETE_CONFIRM +' answer = MsgBox("Are you sure you want to delete ALL macros from target file?", vbYesNo + vbQuestion) + + Case Else + Call MsgBox(" ", vbCritical) + End Select + UIAskQuestion = answer = vbYes +End Function diff --git a/src/z_UIRibbon.bas b/src/z_UIRibbon.bas new file mode 100644 index 0000000..433e720 --- /dev/null +++ b/src/z_UIRibbon.bas @@ -0,0 +1,30 @@ +Attribute VB_Name = "z_UIRibbon" +Option Explicit + +Sub CC_OnRibbonBtn(iControl As IRibbonControl) + Select Case iControl.ID + Case "CheckLinks": Call CC_CheckLinks + Case "AddQuote": Call CC_AddQuote + Case "EditCut": Call CC_EditCut + Case "EditCopy": Call CC_EditCopy + Case "CreateLink": Call CC_CreateHLink + + Case "FixParagraph": Call CC_ParFix + Case "ExportComments": Call CC_ExportComments + Case "RemoveExternalHyperlinks": Call CC_RemoveExternalHyperlinks + Case "RemoveAllHyperlinks": Call CC_RemoveAllHyperlinks + Case "RemoveImages": Call CC_RemoveImages + Case "RemoveBookmarks": Call CC_RemoveBookmarks + Case "RemoveAccents": Call CC_RemoveAccents + + Case "MasterText": Call CC_MasterText + Case "UpdateConcept": Call CC_UpdateConcept + + Case "Help": Call CC_Help + + Case "Checkup": Call CC_Checkup + Case "FixErrors": Call CC_FixErrors + Case "IgnoreSelected": Call CC_IgnoreSelected + Case "MasterConfig": Call CC_MasterConfig + End Select +End Sub diff --git a/ui/.rels b/ui/.rels new file mode 100644 index 0000000..2b00f63 --- /dev/null +++ b/ui/.rels @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/ui/customUI.xml b/ui/customUI.xml new file mode 100644 index 0000000..a58d24a --- /dev/null +++ b/ui/customUI.xml @@ -0,0 +1,101 @@ + + + + + + +