Initial commit
This commit is contained in:
commit
152c6e471e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
~*
|
||||||
|
|
183
api/API_Path.cls
Normal file
183
api/API_Path.cls
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_Path"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ================ Ðàáîòà ñ ïóòÿìè ê ôàéëàì =============
|
||||||
|
' Shared module version: 20221031
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: ex_VBA
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Enum PathType
|
||||||
|
T_PATH_INVALID = 0
|
||||||
|
|
||||||
|
T_PATH_GLOBAL = 1
|
||||||
|
T_PATH_LOCAL = 2
|
||||||
|
T_PATH_ENVIRON = 3
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Private data_ As String
|
||||||
|
Private type_ As PathType
|
||||||
|
|
||||||
|
Public Function FromString(sText$) As API_Path
|
||||||
|
data_ = sText
|
||||||
|
type_ = InternalEvaluate(data_)
|
||||||
|
Set FromString = Me
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Clone() As API_Path
|
||||||
|
Set Clone = New API_Path
|
||||||
|
Call Clone.FromString(data_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get Text() As String
|
||||||
|
Text = data_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get ParentFolder() As String
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
ParentFolder = fso.GetParentFolderName(data_)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get FileName() As String
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
FileName = fso.GetFileName(data_)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get BaseName() As String
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
BaseName = fso.GetBaseName(data_)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Extension() As String
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Extension = fso.GetExtensionName(data_)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function ToGlobal(sHome$) As API_Path
|
||||||
|
Select Case type_
|
||||||
|
Case T_PATH_LOCAL: data_ = ConvertLocal(data_, sHome)
|
||||||
|
Case T_PATH_ENVIRON: data_ = ConvertEnviron(data_)
|
||||||
|
Case T_PATH_INVALID, T_PATH_GLOBAL:
|
||||||
|
End Select
|
||||||
|
type_ = InternalEvaluate(data_)
|
||||||
|
Set ToGlobal = Me
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ToLocal(sHome$) As API_Path
|
||||||
|
Set ToLocal = Me
|
||||||
|
Dim nPrefix&: nPrefix = CommonPrefixLength(data_, sHome, vbTextCompare)
|
||||||
|
If nPrefix = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
If VBA.Mid(data_, nPrefix, 1) = "\" Then
|
||||||
|
nPrefix = nPrefix - 1
|
||||||
|
ElseIf VBA.Mid(data_, nPrefix + 1, 1) <> "\" Then
|
||||||
|
nPrefix = VBA.InStrRev(data_, "\", nPrefix)
|
||||||
|
If nPrefix < 3 Then _
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Dim sFolder$: sFolder = sHome
|
||||||
|
Dim sPrefix$
|
||||||
|
Do While VBA.Len(sFolder) > nPrefix
|
||||||
|
sFolder = fso.GetParentFolderName(sFolder)
|
||||||
|
sPrefix = sPrefix & "..\"
|
||||||
|
Loop
|
||||||
|
|
||||||
|
data_ = VBA.Right(data_, VBA.Len(data_) - nPrefix - 1)
|
||||||
|
If sPrefix <> vbNullString Then _
|
||||||
|
data_ = sPrefix & data_
|
||||||
|
type_ = InternalEvaluate(data_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ToServer(sHome$) As API_Path
|
||||||
|
Call ToGlobal(sHome)
|
||||||
|
data_ = SubstituteServer(data_)
|
||||||
|
type_ = InternalEvaluate(data_)
|
||||||
|
Set ToServer = Me
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GlobalToServer() As API_Path
|
||||||
|
data_ = SubstituteServer(data_)
|
||||||
|
type_ = InternalEvaluate(data_)
|
||||||
|
Set GlobalToServer = Me
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function LocalExists(sHome$) As Boolean
|
||||||
|
Dim sFile$
|
||||||
|
If type_ = T_PATH_LOCAL Then
|
||||||
|
sFile = ConvertLocal(data_, sHome)
|
||||||
|
Else
|
||||||
|
sFile = data_
|
||||||
|
End If
|
||||||
|
LocalExists = CheckPath(sFile)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GlobalExists() As Boolean
|
||||||
|
GlobalExists = CheckPath(data_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========
|
||||||
|
Private Function InternalEvaluate(sPath$) As PathType
|
||||||
|
If VBA.Len(sPath) < 3 Then
|
||||||
|
InternalEvaluate = T_PATH_INVALID
|
||||||
|
ElseIf VBA.Mid(sPath, 2, 1) = ":" Or VBA.Left(sPath, 2) = "\\" Then
|
||||||
|
InternalEvaluate = T_PATH_GLOBAL
|
||||||
|
ElseIf sPath Like "%*%*" Then
|
||||||
|
InternalEvaluate = T_PATH_ENVIRON
|
||||||
|
Else
|
||||||
|
InternalEvaluate = T_PATH_LOCAL
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SubstituteServer(sPath$) As String
|
||||||
|
If VBA.Len(sPath) < 3 Then
|
||||||
|
SubstituteServer = sPath
|
||||||
|
ElseIf VBA.Left(sPath, 3) = "P:\" Then
|
||||||
|
SubstituteServer = "\\fs1.concept.ru\projects\" & VBA.Right(sPath, VBA.Len(sPath) - 3)
|
||||||
|
ElseIf VBA.Left(sPath, 3) = "X:\" Then
|
||||||
|
SubstituteServer = "\\fs1.concept.ru\Exchange\" & VBA.Right(sPath, VBA.Len(sPath) - 3)
|
||||||
|
Else
|
||||||
|
SubstituteServer = sPath
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ConvertLocal(sPath$, sHome$) As String
|
||||||
|
Dim nEllipsis&: nEllipsis = VBA.InStrRev(sPath, "..\")
|
||||||
|
If nEllipsis = 0 Then
|
||||||
|
ConvertLocal = sHome & "\" & sPath
|
||||||
|
Else
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Dim nCount&: nCount = 1 + nEllipsis / 3
|
||||||
|
Dim sFolder$: sFolder = sHome
|
||||||
|
Do While nCount > 0
|
||||||
|
sFolder = fso.GetParentFolderName(sFolder)
|
||||||
|
nCount = nCount - 1
|
||||||
|
Loop
|
||||||
|
ConvertLocal = sFolder & "\" & VBA.Right(sPath, VBA.Len(sPath) - nEllipsis - 2)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ConvertEnviron(sPath$) As String
|
||||||
|
Dim nHeader&: nHeader = VBA.InStr(2, sPath, "%")
|
||||||
|
Dim sVariable$: sVariable = VBA.Mid(sPath, 2, nHeader - 2)
|
||||||
|
ConvertEnviron = VBA.Environ$(sVariable) & "\" & VBA.Right(sPath, VBA.Len(sPath) - nHeader - 1)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CheckPath(sPath$) As Boolean
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
CheckPath = True
|
||||||
|
If fso.FileExists(sPath) Then _
|
||||||
|
Exit Function
|
||||||
|
If fso.FolderExists(sPath) Then _
|
||||||
|
Exit Function
|
||||||
|
CheckPath = False
|
||||||
|
End Function
|
235
api/API_Project.cls
Normal file
235
api/API_Project.cls
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_Project"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ================ Import / export code functionality =============
|
||||||
|
' Shared module version: 20220407
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting, VBIDE
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private fso_ As Scripting.FileSystemObject
|
||||||
|
Private proj_ As VBIDE.VBProject
|
||||||
|
|
||||||
|
Private contents_ As Scripting.Dictionary
|
||||||
|
Private sharedContents_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Private sourceHome_ As String
|
||||||
|
Private importsHome_ As String
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Set fso_ = New Scripting.FileSystemObject
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub Class_Terminate()
|
||||||
|
Call Detach
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(target As VBIDE.VBProject, Optional sImportsHome$ = "") As Boolean
|
||||||
|
Init = target.Protection <> 1
|
||||||
|
If Not Init Then
|
||||||
|
Call MsgBox("Project code is protected, cannot acccess", vbCritical)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Set proj_ = target
|
||||||
|
importsHome_ = sImportsHome
|
||||||
|
|
||||||
|
Set contents_ = New Scripting.Dictionary
|
||||||
|
Set sharedContents_ = New Scripting.Dictionary
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Detach()
|
||||||
|
Set proj_ = Nothing
|
||||||
|
importsHome_ = ""
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetInternals(ByRef oContents As Scripting.Dictionary, _
|
||||||
|
ByRef oSharedCont As Scripting.Dictionary)
|
||||||
|
Set contents_ = oContents
|
||||||
|
Set sharedContents_ = oSharedCont
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExportSrcTo(sPath$)
|
||||||
|
sourceHome_ = sPath
|
||||||
|
Call PrepareFolders(contents_)
|
||||||
|
Call InternalExport(contents_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExportShared()
|
||||||
|
sourceHome_ = importsHome_
|
||||||
|
Call PrepareFolders(sharedContents_)
|
||||||
|
Call InternalExport(sharedContents_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ImportSrcFrom(sPath$)
|
||||||
|
sourceHome_ = sPath
|
||||||
|
Call InternalImport(contents_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ImportShared()
|
||||||
|
sourceHome_ = importsHome_
|
||||||
|
Call InternalImport(sharedContents_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RemoveAll()
|
||||||
|
Dim nItem&: nItem = 1
|
||||||
|
Dim aComponent As VBIDE.VBComponent
|
||||||
|
Do While nItem <= proj_.VBComponents.Count
|
||||||
|
Set aComponent = proj_.VBComponents.Item(nItem)
|
||||||
|
If aComponent.Type <> vbext_ct_Document Then
|
||||||
|
If Not TryRemoving(aComponent) Then _
|
||||||
|
nItem = nItem + 1
|
||||||
|
Else
|
||||||
|
nItem = nItem + 1
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AddItem(sName$, sFolder$)
|
||||||
|
Call contents_.Add(sName, sFolder)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AddSharedItem(sName$, sFolder$)
|
||||||
|
Call sharedContents_.Add(sName, sFolder)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ReloadFrom(sFile$) As Boolean
|
||||||
|
ReloadFrom = False
|
||||||
|
If fso_.FileExists(sFile) Then _
|
||||||
|
ReloadFrom = TryImportFile(fso_.GetFile(sFile))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =========
|
||||||
|
Private Function PrepareFolders(target As Scripting.Dictionary)
|
||||||
|
Call ProjEnsureFolderExists(sourceHome_)
|
||||||
|
|
||||||
|
Dim sKey As Variant
|
||||||
|
For Each sKey In target
|
||||||
|
Call ProjEnsureFolderExists(sourceHome_ & "\" & CStr(target(sKey)))
|
||||||
|
Next sKey
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function InternalExport(target As Scripting.Dictionary)
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Dim aComponent As VBComponent
|
||||||
|
For Each aComponent In proj_.VBComponents
|
||||||
|
Dim sName$: sName = ComponentName(aComponent)
|
||||||
|
If Not target.Exists(sName) Then _
|
||||||
|
GoTo NEXT_COMPONENT
|
||||||
|
|
||||||
|
Dim sPath$: sPath = sourceHome_ & "\" & target(sName) & "\" & sName
|
||||||
|
If aComponent.Type = vbext_ct_MSForm Then _
|
||||||
|
If HasNoChanges(aComponent, sPath) Then _
|
||||||
|
GoTo NEXT_COMPONENT
|
||||||
|
Call aComponent.Export(sPath)
|
||||||
|
NEXT_COMPONENT:
|
||||||
|
Next aComponent
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function InternalImport(tContents As Scripting.Dictionary)
|
||||||
|
Dim sFileName As Variant
|
||||||
|
Dim sPath$
|
||||||
|
For Each sFileName In tContents
|
||||||
|
sPath = sourceHome_ & "\" & tContents(sFileName) & "\" & CStr(sFileName)
|
||||||
|
If fso_.FileExists(sPath) Then _
|
||||||
|
Call TryImportFile(fso_.GetFile(sPath))
|
||||||
|
Next sFileName
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TryImportFile(target As Scripting.File) As Boolean
|
||||||
|
TryImportFile = False
|
||||||
|
|
||||||
|
Dim sExt$: sExt = fso_.GetExtensionName(target.Name)
|
||||||
|
Dim sName$: sName = fso_.GetBaseName(target.Name)
|
||||||
|
Dim sComp As VBIDE.VBComponent: Set sComp = ComponentByName(sName)
|
||||||
|
If Not sComp Is Nothing Then _
|
||||||
|
If Not TryRemoving(sComp) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Call proj_.VBComponents.Import(target.Path)
|
||||||
|
If sExt = "frm" Then _
|
||||||
|
Call RemoveEmptyFirstLine(ComponentByName(sName).CodeModule)
|
||||||
|
|
||||||
|
TryImportFile = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ComponentName(target As VBComponent) As String
|
||||||
|
ComponentName = target.Name
|
||||||
|
Select Case target.Type
|
||||||
|
Case vbext_ct_ClassModule: ComponentName = ComponentName & ".cls"
|
||||||
|
Case vbext_ct_MSForm: ComponentName = ComponentName & ".frm"
|
||||||
|
Case vbext_ct_StdModule: ComponentName = ComponentName & ".bas"
|
||||||
|
Case Else: ComponentName = vbNullString
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function RemoveEmptyFirstLine(target As CodeModule)
|
||||||
|
Dim firstLineTxt$: firstLineTxt = target.Lines(1, 1)
|
||||||
|
If firstLineTxt = vbNullString Then _
|
||||||
|
Call target.DeleteLines(1, 1)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TryRemoving(target As VBIDE.VBComponent) As Boolean
|
||||||
|
Dim nCount&
|
||||||
|
nCount = proj_.VBComponents.Count
|
||||||
|
Call proj_.VBComponents.Remove(target)
|
||||||
|
TryRemoving = nCount <> proj_.VBComponents.Count
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ComponentByName(sName$) As VBIDE.VBComponent
|
||||||
|
Dim aComponent As VBComponent
|
||||||
|
For Each aComponent In proj_.VBComponents
|
||||||
|
If aComponent.Name = sName Then
|
||||||
|
Set ComponentByName = aComponent
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next aComponent
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ProjEnsureFolderExists(sPath$)
|
||||||
|
If fso_.FolderExists(sPath) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim sParent$: sParent = sPath
|
||||||
|
Do While VBA.Right(sParent, 1) = "\"
|
||||||
|
sParent = VBA.Left(sParent, VBA.Len(sParent) - 1)
|
||||||
|
Loop
|
||||||
|
sParent = VBA.Left(sParent, VBA.InStrRev(sParent, "\") - 1)
|
||||||
|
|
||||||
|
Call ProjEnsureFolderExists(sParent)
|
||||||
|
Call fso_.CreateFolder(sPath)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function HasNoChanges(aForm As VBIDE.VBComponent, sPath$) As Boolean
|
||||||
|
HasNoChanges = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FileExists(sPath) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim tmpDir$: tmpDir = fso.GetParentFolderName(sPath) & "\tmpFrm"
|
||||||
|
Dim tmpFile$: tmpFile = tmpDir & "\" & aForm.Name & ".frm"
|
||||||
|
Call fso.CreateFolder(tmpDir)
|
||||||
|
Call aForm.Export(tmpFile)
|
||||||
|
|
||||||
|
Dim nFile1&: nFile1 = FreeFile
|
||||||
|
Open sPath For Input As #nFile1
|
||||||
|
Dim cont1 As Variant: cont1 = Input(LOF(nFile1), nFile1)
|
||||||
|
Close nFile1
|
||||||
|
|
||||||
|
Dim nFile2&: nFile2 = FreeFile
|
||||||
|
Open tmpFile For Input As #nFile2
|
||||||
|
Dim cont2 As Variant: cont2 = Input(LOF(nFile2), nFile2)
|
||||||
|
Close nFile2
|
||||||
|
|
||||||
|
HasNoChanges = cont1 = cont2
|
||||||
|
Call fso.DeleteFolder(tmpDir)
|
||||||
|
End Function
|
||||||
|
|
221
api/API_Python.cls
Normal file
221
api/API_Python.cls
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_Python"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
'================ Python process object =========================
|
||||||
|
' Shared module version: 20220713
|
||||||
|
' Tested in: PythonManager
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const CONCEPT_DLL_LOCATION = "C:\Tools\dll"
|
||||||
|
|
||||||
|
#If Win64 Then
|
||||||
|
Private Const PY_DLL_NAME As String = "vbatopy-connector64.dll"
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function StartPythonServer Lib "vbatopy-connector64.dll" ( _
|
||||||
|
ByRef vResult As Variant, _
|
||||||
|
ByVal sPython As String, _
|
||||||
|
ByVal sModules As String, _
|
||||||
|
ByVal bShowConsole As Long) As Long
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function KillPythonServer Lib "vbatopy-connector64.dll" Alias "KillServer" () As Long
|
||||||
|
Private Declare PtrSafe Function GetServer Lib "vbatopy-connector64.dll" (ByRef vResult As Variant) As Long
|
||||||
|
#Else
|
||||||
|
Private Const PY_DLL_NAME As String = "vbatopy-connector32.dll"
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function StartPythonServer Lib "vbatopy-connector32.dll" ( _
|
||||||
|
ByRef vResult As Variant, _
|
||||||
|
ByVal sPython As String, _
|
||||||
|
ByVal sModules As String, _
|
||||||
|
ByVal bShowConsole As Long) As Long
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function KillPythonServer Lib "vbatopy-connector32.dll" Alias "KillServer" () As Long
|
||||||
|
Private Declare PtrSafe Function GetServer Lib "vbatopy-connector32.dll" (ByRef vResult As Variant) As Long
|
||||||
|
#End If
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal sLibrary As String) As Long
|
||||||
|
Private Declare PtrSafe Function FreeLibrary Lib "kernel32" (ByVal nLibraryHandle As Long) As Long
|
||||||
|
|
||||||
|
Private python_ As Variant
|
||||||
|
Private pythonPath_ As String
|
||||||
|
Private modules_ As String
|
||||||
|
Private showConsole_ As Boolean
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Call ClearCahce
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub Class_Terminate()
|
||||||
|
Call ClearCahce
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function ClearCahce()
|
||||||
|
Set python_ = Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Initialize Python process
|
||||||
|
' iPython - path to python / python command
|
||||||
|
' sModules - path to python source code modules, delimiter = ';'
|
||||||
|
' bDoDebug - flag to show console output for python process
|
||||||
|
Public Function Init(iPython$, sModules$, Optional bDoDebug As Boolean = False)
|
||||||
|
Set python_ = Nothing
|
||||||
|
pythonPath_ = VBA.LCase(iPython)
|
||||||
|
pythonPath_ = VBA.Replace(pythonPath_, "program files (x86)", """program files (x86)""")
|
||||||
|
pythonPath_ = VBA.Replace(pythonPath_, "program files", """program files""")
|
||||||
|
pythonPath_ = VBA.Replace(pythonPath_, "\", "\\")
|
||||||
|
modules_ = sModules
|
||||||
|
showConsole_ = bDoDebug
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Validate server is running
|
||||||
|
' If @python_ is not setup, then load current server dispatch into @python_
|
||||||
|
Public Function Validate() As Boolean
|
||||||
|
Call LoadDLL
|
||||||
|
Call GetServer(python_)
|
||||||
|
Validate = Not python_ Is Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function StartServer()
|
||||||
|
Call Validate
|
||||||
|
If Not python_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
If StartPythonServer(python_, pythonPath_, modules_, IIf(showConsole_, 1, 0)) <> 0 Then _
|
||||||
|
Call RaiseServerError
|
||||||
|
|
||||||
|
If VBA.VarType(python_) = vbString Then
|
||||||
|
Call RaiseServerError
|
||||||
|
Set python_ = Nothing
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function KillServer()
|
||||||
|
Call Validate
|
||||||
|
If python_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If KillPythonServer() <> 0 Then _
|
||||||
|
Call RaiseServerError
|
||||||
|
Set python_ = Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Preload Python module before using it - use if long setup is needed to load module
|
||||||
|
Public Function LoadModule(sFullPath$) As Boolean
|
||||||
|
On Error GoTo HANDLE_ERROR
|
||||||
|
LoadModule = Py.ImportModule(sFullPath) <> ""
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
HANDLE_ERROR:
|
||||||
|
Debug.Print Err.Description
|
||||||
|
LoadModule = False
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Run python command
|
||||||
|
Public Function Execute(sPyCommand$) As Boolean
|
||||||
|
On Error GoTo HANDLE_ERROR
|
||||||
|
Call Py.Exec("" & sPyCommand & "")
|
||||||
|
On Error GoTo 0
|
||||||
|
Execute = True
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
HANDLE_ERROR:
|
||||||
|
Debug.Print Err.Description
|
||||||
|
Execute = False
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Evaluate python statement
|
||||||
|
' Warning! Returns only basic types. Objects are not supported
|
||||||
|
Public Function Evaluate(sPyStatement$) As Variant
|
||||||
|
On Error GoTo HANDLE_ERROR
|
||||||
|
Evaluate = Py.Eval("" & sPyStatement & "")
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
HANDLE_ERROR:
|
||||||
|
Debug.Print Err.Description
|
||||||
|
Evaluate = Err.Description
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Call function from module. Arguments will be available from python including COM wrappers for any Office objects
|
||||||
|
' Warning! Returns basic types. To get objects from python use CallFunctionReturnObject
|
||||||
|
Public Function CallFunction(sModule$, sFunc$, Optional vArgs As Variant) As Variant
|
||||||
|
On Error GoTo HANDLE_ERROR
|
||||||
|
If IsMissing(vArgs) Then
|
||||||
|
CallFunction = Py.CallFunction(sModule, sFunc)
|
||||||
|
Else
|
||||||
|
CallFunction = Py.CallFunction(sModule, sFunc, vArgs)
|
||||||
|
End If
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
HANDLE_ERROR:
|
||||||
|
Debug.Print Err.Description
|
||||||
|
CallFunction = Err.Description
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CallFunctionReturnObject(sModule$, sFunc$, Optional vArgs As Variant) As Object
|
||||||
|
On Error GoTo HANDLE_ERROR
|
||||||
|
If IsMissing(vArgs) Then
|
||||||
|
Set CallFunctionReturnObject = Py.CallFunction(sModule, sFunc)
|
||||||
|
Else
|
||||||
|
Set CallFunctionReturnObject = Py.CallFunction(sModule, sFunc, vArgs)
|
||||||
|
End If
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
HANDLE_ERROR:
|
||||||
|
Debug.Print Err.Description
|
||||||
|
Set CallFunctionReturnObject = Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Create VBA wrapper for target Python module in destination file path
|
||||||
|
Public Function WrapPython(sSourceModule$, sDestination$) As String
|
||||||
|
WrapPython = Py.WrapPython(sSourceModule, sDestination)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Preload @vbatopy DLL
|
||||||
|
' Note: Mostly usefull for testing setup because it allows using DLL calls without starting a server
|
||||||
|
Public Function LoadDLL()
|
||||||
|
If LoadLibrary(PY_DLL_NAME) <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
If LoadLibrary(CONCEPT_DLL_LOCATION & "\" & PY_DLL_NAME) <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
' TODO: remove fallback after some time
|
||||||
|
' fallback path for earlier versions of distribution
|
||||||
|
If LoadLibrary(VBA.Environ("USERPROFILE") & "\.concept\dll\" & PY_DLL_NAME) = 0 Then _
|
||||||
|
Call Err.Raise(1, Description:="Could not load " & PY_DLL_NAME)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Unload DLL - need to call this before replacing dll file
|
||||||
|
Public Function UnloadDLL()
|
||||||
|
Dim nHandle&: nHandle = LoadLibrary(PY_DLL_NAME)
|
||||||
|
If nHandle <> 0 Then
|
||||||
|
Call KillServer
|
||||||
|
Call FreeLibrary(nHandle)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =========
|
||||||
|
Private Property Get Py() As Variant
|
||||||
|
If python_ Is Nothing Then _
|
||||||
|
Call StartServer
|
||||||
|
Set Py = python_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Private Function RaiseServerError()
|
||||||
|
Dim sErr$: sErr = python_
|
||||||
|
Set python_ = Nothing
|
||||||
|
Debug.Print sErr
|
||||||
|
Call Err.Raise(1000, Description:=sErr)
|
||||||
|
End Function
|
||||||
|
|
85
api/API_Ribbon.cls
Normal file
85
api/API_Ribbon.cls
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_Ribbon"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
'==== Îáîëî÷êà äëÿ õðàíåíèÿ è çàãðóçêè èäåíòèôèêàòîðà ëåíòû (Ribbon) =========================
|
||||||
|
' Shared module version: 20210217
|
||||||
|
' Required reference: Microsoft Scripting Runtime
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, src As Any, ByVal nLen As Long)
|
||||||
|
|
||||||
|
Private ribbon_ As IRibbonUI
|
||||||
|
Private backup_ As String
|
||||||
|
|
||||||
|
Private Sub Class_Terminate()
|
||||||
|
If Not ribbon_ Is Nothing Then _
|
||||||
|
Call KillBackup
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(target As IRibbonUI, sBackup$)
|
||||||
|
Set ribbon_ = target
|
||||||
|
backup_ = sBackup
|
||||||
|
Call SaveBackup
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get Value() As IRibbonUI
|
||||||
|
Set Value = ribbon_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function LoadFrom(sFilePath$) As IRibbonUI
|
||||||
|
Set ribbon_ = Nothing
|
||||||
|
backup_ = sFilePath
|
||||||
|
|
||||||
|
Call LoadBackup
|
||||||
|
|
||||||
|
Set LoadFrom = ribbon_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =======
|
||||||
|
Private Function SaveBackup()
|
||||||
|
Dim ribbonPtr As LongPtr: ribbonPtr = ObjPtr(ribbon_)
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Dim textOut As Scripting.TextStream: Set textOut = fso.CreateTextFile(backup_, Overwrite:=True)
|
||||||
|
If Not textOut Is Nothing Then
|
||||||
|
Call textOut.WriteLine(ribbonPtr)
|
||||||
|
Call textOut.Close
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function LoadBackup()
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FileExists(backup_) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim textIn As Scripting.TextStream: Set textIn = fso.OpenTextFile(backup_)
|
||||||
|
If textIn Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim ptrSize&
|
||||||
|
#If Win64 Then
|
||||||
|
ptrSize = 8
|
||||||
|
#Else
|
||||||
|
ptrSize = 4
|
||||||
|
#End If
|
||||||
|
|
||||||
|
Dim aLine$: aLine = textIn.ReadLine
|
||||||
|
If IsNumeric(aLine) Then
|
||||||
|
Dim nPtr As LongPtr: nPtr = CLngPtr(aLine)
|
||||||
|
Dim objRibbon As Object
|
||||||
|
Call CopyMemory(objRibbon, nPtr, ptrSize)
|
||||||
|
Set ribbon_ = objRibbon
|
||||||
|
Call CopyMemory(objRibbon, 0&, ptrSize)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function KillBackup()
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If fso.FileExists(backup_) Then _
|
||||||
|
Call fso.DeleteFile(backup_)
|
||||||
|
End Function
|
121
api/API_UserInteraction.cls
Normal file
121
api/API_UserInteraction.cls
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_UserInteraction"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
'================ Êëàññ èíêàïñóëÿöèè âçàèìîäåéñòâèÿ ñ ïîëüçîâàòåëåì =========================
|
||||||
|
' Shared module version: 20220611
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Function ShowMessage(nMsg&, ParamArray params() As Variant)
|
||||||
|
Call UIShowMessage(nMsg, params)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AskQuestion(nQuestion&, ParamArray params() As Variant) As Boolean
|
||||||
|
AskQuestion = UIAskQuestion(nQuestion, params)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FollowHyperlink(oDocument As Object, sAddress$)
|
||||||
|
Call oDocument.FollowHyperlink(sAddress)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function PromptInput(sPrompt$, Optional sTitle$ = vbNullString, Optional sInitial As Variant) As String
|
||||||
|
PromptInput = VBA.InputBox(sPrompt, sTitle, sInitial)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function PromptFileFilter(sInitialPath$, sDescription$, sFilter$, _
|
||||||
|
Optional sTitle$ = "Âûáåðèòå ôàéë", _
|
||||||
|
Optional bNewApplication As Boolean = False) As String
|
||||||
|
Dim cFilters As New Collection
|
||||||
|
Dim cDescriptions As New Collection
|
||||||
|
|
||||||
|
Call cFilters.Add("*.*")
|
||||||
|
Call cDescriptions.Add("Âñå ôàéëû")
|
||||||
|
Call cFilters.Add(sFilter)
|
||||||
|
Call cDescriptions.Add(sDescription)
|
||||||
|
|
||||||
|
PromptFileFilter = PromptFile(sInitialPath, sTitle, cDescriptions, cFilters, bNewApplication)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function PromptFile(sInitialPath$, _
|
||||||
|
Optional sTitle$ = "Âûáåðèòå ôàéë", _
|
||||||
|
Optional cDescriptions As Collection = Nothing, _
|
||||||
|
Optional cFilters As Collection = Nothing, _
|
||||||
|
Optional bNewApplication As Boolean = False) As String
|
||||||
|
PromptFile = vbNullString
|
||||||
|
Dim oApplication As Object
|
||||||
|
If Not bNewApplication Then
|
||||||
|
Set oApplication = Application
|
||||||
|
Else
|
||||||
|
Set oApplication = CreateObject("Excel.Application")
|
||||||
|
oApplication.Visible = True
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim filterCount&: filterCount = 0
|
||||||
|
If Not cDescriptions Is Nothing And Not cFilters Is Nothing Then _
|
||||||
|
If cDescriptions.Count = cFilters.Count Then _
|
||||||
|
filterCount = cFilters.Count
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
|
||||||
|
With oApplication.FileDialog(msoFileDialogFilePicker)
|
||||||
|
.InitialFileName = sInitialPath
|
||||||
|
.AllowMultiSelect = False
|
||||||
|
.ButtonName = "Âûáðàòü"
|
||||||
|
.Title = sTitle
|
||||||
|
|
||||||
|
Call .Filters.Clear
|
||||||
|
If filterCount > 0 Then
|
||||||
|
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 1 To filterCount Step 1
|
||||||
|
Call .Filters.Add(cDescriptions.Item(nItem), cFilters.Item(nItem), 1)
|
||||||
|
Next nItem
|
||||||
|
Else
|
||||||
|
Call .Filters.Add("Âñå ôàéëû", "*.*")
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call .Show
|
||||||
|
If .SelectedItems.Count > 0 Then _
|
||||||
|
PromptFile = .SelectedItems(1)
|
||||||
|
End With
|
||||||
|
|
||||||
|
If bNewApplication Then _
|
||||||
|
Call oApplication.Quit
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function PromptFolder(sInitialPath$, _
|
||||||
|
Optional sTitle$ = "Âûáåðèòå êàòàëîã", _
|
||||||
|
Optional bNewApplication As Boolean = False) As String
|
||||||
|
PromptFolder = vbNullString
|
||||||
|
Dim oApplication As Object
|
||||||
|
If Not bNewApplication Then
|
||||||
|
Set oApplication = Application
|
||||||
|
Else
|
||||||
|
Set oApplication = CreateObject("Excel.Application")
|
||||||
|
oApplication.Visible = True
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
With oApplication.FileDialog(msoFileDialogFolderPicker)
|
||||||
|
.InitialFileName = sInitialPath
|
||||||
|
.AllowMultiSelect = False
|
||||||
|
.ButtonName = "Âûáðàòü"
|
||||||
|
.Title = sTitle
|
||||||
|
|
||||||
|
Call .Show
|
||||||
|
|
||||||
|
If .SelectedItems.Count > 0 Then _
|
||||||
|
PromptFolder = .SelectedItems(1)
|
||||||
|
End With
|
||||||
|
|
||||||
|
If bNewApplication Then _
|
||||||
|
Call oApplication.Quit
|
||||||
|
End Function
|
||||||
|
|
376
api/API_VsoWrapper.cls
Normal file
376
api/API_VsoWrapper.cls
Normal file
|
@ -0,0 +1,376 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_VsoWrapper"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ================ Êëàññ-îáîëî÷êà äëÿ äîêóìåíòà MS Visio =========================
|
||||||
|
' Shared module version: 20220410
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting, Visio
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function GetWindowThreadProcessId Lib "user32" (ByVal nHwnd As Long, nProcID As Long) As Long
|
||||||
|
|
||||||
|
Private application_ As Visio.Application
|
||||||
|
Private document_ As Visio.Document
|
||||||
|
Private reporter_ As Object
|
||||||
|
Private bSilent_ As Boolean
|
||||||
|
|
||||||
|
Private deferRel_ As Boolean
|
||||||
|
Private deferRecalc_ As Boolean
|
||||||
|
Private isPaused_ As Boolean
|
||||||
|
|
||||||
|
Private bCloseOnFail_ As Boolean
|
||||||
|
Private bOwnsDoc_ As Boolean
|
||||||
|
Private bOwnsApp_ As Boolean
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Call ResetAll
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub Class_Terminate()
|
||||||
|
If application_ Is Nothing Then _
|
||||||
|
Exit Sub
|
||||||
|
' Note: application could be owned by another Wrapper and already quit
|
||||||
|
On Error GoTo SKIP_RELEASE
|
||||||
|
If application_.Documents.Count = 0 Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
SKIP_RELEASE:
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function ResetAll()
|
||||||
|
Call ResetDocument
|
||||||
|
|
||||||
|
deferRel_ = False
|
||||||
|
deferRecalc_ = False
|
||||||
|
bSilent_ = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ResetDocument()
|
||||||
|
Set application_ = Nothing
|
||||||
|
Set document_ = Nothing
|
||||||
|
bOwnsDoc_ = False
|
||||||
|
bOwnsApp_ = False
|
||||||
|
bCloseOnFail_ = False
|
||||||
|
isPaused_ = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Reporter object should implement callback function Report(sMsg$, nFlags&)
|
||||||
|
Public Function SetReporter(aReporter As Object)
|
||||||
|
Set reporter_ = aReporter
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get Document() As Visio.Document
|
||||||
|
Set Document = document_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Application() As Visio.Application
|
||||||
|
Set Application = application_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get IsUIPaused() As Boolean
|
||||||
|
IsUIPaused = isPaused_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function DisableMessages()
|
||||||
|
bSilent_ = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function EnableMessages()
|
||||||
|
bSilent_ = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CreateApplication(Optional bIsVisible As Boolean = True) As Visio.Application
|
||||||
|
Call ResetDocument
|
||||||
|
Set application_ = CreateObject("Visio.Application")
|
||||||
|
bOwnsApp_ = True
|
||||||
|
application_.Visible = bIsVisible
|
||||||
|
Set CreateApplication = application_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetApplication(target As Visio.Application)
|
||||||
|
If ObjPtr(application_) = ObjPtr(target) Then _
|
||||||
|
Exit Function
|
||||||
|
Call ResetDocument
|
||||||
|
Set application_ = target
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetDocument(target As Visio.Document, Optional bOwnership = False)
|
||||||
|
If ObjPtr(target) = ObjPtr(document_) Then
|
||||||
|
bOwnsDoc_ = bOwnership
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
If Not target Is Nothing Then _
|
||||||
|
Call SetApplication(target.Application)
|
||||||
|
bOwnsDoc_ = bOwnership
|
||||||
|
Set document_ = target
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function PauseUI()
|
||||||
|
If application_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Debug.Assert Not isPaused_
|
||||||
|
isPaused_ = True
|
||||||
|
deferRel_ = application_.DeferRelationshipRecalc
|
||||||
|
deferRecalc_ = application_.DeferRecalc
|
||||||
|
application_.ScreenUpdating = False
|
||||||
|
application_.DeferRecalc = True
|
||||||
|
application_.DeferRelationshipRecalc = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ResumeUI()
|
||||||
|
If application_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
isPaused_ = False
|
||||||
|
application_.DeferRelationshipRecalc = deferRel_
|
||||||
|
application_.DeferRecalc = deferRecalc_
|
||||||
|
application_.ScreenUpdating = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Run(sFunc$, ParamArray vArgs() As Variant)
|
||||||
|
' Note: running code in Visio cannot return value, so use file IO instead if needed (pass output filename through args)
|
||||||
|
Dim nArgCount&: nArgCount = UBound(vArgs) - LBound(vArgs) + 1
|
||||||
|
Dim sCommand$: sCommand = "Call " & sFunc & "("
|
||||||
|
Dim nArg&
|
||||||
|
For nArg = 0 To nArgCount - 1 Step 1
|
||||||
|
If nArg <> 0 Then _
|
||||||
|
sCommand = sCommand & ", "
|
||||||
|
sCommand = sCommand & """" & CStr(vArgs(nArg)) & """"
|
||||||
|
Next nArg
|
||||||
|
sCommand = sCommand & ")"
|
||||||
|
Call Document.ExecuteLine(sCommand)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function NewDocument(Optional sTemplate$ = vbNullString, _
|
||||||
|
Optional bDefaultIfFail As Boolean = True) As Visio.Document
|
||||||
|
If Not document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
If application_ Is Nothing Then
|
||||||
|
bCloseOnFail_ = True
|
||||||
|
Call CreateApplication
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error GoTo TRY_DEFAULT
|
||||||
|
|
||||||
|
Set document_ = application_.Documents.Add(sTemplate)
|
||||||
|
bOwnsDoc_ = True
|
||||||
|
Set NewDocument = document_
|
||||||
|
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
TRY_DEFAULT:
|
||||||
|
If sTemplate <> vbNullString Then
|
||||||
|
Call Report("Íå óäàëîñü ñîçäàòü äîêóìåíò èç øàáëîíà " & sTemplate)
|
||||||
|
If Not bDefaultIfFail Then
|
||||||
|
If bCloseOnFail_ Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
Else
|
||||||
|
On Error GoTo ERR_DEFAULT
|
||||||
|
Set document_ = application_.Documents.Add("")
|
||||||
|
Set NewDocument = document_
|
||||||
|
End If
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
ERR_DEFAULT:
|
||||||
|
Call Report("Íå óäàëîñü ñîçäàòü íîâûé äîêóìåíò èç áàçîâîãî øàáëîíà")
|
||||||
|
If bCloseOnFail_ Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function OpenDocument(sFile$, Optional nOpenFlags As Integer = 0) As Visio.Document
|
||||||
|
' Note: Äîêóìåíòû Visio íå ïîääåðæèâàþò îïöèþ ReadOnly
|
||||||
|
If Not document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim bResetApplication As Boolean
|
||||||
|
bResetApplication = application_ Is Nothing
|
||||||
|
If bResetApplication Then _
|
||||||
|
Call DefaultApplication
|
||||||
|
|
||||||
|
If TryAlreadyOpened(sFile) Then
|
||||||
|
Set OpenDocument = document_
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
If Not TestFile(sFile) Then _
|
||||||
|
GoTo SAFE_EXIT
|
||||||
|
|
||||||
|
On Error GoTo SAFE_EXIT
|
||||||
|
Set document_ = application_.Documents.OpenEx(sFile, nOpenFlags)
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not document_ Is Nothing Then
|
||||||
|
bOwnsDoc_ = True
|
||||||
|
Set OpenDocument = document_
|
||||||
|
Else
|
||||||
|
SAFE_EXIT:
|
||||||
|
bOwnsDoc_ = False
|
||||||
|
If bCloseOnFail_ Then _
|
||||||
|
Call KillApplication
|
||||||
|
If bResetApplication Then _
|
||||||
|
Set application_ = Nothing
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ReleaseApplication()
|
||||||
|
If bOwnsApp_ Then _
|
||||||
|
Call KillApplication
|
||||||
|
Call ResetDocument
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ReleaseDocument(Optional bCloseApplication As Boolean = True, _
|
||||||
|
Optional bSaveChanges As Boolean = False) As Boolean
|
||||||
|
ReleaseDocument = False
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If Not bOwnsDoc_ Then
|
||||||
|
Set document_ = Nothing
|
||||||
|
ReleaseDocument = True
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error GoTo EXIT_FUNC
|
||||||
|
|
||||||
|
If (bSaveChanges And document_.Path <> vbNullString) Then
|
||||||
|
Call document_.OpenStencilWindow
|
||||||
|
Call document_.Save
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim nInitialResponse&: nInitialResponse = application_.AlertResponse
|
||||||
|
application_.AlertResponse = vbNo
|
||||||
|
Call document_.Close
|
||||||
|
application_.AlertResponse = nInitialResponse
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
Set document_ = Nothing
|
||||||
|
ReleaseDocument = True
|
||||||
|
If bCloseApplication And application_.Documents.Count = 0 Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
EXIT_FUNC:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SaveAs(sTargetName$) As Boolean
|
||||||
|
SaveAs = False
|
||||||
|
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
Call document_.SaveAs(sTargetName)
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
SaveAs = True
|
||||||
|
Exit Function
|
||||||
|
RETURN_FALSE:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ===========
|
||||||
|
Private Function Report(sMsg$)
|
||||||
|
If bSilent_ Then
|
||||||
|
Debug.Print "VisioWrapper: " & sMsg
|
||||||
|
Exit Function
|
||||||
|
ElseIf reporter_ Is Nothing Then
|
||||||
|
Call MsgBox(sMsg, vbExclamation)
|
||||||
|
Else
|
||||||
|
Call reporter_.Report(sMsg, vbExclamation)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function DefaultApplication()
|
||||||
|
bOwnsApp_ = False
|
||||||
|
On Error GoTo CREATE_NEW_APPL
|
||||||
|
Set application_ = GetObject(, "Visio.Application")
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
CREATE_NEW_APPL:
|
||||||
|
Set application_ = CreateObject("Visio.Application")
|
||||||
|
bOwnsApp_ = True
|
||||||
|
bCloseOnFail_ = True
|
||||||
|
application_.Visible = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function KillApplication()
|
||||||
|
Const HIDE_SHELL = 0
|
||||||
|
Const WAIT_RETURN = True
|
||||||
|
|
||||||
|
On Error GoTo OFFICE_QUIT
|
||||||
|
|
||||||
|
Dim nThreadID&, nProcID&
|
||||||
|
nThreadID = GetWindowThreadProcessId(application_.WindowHandle32, nProcID)
|
||||||
|
|
||||||
|
Dim iShell As Object: Set iShell = VBA.CreateObject("WScript.Shell")
|
||||||
|
If iShell.Run("TaskKill /F /PID " & nProcID, HIDE_SHELL, WAIT_RETURN) = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
OFFICE_QUIT:
|
||||||
|
Call application_.Quit
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TryAlreadyOpened(sFile$) As Boolean
|
||||||
|
TryAlreadyOpened = False
|
||||||
|
|
||||||
|
Set document_ = FindByName(sFile)
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
bOwnsDoc_ = False
|
||||||
|
TryAlreadyOpened = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TestFile(sFile$) As Boolean
|
||||||
|
Const ATTRIBUTE_READONLY = 1
|
||||||
|
TestFile = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FileExists(sFile) Then
|
||||||
|
Call Report("Ôàéë íå íàéäåí: " & sFile)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
If fso.GetFile(sFile).Attributes And ATTRIBUTE_READONLY Then
|
||||||
|
Call Report("Ôàéë ReadOnly: " & sFile)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
If IsFileInUse(sFile) Then
|
||||||
|
Call Report("Ôàéë çàáëîêèðîâàí: " & sFile)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
TestFile = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function FindByName(sName$) As Visio.Document
|
||||||
|
Dim aDoc As Visio.Document
|
||||||
|
For Each aDoc In application_.Documents
|
||||||
|
If aDoc.FullName = sName Then
|
||||||
|
Set FindByName = aDoc
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next aDoc
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IsFileInUse(sFileName$) As Boolean
|
||||||
|
Dim nFile%: nFile = FreeFile
|
||||||
|
On Error Resume Next
|
||||||
|
|
||||||
|
Open sFileName For Binary Access Read Lock Read Write As #nFile
|
||||||
|
Close #nFile
|
||||||
|
IsFileInUse = Err.Number > 0
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
394
api/API_WordWrapper.cls
Normal file
394
api/API_WordWrapper.cls
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_WordWrapper"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ================ Êëàññ-îáîëî÷êà äëÿ äîêóìåíòà MS Word =========================
|
||||||
|
' Shared module version: 20220410
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting, Word
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function GetWindowThreadProcessId Lib "user32" (ByVal nHwnd As Long, nProcID As Long) As Long
|
||||||
|
|
||||||
|
Private application_ As Word.Application
|
||||||
|
Private document_ As Word.Document
|
||||||
|
Private reporter_ As Object
|
||||||
|
Private bSilent_ As Boolean
|
||||||
|
|
||||||
|
Private isPaused_ As Boolean
|
||||||
|
Private screenUpdate_ As Boolean
|
||||||
|
Private proofing_ As Boolean
|
||||||
|
|
||||||
|
Private bCloseOnFail_ As Boolean
|
||||||
|
Private bOwnsDoc_ As Boolean
|
||||||
|
Private bOwnsApp_ As Boolean
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Call ResetAll
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub Class_Terminate()
|
||||||
|
If application_ Is Nothing Then _
|
||||||
|
Exit Sub
|
||||||
|
' Note: application could be owned by another Wrapper and already quit
|
||||||
|
On Error GoTo SKIP_RELEASE
|
||||||
|
If application_.Documents.Count = 0 Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
SKIP_RELEASE:
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function ResetAll()
|
||||||
|
Call ResetDocument
|
||||||
|
|
||||||
|
screenUpdate_ = True
|
||||||
|
proofing_ = True
|
||||||
|
bSilent_ = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ResetDocument()
|
||||||
|
Set application_ = Nothing
|
||||||
|
Set document_ = Nothing
|
||||||
|
bOwnsDoc_ = False
|
||||||
|
bOwnsApp_ = False
|
||||||
|
bCloseOnFail_ = False
|
||||||
|
isPaused_ = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Reporter object should implement callback function Report(sMsg$, nFlags&)
|
||||||
|
Public Function SetReporter(aReporter As Object)
|
||||||
|
Set reporter_ = aReporter
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get Document() As Word.Document
|
||||||
|
Set Document = document_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Application() As Word.Application
|
||||||
|
Set Application = application_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get IsUIPaused() As Boolean
|
||||||
|
IsUIPaused = isPaused_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function DisableMessages()
|
||||||
|
bSilent_ = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function EnableMessages()
|
||||||
|
bSilent_ = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CreateApplication(Optional bIsVisible As Boolean = True) As Word.Application
|
||||||
|
Call ResetDocument
|
||||||
|
Set application_ = CreateObject("Word.Application")
|
||||||
|
bOwnsApp_ = True
|
||||||
|
application_.Visible = bIsVisible
|
||||||
|
Set CreateApplication = application_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetApplication(target As Word.Application)
|
||||||
|
If ObjPtr(application_) = ObjPtr(target) Then _
|
||||||
|
Exit Function
|
||||||
|
Call ResetDocument
|
||||||
|
Set application_ = target
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetDocument(target As Word.Document, Optional bOwnership = False)
|
||||||
|
If ObjPtr(target) = ObjPtr(document_) Then
|
||||||
|
bOwnsDoc_ = bOwnership
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
If Not target Is Nothing Then _
|
||||||
|
Call SetApplication(target.Application)
|
||||||
|
bOwnsDoc_ = bOwnership
|
||||||
|
Set document_ = target
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function PauseUI()
|
||||||
|
If application_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Debug.Assert Not isPaused_
|
||||||
|
isPaused_ = True
|
||||||
|
screenUpdate_ = application_.ScreenUpdating
|
||||||
|
proofing_ = document_.Range.NoProofing
|
||||||
|
application_.ScreenUpdating = False
|
||||||
|
document_.Range.NoProofing = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ResumeUI()
|
||||||
|
If application_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
isPaused_ = False
|
||||||
|
application_.ScreenUpdating = screenUpdate_
|
||||||
|
document_.Range.NoProofing = proofing_
|
||||||
|
Call application_.ScreenRefresh
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Run(sCommand$, ParamArray vArgs() As Variant) As Variant
|
||||||
|
Dim nArgCount&: nArgCount = UBound(vArgs) - LBound(vArgs) + 1
|
||||||
|
Select Case nArgCount
|
||||||
|
Case 0: Run = application_.Run(sCommand)
|
||||||
|
Case 1: Run = application_.Run(sCommand, vArgs(0))
|
||||||
|
Case 2: Run = application_.Run(sCommand, vArgs(0), vArgs(1))
|
||||||
|
Case 3: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2))
|
||||||
|
Case 4: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2), vArgs(3))
|
||||||
|
Case 5: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4))
|
||||||
|
Case 6: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5))
|
||||||
|
Case 7: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6))
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function NewDocument(Optional sTemplate$ = vbNullString, _
|
||||||
|
Optional bDefaultIfFail As Boolean = True) As Word.Document
|
||||||
|
If Not document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
If application_ Is Nothing Then
|
||||||
|
bCloseOnFail_ = True
|
||||||
|
Call CreateApplication
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error GoTo TRY_DEFAULT
|
||||||
|
|
||||||
|
Set document_ = application_.Documents.Add(sTemplate)
|
||||||
|
bOwnsDoc_ = True
|
||||||
|
Set NewDocument = document_
|
||||||
|
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
TRY_DEFAULT:
|
||||||
|
If sTemplate <> vbNullString Then
|
||||||
|
Call Report("Íå óäàëîñü ñîçäàòü äîêóìåíò èç øàáëîíà " & sTemplate)
|
||||||
|
If Not bDefaultIfFail Then
|
||||||
|
If bCloseOnFail_ Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
Else
|
||||||
|
On Error GoTo ERR_DEFAULT
|
||||||
|
Set document_ = application_.Documents.Add
|
||||||
|
Set NewDocument = document_
|
||||||
|
End If
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
ERR_DEFAULT:
|
||||||
|
Call Report("Íå óäàëîñü ñîçäàòü íîâûé äîêóìåíò èç áàçîâîãî øàáëîíà")
|
||||||
|
If bCloseOnFail_ Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function OpenDocument(sFile$, _
|
||||||
|
Optional bReadOnly As Boolean = False, _
|
||||||
|
Optional bTrackMRU As Boolean = False) As Word.Document
|
||||||
|
If Not document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim bResetApplication As Boolean
|
||||||
|
bResetApplication = application_ Is Nothing
|
||||||
|
If bResetApplication Then _
|
||||||
|
Call DefaultApplication
|
||||||
|
|
||||||
|
If TryAlreadyOpened(sFile, bReadOnly) Then
|
||||||
|
Set OpenDocument = document_
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
If Not TestFile(sFile, bReadOnly) Then _
|
||||||
|
GoTo SAFE_EXIT
|
||||||
|
|
||||||
|
On Error GoTo SAFE_EXIT
|
||||||
|
Set document_ = application_.Documents.Open(sFile, AddToRecentFiles:=bTrackMRU, ReadOnly:=bReadOnly)
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not document_ Is Nothing Then
|
||||||
|
bOwnsDoc_ = True
|
||||||
|
Set OpenDocument = document_
|
||||||
|
Else
|
||||||
|
SAFE_EXIT:
|
||||||
|
bOwnsDoc_ = False
|
||||||
|
If bCloseOnFail_ Then _
|
||||||
|
Call KillApplication
|
||||||
|
If bResetApplication Then _
|
||||||
|
Set application_ = Nothing
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ReleaseApplication()
|
||||||
|
If bOwnsApp_ Then _
|
||||||
|
Call KillApplication
|
||||||
|
Call ResetDocument
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ReleaseDocument(Optional bCloseApplication As Boolean = True, _
|
||||||
|
Optional bSaveChanges As Boolean = False) As Boolean
|
||||||
|
ReleaseDocument = False
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If Not bOwnsDoc_ Then
|
||||||
|
Set document_ = Nothing
|
||||||
|
ReleaseDocument = True
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error GoTo EXIT_FUNC
|
||||||
|
Call document_.Close(SaveChanges:=(bSaveChanges And document_.Path <> vbNullString))
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
Set document_ = Nothing
|
||||||
|
ReleaseDocument = True
|
||||||
|
If (bCloseApplication And application_.Documents.Count = 0) Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
EXIT_FUNC:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SaveAs(sTargetName$) As Boolean
|
||||||
|
SaveAs = False
|
||||||
|
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Dim nFormat As WdSaveFormat
|
||||||
|
If GetSaveFormat(fso.GetExtensionName(sTargetName), nFormat) Then
|
||||||
|
Call document_.SaveAs2(sTargetName, FileFormat:=nFormat)
|
||||||
|
Else
|
||||||
|
Call document_.SaveAs2(sTargetName)
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
SaveAs = True
|
||||||
|
Exit Function
|
||||||
|
RETURN_FALSE:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =======
|
||||||
|
Private Function Report(sMsg$)
|
||||||
|
If bSilent_ Then
|
||||||
|
Debug.Print "WordWrapper: " & sMsg
|
||||||
|
Exit Function
|
||||||
|
ElseIf reporter_ Is Nothing Then
|
||||||
|
Call MsgBox(sMsg, vbExclamation)
|
||||||
|
Else
|
||||||
|
Call reporter_.Report(sMsg, vbExclamation)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function DefaultApplication()
|
||||||
|
bOwnsApp_ = False
|
||||||
|
On Error GoTo CREATE_NEW_APPL
|
||||||
|
Set application_ = GetObject(, "Word.Application")
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
CREATE_NEW_APPL:
|
||||||
|
Set application_ = CreateObject("Word.Application")
|
||||||
|
bOwnsApp_ = True
|
||||||
|
bCloseOnFail_ = True
|
||||||
|
application_.Visible = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function KillApplication()
|
||||||
|
Const HIDE_SHELL = 0
|
||||||
|
Const WAIT_RETURN = True
|
||||||
|
|
||||||
|
On Error GoTo OFFICE_QUIT
|
||||||
|
|
||||||
|
Dim nThreadID&, nProcID&
|
||||||
|
Call application_.Documents.Add ' Create new document for ActiveWindow to become available
|
||||||
|
nThreadID = GetWindowThreadProcessId(application_.ActiveWindow.Hwnd, nProcID)
|
||||||
|
|
||||||
|
Dim iShell As Object: Set iShell = VBA.CreateObject("WScript.Shell")
|
||||||
|
If iShell.Run("TaskKill /F /PID " & nProcID, HIDE_SHELL, WAIT_RETURN) = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
OFFICE_QUIT:
|
||||||
|
Call application_.Quit(SaveChanges:=False)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TryAlreadyOpened(sFile$, bReadOnly As Boolean) As Boolean
|
||||||
|
TryAlreadyOpened = False
|
||||||
|
|
||||||
|
Set document_ = FindByName(sFile)
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If document_.ReadOnly And Not bReadOnly Then
|
||||||
|
Set document_ = Nothing
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
bOwnsDoc_ = False
|
||||||
|
TryAlreadyOpened = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TestFile(sFile$, bReadOnly As Boolean) As Boolean
|
||||||
|
Const ATTRIBUTE_READONLY = 1
|
||||||
|
TestFile = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FileExists(sFile) Then
|
||||||
|
Call Report("Ôàéë íå íàéäåí: " & sFile)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
If Not bReadOnly Then
|
||||||
|
If fso.GetFile(sFile).Attributes And ATTRIBUTE_READONLY Then
|
||||||
|
Call Report("Ôàéë ReadOnly: " & sFile)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
If IsFileInUse(sFile) Then
|
||||||
|
Call Report("Ôàéë çàáëîêèðîâàí: " & sFile)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
TestFile = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function FindByName(sName$) As Word.Document
|
||||||
|
Dim aDoc As Word.Document
|
||||||
|
For Each aDoc In application_.Documents
|
||||||
|
If aDoc.FullName = sName Then
|
||||||
|
Set FindByName = aDoc
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next aDoc
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IsFileInUse(sFileName$) As Boolean
|
||||||
|
Dim nFile%: nFile = FreeFile
|
||||||
|
On Error Resume Next
|
||||||
|
|
||||||
|
Open sFileName For Binary Access Read Lock Read Write As #nFile
|
||||||
|
Close #nFile
|
||||||
|
IsFileInUse = Err.Number > 0
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GetSaveFormat(sExtension$, ByRef nFormat As WdSaveFormat) As Boolean
|
||||||
|
GetSaveFormat = True
|
||||||
|
Select Case VBA.UCase(sExtension)
|
||||||
|
Case "DOCX": nFormat = wdFormatXMLDocument
|
||||||
|
Case "DOCM": nFormat = wdFormatXMLDocumentMacroEnabled
|
||||||
|
Case "DOTX": nFormat = wdFormatXMLTemplate
|
||||||
|
Case "DOTM": nFormat = wdFormatXMLTemplateMacroEnabled
|
||||||
|
Case "TXT": nFormat = wdFormatText
|
||||||
|
Case "PDF": nFormat = wdFormatPDF
|
||||||
|
Case Else: GetSaveFormat = False
|
||||||
|
End Select
|
||||||
|
End Function
|
412
api/API_XLWrapper.cls
Normal file
412
api/API_XLWrapper.cls
Normal file
|
@ -0,0 +1,412 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_XLWrapper"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ================ Êëàññ-îáîëî÷êà äëÿ äîêóìåíòà MS Excel =========================
|
||||||
|
' Shared module version: 20220421
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting, Excel
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function GetWindowThreadProcessId Lib "user32" (ByVal nHwnd As Long, nProcID As Long) As Long
|
||||||
|
|
||||||
|
Private application_ As Excel.Application
|
||||||
|
Private document_ As Excel.Workbook
|
||||||
|
Private reporter_ As Object
|
||||||
|
Private bSilent_ As Boolean
|
||||||
|
|
||||||
|
Private isPaused_ As Boolean
|
||||||
|
Private calculation_ As Excel.XlCalculation
|
||||||
|
|
||||||
|
Private bCloseOnFail_ As Boolean
|
||||||
|
Private bOwnsDoc_ As Boolean
|
||||||
|
Private bOwnsApp_ As Boolean
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Call ResetAll
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub Class_Terminate()
|
||||||
|
If application_ Is Nothing Then _
|
||||||
|
Exit Sub
|
||||||
|
' Note: application could be owned by another Wrapper and already quit
|
||||||
|
On Error GoTo SKIP_RELEASE
|
||||||
|
If application_.Workbooks.Count = 0 Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
SKIP_RELEASE:
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function ResetAll()
|
||||||
|
Call ResetDocument
|
||||||
|
|
||||||
|
calculation_ = xlCalculationAutomatic
|
||||||
|
bSilent_ = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ResetDocument()
|
||||||
|
Set application_ = Nothing
|
||||||
|
Set document_ = Nothing
|
||||||
|
bOwnsDoc_ = False
|
||||||
|
bOwnsApp_ = False
|
||||||
|
bCloseOnFail_ = False
|
||||||
|
isPaused_ = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Reporter object should implement callback function Report(sMsg$, nFlags&)
|
||||||
|
Public Function SetReporter(aReporter As Object)
|
||||||
|
Set reporter_ = aReporter
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get Document() As Excel.Workbook
|
||||||
|
Set Document = document_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Application() As Excel.Application
|
||||||
|
Set Application = application_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get IsUIPaused() As Boolean
|
||||||
|
IsUIPaused = isPaused_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function DisableMessages()
|
||||||
|
bSilent_ = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function EnableMessages()
|
||||||
|
bSilent_ = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CreateApplication(Optional bIsVisible As Boolean = True) As Excel.Application
|
||||||
|
Call ResetDocument
|
||||||
|
Set application_ = CreateObject("Excel.Application")
|
||||||
|
bOwnsApp_ = True
|
||||||
|
application_.Visible = bIsVisible
|
||||||
|
Set CreateApplication = application_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetApplication(target As Excel.Application)
|
||||||
|
If ObjPtr(application_) = ObjPtr(target) Then _
|
||||||
|
Exit Function
|
||||||
|
Call ResetDocument
|
||||||
|
Set application_ = target
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetDocument(target As Excel.Workbook, Optional bOwnership = False)
|
||||||
|
If ObjPtr(target) = ObjPtr(document_) Then
|
||||||
|
bOwnsDoc_ = bOwnership
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
If Not target Is Nothing Then _
|
||||||
|
Call SetApplication(target.Application)
|
||||||
|
bOwnsDoc_ = bOwnership
|
||||||
|
Set document_ = target
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function PauseUI()
|
||||||
|
If application_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Debug.Assert Not isPaused_
|
||||||
|
isPaused_ = True
|
||||||
|
calculation_ = application_.Calculation
|
||||||
|
application_.Calculation = xlCalculationManual
|
||||||
|
application_.ScreenUpdating = False
|
||||||
|
application_.EnableEvents = False
|
||||||
|
application_.AskToUpdateLinks = False
|
||||||
|
application_.Cursor = xlWait
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ResumeUI()
|
||||||
|
If application_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
isPaused_ = False
|
||||||
|
application_.Cursor = xlNormal
|
||||||
|
application_.AskToUpdateLinks = True
|
||||||
|
application_.EnableEvents = True
|
||||||
|
application_.ScreenUpdating = True
|
||||||
|
application_.Calculation = calculation_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Run(sFunction$, ParamArray vArgs() As Variant) As Variant
|
||||||
|
Dim sCommand$: sCommand = "'" & document_.Name & "'" & "!" & sFunction
|
||||||
|
Dim nArgCount&: nArgCount = UBound(vArgs) - LBound(vArgs) + 1
|
||||||
|
Select Case nArgCount
|
||||||
|
Case 0: Run = application_.Run(sCommand)
|
||||||
|
Case 1: Run = application_.Run(sCommand, vArgs(0))
|
||||||
|
Case 2: Run = application_.Run(sCommand, vArgs(0), vArgs(1))
|
||||||
|
Case 3: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2))
|
||||||
|
Case 4: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2), vArgs(3))
|
||||||
|
Case 5: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4))
|
||||||
|
Case 6: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5))
|
||||||
|
Case 7: Run = application_.Run(sCommand, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6))
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function NewDocument(Optional sTemplate$ = vbNullString, _
|
||||||
|
Optional bDefaultIfFail As Boolean = True) As Excel.Workbook
|
||||||
|
If Not document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If application_ Is Nothing Then
|
||||||
|
bCloseOnFail_ = True
|
||||||
|
Call CreateApplication
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error GoTo TRY_DEFAULT
|
||||||
|
|
||||||
|
Set document_ = application_.Workbooks.Add(sTemplate)
|
||||||
|
bOwnsDoc_ = True
|
||||||
|
Set NewDocument = document_
|
||||||
|
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
TRY_DEFAULT:
|
||||||
|
If sTemplate <> vbNullString Then
|
||||||
|
Call Report("Íå óäàëîñü ñîçäàòü äîêóìåíò èç øàáëîíà " & sTemplate)
|
||||||
|
If Not bDefaultIfFail Then
|
||||||
|
If bCloseOnFail_ Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
Else
|
||||||
|
On Error GoTo ERR_DEFAULT
|
||||||
|
Set document_ = application_.Workbooks.Add
|
||||||
|
Set NewDocument = document_
|
||||||
|
End If
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
ERR_DEFAULT:
|
||||||
|
Call Report("Íå óäàëîñü ñîçäàòü íîâûé äîêóìåíò èç áàçîâîãî øàáëîíà")
|
||||||
|
If bCloseOnFail_ Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function OpenDocument(sFile$, _
|
||||||
|
Optional bReadOnly As Boolean = False, _
|
||||||
|
Optional bTrackMRU As Boolean = False, _
|
||||||
|
Optional bIgnoreFolder As Boolean = False) As Excel.Workbook
|
||||||
|
If Not document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim bResetApplication As Boolean
|
||||||
|
bResetApplication = application_ Is Nothing
|
||||||
|
If bResetApplication Then _
|
||||||
|
Call DefaultApplication
|
||||||
|
|
||||||
|
If TryAlreadyOpened(sFile, bReadOnly, bIgnoreFolder) Then
|
||||||
|
Set OpenDocument = document_
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
If Not TestFile(sFile, bReadOnly, bIgnoreFolder) Then _
|
||||||
|
GoTo SAFE_EXIT
|
||||||
|
|
||||||
|
On Error GoTo SAFE_EXIT
|
||||||
|
If sFile Like "*.csv" Then
|
||||||
|
Set document_ = application_.Workbooks.Open(sFile, addToMRU:=bTrackMRU, ReadOnly:=bReadOnly, Editable:=True, Format:=6, Delimiter:=",")
|
||||||
|
Else
|
||||||
|
Set document_ = application_.Workbooks.Open(sFile, addToMRU:=bTrackMRU, ReadOnly:=bReadOnly, Editable:=True)
|
||||||
|
End If
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not document_ Is Nothing Then
|
||||||
|
bOwnsDoc_ = True
|
||||||
|
Set OpenDocument = document_
|
||||||
|
Else
|
||||||
|
SAFE_EXIT:
|
||||||
|
bOwnsDoc_ = False
|
||||||
|
If bCloseOnFail_ Then _
|
||||||
|
Call KillApplication
|
||||||
|
If bResetApplication Then _
|
||||||
|
Set application_ = Nothing
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ReleaseApplication()
|
||||||
|
If bOwnsApp_ Then _
|
||||||
|
Call KillApplication
|
||||||
|
Call ResetDocument
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ReleaseDocument(Optional bCloseApplication As Boolean = True, _
|
||||||
|
Optional bSaveChanges As Boolean = False) As Boolean
|
||||||
|
ReleaseDocument = False
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If Not bOwnsDoc_ Then
|
||||||
|
Set document_ = Nothing
|
||||||
|
ReleaseDocument = True
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error GoTo EXIT_FUNC
|
||||||
|
Call document_.Close(SaveChanges:=(bSaveChanges And document_.Path <> vbNullString))
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
Set document_ = Nothing
|
||||||
|
ReleaseDocument = True
|
||||||
|
If bCloseApplication And application_.Workbooks.Count = 0 Then _
|
||||||
|
Call ReleaseApplication
|
||||||
|
EXIT_FUNC:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SaveAs(sTargetName$) As Boolean
|
||||||
|
SaveAs = False
|
||||||
|
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim bDisplayAlerts As Boolean: bDisplayAlerts = application_.DisplayAlerts
|
||||||
|
application_.DisplayAlerts = False
|
||||||
|
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Dim nFormat As Excel.XlFileFormat
|
||||||
|
If GetSaveFormat(fso.GetExtensionName(sTargetName), nFormat) Then
|
||||||
|
Call document_.SaveAs(sTargetName, FileFormat:=nFormat, ConflictResolution:=xlLocalSessionChanges)
|
||||||
|
Else
|
||||||
|
Call document_.SaveAs(sTargetName, ConflictResolution:=xlLocalSessionChanges)
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
application_.DisplayAlerts = bDisplayAlerts
|
||||||
|
SaveAs = True
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
RETURN_FALSE:
|
||||||
|
application_.DisplayAlerts = bDisplayAlerts
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========
|
||||||
|
Private Function Report(sMsg$)
|
||||||
|
If bSilent_ Then
|
||||||
|
Debug.Print "XLWrapper: " & sMsg
|
||||||
|
Exit Function
|
||||||
|
ElseIf reporter_ Is Nothing Then
|
||||||
|
Call MsgBox(sMsg, vbExclamation)
|
||||||
|
Else
|
||||||
|
Call reporter_.Report(sMsg, vbExclamation)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function DefaultApplication()
|
||||||
|
bOwnsApp_ = False
|
||||||
|
On Error GoTo CREATE_NEW_APPL
|
||||||
|
Set application_ = GetObject(, "Excel.Application")
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
CREATE_NEW_APPL:
|
||||||
|
Set application_ = CreateObject("Excel.Application")
|
||||||
|
bOwnsApp_ = True
|
||||||
|
bCloseOnFail_ = True
|
||||||
|
application_.Visible = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function KillApplication()
|
||||||
|
Const HIDE_SHELL = 0
|
||||||
|
Const WAIT_RETURN = True
|
||||||
|
|
||||||
|
On Error GoTo OFFICE_QUIT
|
||||||
|
|
||||||
|
Dim nThreadID&, nProcID&
|
||||||
|
nThreadID = GetWindowThreadProcessId(application_.Hwnd, nProcID)
|
||||||
|
|
||||||
|
Dim iShell As Object: Set iShell = VBA.CreateObject("WScript.Shell")
|
||||||
|
If iShell.Run("TaskKill /F /PID " & nProcID, HIDE_SHELL, WAIT_RETURN) = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
OFFICE_QUIT:
|
||||||
|
Call application_.Quit
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TryAlreadyOpened(sFile$, bReadOnly As Boolean, bIgnoreFolder As Boolean) As Boolean
|
||||||
|
TryAlreadyOpened = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Set document_ = FindByName(IIf(bIgnoreFolder, fso.GetFileName(sFile), sFile), bIgnoreFolder)
|
||||||
|
If document_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If document_.ReadOnly And Not bReadOnly Then
|
||||||
|
Set document_ = Nothing
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
bOwnsDoc_ = False
|
||||||
|
TryAlreadyOpened = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TestFile(sFile$, bReadOnly As Boolean, bIgnoreFolder As Boolean) As Boolean
|
||||||
|
Const ATTRIBUTE_READONLY = 1
|
||||||
|
TestFile = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not bIgnoreFolder And Not fso.FileExists(sFile) Then
|
||||||
|
Call Report("Ôàéë íå íàéäåí: " & sFile)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
If Not bReadOnly And fso.FileExists(sFile) Then
|
||||||
|
If fso.GetFile(sFile).Attributes And ATTRIBUTE_READONLY Then
|
||||||
|
Call Report("Ôàéë ReadOnly: " & sFile)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
If Not bIgnoreFolder Then
|
||||||
|
If IsFileInUse(sFile) Then
|
||||||
|
Call Report("Ôàéë çàáëîêèðîâàí: " & sFile)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
TestFile = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function FindByName(sName$, bIgnoreFolder As Boolean) As Excel.Workbook
|
||||||
|
Dim aBook As Excel.Workbook
|
||||||
|
For Each aBook In application_.Workbooks
|
||||||
|
If bIgnoreFolder And aBook.Name = sName Or _
|
||||||
|
Not bIgnoreFolder And aBook.FullName = sName Then
|
||||||
|
Set FindByName = aBook
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next aBook
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IsFileInUse(sFileName$) As Boolean
|
||||||
|
Dim nFile%: nFile = FreeFile
|
||||||
|
On Error Resume Next
|
||||||
|
|
||||||
|
Open sFileName For Binary Access Read Lock Read Write As #nFile
|
||||||
|
Close #nFile
|
||||||
|
IsFileInUse = Err.Number > 0
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GetSaveFormat(sExtension$, ByRef nFormat As Excel.XlFileFormat) As Boolean
|
||||||
|
GetSaveFormat = True
|
||||||
|
Select Case VBA.UCase(sExtension)
|
||||||
|
Case "XLSX": nFormat = xlOpenXMLWorkbook
|
||||||
|
Case "XLSM": nFormat = xlOpenXMLWorkbookMacroEnabled
|
||||||
|
Case "XLTX": nFormat = xlOpenXMLTemplate
|
||||||
|
Case "XLTM": nFormat = xlOpenXMLTemplateMacroEnabled
|
||||||
|
Case "XLAM": nFormat = xlOpenXMLAddIn
|
||||||
|
Case "TXT": nFormat = xlText
|
||||||
|
Case "CSV": nFormat = xlCSV
|
||||||
|
Case "PDF": nFormat = xlTypePDF
|
||||||
|
Case Else: GetSaveFormat = False
|
||||||
|
End Select
|
||||||
|
End Function
|
117
api/ex_ConceptCore.bas
Normal file
117
api/ex_ConceptCore.bas
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
Attribute VB_Name = "ex_ConceptCore"
|
||||||
|
'================ CCL wrapper =========================
|
||||||
|
' Shared module version: 20220713
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const CONCEPT_DLL_LOCATION = "C:\Tools\dll"
|
||||||
|
|
||||||
|
#If Win64 Then
|
||||||
|
Private Const CCL_DLL_NAME = "ConceptCore64.dll"
|
||||||
|
Private Declare PtrSafe Function ConvertToASCII Lib "ConceptCore64.dll" ( _
|
||||||
|
ByRef vResult As Variant, ByRef vText As Variant) As Long
|
||||||
|
Private Declare PtrSafe Function ConvertToMath Lib "ConceptCore64.dll" ( _
|
||||||
|
ByRef vResult As Variant, ByVal sText As String) As Long
|
||||||
|
Private Declare PtrSafe Function ASTasText Lib "ConceptCore64.dll" ( _
|
||||||
|
ByRef vResult As Variant, ByVal sText As String) As Long
|
||||||
|
#Else
|
||||||
|
Private Const CCL_DLL_NAME = "ConceptCore32.dll"
|
||||||
|
Private Declare PtrSafe Function ConvertToASCII Lib "ConceptCore32.dll" ( _
|
||||||
|
ByRef vResult As Variant, ByRef vText As Variant) As Long
|
||||||
|
Private Declare PtrSafe Function ConvertToMath Lib "ConceptCore32.dll" ( _
|
||||||
|
ByRef vResult As Variant, ByVal sText As String) As Long
|
||||||
|
Private Declare PtrSafe Function ASTasText Lib "ConceptCore32.dll" ( _
|
||||||
|
ByRef vResult As Variant, ByVal sText As String) As Long
|
||||||
|
#End If
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal sLibrary As String) As Long
|
||||||
|
Private Declare PtrSafe Function FreeLibrary Lib "kernel32" (ByVal nLibraryHandle As Long) As Long
|
||||||
|
Private Const HRESULT_OK = 0
|
||||||
|
|
||||||
|
Private g_IsConceptLoaded As Boolean
|
||||||
|
|
||||||
|
Public Function LoadConceptCore()
|
||||||
|
If g_IsConceptLoaded Then _
|
||||||
|
Exit Function
|
||||||
|
g_IsConceptLoaded = True
|
||||||
|
|
||||||
|
If LoadLibrary(CCL_DLL_NAME) <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
If LoadLibrary(CONCEPT_DLL_LOCATION & "\" & CCL_DLL_NAME) <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
' TODO: remove fallback after some time
|
||||||
|
' fallback path for earlier versions of distribution
|
||||||
|
If LoadLibrary(VBA.Environ("USERPROFILE") & "\.concept\dll\" & CCL_DLL_NAME) = 0 Then _
|
||||||
|
Call Err.Raise(1, Description:="Could not load " & CCL_DLL_NAME)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function UnloadConceptCore()
|
||||||
|
If Not g_IsConceptLoaded Then _
|
||||||
|
Exit Function
|
||||||
|
Dim nHandle&: nHandle = LoadLibrary(CCL_DLL_NAME)
|
||||||
|
Call FreeLibrary(nHandle)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function MathToASCII(sText$) As String
|
||||||
|
On Error GoTo HANDLE_ERROR
|
||||||
|
|
||||||
|
LoadConceptCore
|
||||||
|
|
||||||
|
Dim vText As Variant: vText = sText
|
||||||
|
Dim vResult As Variant
|
||||||
|
If ConvertToASCII(vResult, vText) <> HRESULT_OK Then _
|
||||||
|
MathToASCII = "ERR: "
|
||||||
|
MathToASCII = MathToASCII & vResult
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
HANDLE_ERROR:
|
||||||
|
Debug.Print Err.Description
|
||||||
|
MathToASCII = Err.Description
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ASCIItoMath(sText$) As String
|
||||||
|
On Error GoTo HANDLE_ERROR
|
||||||
|
|
||||||
|
LoadConceptCore
|
||||||
|
|
||||||
|
Dim vResult As Variant
|
||||||
|
If ConvertToMath(vResult, sText) <> HRESULT_OK Then _
|
||||||
|
ASCIItoMath = "ERR: "
|
||||||
|
ASCIItoMath = ASCIItoMath & " " & vResult
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
HANDLE_ERROR:
|
||||||
|
Debug.Print Err.Description
|
||||||
|
ASCIItoMath = Err.Description
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ASCIItoAST(sText$) As String
|
||||||
|
On Error GoTo HANDLE_ERROR
|
||||||
|
|
||||||
|
LoadConceptCore
|
||||||
|
|
||||||
|
Dim vResult As Variant
|
||||||
|
If ASTasText(vResult, sText) <> HRESULT_OK Then _
|
||||||
|
ASCIItoAST = "ERR: "
|
||||||
|
ASCIItoAST = ASCIItoAST & vResult
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
HANDLE_ERROR:
|
||||||
|
Debug.Print Err.Description
|
||||||
|
ASCIItoAST = Err.Description
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
40
api/ex_Metadata.bas
Normal file
40
api/ex_Metadata.bas
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
Attribute VB_Name = "ex_Metadata"
|
||||||
|
'================ Ìîäóëü äëÿ ðàáîòû ñ äàííûìè äîêóìåíòà =============
|
||||||
|
' Shared module version: 20210506
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Function MetadataExists(iDoc As Object, varName$) As Boolean
|
||||||
|
Dim aVar As Variant
|
||||||
|
For Each aVar In iDoc.Variables
|
||||||
|
If aVar.Name = varName Then
|
||||||
|
MetadataExists = True
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next aVar
|
||||||
|
MetadataExists = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetMetadata(iDoc As Object, varName$) As Variant
|
||||||
|
Dim aVar As Variant
|
||||||
|
For Each aVar In iDoc.Variables
|
||||||
|
If aVar.Name = varName Then
|
||||||
|
GetMetadata = aVar.Value
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next aVar
|
||||||
|
GetMetadata = ""
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetMetadata(iDoc As Object, varName$, val As Variant)
|
||||||
|
Dim aVar As Variant
|
||||||
|
For Each aVar In iDoc.Variables
|
||||||
|
If aVar.Name = varName Then
|
||||||
|
aVar.Value = val
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next aVar
|
||||||
|
Call iDoc.Variables.Add(varName, val)
|
||||||
|
End Function
|
125
api/ex_Python.bas
Normal file
125
api/ex_Python.bas
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
Attribute VB_Name = "ex_Python"
|
||||||
|
' ======== Python runner extension ========
|
||||||
|
' Shared module version: 20240330
|
||||||
|
' Tested in: PythonManager
|
||||||
|
' Depends on: ex_VBA, ex_WinAPI, API_Config
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
' Switch flag for Debug purposes
|
||||||
|
Private Const SHOW_PYTHON = False
|
||||||
|
|
||||||
|
Public Const PY_KEY_INTERPRETER = "Python3"
|
||||||
|
Public Const PY_KEY_SOURCE_PATH = "PythonImports"
|
||||||
|
|
||||||
|
Public Const PY_MODULE_TEXT = "cctext"
|
||||||
|
Public Const PY_MODULE_UTILS = "vbatopy"
|
||||||
|
|
||||||
|
Public Const PY_DEFVALUE_INTERPRETER = "C:\Tools\Python312-venv\Scripts\python.exe"
|
||||||
|
|
||||||
|
Public Function AccessPython() As API_Python
|
||||||
|
Static s_Python As API_Python
|
||||||
|
If s_Python Is Nothing Then
|
||||||
|
Set s_Python = New API_Python
|
||||||
|
Call s_Python.Init( _
|
||||||
|
iPython:=ConceptConfig.GetValue(PY_KEY_INTERPRETER), _
|
||||||
|
sModules:=OfficeActivePath & ";" & ConceptConfig.GetValue(PY_KEY_SOURCE_PATH), _
|
||||||
|
bDoDebug:=SHOW_PYTHON)
|
||||||
|
End If
|
||||||
|
Set AccessPython = s_Python
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ConceptConfig() As API_Config
|
||||||
|
Dim iConfig As New API_Config
|
||||||
|
Set ConceptConfig = iConfig
|
||||||
|
If Not iConfig.LoadFromFile(ConceptConfigPath()) Then _
|
||||||
|
Call LoadDefaultConfig(iConfig)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Runs the Python command, e.g.: Call RunPython("import bar; bar.foo()", ThisWorkbook.Path)
|
||||||
|
Public Function RunPyStandalone(sPyCommand$, Optional sWorkDir$ = vbNullString) As Long
|
||||||
|
Dim iConfig As API_Config: Set iConfig = ConceptConfig
|
||||||
|
Dim iPython$: iPython = iConfig.GetValue(PY_KEY_INTERPRETER)
|
||||||
|
Dim sSource$
|
||||||
|
If Not sWorkDir = vbNullString Then _
|
||||||
|
sSource = sWorkDir & ";"
|
||||||
|
sSource = sSource & iConfig.GetValue(PY_KEY_SOURCE_PATH)
|
||||||
|
If sSource <> "" Then _
|
||||||
|
sSource = sSource & ";"
|
||||||
|
sSource = VBA.Replace(sSource, "&", "^&")
|
||||||
|
sSource = VBA.Replace(sSource, "\", "\\")
|
||||||
|
|
||||||
|
Dim sChangeDir$: sChangeDir = GenerateCDCommandFor(iPython)
|
||||||
|
Dim sExtendedPyCommand$: sExtendedPyCommand = Fmt( _
|
||||||
|
"{1} & ""{2}"" -B -c " & _
|
||||||
|
"""import sys, os; sys.path[0:0]=os.path.normcase(os.path.expandvars(\""{3}\"")).split(';'); " & _
|
||||||
|
"{4}""", _
|
||||||
|
sChangeDir, iPython, sSource, sPyCommand)
|
||||||
|
|
||||||
|
RunPyStandalone = PyExecute(sExtendedPyCommand)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Runs a Python executable that has been frozen by PyInstaller and the like
|
||||||
|
' Call RunFrozenPython("C:\path\to\frozen_executable.exe", "arg1 arg2")
|
||||||
|
Public Function RunPyFrozen(iPython$, Optional sArgs$ = vbNullString) As Long
|
||||||
|
Dim sChangeDir$: sChangeDir = GenerateCDCommandFor(iPython)
|
||||||
|
Dim sPyCommand$: sPyCommand = Fmt("{1} & ""{2}"" {3}", sChangeDir, iPython, sArgs)
|
||||||
|
RunPyFrozen = PyExecute(sPyCommand)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ConceptConfigPath() As String
|
||||||
|
ConceptConfigPath = VBA.Environ$("USERPROFILE") & "\.concept\concept-options.json"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========
|
||||||
|
Private Function PyExecute(sPyCommand$) As Long
|
||||||
|
Dim sLog$: sLog = GetLogFile()
|
||||||
|
|
||||||
|
Dim sShellCommand$: sShellCommand = Fmt("cmd.exe /C {1} 2> ""{3}""", _
|
||||||
|
sPyCommand, sLog)
|
||||||
|
|
||||||
|
Dim nWindowStyle%: nWindowStyle = IIf(SHOW_PYTHON, 1, 0)
|
||||||
|
Dim iShell As Object: Set iShell = CreateObject("WScript.Shell")
|
||||||
|
Dim nExitCode&: nExitCode = iShell.Run(sShellCommand, nWindowStyle, True)
|
||||||
|
|
||||||
|
If nExitCode <> 0 Then
|
||||||
|
Call MsgBox(ReadTextFile(sLog), vbCritical)
|
||||||
|
PyExecute = -1
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
Call Kill(sLog)
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
Set iShell = Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function LoadDefaultConfig(ByRef iConfig As API_Config)
|
||||||
|
Call iConfig.SetValue(PY_KEY_INTERPRETER, PY_DEFVALUE_INTERPRETER)
|
||||||
|
Call iConfig.SetValue(PY_KEY_SOURCE_PATH, "")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GenerateCDCommandFor(sFile$) As String
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
GenerateCDCommandFor = Fmt("{1} & cd ""{2}""", VBA.Left(sFile, 2), fso.GetParentFolderName(sFile$))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GetLogFile() As String
|
||||||
|
GetLogFile = Fmt("{1}\pymanager-{2}.log", VBA.Environ("Temp"), GetCurrentProcessId())
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ReadTextFile(sFile$) As String
|
||||||
|
Dim adoStream As Object: Set adoStream = CreateObject("ADODB.Stream")
|
||||||
|
adoStream.Charset = "utf-8"
|
||||||
|
Call adoStream.Open
|
||||||
|
|
||||||
|
On Error GoTo ERROR_FILE
|
||||||
|
Call adoStream.LoadFromFile(sFile)
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
ReadTextFile = adoStream.ReadText
|
||||||
|
|
||||||
|
ERROR_FILE:
|
||||||
|
Call adoStream.Close
|
||||||
|
End Function
|
541
api/ex_WinAPI.bas
Normal file
541
api/ex_WinAPI.bas
Normal file
|
@ -0,0 +1,541 @@
|
||||||
|
Attribute VB_Name = "ex_WinAPI"
|
||||||
|
' ================ Ìîäóëü äëÿ ðàáîòû ñ ôóíêöèÿìè óðîâíÿ ïðèëîæåíèÿ =============
|
||||||
|
' Shared module version: 20220812
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting, Shell32
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal nMilliseconds As LongPtr)
|
||||||
|
Public Declare PtrSafe Function GetCurrentProcessId Lib "kernel32" () As Long
|
||||||
|
Public Declare PtrSafe Function GetWindowThreadProcessId Lib "user32" (ByVal nHwnd As Long, nProcID As Long) As Long
|
||||||
|
Public Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
|
||||||
|
Public Declare PtrSafe Function PostMessage Lib "user32" Alias "PostMessageA" _
|
||||||
|
(ByVal nHwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
|
||||||
|
|
||||||
|
Public Declare PtrSafe Function SetTimer Lib "user32" (ByVal nHwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
|
||||||
|
Public Declare PtrSafe Function KillTimer Lib "user32" (ByVal nHwnd As Long, ByVal nDEvent As Long) As Long
|
||||||
|
|
||||||
|
Private Const DATAOBJECT_BINDING$ = "new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}" ' ID Äëÿ îáúåêòà DataObject
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function BringWindowToTop Lib "user32" (ByVal nHwnd As Long) As Long
|
||||||
|
Private Declare PtrSafe Function SetFocus Lib "user32" (ByVal nHwnd As Long) As Long
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function GetCommandLine Lib "kernel32" Alias "GetCommandLineW" () As LongPtr
|
||||||
|
Private Declare PtrSafe Function lstrlenW Lib "kernel32" (ByVal lpString As LongPtr) As Long
|
||||||
|
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (iDestination As Any, iSource As Any, ByVal nLength As LongPtr)
|
||||||
|
|
||||||
|
Public Enum TApplication
|
||||||
|
T_APP_UNDEF = 0
|
||||||
|
[_First] = 1
|
||||||
|
|
||||||
|
T_APP_EXCEL = 1
|
||||||
|
T_APP_WORD = 2
|
||||||
|
T_APP_VISIO = 3
|
||||||
|
|
||||||
|
[_Last] = 3
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Function OfficeCommandLine() As String
|
||||||
|
OfficeCommandLine = String$(lstrlenW(GetCommandLine()), 0)
|
||||||
|
Call CopyMemory(ByVal StrPtr(OfficeCommandLine), ByVal GetCommandLine(), LenB(OfficeCommandLine))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ActivateWindow(sClass$, Optional sWindow$ = vbNullString) As Boolean
|
||||||
|
On Error Resume Next
|
||||||
|
ActivateWindow = False
|
||||||
|
Dim nWindow&: nWindow = FindWindow(sClass, sWindow)
|
||||||
|
If nWindow <= 0 Then _
|
||||||
|
Exit Function
|
||||||
|
ActivateWindow = ActivateWindowHwnd(nWindow)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ActivateWindowHwnd(nWindow&) As Boolean
|
||||||
|
On Error Resume Next
|
||||||
|
ActivateWindowHwnd = False
|
||||||
|
If nWindow <= 0 Then _
|
||||||
|
Exit Function
|
||||||
|
If BringWindowToTop(nWindow) = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Call SetFocus(nWindow)
|
||||||
|
ActivateWindowHwnd = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ApplicationFromExtension(sExtension$) As TApplication
|
||||||
|
ApplicationFromExtension = T_APP_UNDEF
|
||||||
|
Select Case VBA.UCase(sExtension)
|
||||||
|
Case "XLS": ApplicationFromExtension = T_APP_EXCEL
|
||||||
|
Case "XLSX": ApplicationFromExtension = T_APP_EXCEL
|
||||||
|
Case "XLSM": ApplicationFromExtension = T_APP_EXCEL
|
||||||
|
Case "XLTM": ApplicationFromExtension = T_APP_EXCEL
|
||||||
|
Case "RTF": ApplicationFromExtension = T_APP_WORD
|
||||||
|
Case "DOC": ApplicationFromExtension = T_APP_WORD
|
||||||
|
Case "DOCX": ApplicationFromExtension = T_APP_WORD
|
||||||
|
Case "DOCM": ApplicationFromExtension = T_APP_WORD
|
||||||
|
Case "DOTM": ApplicationFromExtension = T_APP_WORD
|
||||||
|
Case "VSDX": ApplicationFromExtension = T_APP_VISIO
|
||||||
|
Case "VSDM": ApplicationFromExtension = T_APP_VISIO
|
||||||
|
Case "VSTX": ApplicationFromExtension = T_APP_VISIO
|
||||||
|
Case "VSTM": ApplicationFromExtension = T_APP_VISIO
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ApplicationTypeFor(iApp As Object) As TApplication
|
||||||
|
ApplicationTypeFor = T_APP_UNDEF
|
||||||
|
Select Case iApp.Name
|
||||||
|
Case "Microsoft Excel": ApplicationTypeFor = T_APP_EXCEL
|
||||||
|
Case "Microsoft Word": ApplicationTypeFor = T_APP_WORD
|
||||||
|
Case "Microsoft Visio": ApplicationTypeFor = T_APP_VISIO
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ApplicationWrapper(iType As TApplication) As Object
|
||||||
|
Dim iApplication As Object: Set iApplication = Application
|
||||||
|
If Application.Name = "Microsoft Visio" Then _
|
||||||
|
Call Err.Raise(vbObjectError, Description:="Visio does not support meta wrappers")
|
||||||
|
Select Case iType
|
||||||
|
Case T_APP_EXCEL: Set ApplicationWrapper = iApplication.Run("CreateWrapperXL")
|
||||||
|
Case T_APP_WORD: Set ApplicationWrapper = iApplication.Run("CreateWrapperWord")
|
||||||
|
Case T_APP_VISIO: Set ApplicationWrapper = iApplication.Run("CreateWrapperVisio")
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' To use application wrapper copy required CreateWrapper functions to Main module and uncomment them
|
||||||
|
'Public Function CreateWrapperXL() As API_XLWrapper
|
||||||
|
' Set CreateWrapperXL = New API_XLWrapper
|
||||||
|
'End Function
|
||||||
|
'
|
||||||
|
'Public Function CreateWrapperWord() As API_WordWrapper
|
||||||
|
' Set CreateWrapperWord = New API_WordWrapper
|
||||||
|
'End Function
|
||||||
|
'
|
||||||
|
'Public Function CreateWrapperVisio() As API_VisioWrapper
|
||||||
|
' Set CreateWrapperVisio = New API_VisioWrapper
|
||||||
|
'End Function
|
||||||
|
|
||||||
|
' Wrapper for active document
|
||||||
|
Public Function OfficeActiveWrapper() As Object
|
||||||
|
Dim activeApp As Object: Set activeApp = Application
|
||||||
|
Dim iWrapper As Object
|
||||||
|
|
||||||
|
Select Case ApplicationTypeFor(Application)
|
||||||
|
Case T_APP_EXCEL
|
||||||
|
Set iWrapper = ApplicationWrapper(T_APP_EXCEL)
|
||||||
|
Call iWrapper.SetDocument(activeApp.ActiveWorkbook)
|
||||||
|
|
||||||
|
Case T_APP_WORD
|
||||||
|
Set iWrapper = ApplicationWrapper(T_APP_WORD)
|
||||||
|
Call iWrapper.SetDocument(activeApp.ActiveDocument)
|
||||||
|
|
||||||
|
Case T_APP_VISIO
|
||||||
|
Set iWrapper = ApplicationWrapper(T_APP_VISIO)
|
||||||
|
Call iWrapper.SetDocument(activeApp.ActiveDocument)
|
||||||
|
End Select
|
||||||
|
|
||||||
|
Set OfficeActiveWrapper = iWrapper
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Wrapper for this document
|
||||||
|
Public Function OfficeThisWrapper() As Object
|
||||||
|
Dim activeApp As Object: Set activeApp = Application
|
||||||
|
Dim iWrapper As Object
|
||||||
|
|
||||||
|
Select Case ApplicationTypeFor(Application)
|
||||||
|
Case T_APP_EXCEL
|
||||||
|
Set iWrapper = ApplicationWrapper(T_APP_EXCEL)
|
||||||
|
Call iWrapper.SetDocument(activeApp.ThisWorkbook)
|
||||||
|
|
||||||
|
Case T_APP_WORD
|
||||||
|
Set iWrapper = ApplicationWrapper(T_APP_WORD)
|
||||||
|
Call iWrapper.SetDocument(activeApp.ThisDocument)
|
||||||
|
|
||||||
|
Case T_APP_VISIO
|
||||||
|
Set iWrapper = ApplicationWrapper(T_APP_VISIO)
|
||||||
|
Call iWrapper.SetDocument(activeApp.ThisDocument)
|
||||||
|
End Select
|
||||||
|
|
||||||
|
Set OfficeThisWrapper = iWrapper
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Path to active document folder
|
||||||
|
Public Function OfficeActivePath() As String
|
||||||
|
Dim activeApp As Object: Set activeApp = Application
|
||||||
|
Select Case ApplicationTypeFor(Application)
|
||||||
|
Case T_APP_EXCEL: OfficeActivePath = activeApp.ActiveWorkbook.Path
|
||||||
|
Case T_APP_WORD: OfficeActivePath = activeApp.ActiveDocument.Path
|
||||||
|
Case T_APP_VISIO: OfficeActivePath = activeApp.ActiveDocument.Path
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Path to this document folder
|
||||||
|
Public Function OfficeThisPath() As String
|
||||||
|
Dim activeApp As Object: Set activeApp = Application
|
||||||
|
Select Case ApplicationTypeFor(Application)
|
||||||
|
Case T_APP_EXCEL: OfficeThisPath = activeApp.ThisWorkbook.Path
|
||||||
|
Case T_APP_WORD: OfficeThisPath = activeApp.ThisDocument.Path
|
||||||
|
Case T_APP_VISIO: OfficeThisPath = activeApp.ThisDocument.Path
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AddToClipboard(sText$)
|
||||||
|
With CreateObject(DATAOBJECT_BINDING)
|
||||||
|
Call .SetText(sText)
|
||||||
|
.PutInClipboard
|
||||||
|
End With
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RetrieveFromClipboard() As String
|
||||||
|
With CreateObject(DATAOBJECT_BINDING)
|
||||||
|
Call .GetFromClipboard
|
||||||
|
RetrieveFromClipboard = .GetText
|
||||||
|
End With
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function WindowsTempFolder() As String
|
||||||
|
WindowsTempFolder = Environ("TEMP")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function EnsureFolderExists(sPath$, Optional ByRef fso As Scripting.FileSystemObject = Nothing)
|
||||||
|
If fso Is Nothing Then _
|
||||||
|
Set fso = New Scripting.FileSystemObject
|
||||||
|
If fso.FolderExists(sPath) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim sParent$: sParent = sPath
|
||||||
|
Do
|
||||||
|
If sParent = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
If VBA.Right(sParent, 1) <> "\" Then _
|
||||||
|
Exit Do
|
||||||
|
sParent = VBA.Left(sParent, VBA.Len(sParent) - 1)
|
||||||
|
Loop
|
||||||
|
|
||||||
|
Dim nDelim&: nDelim = VBA.InStrRev(sParent, "\")
|
||||||
|
If nDelim = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
sParent = VBA.Left(sParent, nDelim - 1)
|
||||||
|
|
||||||
|
Call EnsureFolderExists(sParent, fso)
|
||||||
|
Call fso.CreateFolder(sPath)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CreateTextFileUTF16(sFile$, Optional bOverwrite = False) As Boolean
|
||||||
|
CreateTextFileUTF16 = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not bOverwrite Then _
|
||||||
|
If fso.FileExists(sFile) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo EXIT_FALSE
|
||||||
|
Call fso.CreateTextFile(sFile, Overwrite:=bOverwrite, Unicode:=True).Close
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
CreateTextFileUTF16 = True
|
||||||
|
Exit Function
|
||||||
|
EXIT_FALSE:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AppendTextToFileUTF16(sFile$, sMsg$) As Boolean
|
||||||
|
Const UTF16_MODE = TristateTrue
|
||||||
|
AppendTextToFileUTF16 = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FileExists(sFile) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
Dim fs As Scripting.TextStream
|
||||||
|
Set fs = fso.OpenTextFile(sFile, ForAppending, Format:=UTF16_MODE)
|
||||||
|
If fs Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Call fs.WriteLine(sMsg)
|
||||||
|
Call fs.Close
|
||||||
|
|
||||||
|
AppendTextToFileUTF16 = True
|
||||||
|
Exit Function
|
||||||
|
RETURN_FALSE:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ReadTextFromFileUTF16(sFile$, ByRef sResult$) As Boolean
|
||||||
|
Const UTF16_MODE = TristateTrue
|
||||||
|
ReadTextFromFileUTF16 = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FileExists(sFile) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
Dim fs As Scripting.TextStream
|
||||||
|
Set fs = fso.OpenTextFile(sFile, ForReading, Format:=UTF16_MODE)
|
||||||
|
If fs Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
sResult = fs.ReadAll
|
||||||
|
Call fs.Close
|
||||||
|
|
||||||
|
ReadTextFromFileUTF16 = True
|
||||||
|
Exit Function
|
||||||
|
RETURN_FALSE:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CopyFileOrFolder(sSource$, sDestination$, Optional ByRef fso As Scripting.FileSystemObject = Nothing) As Boolean
|
||||||
|
CopyFileOrFolder = False
|
||||||
|
|
||||||
|
If fso Is Nothing Then _
|
||||||
|
Set fso = New Scripting.FileSystemObject
|
||||||
|
|
||||||
|
Dim bIsFile As Boolean: bIsFile = fso.FileExists(sSource)
|
||||||
|
Dim bIsFolder As Boolean: bIsFolder = fso.FolderExists(sSource)
|
||||||
|
If Not bIsFile And Not bIsFolder Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Call EnsureFolderExists(fso.GetParentFolderName(sDestination), fso)
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
If bIsFile Then
|
||||||
|
Call fso.CopyFile(sSource, sDestination, OverWriteFiles:=True)
|
||||||
|
Else
|
||||||
|
Call fso.CopyFolder(sSource, sDestination, OverWriteFiles:=True)
|
||||||
|
End If
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
CopyFileOrFolder = True
|
||||||
|
RETURN_FALSE:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function MoveFileOrFolder(sSource$, sDestination$, Optional fso As Scripting.FileSystemObject = Nothing) As Boolean
|
||||||
|
MoveFileOrFolder = False
|
||||||
|
If sSource = sDestination Then _
|
||||||
|
Exit Function
|
||||||
|
If Not CopyFileOrFolder(sSource, sDestination, fso) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
If fso.FileExists(sSource) Then _
|
||||||
|
Call fso.DeleteFile(sSource)
|
||||||
|
If fso.FolderExists(sSource) Then _
|
||||||
|
Call fso.DeleteFolder(sSource)
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
MoveFileOrFolder = True
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
RETURN_FALSE:
|
||||||
|
MoveFileOrFolder = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ListFilesIn(sTargetFolder$, Optional sMask$ = "*.*") As Collection
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FolderExists(sTargetFolder) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim iFiles As New Collection
|
||||||
|
Dim cQueue As New Collection
|
||||||
|
Call cQueue.Add(fso.GetFolder(sTargetFolder))
|
||||||
|
|
||||||
|
Dim aFolder As Scripting.Folder
|
||||||
|
Dim subFolder As Scripting.Folder
|
||||||
|
Dim aFile As Scripting.File
|
||||||
|
Do While cQueue.Count > 0
|
||||||
|
Set aFolder = cQueue(1)
|
||||||
|
Call cQueue.Remove(1)
|
||||||
|
|
||||||
|
For Each subFolder In aFolder.SubFolders
|
||||||
|
Call cQueue.Add(subFolder)
|
||||||
|
Next subFolder
|
||||||
|
|
||||||
|
For Each aFile In aFolder.files
|
||||||
|
If VBA.UCase(aFile.Name) Like VBA.UCase(sMask) Then _
|
||||||
|
Call iFiles.Add(aFile.Path)
|
||||||
|
Next aFile
|
||||||
|
Loop
|
||||||
|
|
||||||
|
Set ListFilesIn = iFiles
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ForEachFileRecursive(sTargetFolder$, oCallback As Object, sFuncName$)
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FolderExists(sTargetFolder) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim cQueue As New Collection
|
||||||
|
Call cQueue.Add(fso.GetFolder(sTargetFolder))
|
||||||
|
|
||||||
|
Dim aFolder As Scripting.Folder
|
||||||
|
Dim subFolder As Scripting.Folder
|
||||||
|
Dim aFile As Scripting.File
|
||||||
|
Do While cQueue.Count > 0
|
||||||
|
Set aFolder = cQueue(1)
|
||||||
|
Call cQueue.Remove(1)
|
||||||
|
|
||||||
|
For Each subFolder In aFolder.SubFolders
|
||||||
|
Call cQueue.Add(subFolder)
|
||||||
|
Next subFolder
|
||||||
|
|
||||||
|
For Each aFile In aFolder.files
|
||||||
|
Call CallByName(oCallback, sFuncName, VbMethod, aFile)
|
||||||
|
Next aFile
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Callback should return TRUE to continue recursive
|
||||||
|
Public Function ForEachFolderRecursive(sTargetFolder$, oCallback As Object, sFuncName$)
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FolderExists(sTargetFolder) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim cQueue As New Collection
|
||||||
|
Call cQueue.Add(fso.GetFolder(sTargetFolder))
|
||||||
|
|
||||||
|
Dim aFolder As Scripting.Folder
|
||||||
|
Dim subFolder As Scripting.Folder
|
||||||
|
Dim aFile As Scripting.File
|
||||||
|
Do While cQueue.Count > 0
|
||||||
|
Set aFolder = cQueue(1)
|
||||||
|
Call cQueue.Remove(1)
|
||||||
|
|
||||||
|
If CallByName(oCallback, sFuncName, VbMethod, aFolder) Then
|
||||||
|
For Each subFolder In aFolder.SubFolders
|
||||||
|
Call cQueue.Add(subFolder)
|
||||||
|
Next subFolder
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FileToMD5(sTargetFile$) As String
|
||||||
|
FileToMD5 = ComputeFileHash(sTargetFile, "MD5CryptoServiceProvider")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FileToSHA1(sTargetFile$) As String
|
||||||
|
FileToSHA1 = ComputeFileHash(sTargetFile, "SHA1CryptoServiceProvider")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function UnzipFile(sTargetFile$, sDestinationFolder$) As Boolean
|
||||||
|
Const OPT_DISABLE_DIALOGS = 4
|
||||||
|
Const OPT_YES_TO_ALL = 16
|
||||||
|
|
||||||
|
UnzipFile = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FileExists(sTargetFile) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim sExt$: sExt = fso.GetExtensionName(sTargetFile)
|
||||||
|
Dim sZip$: sZip = sTargetFile
|
||||||
|
If sExt <> "zip" Then
|
||||||
|
sZip = Left(sTargetFile, Len(sTargetFile) - Len(sExt) - 1) & ".tmp.zip"
|
||||||
|
Call fso.GetFile(sTargetFile).Copy(sZip)
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call EnsureFolderExists(sDestinationFolder, fso)
|
||||||
|
|
||||||
|
Dim aShell As New Shell32.Shell
|
||||||
|
Call aShell.Namespace(sDestinationFolder).CopyHere(aShell.Namespace(sZip).Items, OPT_DISABLE_DIALOGS + OPT_YES_TO_ALL)
|
||||||
|
|
||||||
|
If sExt <> "zip" Then _
|
||||||
|
Call fso.DeleteFile(sZip)
|
||||||
|
|
||||||
|
UnzipFile = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ZipFolder(sTargetFolder$, sDestinationFile$) As Boolean
|
||||||
|
ZipFolder = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FolderExists(sTargetFolder) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim sZip$: sZip = sDestinationFile
|
||||||
|
Dim sExt$: sExt = fso.GetExtensionName(sDestinationFile)
|
||||||
|
If sExt <> "zip" Then _
|
||||||
|
sZip = Left(sDestinationFile, Len(sDestinationFile) - Len(sExt) - 1) & ".zip"
|
||||||
|
|
||||||
|
Call CreateEmptyZip(sZip)
|
||||||
|
Dim aShell As New Shell32.Shell
|
||||||
|
Call aShell.Namespace(sZip).CopyHere(aShell.Namespace(sTargetFolder).Items)
|
||||||
|
|
||||||
|
' Wait for zipping to finish
|
||||||
|
On Error Resume Next
|
||||||
|
Dim nTimeout&: nTimeout = 90 ' seconds
|
||||||
|
Do Until aShell.Namespace(sZip).Items.Count = aShell.Namespace(sTargetFolder).Items.Count
|
||||||
|
Call Sleep(1000)
|
||||||
|
nTimeout = nTimeout - 1
|
||||||
|
If nTimeout = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
Loop
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If sExt <> "zip" Then
|
||||||
|
Call fso.CopyFile(sZip, sDestinationFile)
|
||||||
|
Call fso.DeleteFile(sZip)
|
||||||
|
End If
|
||||||
|
|
||||||
|
ZipFolder = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function PositionInMiddle(targetForm As Object)
|
||||||
|
Dim nTopOffset&: nTopOffset = (Application.UsableHeight / 2) - (targetForm.Height / 2)
|
||||||
|
Dim nLeftOffset&: nLeftOffset = (Application.UsableWidth / 2) - (targetForm.Width / 2)
|
||||||
|
|
||||||
|
targetForm.StartUpPosition = 0
|
||||||
|
targetForm.Top = Application.Top + IIf(nTopOffset > 0, nTopOffset, 0)
|
||||||
|
targetForm.Left = Application.Left + IIf(nLeftOffset > 0, nLeftOffset, 0)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========
|
||||||
|
Private Function CreateEmptyZip(sTarget$)
|
||||||
|
Dim nFileID%: nFileID = FreeFile
|
||||||
|
Open sTarget For Output As #nFileID
|
||||||
|
Print #nFileID, Chr$(80) & Chr$(75) & Chr$(5) & Chr$(6) & String(18, 0)
|
||||||
|
Close #nFileID
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ComputeFileHash(sTargetFile$, sCryptoProvider$) As String
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FileExists(sTargetFile) Then
|
||||||
|
ComputeFileHash = "File not exists"
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim nFileID&: nFileID = VBA.FreeFile
|
||||||
|
On Error GoTo CANNOT_OPEN
|
||||||
|
Open sTargetFile For Binary Access Read As nFileID
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
Dim iEncrypter As Object: Set iEncrypter = CreateObject("System.Security.Cryptography." & sCryptoProvider)
|
||||||
|
Dim iBuffer() As Byte
|
||||||
|
Dim nBlockSize&: nBlockSize = 2 ^ 16
|
||||||
|
Dim nFileSize&: nFileSize = VBA.LOF(nFileID)
|
||||||
|
If nFileSize < nBlockSize Then _
|
||||||
|
nBlockSize = ((nFileSize + 1024) \ 1024) * 1024
|
||||||
|
|
||||||
|
ReDim iBuffer(0 To nBlockSize - 1)
|
||||||
|
Dim i&
|
||||||
|
For i = 1 To nFileSize \ nBlockSize Step 1
|
||||||
|
Get nFileID, , iBuffer
|
||||||
|
Call iEncrypter.TransformBlock(iBuffer, 0, nBlockSize, iBuffer, 0)
|
||||||
|
Next
|
||||||
|
|
||||||
|
Get nFileID, , iBuffer
|
||||||
|
Call iEncrypter.TransformFinalBlock(iBuffer, 0, nFileSize Mod nBlockSize)
|
||||||
|
iBuffer = iEncrypter.Hash
|
||||||
|
|
||||||
|
Close nFileID
|
||||||
|
Call iEncrypter.Clear
|
||||||
|
|
||||||
|
Dim nPos&
|
||||||
|
Dim sHash$
|
||||||
|
For nPos = 1 To VBA.LenB(iBuffer)
|
||||||
|
sHash = sHash & VBA.LCase(VBA.Right("0" & VBA.Hex(VBA.AscB(VBA.MidB(iBuffer, nPos, 1))), 2))
|
||||||
|
Next nPos
|
||||||
|
|
||||||
|
Set iEncrypter = Nothing
|
||||||
|
ComputeFileHash = sHash
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
CANNOT_OPEN:
|
||||||
|
ComputeFileHash = "Cannot open file"
|
||||||
|
End Function
|
97
api/z_LoadPictureAPI.bas
Normal file
97
api/z_LoadPictureAPI.bas
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
Attribute VB_Name = "z_LoadPictureAPI"
|
||||||
|
'================ Ìîäóëü çàãðóçêè èçîáðàæåíèÿ ÷åðåç GDI =============
|
||||||
|
' Shared module version: 20210228
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
'Declare a UDT to store a GUID for the IPicture OLE Interface
|
||||||
|
Private Type GUID
|
||||||
|
Data1 As Long
|
||||||
|
Data2 As Integer
|
||||||
|
Data3 As Integer
|
||||||
|
Data4(0 To 7) As Byte
|
||||||
|
End Type
|
||||||
|
|
||||||
|
'Declare a UDT to store the bitmap information
|
||||||
|
Private Type PictDescriptor
|
||||||
|
size_ As LongLong
|
||||||
|
type_ As LongLong
|
||||||
|
hPic_ As LongPtr
|
||||||
|
hPal_ As LongLong
|
||||||
|
End Type
|
||||||
|
|
||||||
|
'Declare a UDT to store the GDI+ Startup information
|
||||||
|
Private Type GdiplusStartupInput
|
||||||
|
version_ As Long
|
||||||
|
callback_ As LongPtr
|
||||||
|
suppBgThread_ As LongLong
|
||||||
|
suppExtCodecs_ As LongLong
|
||||||
|
End Type
|
||||||
|
|
||||||
|
'Windows API calls into the GDI+ library
|
||||||
|
Private Declare PtrSafe Function GdiplusStartup Lib "GDIPlus" (pToken As LongPtr, pInputBuf As GdiplusStartupInput, Optional ByVal pOutputBuf As LongPtr = 0) As Long
|
||||||
|
Private Declare PtrSafe Function GdipCreateBitmapFromFile Lib "GDIPlus" (ByVal pFile As LongPtr, pBitmap As LongPtr) As Long
|
||||||
|
Private Declare PtrSafe Function GdipCreateHBITMAPFromBitmap Lib "GDIPlus" (ByVal pBitmap As LongPtr, hbmReturn As LongPtr, ByVal pBackground As LongPtr) As Long
|
||||||
|
Private Declare PtrSafe Function GdipDisposeImage Lib "GDIPlus" (ByVal pImage As LongPtr) As Long
|
||||||
|
Private Declare PtrSafe Function GdiplusShutdown Lib "GDIPlus" (ByVal pToken As LongPtr) As Long
|
||||||
|
Private Declare PtrSafe Function OleCreatePictureIndirect Lib "oleaut32.dll" (PicDesc As PictDescriptor, RefIID As GUID, ByVal fPictureOwnsHandle As LongPtr, iPic As IPicture) As LongPtr
|
||||||
|
|
||||||
|
Private Const PICTYPE_BITMAP = 1
|
||||||
|
|
||||||
|
' Procedure: LoadPictureGDI
|
||||||
|
' Purpose: Loads an image using GDI+
|
||||||
|
' Returns: The image as an IPicture Object
|
||||||
|
Public Function LoadPictureGDI(ByVal sFileName$) As IPicture
|
||||||
|
'Initialize GDI+
|
||||||
|
Dim uGdiInput As GdiplusStartupInput
|
||||||
|
uGdiInput.version_ = 1
|
||||||
|
Dim hGdiPlus As LongPtr
|
||||||
|
Dim lResult&: lResult = GdiplusStartup(hGdiPlus, uGdiInput)
|
||||||
|
|
||||||
|
If lResult <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim hGdiImage As LongPtr
|
||||||
|
If GdipCreateBitmapFromFile(StrPtr(sFileName), hGdiImage) = 0 Then
|
||||||
|
Dim hBitmap As LongPtr
|
||||||
|
Call GdipCreateHBITMAPFromBitmap(hGdiImage, hBitmap, 0)
|
||||||
|
Set LoadPictureGDI = CreateIPicture(hBitmap)
|
||||||
|
Call GdipDisposeImage(hGdiImage)
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call GdiplusShutdown(hGdiPlus)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Procedure: CreateIPicture
|
||||||
|
' Purpose: Converts a image handle into an IPicture object.
|
||||||
|
' Returns: The IPicture object
|
||||||
|
Private Function CreateIPicture(ByVal hPic As LongPtr) As IPicture
|
||||||
|
Dim IID_IDispatch As GUID
|
||||||
|
With IID_IDispatch
|
||||||
|
.Data1 = &H7BF80980
|
||||||
|
.Data2 = &HBF32
|
||||||
|
.Data3 = &H101A
|
||||||
|
.Data4(0) = &H8B
|
||||||
|
.Data4(1) = &HBB
|
||||||
|
.Data4(2) = &H0
|
||||||
|
.Data4(3) = &HAA
|
||||||
|
.Data4(4) = &H0
|
||||||
|
.Data4(5) = &H30
|
||||||
|
.Data4(6) = &HC
|
||||||
|
.Data4(7) = &HAB
|
||||||
|
End With
|
||||||
|
|
||||||
|
Dim uPicInfo As PictDescriptor
|
||||||
|
With uPicInfo
|
||||||
|
.size_ = Len(uPicInfo)
|
||||||
|
.type_ = PICTYPE_BITMAP
|
||||||
|
.hPic_ = hPic
|
||||||
|
.hPal_ = 0
|
||||||
|
End With
|
||||||
|
|
||||||
|
Dim iPic As IPicture
|
||||||
|
Call OleCreatePictureIndirect(uPicInfo, IID_IDispatch, True, iPic)
|
||||||
|
Set CreateIPicture = iPic
|
||||||
|
End Function
|
||||||
|
|
156
api/z_PastePictureAPI.bas
Normal file
156
api/z_PastePictureAPI.bas
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
Attribute VB_Name = "z_PastePictureAPI"
|
||||||
|
'================ Ìîäóëü çàãðóçêè èçîáðàæåíèÿ ÷åðåç Copy/Paste =============
|
||||||
|
' Shared module version: 20210228
|
||||||
|
' Required reference: OLE Automation
|
||||||
|
|
||||||
|
' PastePicture The entry point for the routine
|
||||||
|
' CreatePicture Private function to convert a bitmap or metafile handle to an OLE reference
|
||||||
|
' ErrorDescription Get the error text for an OLE error code
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
Option Compare Text
|
||||||
|
|
||||||
|
' Declare a UDT to store a GUID for the IPicture OLE Interface
|
||||||
|
Private Type GUID
|
||||||
|
Data1 As Long
|
||||||
|
Data2 As Integer
|
||||||
|
Data3 As Integer
|
||||||
|
Data4(0 To 7) As Byte
|
||||||
|
End Type
|
||||||
|
|
||||||
|
' Declare a UDT to store the bitmap information
|
||||||
|
Private Type PictDescriptor
|
||||||
|
size_ As LongLong
|
||||||
|
type_ As LongLong
|
||||||
|
hPic_ As LongPtr
|
||||||
|
hPal_ As LongLong
|
||||||
|
End Type
|
||||||
|
|
||||||
|
'Does the clipboard contain a bitmap/metafile?
|
||||||
|
Private Declare PtrSafe Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Integer) As Long
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
|
||||||
|
Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long
|
||||||
|
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
|
||||||
|
|
||||||
|
'Convert the handle into an OLE IPicture interface.
|
||||||
|
Private Declare PtrSafe Function OleCreatePictureIndirect Lib "olepro32.dll" (pDesct As PictDescriptor, refID As GUID, ByVal nPicHandle As Long, iPic As IPicture) As Long
|
||||||
|
|
||||||
|
'Create our own copy of the metafile, so it doesn't get wiped out by subsequent clipboard updates.
|
||||||
|
Private Declare PtrSafe Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
|
||||||
|
|
||||||
|
'Create our own copy of the bitmap, so it doesn't get wiped out by subsequent clipboard updates.
|
||||||
|
Private Declare PtrSafe Function CopyImage Lib "user32" (ByVal nHandle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long
|
||||||
|
|
||||||
|
'The API format types
|
||||||
|
Private Const CF_BITMAP = 2
|
||||||
|
Private Const CF_PALETTE = 9
|
||||||
|
Private Const CF_ENHMETAFILE = 14
|
||||||
|
Private Const IMAGE_BITMAP = 0
|
||||||
|
Private Const LR_COPYRETURNORG = &H4
|
||||||
|
|
||||||
|
Public Function PastePicture(Optional nType& = xlPicture) As IPicture
|
||||||
|
'Convert the type of picture requested from the xl constant to the API constant
|
||||||
|
Dim lPicType&: lPicType = IIf(nType = xlBitmap, CF_BITMAP, CF_ENHMETAFILE)
|
||||||
|
|
||||||
|
'Check if the clipboard contains the required format
|
||||||
|
Dim hPicAvail&: hPicAvail = IsClipboardFormatAvailable(lPicType)
|
||||||
|
If hPicAvail = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim cbHandle&: cbHandle = OpenClipboard(0&)
|
||||||
|
If cbHandle <= 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
'Get a handle to the image data
|
||||||
|
Dim hPtr&: hPtr = GetClipboardData(lPicType)
|
||||||
|
|
||||||
|
'Create our own copy of the image on the clipboard, in the appropriate format.
|
||||||
|
Dim hCopy&
|
||||||
|
If lPicType = CF_BITMAP Then
|
||||||
|
hCopy = CopyImage(hPtr, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
|
||||||
|
Else
|
||||||
|
hCopy = CopyEnhMetaFile(hPtr, vbNullString)
|
||||||
|
End If
|
||||||
|
|
||||||
|
'Release the clipboard to other programs
|
||||||
|
cbHandle = CloseClipboard
|
||||||
|
|
||||||
|
'If we got a handle to the image, convert it into a Picture object and return it
|
||||||
|
If hPtr <> 0 Then _
|
||||||
|
Set PastePicture = CreatePicture(hCopy, 0, lPicType)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CreatePicture(ByVal hPic&, ByVal hPal&, ByVal lPicType) As IPicture
|
||||||
|
'OLE Picture types
|
||||||
|
Const PICTYPE_BITMAP = 1
|
||||||
|
Const PICTYPE_ENHMETAFILE = 4
|
||||||
|
|
||||||
|
' Create the Interface GUID (for the IPicture interface)
|
||||||
|
Dim IID_IDispatch As GUID
|
||||||
|
With IID_IDispatch
|
||||||
|
.Data1 = &H7BF80980
|
||||||
|
.Data2 = &HBF32
|
||||||
|
.Data3 = &H101A
|
||||||
|
.Data4(0) = &H8B
|
||||||
|
.Data4(1) = &HBB
|
||||||
|
.Data4(2) = &H0
|
||||||
|
.Data4(3) = &HAA
|
||||||
|
.Data4(4) = &H0
|
||||||
|
.Data4(5) = &H30
|
||||||
|
.Data4(6) = &HC
|
||||||
|
.Data4(7) = &HAB
|
||||||
|
End With
|
||||||
|
|
||||||
|
' Fill uPicInfo with necessary parts.
|
||||||
|
Dim uPicInfo As PictDescriptor
|
||||||
|
With uPicInfo
|
||||||
|
.size_ = Len(uPicInfo) ' Length of structure.
|
||||||
|
.type_ = IIf(lPicType = CF_BITMAP, PICTYPE_BITMAP, PICTYPE_ENHMETAFILE) ' Type of Picture
|
||||||
|
.hPic_ = hPic ' Handle to image.
|
||||||
|
.hPal_ = IIf(lPicType = CF_BITMAP, hPal, 0) ' Handle to palette (if bitmap).
|
||||||
|
End With
|
||||||
|
|
||||||
|
' Create the Picture object.
|
||||||
|
Dim r&
|
||||||
|
Dim iPic As IPicture
|
||||||
|
r = OleCreatePictureIndirect(uPicInfo, IID_IDispatch, True, iPic)
|
||||||
|
|
||||||
|
' If an error occured, show the description
|
||||||
|
If r <> 0 Then _
|
||||||
|
Debug.Print "Create Picture: " & ErrorDescription(r)
|
||||||
|
|
||||||
|
' Return the new Picture object.
|
||||||
|
Set CreatePicture = iPic
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
Private Function ErrorDescription(nErr&) As String
|
||||||
|
'OLECreatePictureIndirect return values
|
||||||
|
Const E_ABORT = &H80004004
|
||||||
|
Const E_ACCESSDENIED = &H80070005
|
||||||
|
Const E_FAIL = &H80004005
|
||||||
|
Const E_HANDLE = &H80070006
|
||||||
|
Const E_INVALIDARG = &H80070057
|
||||||
|
Const E_NOINTERFACE = &H80004002
|
||||||
|
Const E_NOTIMPL = &H80004001
|
||||||
|
Const E_OUTOFMEMORY = &H8007000E
|
||||||
|
Const E_POINTER = &H80004003
|
||||||
|
Const E_UNEXPECTED = &H8000FFFF
|
||||||
|
Const S_OK = &H0
|
||||||
|
|
||||||
|
Select Case nErr
|
||||||
|
Case E_ABORT: ErrorDescription = " Aborted"
|
||||||
|
Case E_ACCESSDENIED: ErrorDescription = " Access Denied"
|
||||||
|
Case E_FAIL: ErrorDescription = " General Failure"
|
||||||
|
Case E_HANDLE: ErrorDescription = " Bad/Missing Handle"
|
||||||
|
Case E_INVALIDARG: ErrorDescription = " Invalid Argument"
|
||||||
|
Case E_NOINTERFACE: ErrorDescription = " No Interface"
|
||||||
|
Case E_NOTIMPL: ErrorDescription = " Not Implemented"
|
||||||
|
Case E_OUTOFMEMORY: ErrorDescription = " Out of Memory"
|
||||||
|
Case E_POINTER: ErrorDescription = " Invalid Pointer"
|
||||||
|
Case E_UNEXPECTED: ErrorDescription = " Unknown Error"
|
||||||
|
Case S_OK: ErrorDescription = " Success!"
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
96
dev/API_Logger.cls
Normal file
96
dev/API_Logger.cls
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_Logger"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ================ Log functionality =============
|
||||||
|
' Shared module version: 20210413
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: ex_VBA
|
||||||
|
' Required reference: Scripting
|
||||||
|
' Note: log is saved as UTF-16 encoded text file
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private fileName_ As String
|
||||||
|
|
||||||
|
Public Function Init(sName$)
|
||||||
|
If sName = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
fileName_ = sName
|
||||||
|
Call EnsureOutputExists
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get LogFileName() As String
|
||||||
|
LogFileName = fileName_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function Log(sMsg$)
|
||||||
|
If fileName_ <> vbNullString Then
|
||||||
|
Call LogIntoFile(StampTime(sMsg))
|
||||||
|
Else
|
||||||
|
Call LogDefault(StampTime(sMsg))
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Report(sMsg$, Optional nFlags& = 0)
|
||||||
|
' TODO: indicate flags
|
||||||
|
Call Log(sMsg)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Clear()
|
||||||
|
If fileName_ <> vbNullString Then _
|
||||||
|
Call CreateLogFile
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ==========
|
||||||
|
Private Function LogIntoFile(sMsg$)
|
||||||
|
Const UTF16_MODE = TristateTrue
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FileExists(fileName_) Then _
|
||||||
|
GoTo CANNOT_ACCESS
|
||||||
|
|
||||||
|
Dim fs As Scripting.TextStream: Set fs = fso.OpenTextFile(fileName_, ForAppending, Format:=UTF16_MODE)
|
||||||
|
If fs Is Nothing Then _
|
||||||
|
GoTo CANNOT_ACCESS
|
||||||
|
|
||||||
|
Call fs.WriteLine(sMsg)
|
||||||
|
Call fs.Close
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
CANNOT_ACCESS:
|
||||||
|
Call LogDefault(StampTime("Cannot access log"))
|
||||||
|
Call LogDefault(sMsg)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function LogDefault(sMsg$)
|
||||||
|
Debug.Print sMsg
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function EnsureOutputExists()
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If fso.FileExists(fileName_) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Call CreateLogFile
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function StampTime(sMsg$) As String
|
||||||
|
StampTime = Format(Now(), "yyyy-mm-dd hh:mm:ss") & ": " & sMsg
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CreateLogFile()
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Call EnsureFolderExists(fso.GetParentFolderName(fileName_))
|
||||||
|
Dim fs As Scripting.TextStream: Set fs = fso.CreateTextFile(fileName_, Unicode:=True)
|
||||||
|
If fs Is Nothing Then
|
||||||
|
Call LogDefault(StampTime("Cannot create log: " & fileName_))
|
||||||
|
Else
|
||||||
|
Call fs.Close
|
||||||
|
Call Log("Log file created")
|
||||||
|
End If
|
||||||
|
End Function
|
247
dev/API_MockInteraction.cls
Normal file
247
dev/API_MockInteraction.cls
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_MockInteraction"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
'================ Mock âçàèìîäåéñòâèÿ ñ ïîëüçîâàòåëåì =========================
|
||||||
|
' Shared module version: 20210331
|
||||||
|
' Depends on: DevTester, API_UserInteraction
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
Implements API_UserInteraction
|
||||||
|
|
||||||
|
Private Enum InteractionType
|
||||||
|
T_UI_INVALID = 0
|
||||||
|
T_UI_MESSAGE
|
||||||
|
T_UI_QUESTION
|
||||||
|
T_UI_PROMPT_INPUT
|
||||||
|
T_UI_PROMPT_FILE
|
||||||
|
T_UI_FOLLOW_HYPERLINK
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Private Type ExpectationInfo
|
||||||
|
type_ As InteractionType
|
||||||
|
id_ As Long
|
||||||
|
response_ As String
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Private types_ As Collection ' of InteractionType
|
||||||
|
Private ids_ As Collection ' of Long
|
||||||
|
Private responses_ As Collection ' of String
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Call ResetCollection
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function ExpectNoInteraction()
|
||||||
|
Call ResetExpectations
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpectMessage(nMsg&)
|
||||||
|
Call AddExpectation(T_UI_MESSAGE, nMsg)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpectQuestion(nMsg&, bAnswerYes As Boolean)
|
||||||
|
Call AddExpectation(T_UI_QUESTION, nMsg, IIf(bAnswerYes, "YES", "NO"))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpectInputPrompt(sResponse$)
|
||||||
|
Call AddExpectation(T_UI_PROMPT_INPUT, sResponse:=sResponse)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpectFilePrompt(sResponse$)
|
||||||
|
Call AddExpectation(T_UI_PROMPT_FILE, sResponse:=sResponse)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpectFollowHyperlink(sLink$)
|
||||||
|
Call AddExpectation(T_UI_FOLLOW_HYPERLINK, sResponse:=sLink)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ResetExpectations() As Boolean
|
||||||
|
ResetExpectations = ids_.Count = 0
|
||||||
|
If ResetExpectations Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim sMsg$: sMsg = "Unrealized expectations: "
|
||||||
|
Dim info As ExpectationInfo
|
||||||
|
Do While ids_.Count <> 0
|
||||||
|
sMsg = sMsg & vbNewLine & InfoToString(PopExpectation)
|
||||||
|
Loop
|
||||||
|
Call Dev_LogMsg(sMsg)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function API_UserInteraction_ShowMessage(nMsg&, ParamArray params() As Variant)
|
||||||
|
Dim iExpected As ExpectationInfo: iExpected = PopExpectation
|
||||||
|
If iExpected.type_ = T_UI_INVALID Then _
|
||||||
|
Call Err.Raise(UNEXPECTED_INTERACTION, Description:="ShowMessage " & nMsg)
|
||||||
|
|
||||||
|
Dim iActual As ExpectationInfo
|
||||||
|
iActual.type_ = T_UI_MESSAGE
|
||||||
|
iActual.id_ = nMsg
|
||||||
|
iActual.response_ = vbNullString
|
||||||
|
|
||||||
|
If Not CompareInfo(iActual, iExpected) Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Unrecognized interaction" & vbNewLine & _
|
||||||
|
"Expected: {1}" & vbNewLine & _
|
||||||
|
"Actual: {2}", _
|
||||||
|
InfoToString(iExpected), InfoToString(iActual)))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function API_UserInteraction_AskQuestion(nQuestion&, ParamArray params() As Variant) As Boolean
|
||||||
|
Dim iExpected As ExpectationInfo: iExpected = PopExpectation
|
||||||
|
If iExpected.type_ = T_UI_INVALID Then _
|
||||||
|
Call Err.Raise(UNEXPECTED_INTERACTION, Description:="AskQuestion " & nQuestion)
|
||||||
|
|
||||||
|
Dim iActual As ExpectationInfo
|
||||||
|
iActual.type_ = T_UI_QUESTION
|
||||||
|
iActual.id_ = nQuestion
|
||||||
|
|
||||||
|
If Not CompareInfo(iActual, iExpected) Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Unrecognized interaction" & vbNewLine & _
|
||||||
|
"Expected: {1}" & vbNewLine & _
|
||||||
|
"Actual: {2}", _
|
||||||
|
InfoToString(iExpected), InfoToString(iActual)))
|
||||||
|
API_UserInteraction_AskQuestion = iExpected.response_ = "YES"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function API_UserInteraction_PromptInput( _
|
||||||
|
sPrompt$, _
|
||||||
|
Optional sTitle$ = vbNullString, _
|
||||||
|
Optional sInitial As Variant) As String
|
||||||
|
Dim iExpected As ExpectationInfo: iExpected = PopExpectation
|
||||||
|
If iExpected.type_ = T_UI_INVALID Then _
|
||||||
|
Call Err.Raise(UNEXPECTED_INTERACTION, Description:="PromptInput")
|
||||||
|
|
||||||
|
Dim iActual As ExpectationInfo
|
||||||
|
iActual.type_ = T_UI_PROMPT_INPUT
|
||||||
|
|
||||||
|
If Not CompareInfo(iActual, iExpected) Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Unrecognized interaction" & vbNewLine & _
|
||||||
|
"Expected: {1}" & vbNewLine & _
|
||||||
|
"Actual: {2}", _
|
||||||
|
InfoToString(iExpected), InfoToString(iActual)))
|
||||||
|
API_UserInteraction_PromptInput = iExpected.response_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function API_UserInteraction_PromptFileFilter( _
|
||||||
|
sInitialPath$, sDescription$, sFilter$, _
|
||||||
|
Optional sTitle$ = "Âûáåðèòå ôàéë", _
|
||||||
|
Optional bNewApplication As Boolean = False) As String
|
||||||
|
Dim iExpected As ExpectationInfo: iExpected = PopExpectation
|
||||||
|
If iExpected.type_ = T_UI_INVALID Then _
|
||||||
|
Call Err.Raise(UNEXPECTED_INTERACTION, Description:="PromptFileFilter")
|
||||||
|
|
||||||
|
Dim iActual As ExpectationInfo
|
||||||
|
iActual.type_ = T_UI_PROMPT_FILE
|
||||||
|
|
||||||
|
If Not CompareInfo(iActual, iExpected) Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Unrecognized interaction" & vbNewLine & _
|
||||||
|
"Expected: {1}" & vbNewLine & _
|
||||||
|
"Actual: {2}", _
|
||||||
|
InfoToString(iExpected), InfoToString(iActual)))
|
||||||
|
API_UserInteraction_PromptFileFilter = iExpected.response_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function API_UserInteraction_PromptFile(sInitialPath$, _
|
||||||
|
Optional sTitle$ = "Âûáåðèòå ôàéë", _
|
||||||
|
Optional cDescriptions As Collection = Nothing, _
|
||||||
|
Optional cFilters As Collection = Nothing, _
|
||||||
|
Optional bNewApplication As Boolean = False) As String
|
||||||
|
Dim iExpected As ExpectationInfo: iExpected = PopExpectation
|
||||||
|
If iExpected.type_ = T_UI_INVALID Then _
|
||||||
|
Call Err.Raise(UNEXPECTED_INTERACTION, Description:="PromptFile")
|
||||||
|
|
||||||
|
Dim iActual As ExpectationInfo
|
||||||
|
iActual.type_ = T_UI_PROMPT_FILE
|
||||||
|
|
||||||
|
If Not CompareInfo(iActual, iExpected) Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Unrecognized interaction" & vbNewLine & _
|
||||||
|
"Expected: {1}" & vbNewLine & _
|
||||||
|
"Actual: {2}", _
|
||||||
|
InfoToString(iExpected), InfoToString(iActual)))
|
||||||
|
API_UserInteraction_PromptFile = iExpected.response_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function API_UserInteraction_PromptFolder(sInitialPath$, _
|
||||||
|
Optional sTitle$ = "Âûáåðèòå êàòàëîã", _
|
||||||
|
Optional bNewApplication As Boolean = False) As String
|
||||||
|
Dim iExpected As ExpectationInfo: iExpected = PopExpectation
|
||||||
|
If iExpected.type_ = T_UI_INVALID Then _
|
||||||
|
Call Err.Raise(UNEXPECTED_INTERACTION, Description:="PromptFile")
|
||||||
|
|
||||||
|
Dim iActual As ExpectationInfo
|
||||||
|
iActual.type_ = T_UI_PROMPT_FILE
|
||||||
|
|
||||||
|
If Not CompareInfo(iActual, iExpected) Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Unrecognized interaction" & vbNewLine & _
|
||||||
|
"Expected: {1}" & vbNewLine & _
|
||||||
|
"Actual: {2}", _
|
||||||
|
InfoToString(iExpected), InfoToString(iActual)))
|
||||||
|
API_UserInteraction_PromptFolder = iExpected.response_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function API_UserInteraction_FollowHyperlink(oDocument As Object, sAddress$)
|
||||||
|
Dim iExpected As ExpectationInfo: iExpected = PopExpectation
|
||||||
|
If iExpected.type_ = T_UI_INVALID Then _
|
||||||
|
Call Err.Raise(UNEXPECTED_INTERACTION, Description:="FollowHyperlink")
|
||||||
|
|
||||||
|
Dim iActual As ExpectationInfo
|
||||||
|
iActual.type_ = T_UI_FOLLOW_HYPERLINK
|
||||||
|
|
||||||
|
If Not CompareInfo(iActual, iExpected) Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Unrecognized interaction" & vbNewLine & _
|
||||||
|
"Expected: {1}" & vbNewLine & _
|
||||||
|
"Actual: {2}", _
|
||||||
|
InfoToString(iExpected), InfoToString(iActual)))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =====
|
||||||
|
Private Function ResetCollection()
|
||||||
|
Set ids_ = New Collection
|
||||||
|
Set types_ = New Collection
|
||||||
|
Set responses_ = New Collection
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function AddExpectation(nType As InteractionType, Optional nID& = 0, Optional sResponse$ = vbNullString)
|
||||||
|
Call types_.Add(nType)
|
||||||
|
Call ids_.Add(nID)
|
||||||
|
Call responses_.Add(sResponse)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function PopExpectation() As ExpectationInfo
|
||||||
|
If ids_.Count = 0 Then
|
||||||
|
PopExpectation.id_ = T_UI_INVALID
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
PopExpectation.id_ = ids_.Item(1)
|
||||||
|
PopExpectation.type_ = types_.Item(1)
|
||||||
|
PopExpectation.response_ = responses_.Item(1)
|
||||||
|
|
||||||
|
Call ids_.Remove(1)
|
||||||
|
Call types_.Remove(1)
|
||||||
|
Call responses_.Remove(1)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function InfoToString(target As ExpectationInfo) As String
|
||||||
|
Select Case target.type_
|
||||||
|
Case T_UI_MESSAGE: InfoToString = "Message " & target.id_
|
||||||
|
Case T_UI_QUESTION: InfoToString = "Question " & target.id_
|
||||||
|
Case T_UI_PROMPT_INPUT: InfoToString = "InputBox with response " & target.response_
|
||||||
|
Case T_UI_PROMPT_FILE: InfoToString = "FilePromp with response " & target.response_
|
||||||
|
Case T_UI_FOLLOW_HYPERLINK: InfoToString = "Follow hyperlink " & target.response_
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CompareInfo(val1 As ExpectationInfo, val2 As ExpectationInfo) As Boolean
|
||||||
|
CompareInfo = False
|
||||||
|
If val1.id_ <> val2.id_ Then _
|
||||||
|
Exit Function
|
||||||
|
If val1.type_ <> val2.type_ Then _
|
||||||
|
Exit Function
|
||||||
|
CompareInfo = True
|
||||||
|
End Function
|
119
dev/API_TestRunner.cls
Normal file
119
dev/API_TestRunner.cls
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_TestRunner"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
'================ VBA tests runner =============
|
||||||
|
' Shared module version: 20220410
|
||||||
|
' Depends on: DevTester, DevTools, DataTools, API_Logger, CDS_InfoTests, CDS_InfoFunction, API_Timer
|
||||||
|
' Required reference: Scripting, VBIDE, Word, Excel, Visio
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public testsDB_ As Scripting.Dictionary ' Value = Collection of tests
|
||||||
|
Private target_ As Object ' Wrapper
|
||||||
|
Private logger_ As API_Logger
|
||||||
|
Private timer_ As API_Timer
|
||||||
|
|
||||||
|
' Sink object should implement function PushTestResult(sSuite$, sTest$, bResult As Boolean, sMsg$, dDuration as Double)
|
||||||
|
Private sink_ As Object
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Set timer_ = New API_Timer
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub Class_Terminate()
|
||||||
|
Set target_ = Nothing
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(targetWrapper As Object, oLog As API_Logger, Optional oSink As Object = Nothing)
|
||||||
|
Set target_ = targetWrapper
|
||||||
|
Set logger_ = oLog
|
||||||
|
Set sink_ = oSink
|
||||||
|
Set testsDB_ = New Scripting.Dictionary
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ScanTests() As Boolean
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
|
||||||
|
Call logger_.Log("Scanning for tests...")
|
||||||
|
Dim nCount&: nCount = Dev_ScanTests(target_.Document.VBProject, testsDB_)
|
||||||
|
ScanTests = True
|
||||||
|
Call logger_.Log("Found tests: " & nCount)
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
RETURN_FALSE:
|
||||||
|
ScanTests = False
|
||||||
|
Call logger_.Log("Failed to scan tests. Error code " & Err.Number & ":" & Err.Description)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RunAllTests() As CDS_InfoTests
|
||||||
|
If Not ScanTests Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim iResult As New CDS_InfoTests
|
||||||
|
Dim sSuite As Variant
|
||||||
|
Dim tests As Collection
|
||||||
|
For Each sSuite In testsDB_
|
||||||
|
Set tests = testsDB_(sSuite)
|
||||||
|
Dim sTest As Variant
|
||||||
|
For Each sTest In tests
|
||||||
|
Call iResult.AddTest(RunTest(CStr(sSuite), CStr(sTest)))
|
||||||
|
Next sTest
|
||||||
|
Next sSuite
|
||||||
|
|
||||||
|
Set RunAllTests = iResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RunTest(sSuite$, sTest$) As Boolean
|
||||||
|
RunTest = False
|
||||||
|
|
||||||
|
Call timer_.Start
|
||||||
|
On Error GoTo CANNOT_RUN
|
||||||
|
Dim sMsg$: sMsg = RunInternal(sSuite, sTest)
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
RunTest = sMsg = "OK"
|
||||||
|
If Not sink_ Is Nothing Then _
|
||||||
|
Call sink_.PushTestResult(sSuite, sTest, RunTest, sMsg, timer_.TimeElapsed)
|
||||||
|
Call logger_.Log(sSuite & "." & sTest & " ... " & sMsg)
|
||||||
|
|
||||||
|
Exit Function
|
||||||
|
CANNOT_RUN:
|
||||||
|
Call logger_.Log(sSuite & "." & sTest & " ... " & "Failed to run the test")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =====
|
||||||
|
Private Function RunInternal(sSuite$, sTest$) As String
|
||||||
|
If TypeOf target_.Application Is Visio.Application Then
|
||||||
|
RunInternal = RunFileIO(sSuite, sTest)
|
||||||
|
Else
|
||||||
|
RunInternal = target_.Run("DevTester.Dev_RunTest", sSuite, sTest)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function RunFileIO(sSuite$, sTest$) As String
|
||||||
|
Const UTF16_MODE = TristateTrue
|
||||||
|
|
||||||
|
Dim sFileName$: sFileName = target_.Document.Name & "_IO.txt"
|
||||||
|
Dim sPath$: sPath = target_.Document.Path & sFileName
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If fso.FileExists(sPath) Then _
|
||||||
|
Call fso.DeleteFile(sPath)
|
||||||
|
|
||||||
|
Call target_.Run("Dev_RunTestFileIO", sSuite, sTest, sFileName)
|
||||||
|
|
||||||
|
If Not fso.FileExists(sPath) Then
|
||||||
|
Call logger_.Log(sSuite & "." & sTest & " ... " & "Failed to run the test")
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim fs As Scripting.TextStream: Set fs = fso.OpenTextFile(sPath, ForReading, Format:=UTF16_MODE)
|
||||||
|
RunFileIO = fs.ReadAll
|
||||||
|
Call fs.Close
|
||||||
|
Call fso.DeleteFile(sPath)
|
||||||
|
End Function
|
16
dev/CDS_InfoFunction.cls
Normal file
16
dev/CDS_InfoFunction.cls
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_InfoFunction"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
'============ Function info ============
|
||||||
|
' Shared module version: 20210220
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public name_ As String
|
||||||
|
Public type_ As VBIDE.vbext_ProcKind
|
||||||
|
Public isPublic_ As Boolean
|
33
dev/CDS_InfoTests.cls
Normal file
33
dev/CDS_InfoTests.cls
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_InfoTests"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
'================ Tests summary ============
|
||||||
|
' Shared module version: 20210306
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public count_ As Long
|
||||||
|
Public success_ As Long
|
||||||
|
Public failed_ As Long
|
||||||
|
|
||||||
|
Public Function AddTest(bResult As Boolean)
|
||||||
|
count_ = count_ + 1
|
||||||
|
If bResult Then
|
||||||
|
success_ = success_ + 1
|
||||||
|
Else
|
||||||
|
failed_ = failed_ + 1
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function MergeStats(iSource As CDS_InfoTests)
|
||||||
|
If iSource Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
count_ = count_ + iSource.count_
|
||||||
|
success_ = success_ + iSource.success_
|
||||||
|
failed_ = failed_ + iSource.failed_
|
||||||
|
End Function
|
679
dev/DevTester.bas
Normal file
679
dev/DevTester.bas
Normal file
|
@ -0,0 +1,679 @@
|
||||||
|
Attribute VB_Name = "DevTester"
|
||||||
|
'================ Developer Testing mechanics =============
|
||||||
|
' Shared module version: 20220614
|
||||||
|
' Depends on: ex_VBA
|
||||||
|
' Required reference: Microsoft Scripting Runtime
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const ENUM_PARAM_CLASS_MODULE = 2
|
||||||
|
Private Const ENUM_PARAM_PROCEDURE = 0
|
||||||
|
|
||||||
|
Public Enum TTestErrors
|
||||||
|
ASSERT_FAIL = vbObjectError + 1
|
||||||
|
UNEXPECTED_INTERACTION
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Private g_TestLog As String
|
||||||
|
Private g_TestCase As String
|
||||||
|
Private g_TestClause As String
|
||||||
|
|
||||||
|
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal nMilliseconds As LongPtr)
|
||||||
|
|
||||||
|
' Run this procedure to add new test suite
|
||||||
|
Public Sub RunAddTestSuite()
|
||||||
|
Dim iProject As Object: Set iProject = GetVBProject
|
||||||
|
|
||||||
|
Dim sName$: sName = InputBox("Input suite name s_Suite")
|
||||||
|
If sName = vbNullString Then _
|
||||||
|
Exit Sub
|
||||||
|
If Not sName Like "s_*" Then
|
||||||
|
Call MsgBox("Invalid suite name", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim aComponent As Object: Set aComponent = FindVBComponent(iProject, sName)
|
||||||
|
If Not aComponent Is Nothing Then
|
||||||
|
Call MsgBox("Test suite already exists", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim iNewComponent As Object
|
||||||
|
Set iNewComponent = iProject.VBComponents.Add(ENUM_PARAM_CLASS_MODULE)
|
||||||
|
iNewComponent.Name = sName
|
||||||
|
Call iNewComponent.CodeModule.AddFromString(DefaultTestCode)
|
||||||
|
|
||||||
|
Dim iHelper As Object: Set iHelper = FindVBComponent(iProject, "DevHelper").CodeModule
|
||||||
|
Dim nStart&: nStart = iHelper.ProcStartLine("Dev_GetTestSuite", ENUM_PARAM_PROCEDURE)
|
||||||
|
Call iHelper.InsertLines(nStart + 3, GetTestSuiteLine(sName))
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Dev_LogMsg(sMsg$)
|
||||||
|
If g_TestLog <> vbNullString Then _
|
||||||
|
g_TestLog = g_TestLog & vbNewLine
|
||||||
|
|
||||||
|
If g_TestCase <> vbNullString Then
|
||||||
|
g_TestLog = g_TestLog & "Test case: " & g_TestCase & vbNewLine
|
||||||
|
g_TestCase = vbNullString
|
||||||
|
End If
|
||||||
|
|
||||||
|
If g_TestClause <> vbNullString Then
|
||||||
|
g_TestLog = g_TestLog & "Test clause: " & g_TestClause & vbNewLine
|
||||||
|
g_TestClause = vbNullString
|
||||||
|
End If
|
||||||
|
|
||||||
|
g_TestLog = g_TestLog & sMsg
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_LogError(nErrID&, Optional sDescription$ = vbNullString)
|
||||||
|
Select Case nErrID
|
||||||
|
Case 0 To 512: Call Dev_LogMsg("System error " & nErrID & vbNewLine & sDescription)
|
||||||
|
Case ASSERT_FAIL: Call Dev_LogMsg("Assertion failed... Aborting test")
|
||||||
|
Case UNEXPECTED_INTERACTION: Call Dev_LogMsg("Unexpected user interaction ... Aborting test")
|
||||||
|
Case Else: Call Dev_LogMsg("Unknown error type " & nErrID & vbNewLine & sDescription)
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_RunTestFileIO(sSuite$, sTest$, sOutputFile$)
|
||||||
|
Const UTF16_MODE = TristateTrue
|
||||||
|
|
||||||
|
Dim sResult$: sResult = Dev_RunTest(sSuite, sTest)
|
||||||
|
Dim sPath$: sPath = Application.ActiveDocument.Path & sOutputFile
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Dim fs As Scripting.TextStream: Set fs = fso.OpenTextFile(sPath, ForWriting, Create:=True, Format:=UTF16_MODE)
|
||||||
|
|
||||||
|
Call fs.Write(sResult)
|
||||||
|
Call fs.Close
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_RunTest(sSuite$, sTest$) As String
|
||||||
|
Dim iSuite As Object: Set iSuite = Dev_GetTestSuite(sSuite)
|
||||||
|
If iSuite Is Nothing Then
|
||||||
|
Dev_RunTest = "Invalid test suite"
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call Randomize
|
||||||
|
Call ResetLog
|
||||||
|
Call ResetDescription
|
||||||
|
|
||||||
|
On Error GoTo ON_ERROR
|
||||||
|
Call iSuite.Setup
|
||||||
|
Call CallByName(iSuite, sTest, VbMethod)
|
||||||
|
Call iSuite.Teardown
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
Dev_RunTest = IIf(g_TestLog = vbNullString, "OK", "FAILED" & vbNewLine & g_TestLog)
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
ON_ERROR:
|
||||||
|
Call Dev_LogError(Err.Number, Err.Description)
|
||||||
|
Dev_RunTest = "FAILED" & vbNewLine & g_TestLog
|
||||||
|
Set iSuite = Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_RunTestDebug(sSuite$, sTest$) As String
|
||||||
|
Dim iSuite As Object: Set iSuite = Dev_GetTestSuite(sSuite)
|
||||||
|
If iSuite Is Nothing Then
|
||||||
|
Dev_RunTestDebug = "Invalid test suite"
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call ResetLog
|
||||||
|
Call ResetDescription
|
||||||
|
|
||||||
|
Call iSuite.Setup
|
||||||
|
Call CallByName(iSuite, sTest, VbMethod)
|
||||||
|
Call iSuite.Teardown
|
||||||
|
|
||||||
|
Dev_RunTestDebug = IIf(g_TestLog = vbNullString, "OK", "FAILED" & vbNewLine & g_TestLog)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ====== Tester functions ========
|
||||||
|
Public Function Dev_NewCase(sDescription$)
|
||||||
|
Call ResetDescription
|
||||||
|
g_TestCase = sDescription
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_NewClause(sDescription$)
|
||||||
|
g_TestClause = sDescription
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectObjectValid(oVal As Object, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
Dev_ExpectObjectValid = TestObjectValid(oVal)
|
||||||
|
If Not Dev_ExpectObjectValid Then _
|
||||||
|
Call Dev_LogMsg("Expected valid object")
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectObjectInvalid(oVal As Object, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
Dev_ExpectObjectInvalid = Not TestObjectValid(oVal)
|
||||||
|
If Not Dev_ExpectObjectInvalid Then _
|
||||||
|
Call Dev_LogMsg("Expected invalid object")
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectNothing(oVal As Object, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
Dev_ExpectNothing = oVal Is Nothing
|
||||||
|
If Not Dev_ExpectNothing Then _
|
||||||
|
Call Dev_LogMsg("Expected Nothing")
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectNotNothing(oVal As Object, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
Dev_ExpectNotNothing = Not oVal Is Nothing
|
||||||
|
If Not Dev_ExpectNotNothing Then _
|
||||||
|
Call Dev_LogMsg("Expected not Nothing")
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectTrue(bVal As Boolean, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
Dev_ExpectTrue = bVal = True
|
||||||
|
If Not Dev_ExpectTrue Then _
|
||||||
|
Call Dev_LogMsg("Expected TRUE statement")
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectFalse(bVal As Boolean, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
Dev_ExpectFalse = bVal = False
|
||||||
|
If Not Dev_ExpectFalse Then _
|
||||||
|
Call Dev_LogMsg("Expected FALSE statement")
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectEQ(actualValue As Variant, expectedValue As Variant, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectEQ = CompareDeep(expectedValue, actualValue) = 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectEQ Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Actual: {1} | Expectedl: {2}", actualValue, expectedValue))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectEQ = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} == {2}", actualValue, expectedValue))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectNE(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectNE = CompareDeep(val1, val2) <> 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectNE Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Unexpected equality: {1} == {2}", val1, val2))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectNE = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} <> {2}", val1, val2))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectGR(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectGR = CompareDeep(val1, val2) > 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectGR Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} > {2}", val1, val2))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectGR = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} > {2}", val1, val2))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectLS(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectLS = CompareDeep(val1, val2) < 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectLS Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} < {2}", val1, val2))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectLS = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} < {2}", val1, val2))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectGE(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectGE = CompareDeep(val1, val2) >= 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectGE Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} >= {2}", val1, val2))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectGE = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} >= {2}", val1, val2))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectLE(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectLE = CompareDeep(val1, val2) <= 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectLE Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} <= {2}", val1, val2))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectLE = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} <= {2}", val1, val2))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectAEQ(actualValue As Variant, expectedValue As Variant, nPrecision&, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectAEQ = CompareApproximate(actualValue, expectedValue, nPrecision) = 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectAEQ Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Actual: {1} | Expectedl: {2} | Precision: {3}", actualValue, expectedValue, nPrecision))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectAEQ = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} == {2}, precision {3}", actualValue, expectedValue, nPrecision))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectANE(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectANE = CompareApproximate(val1, val2, nPrecision) <> 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectANE Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Unexpected equality: {1} == {2}, precision: {3}", val1, val2, nPrecision))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectANE = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} <> {2}, precision {3}", val1, val2, nPrecision))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectAGR(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectAGR = CompareApproximate(val1, val2, nPrecision) > 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectAGR Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} > {2}, precision {3}", val1, val2, nPrecision))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectAGR = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} > {2}, precision {3}", val1, val2, nPrecision))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectALS(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectALS = CompareApproximate(val1, val2, nPrecision) < 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectALS Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} < {2}, precision {3}", val1, val2, nPrecision))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectALS = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} < {2}, precision {3}", val1, val2, nPrecision))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectAGE(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectAGE = CompareApproximate(val1, val2, nPrecision) >= 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectAGE Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} >= {2}, precision {3}", val1, val2, nPrecision))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectAGE = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} >= {2}, precision {3}", val1, val2, nPrecision))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectALE(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
On Error GoTo INCOMPARABLE
|
||||||
|
Dev_ExpectALE = CompareApproximate(val1, val2, nPrecision) <= 0
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not Dev_ExpectALE Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} <= {2}, precision {3}", val1, val2, nPrecision))
|
||||||
|
Call ResetClause
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INCOMPARABLE:
|
||||||
|
Dev_ExpectALE = False
|
||||||
|
Call Dev_LogMsg(Fmt("Invalid comparison: {1} <= {2}, precision {3}", val1, val2, nPrecision))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========= String comparisons ===========
|
||||||
|
Public Function Dev_ExpectLike(sValue$, sMask$, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
Dev_ExpectLike = sValue Like sMask
|
||||||
|
If Not Dev_ExpectLike Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} Like {2}", sValue, sMask))
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectNotLike(sValue$, sMask$, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
Dev_ExpectNotLike = Not sValue Like sMask
|
||||||
|
If Not Dev_ExpectNotLike Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected: {1} not Like {2}", sValue, sMask))
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ======= Error comparisons ===========
|
||||||
|
Public Function Dev_ExpectAnyError(Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
Dev_ExpectAnyError = Err.Number <> 0
|
||||||
|
If Not Dev_ExpectAnyError Then _
|
||||||
|
Call Dev_LogMsg("Expected any Error, but got nothing")
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectError(nError&, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
Dev_ExpectError = Err.Number = nError
|
||||||
|
If Err.Number = 0 Then
|
||||||
|
Call Dev_LogMsg(Fmt("Expected Error #{1}, but got no error", nError))
|
||||||
|
ElseIf Not Dev_ExpectError Then
|
||||||
|
Call Dev_LogMsg(Fmt("Expected Error #{1}, actual Error #{2}", nError, Err.Number))
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call Err.Clear
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectErrorMsg(sDesc$, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
Dev_ExpectErrorMsg = Err.Description = sDesc
|
||||||
|
If Err.Number = 0 Then
|
||||||
|
Call Dev_LogMsg(Fmt("Expected Error, but got no error"))
|
||||||
|
ElseIf Not Dev_ExpectErrorMsg Then
|
||||||
|
Call Dev_LogMsg(Fmt("Expected error msg: #{1}" & vbNewLine & "Actual error message: #{2}", sDesc, Err.Description))
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call Err.Clear
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ExpectNoError(Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If sClause <> vbNullString Then _
|
||||||
|
Call Dev_NewClause(sClause)
|
||||||
|
|
||||||
|
Dev_ExpectNoError = Err.Number = 0
|
||||||
|
If Not Dev_ExpectNoError Then _
|
||||||
|
Call Dev_LogMsg(Fmt("Expected no Error, but got Error #{1}", Err.Number))
|
||||||
|
|
||||||
|
Call Err.Clear
|
||||||
|
Call ResetClause
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =========== Assertion proxies ==========
|
||||||
|
Public Function Dev_AssertObjectValid(oVal As Object, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectObjectValid(oVal, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertObjectInvalid(oVal As Object, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectObjectInvalid(oVal, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertNothing(oVal As Object, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectNothing(oVal, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertNotNothing(oVal As Object, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectNotNothing(oVal, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertTrue(bVal As Boolean, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectTrue(bVal, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertFalse(bVal As Boolean, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectFalse(bVal, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertEQ(expectedValue As Variant, actualValue As Variant, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectEQ(expectedValue, actualValue, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertNE(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectNE(val1, val2, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertGR(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectGR(val1, val2, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertLS(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectLS(val1, val2, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertGE(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectGE(val1, val2, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertLE(val1 As Variant, val2 As Variant, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectLE(val1, val2, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertAEQ(expectedValue As Variant, actualValue As Variant, nPrecision&, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectAEQ(expectedValue, actualValue, nPrecision, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertANE(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectANE(val1, val2, nPrecision, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertAGR(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectAGR(val1, val2, nPrecision, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertALS(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectALS(val1, val2, nPrecision, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertAGE(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectAGE(val1, val2, nPrecision, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertALE(val1 As Variant, val2 As Variant, nPrecision&, Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectALE(val1, val2, nPrecision, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertLike(sValue$, sMask$, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If Not Dev_ExpectLike(sValue, sMask, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertNotLike(sValue$, sMask$, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If Not Dev_ExpectNotLike(sValue, sMask, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertAnyError(Optional sClause$ = vbNullString)
|
||||||
|
If Not Dev_ExpectAnyError(sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertError(nError&, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If Not Dev_ExpectError(nError, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertErrorMsg(sDesc$, Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If Not Dev_ExpectErrorMsg(sDesc, sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_AssertNoError(Optional sClause$ = vbNullString) As Boolean
|
||||||
|
If Not Dev_ExpectNoError(sClause) Then _
|
||||||
|
Call Err.Raise(ASSERT_FAIL)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========
|
||||||
|
Private Function ResetLog()
|
||||||
|
g_TestLog = vbNullString
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ResetDescription()
|
||||||
|
g_TestCase = vbNullString
|
||||||
|
g_TestClause = vbNullString
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ResetClause()
|
||||||
|
g_TestClause = vbNullString
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TestObjectValid(target As Object) As Boolean
|
||||||
|
Call Sleep(100)
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
Dim sName$: sName = target.Name
|
||||||
|
Select Case Err.Number
|
||||||
|
' no error
|
||||||
|
Case 0: TestObjectValid = True
|
||||||
|
|
||||||
|
' object doesnt not support property
|
||||||
|
Case 438: TestObjectValid = True
|
||||||
|
|
||||||
|
Case Else: TestObjectValid = False
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GetVBProject() As Object
|
||||||
|
Dim iApplication As Object: Set iApplication = Application
|
||||||
|
If Application.Name = "Microsoft Excel" Then
|
||||||
|
Set GetVBProject = iApplication.ThisWorkbook.VBProject
|
||||||
|
Else
|
||||||
|
Set GetVBProject = iApplication.VBE.ActiveVBProject
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function FindVBComponent(iProject As Object, sName$) As Object
|
||||||
|
Dim aComponent As Object
|
||||||
|
For Each aComponent In iProject.VBComponents
|
||||||
|
If aComponent.Name = sName Then
|
||||||
|
Set FindVBComponent = aComponent
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next aComponent
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function DefaultTestCode() As String
|
||||||
|
DefaultTestCode = _
|
||||||
|
"Public Function Setup()" & vbNewLine & _
|
||||||
|
" ' Mandatory setup function" & vbNewLine & _
|
||||||
|
"End Function" & vbNewLine & _
|
||||||
|
vbNewLine & _
|
||||||
|
"Public Function Teardown()" & vbNewLine & _
|
||||||
|
" ' Mandatory teardown function" & vbNewLine & _
|
||||||
|
"End Function" & vbNewLine & _
|
||||||
|
vbNewLine & _
|
||||||
|
"Public Function t_Init()" & vbNewLine & _
|
||||||
|
" On Error GoTo PROPAGATE_ERROR" & vbNewLine & _
|
||||||
|
vbNewLine & _
|
||||||
|
" Exit Function" & vbNewLine & _
|
||||||
|
"PROPAGATE_ERROR:" & vbNewLine & _
|
||||||
|
" Call Dev_LogError(Err.Number, Err.Description)" & vbNewLine & _
|
||||||
|
"End Function"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GetTestSuiteLine(sName$) As String
|
||||||
|
GetTestSuiteLine = _
|
||||||
|
" Case " & """" & sName & """" & ": " & _
|
||||||
|
"Set Dev_GetTestSuite = New " & sName
|
||||||
|
End Function
|
21
dev/DevTesterUI.bas
Normal file
21
dev/DevTesterUI.bas
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
Attribute VB_Name = "DevTesterUI"
|
||||||
|
'================ Developer Testing mechanics for UI testing =============
|
||||||
|
' Shared module version: 20210401
|
||||||
|
' Depends on: API_MockInteraction, API_UserInteraction, z_UIMessages
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private g_MockUI As API_MockInteraction
|
||||||
|
|
||||||
|
Public Function Dev_MockUI() As API_MockInteraction
|
||||||
|
If g_MockUI Is Nothing Then _
|
||||||
|
Set g_MockUI = New API_MockInteraction
|
||||||
|
Call SetUserInteraction(g_MockUI)
|
||||||
|
Set Dev_MockUI = g_MockUI
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ResetUI() As Boolean
|
||||||
|
Dev_ResetUI = g_MockUI.ResetExpectations
|
||||||
|
Call SetUserInteraction(New API_UserInteraction)
|
||||||
|
End Function
|
194
dev/DevTools.bas
Normal file
194
dev/DevTools.bas
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
Attribute VB_Name = "DevTools"
|
||||||
|
'================ Developer Tools =============
|
||||||
|
' Shared module version: 20220811
|
||||||
|
' Depends on: ex_WinAPI
|
||||||
|
' Required reference: Scripting, VBIDE
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const TEST_SUITE_MASK = "s_*"
|
||||||
|
Private Const TEST_NAME_MASK = "t_*"
|
||||||
|
Private Const COMPILATION_DELAY As Long = 250 ' ms
|
||||||
|
|
||||||
|
Public Function CompileVBProject(target As VBIDE.VBProject) As Boolean
|
||||||
|
Const CONTROL_ID_COMPILE = 578
|
||||||
|
Const MSGBOX_CLASS_ID As String = "#32770"
|
||||||
|
Const MSGBOX_NAME As String = "Microsoft Visual Basic for Applications"
|
||||||
|
Const WINDOW_MESSAGE_CLOSE As Long = &H10
|
||||||
|
|
||||||
|
target.VBE.MainWindow.Visible = True
|
||||||
|
With target.VBComponents(1).CodeModule
|
||||||
|
Call .CodePane.Show
|
||||||
|
Call .InsertLines(1, "123")
|
||||||
|
Call .DeleteLines(1, 1)
|
||||||
|
End With
|
||||||
|
|
||||||
|
Dim btnCompile As CommandBarControl: Set btnCompile = target.VBE.CommandBars.FindControl(ID:=CONTROL_ID_COMPILE)
|
||||||
|
If Not btnCompile.Enabled Then
|
||||||
|
CompileVBProject = False
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call btnCompile.Execute
|
||||||
|
|
||||||
|
Call Sleep(COMPILATION_DELAY)
|
||||||
|
|
||||||
|
Dim nHwnd&: nHwnd = FindWindow(MSGBOX_CLASS_ID, MSGBOX_NAME)
|
||||||
|
CompileVBProject = nHwnd = 0
|
||||||
|
If Not CompileVBProject Then _
|
||||||
|
Call PostMessage(nHwnd, WINDOW_MESSAGE_CLOSE, 0&, 0&)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function UpdateVersionStamp(target As VBIDE.VBProject, sVersion$) As Boolean
|
||||||
|
UpdateVersionStamp = False
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
Dim iModule As VBComponent: Set iModule = target.VBComponents("Main")
|
||||||
|
If iModule Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim iCode As CodeModule: Set iCode = iModule.CodeModule
|
||||||
|
|
||||||
|
Dim nLineStart&: nLineStart = 1
|
||||||
|
Dim nLineEnd&: nLineEnd = iCode.CountOfLines
|
||||||
|
|
||||||
|
Dim nColumnStart&: nColumnStart = 1
|
||||||
|
Dim nColumnEnd&: nColumnEnd = 255
|
||||||
|
|
||||||
|
If Not iCode.Find("Public Const PRODUCT_VERSION", nLineStart, nColumnStart, nLineEnd, nColumnEnd) Then _
|
||||||
|
Exit Function
|
||||||
|
Call iCode.ReplaceLine(nLineEnd, "Public Const PRODUCT_VERSION = """ & sVersion & """")
|
||||||
|
|
||||||
|
UpdateVersionStamp = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function PrepareSkeletonFor(target As Object)
|
||||||
|
On Error Resume Next
|
||||||
|
Call target.Application.Run("'" & target.Name & "'" & "!Dev_PrepareSkeleton")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExtractProperties(sLines() As String, ByRef nCurrent&, ByRef oSink As Scripting.Dictionary)
|
||||||
|
ExtractProperties = False
|
||||||
|
|
||||||
|
Do While nCurrent <= UBound(sLines, 1)
|
||||||
|
Dim sLine$: sLine = sLines(nCurrent)
|
||||||
|
nCurrent = nCurrent + 1
|
||||||
|
If sLine Like "%%*" Then _
|
||||||
|
Exit Do
|
||||||
|
|
||||||
|
Dim nSeparator&: nSeparator = VBA.InStr(1, sLine, "=")
|
||||||
|
If nSeparator = 0 Then _
|
||||||
|
GoTo NEXT_LINE
|
||||||
|
If VBA.Left(sLine, 1) = "#" Then _
|
||||||
|
GoTo NEXT_LINE
|
||||||
|
|
||||||
|
Dim sKey$: sKey = VBA.Trim(VBA.Left(sLine, nSeparator - 1))
|
||||||
|
Dim sValue$: sValue = VBA.Trim(Right(sLine, VBA.Len(sLine) - nSeparator))
|
||||||
|
If oSink.Exists(sKey) Then _
|
||||||
|
Exit Function
|
||||||
|
Call oSink.Add(sKey, sValue)
|
||||||
|
NEXT_LINE:
|
||||||
|
Loop
|
||||||
|
|
||||||
|
ExtractProperties = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_CountPublicAPI(target As VBIDE.CodeModule) As Long
|
||||||
|
Dim nLine&
|
||||||
|
For nLine = 1 To target.CountOfLines Step 1
|
||||||
|
If InStr(1, target.Lines(nLine, 1), "Public") <> 0 Then _
|
||||||
|
Dev_CountPublicAPI = Dev_CountPublicAPI + 1
|
||||||
|
Next nLine
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ScanFunctions(target As VBIDE.CodeModule) As Collection ' of CDS_InfoFunction
|
||||||
|
Dim cResult As New Collection
|
||||||
|
|
||||||
|
Dim currentFunc As CDS_InfoFunction
|
||||||
|
Dim lastFunc As New CDS_InfoFunction
|
||||||
|
Dim nLine&
|
||||||
|
For nLine = 1 To target.CountOfLines Step 1
|
||||||
|
Set currentFunc = ExtractProcedure(target, nLine)
|
||||||
|
If lastFunc.name_ <> currentFunc.name_ And currentFunc.name_ <> vbNullString Then
|
||||||
|
currentFunc.isPublic_ = IsPublicFunc(target, currentFunc)
|
||||||
|
Call cResult.Add(currentFunc)
|
||||||
|
End If
|
||||||
|
Set lastFunc = currentFunc
|
||||||
|
Next nLine
|
||||||
|
|
||||||
|
Set Dev_ScanFunctions = cResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_ScanTests(target As VBIDE.VBProject, ByRef cTestsDB As Scripting.Dictionary) As Long
|
||||||
|
Dim nCount&: nCount = 0
|
||||||
|
Dim aComponent As VBIDE.VBComponent
|
||||||
|
Dim iComponents As VBIDE.VBComponents: Set iComponents = target.VBComponents
|
||||||
|
Dim tests As Collection
|
||||||
|
For Each aComponent In iComponents
|
||||||
|
If aComponent.Type <> vbext_ct_ClassModule Then _
|
||||||
|
GoTo NEXT_COMPONENT
|
||||||
|
If Not aComponent.Name Like TEST_SUITE_MASK Then _
|
||||||
|
GoTo NEXT_COMPONENT
|
||||||
|
|
||||||
|
Set tests = New Collection
|
||||||
|
Dim funcs As Collection: Set funcs = Dev_ScanFunctions(aComponent.CodeModule)
|
||||||
|
Dim aFunc As CDS_InfoFunction
|
||||||
|
For Each aFunc In funcs
|
||||||
|
If aFunc.name_ Like TEST_NAME_MASK And aFunc.isPublic_ Then _
|
||||||
|
Call tests.Add(aFunc.name_)
|
||||||
|
Next aFunc
|
||||||
|
|
||||||
|
If tests.Count > 0 Then
|
||||||
|
Call cTestsDB.Add(aComponent.Name, tests)
|
||||||
|
nCount = nCount + tests.Count
|
||||||
|
End If
|
||||||
|
NEXT_COMPONENT:
|
||||||
|
Next aComponent
|
||||||
|
|
||||||
|
Set iComponents = Nothing
|
||||||
|
Set aComponent = Nothing
|
||||||
|
|
||||||
|
Dev_ScanTests = nCount
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_RemoveDebugCode(target As VBIDE.VBProject)
|
||||||
|
Dim selectedComps As New Collection
|
||||||
|
Dim aComponent As VBIDE.VBComponent
|
||||||
|
For Each aComponent In target.VBComponents
|
||||||
|
If Dev_IsTestingModule(aComponent.Name) Then _
|
||||||
|
Call selectedComps.Add(aComponent)
|
||||||
|
Next aComponent
|
||||||
|
|
||||||
|
For Each aComponent In selectedComps
|
||||||
|
Call target.VBComponents.Remove(aComponent)
|
||||||
|
Next aComponent
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Dev_IsTestingModule(sName$) As Boolean
|
||||||
|
Dev_IsTestingModule = True
|
||||||
|
|
||||||
|
If sName Like TEST_SUITE_MASK Then _
|
||||||
|
Exit Function
|
||||||
|
If sName Like "DevHelper*" Then _
|
||||||
|
Exit Function
|
||||||
|
If sName Like "DevTester*" Then _
|
||||||
|
Exit Function
|
||||||
|
If sName Like "DevTesterUI*" Then _
|
||||||
|
Exit Function
|
||||||
|
If sName Like "API_MockInteraction*" Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dev_IsTestingModule = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =====
|
||||||
|
Private Function ExtractProcedure(target As VBIDE.CodeModule, nLine&) As CDS_InfoFunction
|
||||||
|
Set ExtractProcedure = New CDS_InfoFunction
|
||||||
|
ExtractProcedure.name_ = target.ProcOfLine(nLine, ExtractProcedure.type_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IsPublicFunc(target As VBIDE.CodeModule, func As CDS_InfoFunction) As Boolean
|
||||||
|
Dim nStart&: nStart = target.ProcBodyLine(func.name_, func.type_)
|
||||||
|
Dim sTxt$: sTxt = target.Lines(nStart, 1)
|
||||||
|
IsPublicFunc = InStr(1, sTxt, "Private") = 0
|
||||||
|
End Function
|
84
dev/ex_ConceptOrganization.bas
Normal file
84
dev/ex_ConceptOrganization.bas
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
Attribute VB_Name = "ex_ConceptOrganization"
|
||||||
|
' ======== Concept organization specific functionality ============
|
||||||
|
' Shared module version: 20221031
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Enum TProjectType
|
||||||
|
T_PRJ_LEAD_ACTIVE = 1
|
||||||
|
T_PRJ_LEAD_TENTATIVE = 2
|
||||||
|
T_PRJ_OUTCOME = 3
|
||||||
|
T_PRJ_INCOME = 4
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Enum TProjectCategory
|
||||||
|
T_PCAT_ERR = 0
|
||||||
|
T_PCAT_MISC = 1
|
||||||
|
T_PCAT_OUTCOME = 2
|
||||||
|
T_PCAT_INCOME = 3
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Const SERVER_PATH_INCOME = "\\fs1.concept.ru\projects\01 Income-ïðîåêòû"
|
||||||
|
Public Const SERVER_PATH_OUTCOME = "\\fs1.concept.ru\projects\02 Outcome-ïðîåêòû"
|
||||||
|
Public Const SERVER_PATH_LEAD_ACTIVE = "\\fs1.concept.ru\projects\03 Ïåðåãîâîðû Àêòèâíûå"
|
||||||
|
Public Const SERVER_PATH_LEAD_TENTATIVE = "\\fs1.concept.ru\projects\03 1 Ïåðåãîâîðû âîçìîæíûå"
|
||||||
|
|
||||||
|
Public Function CCListProjects(nType As TProjectType) As Scripting.Dictionary
|
||||||
|
Dim iProjects As New Scripting.Dictionary
|
||||||
|
Set CCListProjects = iProjects
|
||||||
|
|
||||||
|
Dim sFolder$: sFolder = GetProjectsFolder(nType)
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
If Not fso.FolderExists(sFolder) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim iSubFolder As Scripting.Folder
|
||||||
|
For Each iSubFolder In fso.GetFolder(sFolder).SubFolders
|
||||||
|
If CCTestProjectName(iSubFolder.Name) Then _
|
||||||
|
Call iProjects.Add(iSubFolder.Name, ProjectTypeToCategory(nType))
|
||||||
|
Next iSubFolder
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CCTestProjectName(sProject$) As Boolean
|
||||||
|
CCTestProjectName = sProject Like "####*"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CCCategoryToString(nType As TProjectCategory) As String
|
||||||
|
Select Case nType
|
||||||
|
Case T_PCAT_ERR: CCCategoryToString = "ERR"
|
||||||
|
Case T_PCAT_INCOME: CCCategoryToString = "I"
|
||||||
|
Case T_PCAT_OUTCOME: CCCategoryToString = "O"
|
||||||
|
Case T_PCAT_MISC: CCCategoryToString = "-"
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CCStringToCategory(sType$) As TProjectCategory
|
||||||
|
Select Case sType
|
||||||
|
Case "I": CCStringToCategory = T_PCAT_INCOME
|
||||||
|
Case "O": CCStringToCategory = T_PCAT_OUTCOME
|
||||||
|
Case "-": CCStringToCategory = T_PCAT_MISC
|
||||||
|
Case Else: CCStringToCategory = T_PCAT_ERR
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ============
|
||||||
|
Private Function GetProjectsFolder(nType As TProjectType) As String
|
||||||
|
Select Case nType
|
||||||
|
Case T_PRJ_INCOME: GetProjectsFolder = SERVER_PATH_INCOME
|
||||||
|
Case T_PRJ_OUTCOME: GetProjectsFolder = SERVER_PATH_OUTCOME
|
||||||
|
Case T_PRJ_LEAD_ACTIVE: GetProjectsFolder = SERVER_PATH_LEAD_ACTIVE
|
||||||
|
Case T_PRJ_LEAD_TENTATIVE: GetProjectsFolder = SERVER_PATH_LEAD_TENTATIVE
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ProjectTypeToCategory(nType As TProjectCategory) As TProjectCategory
|
||||||
|
Select Case nType
|
||||||
|
Case T_PRJ_INCOME: ProjectTypeToCategory = T_PCAT_INCOME
|
||||||
|
Case T_PRJ_OUTCOME: ProjectTypeToCategory = T_PCAT_OUTCOME
|
||||||
|
Case T_PRJ_LEAD_ACTIVE: ProjectTypeToCategory = T_PCAT_OUTCOME
|
||||||
|
Case T_PRJ_LEAD_TENTATIVE: ProjectTypeToCategory = T_PCAT_OUTCOME
|
||||||
|
End Select
|
||||||
|
End Function
|
49
excel/API_XLRecordsWrapper.cls
Normal file
49
excel/API_XLRecordsWrapper.cls
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_XLRecordsWrapper"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ==== Îáîëî÷êà äëÿ ëèñòà Excel êàê èñòî÷íèêà çàïèñåé äàííûõ =========
|
||||||
|
' Shared module version: 20210329
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
' Prerequisites: first column should contain data with no empty cells
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private data_ As Excel.Worksheet
|
||||||
|
Private row_ As Long
|
||||||
|
Private start_ As Long
|
||||||
|
|
||||||
|
Public Function Init(ws As Excel.Worksheet, Optional nStart& = 2)
|
||||||
|
start_ = nStart
|
||||||
|
Set data_ = ws
|
||||||
|
Call MoveStart
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function MoveStart()
|
||||||
|
row_ = start_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function IsDone() As Boolean
|
||||||
|
IsDone = data_.Cells(row_, 1) = vbNullString
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function MoveNext() As Boolean
|
||||||
|
MoveNext = Not IsDone
|
||||||
|
If MoveNext Then _
|
||||||
|
row_ = row_ + 1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function MovePrev() As Boolean
|
||||||
|
MovePrev = row_ > start_
|
||||||
|
If MovePrev Then _
|
||||||
|
row_ = row_ - 1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Field(nColumn&) As Excel.Range
|
||||||
|
Set Field = data_.Cells(row_, nColumn)
|
||||||
|
End Function
|
151
excel/ex_Excel.bas
Normal file
151
excel/ex_Excel.bas
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
Attribute VB_Name = "ex_Excel"
|
||||||
|
'================ Äîïîëíèòåëüíûé ôóíêöèîíàë Excel =============
|
||||||
|
' Shared module version: 20220623
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Const XLUTIL_GRAPH_PADDING = 0.1
|
||||||
|
Public Const XL_DEFAULT_FIRST_ROW = 2
|
||||||
|
Public Const XL_INVALID_ROW = -1
|
||||||
|
|
||||||
|
Private Const XL_INFINITY_THRESHOLD = 255
|
||||||
|
|
||||||
|
Public Function XLFirstEmptyCell(iRange As Excel.Range) As Long
|
||||||
|
Dim nItem&: nItem = 1
|
||||||
|
Dim iCell As Excel.Range
|
||||||
|
For Each iCell In iRange
|
||||||
|
If iCell = vbNullString Then
|
||||||
|
XLFirstEmptyCell = nItem
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
nItem = nItem + 1
|
||||||
|
Next iCell
|
||||||
|
|
||||||
|
XLFirstEmptyCell = -1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function XLGetCallerCell() As Excel.Range
|
||||||
|
On Error GoTo RETURN_NOTHING
|
||||||
|
If TypeName(Application.Caller) = "Range" Then
|
||||||
|
Set XLGetCallerCell = Application.Caller
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
RETURN_NOTHING:
|
||||||
|
On Error GoTo 0
|
||||||
|
Set XLGetCallerCell = Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function XLWorksheetExists(sName$, iWhere As Excel.Workbook) As Boolean
|
||||||
|
On Error Resume Next
|
||||||
|
Dim iSheet As Excel.Worksheet
|
||||||
|
Set iSheet = iWhere.Sheets(sName)
|
||||||
|
On Error GoTo 0
|
||||||
|
XLWorksheetExists = Not iSheet Is Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function XLShowAllData(target As Excel.Worksheet, Optional bKeepRows As Boolean = False, Optional bKeepColumns As Boolean = False)
|
||||||
|
On Error Resume Next
|
||||||
|
Call target.ShowAllData
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If Not bKeepRows Then _
|
||||||
|
target.Rows.EntireRow.Hidden = False
|
||||||
|
If Not bKeepColumns Then _
|
||||||
|
target.Columns.EntireColumn.Hidden = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function XLUpdateHyperlink(iCellAnchor As Excel.Range, sAddress$, Optional sSubAddress$ = "")
|
||||||
|
Call iCellAnchor.Hyperlinks.Delete
|
||||||
|
If sAddress = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
Call iCellAnchor.Hyperlinks.Add(iCellAnchor, sAddress, sSubAddress)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AutoScaleGraphAxis(target As Excel.Chart, tValueRange As Excel.Range)
|
||||||
|
Dim dMinValue As Double: dMinValue = WorksheetFunction.Min(tValueRange)
|
||||||
|
Dim dMaxValue As Double: dMaxValue = WorksheetFunction.Max(tValueRange)
|
||||||
|
If dMaxValue = dMinValue Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim dPrecision&: dPrecision = -(CLng(VBA.Log(dMaxValue - dMinValue) / VBA.Log(10#)) - 1)
|
||||||
|
|
||||||
|
If VBA.Sgn(dMinValue) = VBA.Sgn(dMaxValue) Then
|
||||||
|
dMinValue = dMinValue * IIf(dMinValue > 0, 1 - XLUTIL_GRAPH_PADDING, 1 + XLUTIL_GRAPH_PADDING)
|
||||||
|
dMaxValue = dMaxValue * IIf(dMaxValue > 0, 1 + XLUTIL_GRAPH_PADDING, 1 - XLUTIL_GRAPH_PADDING)
|
||||||
|
dMinValue = WorksheetFunction.Round(dMinValue, dPrecision)
|
||||||
|
dMaxValue = WorksheetFunction.Round(dMaxValue, dPrecision)
|
||||||
|
|
||||||
|
target.Axes(xlValue, xlPrimary).MinimumScale = dMinValue
|
||||||
|
target.Axes(xlValue, xlPrimary).MaximumScale = dMaxValue
|
||||||
|
End If
|
||||||
|
|
||||||
|
If dPrecision > 0 Then
|
||||||
|
target.Axes(xlValue, xlPrimary).TickLabels.NumberFormat = "# ##0," & VBA.Replace(VBA.Space(dPrecision), " ", "0")
|
||||||
|
Else
|
||||||
|
target.Axes(xlValue, xlPrimary).TickLabels.NumberFormat = "# ##0"
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function XLFindOrCreateID(sID$, nIdColumn&, target As Excel.Worksheet, _
|
||||||
|
Optional nFirstRow& = XL_DEFAULT_FIRST_ROW) As Long
|
||||||
|
' Note: this function is precise but slow. Consider using Range.Find but beware of filtered cells being ignored
|
||||||
|
Dim nRow&: nRow = nFirstRow
|
||||||
|
Dim sTxt$
|
||||||
|
Do
|
||||||
|
sTxt = target.Cells(nRow, nIdColumn)
|
||||||
|
If sTxt = vbNullString Then _
|
||||||
|
Exit Do
|
||||||
|
If sTxt = sID Then
|
||||||
|
XLFindOrCreateID = nRow
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
nRow = nRow + 1
|
||||||
|
Loop
|
||||||
|
target.Cells(nRow, nIdColumn) = sID
|
||||||
|
XLFindOrCreateID = nRow
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function XLBruteFindRow(sTarget$, nIdColumn&, wsWhere As Excel.Worksheet, _
|
||||||
|
Optional nFirstRow = XL_DEFAULT_FIRST_ROW) As Long
|
||||||
|
XLBruteFindRow = XL_INVALID_ROW
|
||||||
|
Dim nRow&: nRow = nFirstRow
|
||||||
|
Dim sTxt$
|
||||||
|
Do
|
||||||
|
sTxt = wsWhere.Cells(nRow, nIdColumn)
|
||||||
|
If sTxt = vbNullString Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sTxt = sTarget Then
|
||||||
|
XLBruteFindRow = nRow
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
nRow = nRow + 1
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FlipColumnsOrder(target As Excel.Worksheet, nStart&, nFinish&)
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 0 To nFinish - nStart - 1 Step 1
|
||||||
|
Call target.Columns(nStart).Cut
|
||||||
|
Call target.Columns(nFinish - nItem + 1).Insert
|
||||||
|
Next nItem
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function XLForEachNonEmptyCell(target As Excel.Range, oCallback As Object, sFuncName$)
|
||||||
|
Dim aCell As Excel.Range
|
||||||
|
Dim blankCount%: blankCount = 0
|
||||||
|
For Each aCell In target
|
||||||
|
If aCell = vbNullString Then
|
||||||
|
blankCount = blankCount + 1
|
||||||
|
If blankCount > XL_INFINITY_THRESHOLD Then _
|
||||||
|
Exit Function
|
||||||
|
Else
|
||||||
|
blankCount = 0
|
||||||
|
Call CallByName(oCallback, sFuncName, VbMethod, aCell)
|
||||||
|
End If
|
||||||
|
Next aCell
|
||||||
|
End Function
|
||||||
|
|
50
parsers/DetectorClassifier.cls
Normal file
50
parsers/DetectorClassifier.cls
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "DetectorClassifier"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Classification entity detector =======
|
||||||
|
' Shared module version: 20220615
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: API_Python, ex_Python
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private tags_ As String
|
||||||
|
|
||||||
|
Public Function Init(sTags$)
|
||||||
|
tags_ = sTags
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Test(sText$) As Boolean
|
||||||
|
If tags_ = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
Test = AccessPython.CallFunction(PY_MODULE_TEXT, "extract_entities", Array(sText)) <> Array()
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExtractFragments(sText$) As PC_ParsedData
|
||||||
|
Dim iData As New PC_ParsedData
|
||||||
|
|
||||||
|
On Error GoTo SKIP_PYTHON
|
||||||
|
Dim iResult As Variant
|
||||||
|
iResult = AccessPython.CallFunction(PY_MODULE_TEXT, "extract_entities", Array(sText))
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = LBound(iResult) To UBound(iResult) Step 1
|
||||||
|
Dim nStart&: nStart = iResult(nItem, 0)
|
||||||
|
Dim nEnd&: nEnd = iResult(nItem, 1)
|
||||||
|
Dim nClass&: nClass = iResult(nItem, 2)
|
||||||
|
Call iData.AddItem(nStart, nEnd, nClass)
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
SKIP_PYTHON:
|
||||||
|
Set ExtractFragments = iData
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
|
105
parsers/DetectorListWords.cls
Normal file
105
parsers/DetectorListWords.cls
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "DetectorListWords"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Single word dictionary detector =======
|
||||||
|
' Shared module version: 20220623
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: ADODB, Scripting
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private data_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Public Function Init(sDictionaryPath$)
|
||||||
|
Set data_ = New Scripting.Dictionary
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
Call LoadFrom(sDictionaryPath)
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Test(sText$) As Boolean
|
||||||
|
Test = data_.Exists(VBA.LCase(sText))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExtractFragments(sText$) As PC_ParsedData
|
||||||
|
Dim iData As New PC_ParsedData
|
||||||
|
|
||||||
|
Dim nStart&: nStart = 1
|
||||||
|
Dim nLen&: nLen = VBA.Len(sText)
|
||||||
|
Dim nCur&: nCur = nStart
|
||||||
|
Do While nCur <= nLen
|
||||||
|
If IsDelim(VBA.Mid$(sText, nCur, 1)) Then
|
||||||
|
If nCur > nStart Then _
|
||||||
|
If Test(VBA.Mid$(sText, nStart, nCur - nStart)) Then _
|
||||||
|
Call iData.AddItem(nStart - 1, nCur - 1)
|
||||||
|
nStart = nCur + 1
|
||||||
|
End If
|
||||||
|
nCur = nCur + 1
|
||||||
|
Loop
|
||||||
|
|
||||||
|
If nCur > nStart Then _
|
||||||
|
If Test(VBA.Mid$(sText, nStart, nCur - nStart)) Then _
|
||||||
|
Call iData.AddItem(nStart - 1, nCur - 1)
|
||||||
|
|
||||||
|
Set ExtractFragments = iData
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========
|
||||||
|
Public Function LoadFrom(sPath$)
|
||||||
|
Dim adoStream As New ADODB.Stream
|
||||||
|
adoStream.Charset = "utf-8"
|
||||||
|
Call adoStream.Open
|
||||||
|
Call adoStream.LoadFromFile(sPath)
|
||||||
|
|
||||||
|
Dim nLine&: nLine = 1
|
||||||
|
Do Until adoStream.EOS
|
||||||
|
Dim sItem$: sItem = adoStream.ReadText(adReadLine)
|
||||||
|
If sItem <> "" And Not data_.Exists(sItem) Then _
|
||||||
|
Call data_.Add(VBA.LCase(sItem), nLine)
|
||||||
|
nLine = nLine + 1
|
||||||
|
Loop
|
||||||
|
|
||||||
|
Call adoStream.Close
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ==========
|
||||||
|
Private Function IsDelim(sSymbol$) As Boolean
|
||||||
|
IsDelim = True
|
||||||
|
If sSymbol = "." Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = Chr(13) Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = Chr(10) Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = "," Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ChrW(&H2013) Then _
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ChrW(&H2012) Then _
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ChrW(&H2010) Then _
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ";" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = " " Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = "!" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ":" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = "?" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = """" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = "Chr(85)" Then
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
IsDelim = False
|
||||||
|
End Function
|
47
parsers/DetectorMorpho.cls
Normal file
47
parsers/DetectorMorpho.cls
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "DetectorMorpho"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Morphology expression detector =======
|
||||||
|
' Shared module version: 20220613
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: API_Python, ex_Python
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private tags_ As String
|
||||||
|
|
||||||
|
Public Function Init(sTags$)
|
||||||
|
tags_ = sTags
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Test(sText$) As Boolean
|
||||||
|
If tags_ = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
Test = AccessPython.CallFunction(PY_MODULE_TEXT, "parse", Array(sText, tags_)) <> ""
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExtractFragments(sText$) As PC_ParsedData
|
||||||
|
Dim iData As New PC_ParsedData
|
||||||
|
|
||||||
|
On Error GoTo SKIP_PYTHON
|
||||||
|
Dim iResult As Variant
|
||||||
|
iResult = AccessPython.CallFunction(PY_MODULE_TEXT, "match_all_morpho", Array(sText, tags_))
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = LBound(iResult) To UBound(iResult) Step 1
|
||||||
|
Dim nStart&: nStart = iResult(nItem, 0)
|
||||||
|
Dim nEnd&: nEnd = iResult(nItem, 1)
|
||||||
|
Call iData.AddItem(nStart, nEnd)
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
SKIP_PYTHON:
|
||||||
|
Set ExtractFragments = iData
|
||||||
|
End Function
|
||||||
|
|
42
parsers/DetectorRegex.cls
Normal file
42
parsers/DetectorRegex.cls
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "DetectorRegex"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Regular expression based detector =======
|
||||||
|
' Shared module version: 20220613
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: VBScript_RegExp_55
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private regex_ As RegExp
|
||||||
|
|
||||||
|
Public Function Init(theRegex As RegExp)
|
||||||
|
Set regex_ = theRegex
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Test(sText$) As Boolean
|
||||||
|
Dim matches As Object: Set matches = regex_.Execute(sText)
|
||||||
|
If matches.Count <> 1 Then _
|
||||||
|
Exit Function
|
||||||
|
Test = matches.Item(0).Length = VBA.Len(sText)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExtractFragments(sText$) As PC_ParsedData
|
||||||
|
Dim iData As New PC_ParsedData
|
||||||
|
|
||||||
|
Dim matches As Object: Set matches = regex_.Execute(sText)
|
||||||
|
Dim nMatch&
|
||||||
|
For nMatch = 0 To matches.Count - 1 Step 1
|
||||||
|
Dim nStart&: nStart = matches.Item(nMatch).FirstIndex
|
||||||
|
Dim nEnd&: nEnd = nStart + matches.Item(nMatch).Length
|
||||||
|
Call iData.AddItem(nStart, nEnd)
|
||||||
|
Next nMatch
|
||||||
|
|
||||||
|
Set ExtractFragments = iData
|
||||||
|
End Function
|
44
parsers/ExtractionOptions.cls
Normal file
44
parsers/ExtractionOptions.cls
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "ExtractionOptions"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ================ Ïàðàìåòðû èçâëå÷åíèÿ è çàãðóçêè äàííûõ =========================
|
||||||
|
' Shared module version: 20220614
|
||||||
|
' Tested in:
|
||||||
|
' Depends on: ParserDeclarations
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public detector_ As TDetector
|
||||||
|
Public param_ As String
|
||||||
|
Public transform_ As String
|
||||||
|
Public loadCategory_ As Long
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
detector_ = T_DETECTOR_UNKNOWN
|
||||||
|
loadCategory_ = 0
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function FromFlatData(iData As Variant)
|
||||||
|
detector_ = CLng(iData(1))
|
||||||
|
param_ = iData(2)
|
||||||
|
transform_ = iData(3)
|
||||||
|
loadCategory_ = CLng(iData(4))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AsFlatData() As String()
|
||||||
|
Dim iData() As String
|
||||||
|
ReDim iData(1 To 4)
|
||||||
|
|
||||||
|
iData(1) = detector_
|
||||||
|
iData(2) = param_
|
||||||
|
iData(3) = transform_
|
||||||
|
iData(4) = loadCategory_
|
||||||
|
|
||||||
|
AsFlatData = iData
|
||||||
|
End Function
|
34
parsers/PC_Fragment.cls
Normal file
34
parsers/PC_Fragment.cls
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "PC_Fragment"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Ïîëîæåíèÿ ôðàãìåíòà â òåêñòå =======
|
||||||
|
' Shared module version: 20210909
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public start_ As Long
|
||||||
|
Public end_ As Long
|
||||||
|
Public type_ As Long
|
||||||
|
|
||||||
|
Public Function Init(nStart&, nEnd&, nType&)
|
||||||
|
start_ = nStart
|
||||||
|
end_ = nEnd
|
||||||
|
type_ = nType
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Clone() As PC_Fragment
|
||||||
|
Set Clone = New PC_Fragment
|
||||||
|
With Clone
|
||||||
|
.start_ = start_
|
||||||
|
.end_ = end_
|
||||||
|
.type_ = type_
|
||||||
|
End With
|
||||||
|
End Function
|
58
parsers/PC_InfoNPA.cls
Normal file
58
parsers/PC_InfoNPA.cls
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "PC_InfoNPA"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Legal document attributes =======
|
||||||
|
' Shared module version: 20220614
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public prefix_ As String
|
||||||
|
Public date_ As String
|
||||||
|
Public number_ As String
|
||||||
|
Public docType_ As String
|
||||||
|
Public creator_ As String
|
||||||
|
Public title_ As String
|
||||||
|
Public titlePrefix_ As String
|
||||||
|
Public titleDoc_ As String
|
||||||
|
|
||||||
|
Public isTimeBound_ As Boolean
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
isTimeBound_ = False
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function AsCollection() As Collection
|
||||||
|
Dim iData As New Collection
|
||||||
|
Call iData.Add(prefix_)
|
||||||
|
Call iData.Add(docType_)
|
||||||
|
Call iData.Add(creator_)
|
||||||
|
Call iData.Add(number_)
|
||||||
|
Call iData.Add(date_)
|
||||||
|
Call iData.Add(title_)
|
||||||
|
Call iData.Add(titlePrefix_)
|
||||||
|
Call iData.Add(titleDoc_)
|
||||||
|
Call iData.Add(isTimeBound_)
|
||||||
|
Set AsCollection = iData
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AsDescription() As Scripting.Dictionary
|
||||||
|
Dim iData As New Scripting.Dictionary
|
||||||
|
Call iData.Add("ïðåôèêñ", prefix_)
|
||||||
|
Call iData.Add("òèï äîêóìåíòà", docType_)
|
||||||
|
Call iData.Add("âûïóñêàþùèé îðãàí", creator_)
|
||||||
|
Call iData.Add("íîìåð", number_)
|
||||||
|
Call iData.Add("äàòà", date_)
|
||||||
|
Call iData.Add("íàçâàíèå", title_)
|
||||||
|
Call iData.Add("ïðåôèêñ òèòóëà", titlePrefix_)
|
||||||
|
Call iData.Add("âëîæåííûé äîêóìåíò", titleDoc_)
|
||||||
|
Call iData.Add("ñðî÷íûé", isTimeBound_)
|
||||||
|
Set AsDescription = iData
|
||||||
|
End Function
|
42
parsers/PC_ParsedData.cls
Normal file
42
parsers/PC_ParsedData.cls
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "PC_ParsedData"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Ïîëîæåíèÿ íàáîðà ôðàãìåíòîâ â òåêñòå =======
|
||||||
|
' Shared module version: 20220614
|
||||||
|
' Tested in:
|
||||||
|
' Depends on: PC_Fragment, ParserDeclarations
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public data_ As New Collection
|
||||||
|
|
||||||
|
Public Property Get Count() As Long
|
||||||
|
Count = data_.Count
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get IsEmpty() As Boolean
|
||||||
|
IsEmpty = Count = 0
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get First() As PC_Fragment
|
||||||
|
If IsEmpty Then _
|
||||||
|
Exit Property
|
||||||
|
Set First = data_.Item(1)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Last() As PC_Fragment
|
||||||
|
If IsEmpty Then _
|
||||||
|
Exit Property
|
||||||
|
Set Last = data_.Item(data_.Count)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function AddItem(nStart&, nEnd&, Optional nType& = 0)
|
||||||
|
Dim newItem As New PC_Fragment: Call newItem.Init(nStart, nEnd, nType)
|
||||||
|
Call data_.Add(newItem)
|
||||||
|
End Function
|
127
parsers/PC_Tools.cls
Normal file
127
parsers/PC_Tools.cls
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "PC_Tools"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Äîñòóï ê ðàçëè÷íûì ïàðñåðàì =======
|
||||||
|
' Shared module version: 20220801
|
||||||
|
' Tested in:
|
||||||
|
' Depends on: ParserDeclarations, DetectorListWords, DetectorRegex, DetectorMorpho, DetectorClassifier
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Function Detector(iType As TDetector, sParam$) As Object
|
||||||
|
' Any Detector implements methods:
|
||||||
|
' Public Function Test(sText$) As Boolean
|
||||||
|
' Public Function ExtractFragments(sText$) As PC_ParsedData
|
||||||
|
Select Case iType
|
||||||
|
Case TDetector.T_DETECTOR_ACTION: Set Detector = CachedActionDetector
|
||||||
|
Case TDetector.T_DETECTOR_LIST: Set Detector = CachedDetectorList(sParam)
|
||||||
|
Case TDetector.T_DETECTOR_REGEX: Set Detector = CachedDetectorRegex(sParam)
|
||||||
|
Case TDetector.T_DETECTOR_MORPHO: Set Detector = CachedDetectorMorpho(sParam)
|
||||||
|
Case TDetector.T_DETECTOR_DATE:
|
||||||
|
Set Detector = New DetectorRegex: Call Detector.Init(GlobalDateRegex)
|
||||||
|
Case TDetector.T_DETECTOR_NPA:
|
||||||
|
Set Detector = New DetectorRegex: Call Detector.Init(GlobalNPARegex)
|
||||||
|
Case TDetector.T_DETECTOR_BASIC_NER: Set Detector = CachedDetectorNER(sParam)
|
||||||
|
End Select
|
||||||
|
If Detector Is Nothing Then _
|
||||||
|
Call Err.Raise(CUSTOM_ERROR_DEBUG)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Parser(iType As TDetector, sParam$) As Object
|
||||||
|
' Any Parser implements methods:
|
||||||
|
' Public Function Test(sText$) As Boolean
|
||||||
|
' Public Function Parse(sText$) As Boolean
|
||||||
|
' Public Function GetData() As Collection
|
||||||
|
' Public Function GetDataDescription() As Scripting.Dictionary
|
||||||
|
' Public Function Transform(sText$, sParam$) As String
|
||||||
|
Select Case iType
|
||||||
|
Case TDetector.T_DETECTOR_ACTION: Set Parser = Nothing
|
||||||
|
Case TDetector.T_DETECTOR_LIST: Set Parser = Nothing
|
||||||
|
Case TDetector.T_DETECTOR_REGEX: Set Parser = Nothing
|
||||||
|
Case TDetector.T_DETECTOR_MORPHO: Set Parser = Nothing
|
||||||
|
Case TDetector.T_DETECTOR_DATE: Set Parser = CachedParserDate
|
||||||
|
Case TDetector.T_DETECTOR_NPA: Set Parser = CachedParserNPA
|
||||||
|
Case TDetector.T_DETECTOR_BASIC_NER: Set Parser = Nothing
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========
|
||||||
|
Private Function CachedActionDetector() As DetectorListWords
|
||||||
|
Static s_Detector As DetectorListWords
|
||||||
|
If s_Detector Is Nothing Then
|
||||||
|
Set s_Detector = New DetectorListWords
|
||||||
|
Call s_Detector.Init(LOCAL_MODELS & "\" & MODEL_ACTION_VERBS)
|
||||||
|
End If
|
||||||
|
Set CachedActionDetector = s_Detector
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CachedDetectorList(sParam$) As DetectorListWords
|
||||||
|
Static s_Param$
|
||||||
|
Static s_Detector As DetectorListWords
|
||||||
|
If s_Detector Is Nothing Or sParam <> s_Param Then
|
||||||
|
s_Param = sParam
|
||||||
|
Set s_Detector = New DetectorListWords
|
||||||
|
Call s_Detector.Init(sParam)
|
||||||
|
End If
|
||||||
|
Set CachedDetectorList = s_Detector
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CachedDetectorRegex(sParam$) As DetectorRegex
|
||||||
|
Static s_Regex As RegExp
|
||||||
|
Static s_Param$
|
||||||
|
Static s_Detector As DetectorRegex
|
||||||
|
If s_Detector Is Nothing Or sParam <> s_Param Then
|
||||||
|
s_Param = sParam
|
||||||
|
Set s_Regex = New RegExp
|
||||||
|
s_Regex.Global = True
|
||||||
|
s_Regex.Pattern = sParam
|
||||||
|
|
||||||
|
Set s_Detector = New DetectorRegex
|
||||||
|
Call s_Detector.Init(s_Regex)
|
||||||
|
End If
|
||||||
|
Set CachedDetectorRegex = s_Detector
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CachedDetectorMorpho(sParam$) As DetectorMorpho
|
||||||
|
Static s_Param$
|
||||||
|
Static s_Detector As DetectorMorpho
|
||||||
|
If s_Detector Is Nothing Or sParam <> s_Param Then
|
||||||
|
s_Param = sParam
|
||||||
|
Set s_Detector = New DetectorMorpho
|
||||||
|
Call s_Detector.Init(sParam)
|
||||||
|
End If
|
||||||
|
Set CachedDetectorMorpho = s_Detector
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CachedParserDate() As ParserDate
|
||||||
|
Static s_Parser As ParserDate
|
||||||
|
If s_Parser Is Nothing Then
|
||||||
|
Set s_Parser = New ParserDate
|
||||||
|
End If
|
||||||
|
Set CachedParserDate = s_Parser
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CachedParserNPA() As ParserNPA
|
||||||
|
Static s_Parser As ParserNPA
|
||||||
|
If s_Parser Is Nothing Then
|
||||||
|
Set s_Parser = New ParserNPA
|
||||||
|
End If
|
||||||
|
Set CachedParserNPA = s_Parser
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CachedDetectorNER(sParam$) As DetectorClassifier
|
||||||
|
Static s_Param$
|
||||||
|
Static s_Detector As DetectorClassifier
|
||||||
|
If s_Detector Is Nothing Or sParam <> s_Param Then
|
||||||
|
s_Param = sParam
|
||||||
|
Set s_Detector = New DetectorClassifier
|
||||||
|
Call s_Detector.Init(sParam)
|
||||||
|
End If
|
||||||
|
Set CachedDetectorNER = s_Detector
|
||||||
|
End Function
|
151
parsers/ParserDate.cls
Normal file
151
parsers/ParserDate.cls
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "ParserDate"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ========= Date parser ==========
|
||||||
|
' Shared module version: 20220614
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: z_ParserRegex
|
||||||
|
' Required reference: VBScript_RegExp_55
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private rxDate_ As RegExp
|
||||||
|
|
||||||
|
Public day_ As Integer
|
||||||
|
Public month_ As Integer
|
||||||
|
Public year_ As Integer
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Set rxDate_ = New RegExp
|
||||||
|
rxDate_.Pattern = P_DATE_CAPTURE
|
||||||
|
rxDate_.Global = False
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(aDay%, aMonth%, aYear%)
|
||||||
|
day_ = aDay
|
||||||
|
month_ = aMonth
|
||||||
|
year_ = aYear
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Test(target$) As Boolean
|
||||||
|
Test = rxDate_.Test(target)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Parse(target$) As Boolean
|
||||||
|
Parse = Test(target)
|
||||||
|
If Parse Then
|
||||||
|
Dim matches As Object
|
||||||
|
Set matches = rxDate_.Execute(target)
|
||||||
|
If matches.Item(0).SubMatches(0) <> vbNullString Then
|
||||||
|
day_ = CInt(matches.Item(0).SubMatches(0))
|
||||||
|
month_ = ConvertDateText2Int(matches.Item(0).SubMatches(1))
|
||||||
|
year_ = CInt(matches.Item(0).SubMatches(2))
|
||||||
|
Else
|
||||||
|
day_ = CInt(matches.Item(0).SubMatches(3))
|
||||||
|
month_ = CInt(matches.Item(0).SubMatches(4))
|
||||||
|
year_ = CInt(matches.Item(0).SubMatches(5))
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetData() As Collection
|
||||||
|
Set GetData = New Collection
|
||||||
|
Call GetData.Add(year_)
|
||||||
|
Call GetData.Add(month_)
|
||||||
|
Call GetData.Add(day_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetDataDescription() As Scripting.Dictionary
|
||||||
|
Dim iData As New Scripting.Dictionary
|
||||||
|
Call iData.Add("ãîä", year_)
|
||||||
|
Call iData.Add("ìåñÿö", month_)
|
||||||
|
Call iData.Add("äåíü", day_)
|
||||||
|
Set GetDataDescription = iData
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Transform(sText$, sParam$) As String
|
||||||
|
If Not Parse(sText) Then
|
||||||
|
Transform = sText
|
||||||
|
ElseIf sParam = "1" Then
|
||||||
|
Transform = AsTextString
|
||||||
|
ElseIf sParam = "2" Then
|
||||||
|
Transform = AsDigitsString
|
||||||
|
Else
|
||||||
|
Transform = sText
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get IsValidDate() As Boolean
|
||||||
|
IsValidDate = False
|
||||||
|
|
||||||
|
If month_ > 12 Or month_ < 1 Then _
|
||||||
|
Exit Property
|
||||||
|
If day_ > 31 Or day_ < 1 Then _
|
||||||
|
Exit Property
|
||||||
|
|
||||||
|
IsValidDate = IsDate(AsDigitsString)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get DDate() As Double
|
||||||
|
If Not IsValidDate Then _
|
||||||
|
Exit Function
|
||||||
|
DDate = DateSerial(year_, month_, day_)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function AsDigitsString() As String
|
||||||
|
AsDigitsString = Format(day_, "00") & "." & Format(month_, "00") & "." & Format(year_, IIf(year_ > 100, "0000", "00"))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AsTextString() As String
|
||||||
|
If Not IsValidDate Then
|
||||||
|
AsTextString = "INVALID_DATE"
|
||||||
|
Else
|
||||||
|
AsTextString = Format(day_, "00") & " " & MonthStr(month_) & " " & Year(DateSerial(year_, 1, 1)) & " ãîäà"
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ============
|
||||||
|
Private Function ConvertDateText2Int(sMonth$) As Integer
|
||||||
|
If IsNumeric(sMonth) Then
|
||||||
|
ConvertDateText2Int = CInt(sMonth)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Select Case sMonth
|
||||||
|
Case "ÿíâàðÿ": ConvertDateText2Int = 1
|
||||||
|
Case "ôåâðàëÿ": ConvertDateText2Int = 2
|
||||||
|
Case "ìàðòà": ConvertDateText2Int = 3
|
||||||
|
Case "àïðåëÿ": ConvertDateText2Int = 4
|
||||||
|
Case "ìàÿ": ConvertDateText2Int = 5
|
||||||
|
Case "èþíÿ": ConvertDateText2Int = 6
|
||||||
|
Case "èþëÿ": ConvertDateText2Int = 7
|
||||||
|
Case "àâãóñòà": ConvertDateText2Int = 8
|
||||||
|
Case "ñåíòÿáðÿ": ConvertDateText2Int = 9
|
||||||
|
Case "îêòÿáðÿ": ConvertDateText2Int = 10
|
||||||
|
Case "íîÿáðÿ": ConvertDateText2Int = 11
|
||||||
|
Case "äåêàáðÿ": ConvertDateText2Int = 12
|
||||||
|
Case Else: ConvertDateText2Int = 0
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function MonthStr(nMonth%) As String
|
||||||
|
Select Case nMonth
|
||||||
|
Case 1: MonthStr = "ÿíâàðÿ"
|
||||||
|
Case 2: MonthStr = "ôåâðàëÿ"
|
||||||
|
Case 3: MonthStr = "ìàðòà"
|
||||||
|
Case 4: MonthStr = "àïðåëÿ"
|
||||||
|
Case 5: MonthStr = "ìàÿ"
|
||||||
|
Case 6: MonthStr = "èþíÿ"
|
||||||
|
Case 7: MonthStr = "èþëÿ"
|
||||||
|
Case 8: MonthStr = "àâãóñòà"
|
||||||
|
Case 9: MonthStr = "ñåíòÿáðÿ"
|
||||||
|
Case 10: MonthStr = "îêòÿáðÿ"
|
||||||
|
Case 11: MonthStr = "íîÿáðÿ"
|
||||||
|
Case 12: MonthStr = "äåêàáðÿ"
|
||||||
|
Case Else: MonthStr = "INVALID MONTH"
|
||||||
|
End Select
|
||||||
|
End Function
|
54
parsers/ParserDeclarations.bas
Normal file
54
parsers/ParserDeclarations.bas
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
Attribute VB_Name = "ParserDeclarations"
|
||||||
|
' ======== Parser declarations ========
|
||||||
|
' Shared module version: 20220614
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Const LOCAL_MODELS = "C:\Tools\models"
|
||||||
|
Public Const MODEL_ACTION_VERBS = "ActionVerbs.txt"
|
||||||
|
|
||||||
|
Public Const CUSTOM_ERROR_DEBUG = 1025
|
||||||
|
|
||||||
|
Public Enum TDetector
|
||||||
|
[_First] = 1
|
||||||
|
|
||||||
|
T_DETECTOR_UNKNOWN = 0
|
||||||
|
T_DETECTOR_REGEX = 1
|
||||||
|
T_DETECTOR_MORPHO = 2
|
||||||
|
T_DETECTOR_LIST = 3
|
||||||
|
T_DETECTOR_DATE = 4
|
||||||
|
T_DETECTOR_ACTION = 5
|
||||||
|
T_DETECTOR_NPA = 6
|
||||||
|
T_DETECTOR_BASIC_NER = 7
|
||||||
|
|
||||||
|
[_Last] = 7
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Function DetectorFromStr(sName$) As TDetector
|
||||||
|
Select Case sName
|
||||||
|
Case "Äàòà": DetectorFromStr = T_DETECTOR_DATE
|
||||||
|
Case "Äåéñòâèå": DetectorFromStr = T_DETECTOR_ACTION
|
||||||
|
Case "Ñïèñîê ñëîâ": DetectorFromStr = T_DETECTOR_LIST
|
||||||
|
Case "RegExp": DetectorFromStr = T_DETECTOR_REGEX
|
||||||
|
Case "Ìîðôîëîãèÿ": DetectorFromStr = T_DETECTOR_MORPHO
|
||||||
|
Case "ÍÏÀ": DetectorFromStr = T_DETECTOR_NPA
|
||||||
|
Case "Èì. ñóùíîñòè": DetectorFromStr = T_DETECTOR_BASIC_NER
|
||||||
|
Case Else: DetectorFromStr = T_DETECTOR_UNKNOWN
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function DetectorToStr(iType As TDetector) As String
|
||||||
|
Select Case iType
|
||||||
|
Case T_DETECTOR_UNKNOWN: DetectorToStr = "Íåîïðåäåëåííàÿ"
|
||||||
|
Case T_DETECTOR_DATE: DetectorToStr = "Äàòà"
|
||||||
|
Case T_DETECTOR_ACTION: DetectorToStr = "Äåéñòâèå"
|
||||||
|
Case T_DETECTOR_LIST: DetectorToStr = "Ñïèñîê ñëîâ"
|
||||||
|
Case T_DETECTOR_REGEX: DetectorToStr = "RegExp"
|
||||||
|
Case T_DETECTOR_MORPHO: DetectorToStr = "Ìîðôîëîãèÿ"
|
||||||
|
Case T_DETECTOR_NPA: DetectorToStr = "ÍÏÀ"
|
||||||
|
Case T_DETECTOR_BASIC_NER: DetectorToStr = "Èì. ñóùíîñòè"
|
||||||
|
End Select
|
||||||
|
End Function
|
179
parsers/ParserNPA.cls
Normal file
179
parsers/ParserNPA.cls
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "ParserNPA"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Legal document title parser =======
|
||||||
|
' Shared module version: 20220614
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: z_ParserRegex, PC_InfoNPA
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private rxTitleFirst_ As RegExp
|
||||||
|
Private rxTitleLast_ As RegExp
|
||||||
|
Private rxCreatorPrefix_ As RegExp
|
||||||
|
Private rxTitle_ As RegExp
|
||||||
|
Private rxImmediate_ As RegExp
|
||||||
|
|
||||||
|
Public data_ As New PC_InfoNPA
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Set rxTitleFirst_ = New RegExp
|
||||||
|
rxTitleFirst_.Pattern = P_NPA_TITLE_FIRST
|
||||||
|
rxTitleFirst_.Global = False
|
||||||
|
|
||||||
|
Set rxTitleLast_ = New RegExp
|
||||||
|
rxTitleLast_.Pattern = P_NPA_PROPS_FIRST
|
||||||
|
rxTitleLast_.Global = False
|
||||||
|
|
||||||
|
Set rxCreatorPrefix_ = New RegExp
|
||||||
|
rxCreatorPrefix_.Pattern = P_NPA_DOCTYPE
|
||||||
|
rxCreatorPrefix_.Global = False
|
||||||
|
|
||||||
|
Set rxTitle_ = New RegExp
|
||||||
|
rxTitle_.Pattern = P_NPA_COMPOSITE_TITLE
|
||||||
|
rxTitle_.Global = False
|
||||||
|
|
||||||
|
Set rxImmediate_ = New RegExp
|
||||||
|
rxImmediate_.Pattern = P_NPA_IMMEDIATE
|
||||||
|
rxImmediate_.Global = False
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Test(target$) As Boolean
|
||||||
|
Test = rxTitleLast_.Test(target)
|
||||||
|
If Not Test Then Test = rxTitleFirst_.Test(target)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Parse(target$) As Boolean
|
||||||
|
Set data_ = New PC_InfoNPA
|
||||||
|
Dim matches As Object
|
||||||
|
Dim isTitleLast As Boolean
|
||||||
|
isTitleLast = rxTitleLast_.Test(target)
|
||||||
|
If Not isTitleLast Then
|
||||||
|
If Not rxTitleFirst_.Test(target) Then
|
||||||
|
Parse = False
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
Parse = True
|
||||||
|
If isTitleLast Then
|
||||||
|
Set matches = rxTitleLast_.Execute(target)
|
||||||
|
data_.prefix_ = matches.Item(0).SubMatches(0)
|
||||||
|
data_.date_ = matches.Item(0).SubMatches(1)
|
||||||
|
data_.number_ = Trim(matches.Item(0).SubMatches(2))
|
||||||
|
data_.title_ = Trim(matches.Item(0).SubMatches(3))
|
||||||
|
Else
|
||||||
|
Set matches = rxTitleFirst_.Execute(target)
|
||||||
|
data_.title_ = Trim(matches.Item(0).SubMatches(0))
|
||||||
|
data_.prefix_ = Trim(matches.Item(0).SubMatches(1))
|
||||||
|
data_.date_ = Trim(matches.Item(0).SubMatches(2))
|
||||||
|
data_.number_ = Trim(matches.Item(0).SubMatches(3))
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call ParsePrefix
|
||||||
|
Call ParseTitle
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetData() As Collection
|
||||||
|
Set GetData = data_.AsCollection()
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetDataDescription() As Scripting.Dictionary
|
||||||
|
Set GetDataDescription = data_.AsDescription()
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Transform(sText$, sParam$) As String
|
||||||
|
Transform = sText
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =======
|
||||||
|
Private Function ParsePrefix()
|
||||||
|
If data_.prefix_ = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
If Not rxCreatorPrefix_.Test(data_.prefix_) Then
|
||||||
|
data_.creator_ = data_.prefix_
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim matches As Object: Set matches = rxCreatorPrefix_.Execute(data_.prefix_)
|
||||||
|
data_.docType_ = CapitalizeFirstLetter(Trim(matches.Item(0).Value))
|
||||||
|
If Len(data_.docType_) <> Len(data_.prefix_) Then _
|
||||||
|
data_.creator_ = Trim(Right(data_.prefix_, Len(data_.prefix_) - Len(data_.docType_)))
|
||||||
|
Call FixTypeCase
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ParseTitle()
|
||||||
|
Dim theTitle$: theTitle = data_.title_
|
||||||
|
If theTitle = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
data_.isTimeBound_ = rxImmediate_.Test(theTitle)
|
||||||
|
|
||||||
|
If Not rxTitle_.Test(theTitle) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim matches As Object: Set matches = rxTitle_.Execute(theTitle)
|
||||||
|
data_.titlePrefix_ = Trim(matches.Item(0).SubMatches(0))
|
||||||
|
data_.titleDoc_ = Trim(matches.Item(0).SubMatches(1))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function FixTypeCase()
|
||||||
|
If Len(data_.docType_) = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Select Case Left(data_.docType_, 1)
|
||||||
|
Case "Ä"
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Äèðåêòèâû", "Äèðåêòèâà")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Äèðåêòèâîé", "Äèðåêòèâà")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Äèðåêòèâå", "Äèðåêòèâà")
|
||||||
|
Case "Ç"
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Çàêëþ÷åíèåì", "Çàêëþ÷åíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Çàêëþ÷åíèÿ", "Çàêëþ÷åíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Çàêëþ÷åíèþ", "Çàêëþ÷åíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Çàêîíîì", "Çàêîí")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Çàêîíà", "Çàêîí")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Çàêîíó", "Çàêîí")
|
||||||
|
Case "È"
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Èíôîðìàöèîííîãî ïèñüìà", "Èíôîðìàöèîííîå ïèñüìî")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Èíôîðìàöèîííûì ïèñüìîì", "Èíôîðìàöèîííîå ïèñüìî")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Èíôîðìàöèîííîìó ïèñüìó", "Èíôîðìàöèîííîå ïèñüìî")
|
||||||
|
Case "Ï"
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ïðèêàçîì", "Ïðèêàç")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ïðèêàçà", "Ïðèêàç")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ïðèêàçó", "Ïðèêàç")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ïèñüìîì", "Ïèñüìî")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ïèñüìà", "Ïèñüìî")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ïèñüìó", "Ïèñüìî")
|
||||||
|
Case "Ð"
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ðàñïîðÿæåíèåì", "Ðàñïîðÿæåíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ðàñïîðÿæåíèÿ", "Ðàñïîðÿæåíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ðàñïîðÿæåíèþ", "Ðàñïîðÿæåíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ðåøåíèåì", "Ðåøåíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ðåøåíèÿ", "Ðåøåíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ðåøåíèþ", "Ðåøåíèå")
|
||||||
|
Case "Ó"
|
||||||
|
If data_.docType_ = "Óêàçà" Then
|
||||||
|
data_.docType_ = "Óêàç"
|
||||||
|
Else
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Óêàçîì", "Óêàç")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Óêàçó", "Óêàç")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Óêàçàíèÿ", "Óêàçàíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Óêàçàíèåì", "Óêàçàíèå")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Óêàçàíèþ", "Óêàçàíèå")
|
||||||
|
End If
|
||||||
|
Case "Ô"
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ôåäåðàëüíûì", "Ôåäåðàëüíûé")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ôåäåðàëüíîãî", "Ôåäåðàëüíûé")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "Ôåäåðàëüíîìó", "Ôåäåðàëüíûé")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "çàêîíîì", "çàêîí")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "çàêîíà", "çàêîí")
|
||||||
|
data_.docType_ = Replace(data_.docType_, "çàêîíó", "çàêîí")
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
232
parsers/z_ParserRegex.bas
Normal file
232
parsers/z_ParserRegex.bas
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
Attribute VB_Name = "z_ParserRegex"
|
||||||
|
' ======== Parser regex masks ========
|
||||||
|
' Shared module version: 20210909
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const P_SPACE = "(?:\s+?)"
|
||||||
|
|
||||||
|
Private Const P_QUOTE_OPEN = "[""'«“]"
|
||||||
|
Private Const P_QUOTE_CLOSE = "[""'»”]"
|
||||||
|
Private Const P_ANY_SHORTEST = ".*?"
|
||||||
|
Private Const P_ANY = ".*"
|
||||||
|
|
||||||
|
Private Const P_MONTH_RUS = "(?:ÿíâàðÿ|ôåâðàëÿ|ìàðòà|àïðåëÿ|ìàÿ|èþíÿ|èþëÿ|àâãóñòà|ñåíòÿáðÿ|îêòÿáðÿ|íîÿáðÿ|äåêàáðÿ)"
|
||||||
|
Private Const P_MONTH_DIGITS = "[0-1]?\d"
|
||||||
|
Private Const P_DAY_DIGITS = "[0-3]?\d"
|
||||||
|
Private Const P_YEAR_LONG = "[1-2]\d\d\d"
|
||||||
|
Private Const P_YEAR_SHORT = "\d\d"
|
||||||
|
Private Const P_YEAR_DIGITS = "(?:" & P_YEAR_LONG & "|" & P_YEAR_SHORT & ")"
|
||||||
|
|
||||||
|
Private Const P_DATE_TEXT = _
|
||||||
|
"(" & P_DAY_DIGITS & ")" & P_SPACE & _
|
||||||
|
"(" & P_MONTH_RUS & ")" & P_SPACE & _
|
||||||
|
"(" & P_YEAR_LONG & ")"
|
||||||
|
|
||||||
|
Private Const P_DATE_NUMERIC = _
|
||||||
|
"(" & P_DAY_DIGITS & ")" & "\." & _
|
||||||
|
"(" & P_MONTH_DIGITS & ")" & "\." & _
|
||||||
|
"(" & P_YEAR_DIGITS & ")"
|
||||||
|
|
||||||
|
Private Const P_NPA_PREFIX = "[À-ßà-ÿ][\dÀ-ßà-ÿA-Za-z,\-\.""'«»“”\s]+?"
|
||||||
|
Private Const P_NPA_ID_ELEMENT = "[#N¹]" & P_SPACE & "?" & "[\dÀ-ßà-ÿA-Za-z_@\.\-\\/]+"
|
||||||
|
Private Const P_NPA_ID_SEQ = P_NPA_ID_ELEMENT & "(?:," & P_SPACE & P_NPA_ID_ELEMENT & ")*"
|
||||||
|
Private Const P_NPA_INTRO = _
|
||||||
|
"(?:" & _
|
||||||
|
"[Ïï]ðèëîæåíèå ê" & "|" & _
|
||||||
|
"[Ââ] ðåäàêöèè" & "|" & _
|
||||||
|
"(?:" & _
|
||||||
|
"[Ðð]åêîìåíäîâàí[àîû]?" & "|" & _
|
||||||
|
"(?:" & _
|
||||||
|
"[Óó]òâ\." & "|" & _
|
||||||
|
"[Óó]òâåðæä(?:[å¸]í|åíà|åíî|åíû)" & _
|
||||||
|
")" & " è ââåä(?:[å¸]í|åíà|åíî|åíû) â äåéñòâèå" & "|" & _
|
||||||
|
"(?:ïðèíÿò[àîû]? è )?" & _
|
||||||
|
"(?:" & _
|
||||||
|
"[Óó]òâ\." & "|" & _
|
||||||
|
"[Óó]òâåðæä(?:[å¸]í|åíà|åíî|åíû)" & "|" & _
|
||||||
|
"îäîáðåí[àîû]?" & "|" & _
|
||||||
|
"[Ââ]âåä(?:[å¸]í|åíà|åíî|åíû) â äåéñòâèå" & _
|
||||||
|
")" & "|" & _
|
||||||
|
"ïðèíÿò[àîû]?" & _
|
||||||
|
")" & "(?: íà)?" & _
|
||||||
|
")"
|
||||||
|
|
||||||
|
Private Const P_MASK_DOCTYPE = _
|
||||||
|
"(?:" & _
|
||||||
|
"[Êê]îíñòèòóàöè[ÿè]" & "|" & _
|
||||||
|
"[Ôô]åäåðàëüí(?:ûé|ûì|îãî|îìó) çàêîí(?:îì|à|ó)?" & "|" & _
|
||||||
|
"[Ôô]åäåðàëüí(?:ûé|ûì|îãî|îìó) [Êê]îíñòèòóöèîíí(?:ûé|ûì|îãî|îìó)] çàêîí(?:îì|à|ó)?" & "|" & _
|
||||||
|
"[Çç]àêîí(?:îì|à|ó)?(?: ÐÔ| Ðîññèéñêîé Ôåäåðàöèè)?" & "|" & _
|
||||||
|
"[Ää]èðåêòèâ(?:à|îé|û|å)" & "|" & _
|
||||||
|
P_ANY & "[Êê]îäåêñ(?:îì)?" & P_ANY & "|" & _
|
||||||
|
P_ANY & "[Êê]îíâåíöè(?:ÿ|åé|èè)" & "|" & _
|
||||||
|
"[Óó]êàçàíè(?:åì|å|ÿ|þ)" & "|" & _
|
||||||
|
"[Çç]àêëþ÷åíè(?:åì|å|ÿ|þ)" & "|" & _
|
||||||
|
"[Ïï]èñüì(?:îì|î|à|ó)" & "|" & _
|
||||||
|
"[Ðð]àçúÿñíåíè(?:åì|å|ÿ|þ)" & "|" & _
|
||||||
|
"[Ïï]îñòàíîâëåíè(?:åì|å|ÿ|þ)" & "|" & _
|
||||||
|
"[Ïï]ðèêàç(?:îì|à|ó)?" & "|" & _
|
||||||
|
"[Óó]êàç(?:îì|à|ó)?" & "|" & _
|
||||||
|
"[Èè]íôîðìàöèîíí(?:îå|ûì|îãî|îìó) ïèñüì(?:îì|î|à|ó)" & "|" & _
|
||||||
|
"[Ðð]àñïîðÿæåíè(?:åì|å|ÿ|þ)" & "|" & _
|
||||||
|
"[Ññ]îãëàøåíè(?:åì|å|ÿ|þ)(?: î âçàèìîäåéñòâèè)?" & "|" & _
|
||||||
|
"[Ðð]åøåíè(?:åì|å|ÿ|þ)" & _
|
||||||
|
")"
|
||||||
|
|
||||||
|
Private Const P_NPA_TITLE_PREFIX = _
|
||||||
|
"(?:" & _
|
||||||
|
"[Îî] ââåäåíèè â äåéñòâèå" & "(?: íîâîé ðåäàêöèè)?" & "|" & _
|
||||||
|
"[Îî]á èçäàíèè è ïðèìåíåíèè" & "|" & _
|
||||||
|
"[Îî]á óòâåðæäåíèè(?: è(?: î)? ââåäåíèè â äåéñòâèå)?" & _
|
||||||
|
" " & _
|
||||||
|
")"
|
||||||
|
|
||||||
|
' ======================================
|
||||||
|
' ======== Èòîãîâûå âûðàæåíèÿ ==========
|
||||||
|
' ======================================
|
||||||
|
|
||||||
|
' ======= Äàòà ==========
|
||||||
|
Public Const P_DATE_NO_CAPTURE = _
|
||||||
|
"(?:" & _
|
||||||
|
P_DAY_DIGITS & P_SPACE & P_MONTH_RUS & P_SPACE & P_YEAR_LONG & _
|
||||||
|
"|" & _
|
||||||
|
P_DAY_DIGITS & "\." & P_MONTH_DIGITS & "\." & P_YEAR_DIGITS & _
|
||||||
|
")" & _
|
||||||
|
"(?:" & P_SPACE & "?" & "(?:ãîäà|ã\.|ã))?"
|
||||||
|
|
||||||
|
Public Const P_DATE_CAPTURE = _
|
||||||
|
"^" & _
|
||||||
|
"(?:" & _
|
||||||
|
P_DATE_TEXT & "|" & _
|
||||||
|
P_DATE_NUMERIC & _
|
||||||
|
")" & _
|
||||||
|
"(?:" & P_SPACE & "?" & "(?:ãîäà|ã\.|ã))?" & _
|
||||||
|
"$"
|
||||||
|
|
||||||
|
' ====== Íàçâàíèå ÍÏÀ ======
|
||||||
|
Public Const P_NPA_SCAN = _
|
||||||
|
"(?:" & "\b[Ññ]ì\." & P_SPACE & ")?" & _
|
||||||
|
"(?:" & _
|
||||||
|
"\bâ ðåäàêöèè " & "|" & _
|
||||||
|
"\bâ ñîîòâåòñòâèè ñ " & _
|
||||||
|
")?" & _
|
||||||
|
"(" & _
|
||||||
|
P_NPA_PREFIX & P_SPACE & "îò" & P_SPACE & P_DATE_NO_CAPTURE & P_SPACE & "?" & _
|
||||||
|
P_NPA_ID_SEQ & "(?:" & P_SPACE & P_QUOTE_OPEN & P_ANY & P_QUOTE_CLOSE & ")?" & _
|
||||||
|
"|" & _
|
||||||
|
P_ANY_SHORTEST & _
|
||||||
|
"\(" & _
|
||||||
|
P_ANY_SHORTEST & P_NPA_INTRO & P_SPACE & P_NPA_PREFIX & _
|
||||||
|
"(?:" & P_SPACE & "îò" & ")?" & P_SPACE & P_DATE_NO_CAPTURE & _
|
||||||
|
"(?:" & P_SPACE & "?" & "(?:" & P_NPA_ID_SEQ & "))?" & P_ANY_SHORTEST & _
|
||||||
|
"\)" & _
|
||||||
|
")"
|
||||||
|
|
||||||
|
Public Const P_NPA_PROPS_FIRST = _
|
||||||
|
"^" & _
|
||||||
|
"(" & P_NPA_PREFIX & ")" & _
|
||||||
|
P_SPACE & "îò" & P_SPACE & "(" & P_DATE_NO_CAPTURE & ")" & _
|
||||||
|
P_SPACE & "?" & "(" & P_NPA_ID_SEQ & ")" & _
|
||||||
|
"(?:" & P_SPACE & P_QUOTE_OPEN & "(" & P_ANY & ")" & P_QUOTE_CLOSE & ")?" & _
|
||||||
|
P_ANY_SHORTEST & _
|
||||||
|
"$"
|
||||||
|
|
||||||
|
Public Const P_NPA_TITLE_FIRST = _
|
||||||
|
"^" & _
|
||||||
|
"(" & P_ANY_SHORTEST & ")" & _
|
||||||
|
"\(" & _
|
||||||
|
P_ANY_SHORTEST & P_NPA_INTRO & P_SPACE & _
|
||||||
|
"(" & P_NPA_PREFIX & ")" & P_SPACE & _
|
||||||
|
"(?:îò" & P_SPACE & ")?" & "(" & P_DATE_NO_CAPTURE & ")" & _
|
||||||
|
"(?:" & P_SPACE & "?" & "(" & P_NPA_ID_SEQ & "))?" & _
|
||||||
|
P_ANY_SHORTEST & _
|
||||||
|
"\)" & _
|
||||||
|
P_ANY_SHORTEST & _
|
||||||
|
"$"
|
||||||
|
|
||||||
|
' ===== Òèï äîêóìåíòà =========
|
||||||
|
Public Const P_NPA_DOCTYPE = "^" & "(" & P_MASK_DOCTYPE & ")"
|
||||||
|
|
||||||
|
' ======== Òèòóë äîêóìåíòà ======
|
||||||
|
Public Const P_NPA_COMPOSITE_TITLE = _
|
||||||
|
"^" & _
|
||||||
|
"(" & P_NPA_TITLE_PREFIX & ")" & _
|
||||||
|
P_QUOTE_OPEN & "?" & _
|
||||||
|
"(" & P_ANY_SHORTEST & ")" & _
|
||||||
|
P_QUOTE_CLOSE & "?" & _
|
||||||
|
"$"
|
||||||
|
|
||||||
|
' ======== Ñðî÷íîñòü äîêóìåíòà ===
|
||||||
|
Public Const P_NPA_IMMEDIATE = _
|
||||||
|
" " & _
|
||||||
|
"(?:(?:ñ|íà|â|äî) |\()" & _
|
||||||
|
"(?:ïëàíîâûé |ïåðèîä |ïëàíîâûé ïåðèîä )?" & _
|
||||||
|
P_YEAR_DIGITS & _
|
||||||
|
"(?:" & _
|
||||||
|
"(?:\-| \- | è | ïî )" & _
|
||||||
|
P_YEAR_DIGITS & _
|
||||||
|
")?" & _
|
||||||
|
" " & _
|
||||||
|
"(?:ãîä(?:îâ|à|û|àõ)?|ãã\.|ãã|ã\.|ã)"
|
||||||
|
|
||||||
|
' ======== Static access for compiled regexp ======
|
||||||
|
Public Function GlobalDateRegex() As RegExp
|
||||||
|
Static s_Regex As RegExp
|
||||||
|
If s_Regex Is Nothing Then
|
||||||
|
Set s_Regex = New RegExp
|
||||||
|
s_Regex.Global = True
|
||||||
|
s_Regex.Pattern = P_DATE_NO_CAPTURE
|
||||||
|
End If
|
||||||
|
Set GlobalDateRegex = s_Regex
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GlobalNPARegex() As RegExp
|
||||||
|
Static s_Regex As RegExp
|
||||||
|
If s_Regex Is Nothing Then
|
||||||
|
Set s_Regex = New RegExp
|
||||||
|
s_Regex.Global = True
|
||||||
|
s_Regex.Pattern = P_NPA_SCAN
|
||||||
|
End If
|
||||||
|
Set GlobalNPARegex = s_Regex
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ============= Òèïû äîêóìåíòîâ-îïðåäåëåíèé
|
||||||
|
' Ïîëîæåí(?:èÿ|èå|èè) îá?
|
||||||
|
' Ïîðÿä(?:êà|îê|êå)
|
||||||
|
' Ïðàâèëà?
|
||||||
|
' (?:Àäìèíèñòðàòèâíîãî)?[Ðð]åãëàìåíòà?(?: ïî)?
|
||||||
|
' Ðåêîìåíäàöè[èé] ïî
|
||||||
|
' Ñòðàòåãè[èÿ]
|
||||||
|
' Ñòðóêòóð[àû]
|
||||||
|
' Òèïîâî(?:ãî|îå) ïîëîæåíè[ÿå] îá?
|
||||||
|
' Òðåáîâàíèé(?:(?:, ïðåäúÿâëÿåìûõ)? ê)?
|
||||||
|
' Ôîðì[àû]? è ïîðÿäêà
|
||||||
|
' Ôîðì[àû]?
|
||||||
|
' Ïåðå÷åíü
|
||||||
|
|
||||||
|
' ============ Òèïû äîêóìåíòîâ-äåéñòâèé
|
||||||
|
' Î âíåñåíèè èçìåíåíèé â
|
||||||
|
' Î(?: íåêîòîðûõ| íåîòëîæíûõ| ïåðâîî÷åðåäíûõ)? ìåðàõ ïî (?:ðåàëèçàöèè|ñòèìóëèðîâàíèþ|ñîâåðøåíñòâîâàíèþ)
|
||||||
|
' Î ìåðîïðèÿòèÿõ ïî
|
||||||
|
' Î ïåðå÷íå
|
||||||
|
' Î ïîäãîòîâêå ïðîåêòà
|
||||||
|
' Î (ïîäïèñàíèè|ïðèíÿòèè|ðàòèôèêàöèè)
|
||||||
|
' Î ðåàëèçàöèè
|
||||||
|
' Î ñîçäàíèè(?: è âåäåíèè)?
|
||||||
|
' Îá èçìåíåíèè ñîñòàâà
|
||||||
|
' Îá îáðàçîâàíèè ' ÷åãî-ëèáî
|
||||||
|
' Îá îáúÿâëåíèè ' Äðóãîãî äîêóìåíòà
|
||||||
|
' Îá îðãàíèçàöèè(?: ðàáîòû? ïî)?
|
||||||
|
' Îá îñóùåñòâëåíèè
|
||||||
|
' Îá óñèëåíèè ' = cîâåðøåíñòâîâàíèå?
|
||||||
|
' Îá óñòàíîâëåíèè ' çàäàíèå çíà÷åíèÿ
|
||||||
|
' Îá îïðåäåëåíèè ' ïðîöåäóðà çàäàíèÿ çíà÷åíèÿ
|
||||||
|
|
||||||
|
' ========= TODO
|
||||||
|
' Ïàðñèòü âûäåëÿÿ îðãàíû-àêòîðû
|
||||||
|
' Private Const P_ANY_SPECIAL_SPACE = "[ \xA0\u1680\u180E\u2000-\u200B\u202F\u205F\u3000\uFEFF]+"
|
||||||
|
|
BIN
samples/20150923 Таймлайнер.xlsm
Normal file
BIN
samples/20150923 Таймлайнер.xlsm
Normal file
Binary file not shown.
BIN
samples/20180725 Технология разметки.docm
Normal file
BIN
samples/20180725 Технология разметки.docm
Normal file
Binary file not shown.
BIN
samples/20200214 Иерархизатор 3.5.vsdm
Normal file
BIN
samples/20200214 Иерархизатор 3.5.vsdm
Normal file
Binary file not shown.
BIN
samples/20200214 Подстановки Visio.xlsm
Normal file
BIN
samples/20200214 Подстановки Visio.xlsm
Normal file
Binary file not shown.
BIN
samples/20200225 Блоки_v1.1.vsdm
Normal file
BIN
samples/20200225 Блоки_v1.1.vsdm
Normal file
Binary file not shown.
57
samples/DB_Data_sample.cls
Normal file
57
samples/DB_Data_sample.cls
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "DB_Data_sample"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private data_ As Excel.Worksheet
|
||||||
|
|
||||||
|
Public Function Init(target As Excel.Worksheet)
|
||||||
|
Set data_ = target
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get Count() As Long
|
||||||
|
Count = ILast.row_ - IBegin.row_ + 1
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function IBegin() As IteratorWorker
|
||||||
|
Set IBegin = New IteratorWorker
|
||||||
|
Call IBegin.Init(data_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ILast() As IteratorWorker
|
||||||
|
Set ILast = New IteratorWorker
|
||||||
|
Call ILast.Init(data_)
|
||||||
|
Call ILast.GoLast
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function INew() As IteratorWorker
|
||||||
|
Set INew = New IteratorWorker
|
||||||
|
Call INew.Init(data_)
|
||||||
|
Call INew.GoLast
|
||||||
|
Call INew.Increment
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Clear()
|
||||||
|
Call data_.UsedRange.Offset(1).ClearContents
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Contains(sWorkerID$) As Boolean
|
||||||
|
Contains = Not Access(sWorkerID) Is Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Access(sWorkerID$) As IteratorWorker
|
||||||
|
Dim iWorker As IteratorWorker: Set iWorker = IBegin
|
||||||
|
Do While Not iWorker.IsDone
|
||||||
|
If iWorker.WorkerID = sWorkerID Then
|
||||||
|
Set Access = iWorker
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Call iWorker.Increment
|
||||||
|
Loop
|
||||||
|
End Function
|
20
samples/DevHelper_sample.bas
Normal file
20
samples/DevHelper_sample.bas
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Attribute VB_Name = "DevHelper_sample"
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Function Dev_PrepareSkeleton()
|
||||||
|
' Do nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Sub Dev_ManualRunTest()
|
||||||
|
Dim sSuite$: sSuite = "s_UndoWrapper"
|
||||||
|
Dim sTest$: sTest = "t_BasicUndo"
|
||||||
|
Dim sMsg$: sMsg = Dev_RunTestDebug(sSuite, sTest)
|
||||||
|
Debug.Print sMsg
|
||||||
|
Call MsgBox(sMsg)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Dev_GetTestSuite(sName$) As Object
|
||||||
|
Select Case sName
|
||||||
|
Case "s_UndoWrapper": Set Dev_GetTestSuite = New s_UndoWrapper
|
||||||
|
End Select
|
||||||
|
End Function
|
81
samples/Iterator_sample.cls
Normal file
81
samples/Iterator_sample.cls
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "Iterator_sample"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public row_ As Long
|
||||||
|
Private data_ As Excel.Worksheet
|
||||||
|
|
||||||
|
Public Sub Init(target As Excel.Worksheet, Optional tRow& = FIRST_ROW)
|
||||||
|
Set data_ = target
|
||||||
|
row_ = tRow
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Increment(Optional inc& = 1)
|
||||||
|
If row_ + inc > 0 Then _
|
||||||
|
row_ = row_ + inc
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GoFirst()
|
||||||
|
row_ = FIRST_ROW
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GoLast()
|
||||||
|
row_ = data_.Columns(S_E_ID).Find(vbNullString, LookAt:=xlWhole).Row - 1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function IsDone() As Boolean
|
||||||
|
IsDone = data_.Cells(row_, S_E_ID) = vbNullString
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RemoveRow()
|
||||||
|
Call data_.Rows(row_).Delete
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'===== Propertiy Get =====
|
||||||
|
Public Property Get WorkerID() As String
|
||||||
|
WorkerID = data_.Cells(row_, S_E_ID)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get WorkerName() As String
|
||||||
|
WorkerName = data_.Cells(row_, S_E_NAME)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Active() As Boolean
|
||||||
|
Active = data_.Cells(row_, S_E_ACTIVE) = 1
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Hours() As Double
|
||||||
|
Hours = data_.Cells(row_, S_E_HOURS)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get ReportPath() As String
|
||||||
|
ReportPath = data_.Cells(row_, S_E_PATH)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
' ==== Property Let ====
|
||||||
|
Public Property Let WorkerID(newVal$)
|
||||||
|
data_.Cells(row_, S_E_ID) = newVal
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Let WorkerName(newVal$)
|
||||||
|
data_.Cells(row_, S_E_NAME) = newVal
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Let Active(newVal As Boolean)
|
||||||
|
data_.Cells(row_, S_E_ACTIVE) = IIf(newVal, 1, 0)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Let Hours(newVal As Double)
|
||||||
|
data_.Cells(row_, S_E_HOURS) = newVal
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Let ReportPath(newVal$)
|
||||||
|
data_.Cells(row_, S_E_PATH) = newVal
|
||||||
|
End Property
|
31
samples/VBAMake_sample.txt
Normal file
31
samples/VBAMake_sample.txt
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# == Properties Section ==
|
||||||
|
# configuration properties
|
||||||
|
# use .ini format to define properties
|
||||||
|
# mandatory properties: name, artifact_home, source_home, install_home
|
||||||
|
|
||||||
|
name =
|
||||||
|
description =
|
||||||
|
artifact_home =
|
||||||
|
source_home =
|
||||||
|
install_home =
|
||||||
|
|
||||||
|
%%
|
||||||
|
# === Build section ===
|
||||||
|
# Available commands:
|
||||||
|
# build LOCAL_MANIFEST
|
||||||
|
# copy LOCAL_SOURCE -> [LOCAL_ARTIFACT]
|
||||||
|
# save_as LOCAL_ARTIFACT -> LOCAL_ARTIFACT
|
||||||
|
# run LOCAL_SOURCE.bat <- [PARAMETERS]
|
||||||
|
# run APPLICATION <- [PARAMETERS]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
# === Install section ==
|
||||||
|
# Available commands:
|
||||||
|
# install LOCAL_ARTIFACT -> [INSTALL_PATH]
|
||||||
|
# add_template LOCAL_ARTIFACT -> [LOCAL_TEMPLATE]
|
||||||
|
# run LOCAL_ARTIFACT.bat <- [PARAMETERS]
|
||||||
|
# run APPLICATION <- [PARAMETERS]
|
||||||
|
|
||||||
|
|
38
samples/manifest_sample.txt
Normal file
38
samples/manifest_sample.txt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# == Properties Section ==
|
||||||
|
# configuration properties
|
||||||
|
# use .ini format to define properties
|
||||||
|
# mandatory properties: name, artifact
|
||||||
|
|
||||||
|
name =
|
||||||
|
artifact =
|
||||||
|
|
||||||
|
%%
|
||||||
|
# === Imports Section ===
|
||||||
|
# Hierarchy of folders and files
|
||||||
|
# Use Tabulator to mark next level in hierarchy
|
||||||
|
# All folders are nested into SharedHome path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
# === Source Code Section ==
|
||||||
|
# Hierarchy of folders and files
|
||||||
|
# Use Tabulator to mark next level in hierarchy
|
||||||
|
# All folders are nested into SourceHome path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
# ===== 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
# === References Section ===
|
||||||
|
# List dependencies in one of the formats
|
||||||
|
# global : GLOBAL_NAME
|
||||||
|
# guid : {REGISTERED_GUID}
|
||||||
|
# file : PATH_TO_LIBRARY
|
31
samples/s_Test_sample.cls
Normal file
31
samples/s_Test_sample.cls
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "s_Test_sample"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ===== Test suite sample =======
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private parser_ As PC_ParserDate
|
||||||
|
|
||||||
|
Public Function Setup()
|
||||||
|
' Mandatory setup function
|
||||||
|
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Teardown()
|
||||||
|
' Mandatory teardown function
|
||||||
|
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function t_TestName()
|
||||||
|
On Error GoTo PROPAGATE_ERROR
|
||||||
|
|
||||||
|
Exit Function
|
||||||
|
PROPAGATE_ERROR:
|
||||||
|
Call Dev_LogError(Err.Number, Err.Description)
|
||||||
|
End Function
|
71
samples/z_UIMessages_sample.bas
Normal file
71
samples/z_UIMessages_sample.bas
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
Attribute VB_Name = "z_UIMessages_sample"
|
||||||
|
' Ìîäóëü âûâîäà ñîîáùåíèé è âçàèìîäåéñòâèÿ ñ ïîëüçîâàòåëåì
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
'# Êîäû ñîîáùåíèé
|
||||||
|
Public Enum MsgCode
|
||||||
|
MSG_OK = 0
|
||||||
|
|
||||||
|
'EM_DOC_NOT_ADDED
|
||||||
|
|
||||||
|
'IM_SCAN_MODE
|
||||||
|
|
||||||
|
'QM_ADD_TITLELINK
|
||||||
|
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 ProcessErrorMessages(expectedErrors As Scripting.Dictionary)
|
||||||
|
If Err.Number = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
' Call Unload(CSE_ProgressBar)
|
||||||
|
If Not expectedErrors.Exists(Err.Number) Then _
|
||||||
|
Call Err.Raise(Err.Number)
|
||||||
|
|
||||||
|
Select Case Err.Number
|
||||||
|
' Case EM_LINK_NO_FILE: Call UserInteraction.ShowMessage(Err.Number, Err.Source)
|
||||||
|
End Select
|
||||||
|
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_DOC_NOT_ADDED
|
||||||
|
' Call MsgBox("Äîêóìåíò íå äîáàâëåí â Ðååñòð", vbExclamation)
|
||||||
|
|
||||||
|
' Case IM_SCAN_MODE
|
||||||
|
' Call MsgBox("Äîêóìåíò íàõîäèòñÿ â ðåæèìå ñïðàâî÷íèêà, ññûëêè íå ðàçìå÷àþòñÿ", 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 As Long: answer = vbNo
|
||||||
|
Select Case theCode
|
||||||
|
' Case QM_ADD_TITLELINK
|
||||||
|
' answer = MsgBox("Âíèìàíèå! Â ïåðâûé àáçàö áóäåò äîáàâëåíà ñîîòâåòñòâóþùàÿ ññûëêà. Ïðîäîëæèòü?", vbYesNo + vbQuestion)
|
||||||
|
|
||||||
|
Case Else
|
||||||
|
Call MsgBox("Íåâåðíûé êîä ñîîáùåíèÿ", vbCritical)
|
||||||
|
End Select
|
||||||
|
UIAskQuestion = answer = vbYes
|
||||||
|
End Function
|
15
samples/z_UIRibbon_sample.bas
Normal file
15
samples/z_UIRibbon_sample.bas
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Attribute VB_Name = "z_UIRibbon_sample"
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
public Sub OnRibbonBtn(control As IRibbonControl)
|
||||||
|
Select Case control.ID
|
||||||
|
Case "GotoLinkBegin": Call CC_GotoLinkBegin
|
||||||
|
Case "GotoLinkEnd": Call CC_GotoLinkEnd
|
||||||
|
Case "WidthAdd10": Call CC_WidthAdd10
|
||||||
|
Case "WidthSub10": Call CC_WidthSub10
|
||||||
|
Case "TopAlignment": Call CC_TopAlignment
|
||||||
|
Case "LeftAlignment": Call CC_LeftAlignment
|
||||||
|
Case "ToggleInherited": Call ToggleInherited
|
||||||
|
End Select
|
||||||
|
End Sub
|
198
ui/CSE_ListSelector.frm
Normal file
198
ui/CSE_ListSelector.frm
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
VERSION 5.00
|
||||||
|
Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} CSE_ListSelector
|
||||||
|
Caption = "Âûáîð ñïèñêà çíà÷åíèé"
|
||||||
|
ClientHeight = 8910.001
|
||||||
|
ClientLeft = 45
|
||||||
|
ClientTop = 375
|
||||||
|
ClientWidth = 8400.001
|
||||||
|
OleObjectBlob = "CSE_ListSelector.frx":0000
|
||||||
|
StartUpPosition = 1 'CenterOwner
|
||||||
|
End
|
||||||
|
Attribute VB_Name = "CSE_ListSelector"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = True
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' =========== List selection dialog ================
|
||||||
|
' Shared module version: 20220703
|
||||||
|
' Tested in:
|
||||||
|
' Depends on: ex_MSHook
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Explicit
|
||||||
|
Option Compare Text
|
||||||
|
|
||||||
|
Private input_ As Excel.ListObject
|
||||||
|
Private items_() As String
|
||||||
|
Private selected_ As Scripting.Dictionary
|
||||||
|
Private isUpdating_ As Boolean
|
||||||
|
|
||||||
|
Public delimiter_ As String
|
||||||
|
Public isCanceled_ As Boolean
|
||||||
|
|
||||||
|
Private Sub UserForm_Initialize()
|
||||||
|
isUpdating_ = False
|
||||||
|
isCanceled_ = True
|
||||||
|
Call PositionInMiddle
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub UserForm_Finalize()
|
||||||
|
Call HookDisable
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub UserForm_Activate()
|
||||||
|
Call HookEnable(Me, "ProcessMouseWheelMovement")
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub UserForm_Deactivate()
|
||||||
|
Call HookDisable
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(iData As Excel.ListObject, Optional bAllowNewItems As Boolean = True)
|
||||||
|
Set input_ = iData
|
||||||
|
delimiter_ = TBDelim.Text
|
||||||
|
|
||||||
|
Call ResetSelection
|
||||||
|
Call ScanInput
|
||||||
|
Call OnFilter("")
|
||||||
|
|
||||||
|
TBNewItem.Enabled = bAllowNewItems
|
||||||
|
BtnAddItem.Enabled = bAllowNewItems
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetSelectedStr() As String
|
||||||
|
Dim sText$: sText = vbNullString
|
||||||
|
Dim vItem As Variant
|
||||||
|
For Each vItem In selected_.Keys
|
||||||
|
If sText <> vbNullString Then _
|
||||||
|
sText = sText & delimiter_
|
||||||
|
sText = sText & CStr(vItem)
|
||||||
|
Next vItem
|
||||||
|
GetSelectedStr = sText
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========== Event handlers ==========
|
||||||
|
Private Sub OkBtn_Click()
|
||||||
|
isCanceled_ = False
|
||||||
|
Call Me.Hide
|
||||||
|
Call HookDisable
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub CancelBtn_Click()
|
||||||
|
isCanceled_ = True
|
||||||
|
Call Me.Hide
|
||||||
|
Call HookDisable
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub ClearListBtn_Click()
|
||||||
|
Call ResetSelection
|
||||||
|
Call OnFilter(TBFilter.Text)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub TBFilter_Change()
|
||||||
|
Call OnFilter(TBFilter.Text)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub TBDelim_Change()
|
||||||
|
delimiter_ = TBDelim.Text
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub LBItems_Change()
|
||||||
|
If isUpdating_ Then _
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 0 To LBItems.ListCount - 1 Step 1
|
||||||
|
Dim sText$: sText = LBItems.List(nItem)
|
||||||
|
If LBItems.Selected(nItem) Then
|
||||||
|
If Not selected_.Exists(sText) Then _
|
||||||
|
Call selected_.Add(sText, 0)
|
||||||
|
Else
|
||||||
|
If selected_.Exists(sText) Then _
|
||||||
|
Call selected_.Remove(sText)
|
||||||
|
End If
|
||||||
|
Next nItem
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub BtnAddItem_Click()
|
||||||
|
Dim sNewItem$: sNewItem = TBNewItem.Text
|
||||||
|
If sNewItem = vbNullString Then _
|
||||||
|
Exit Sub
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = LBound(items_) To UBound(items_) Step 1
|
||||||
|
If items_(nItem) = sNewItem Then
|
||||||
|
Call UserInteraction.ShowMessage(EM_ITEM_EXISTS, sNewItem)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
Dim iRow As Excel.ListRow: Set iRow = input_.ListRows.Add(1)
|
||||||
|
iRow.Range.Cells(1, 1) = sNewItem
|
||||||
|
Call ScanInput
|
||||||
|
Call OnFilter(TBFilter.Text)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' ======= Mouse scrolling for listbox ========
|
||||||
|
Public Function ProcessMouseWheelMovement(nDirection&)
|
||||||
|
If nDirection > 0 Then
|
||||||
|
If LBItems.TopIndex > 0 Then _
|
||||||
|
LBItems.TopIndex = LBItems.TopIndex - 1
|
||||||
|
Else
|
||||||
|
If LBItems.TopIndex < LBItems.ListCount - 1 Then _
|
||||||
|
LBItems.TopIndex = LBItems.TopIndex + 1
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =========
|
||||||
|
Private Function PositionInMiddle()
|
||||||
|
Dim nTopOffset&: nTopOffset = (Application.UsableHeight / 2) - (Me.Height / 2)
|
||||||
|
Dim nLeftOffset&: nLeftOffset = (Application.UsableWidth / 2) - (Me.Width / 2)
|
||||||
|
|
||||||
|
Me.StartUpPosition = 0
|
||||||
|
Me.Top = Application.Top + IIf(nTopOffset > 0, nTopOffset, 0)
|
||||||
|
Me.Left = Application.Left + IIf(nLeftOffset > 0, nLeftOffset, 0)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ScanInput()
|
||||||
|
ReDim items_(1 To input_.Range.Rows.Count - 1)
|
||||||
|
Dim nRow&
|
||||||
|
For nRow = 2 To input_.Range.Rows.Count Step 1
|
||||||
|
items_(nRow - 1) = input_.Range.Cells(nRow, 1)
|
||||||
|
Next nRow
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ResetSelection()
|
||||||
|
Set selected_ = New Scripting.Dictionary
|
||||||
|
selected_.CompareMode = TextCompare
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function OnFilter(sFilter$)
|
||||||
|
Call LBItems.Clear
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = LBound(items_) To UBound(items_) Step 1
|
||||||
|
If sFilter = vbNullString Or VBA.InStr(1, items_(nItem), sFilter, vbTextCompare) <> 0 Then
|
||||||
|
Call LBItems.AddItem(items_(nItem))
|
||||||
|
End If
|
||||||
|
Next nItem
|
||||||
|
Call UpdateSelected
|
||||||
|
Call UpdateCount
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function UpdateCount()
|
||||||
|
TBViewCount.Value = LBItems.ListCount
|
||||||
|
TBTotalCount.Value = ArraySize(items_)
|
||||||
|
TBSelectedCount.Value = selected_.Count
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function UpdateSelected()
|
||||||
|
isUpdating_ = True
|
||||||
|
|
||||||
|
If LBItems.ListCount = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 0 To LBItems.ListCount - 1 Step 1
|
||||||
|
LBItems.Selected(nItem) = selected_.Exists(LBItems.List(nItem))
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
isUpdating_ = False
|
||||||
|
End Function
|
BIN
ui/CSE_ListSelector.frx
Normal file
BIN
ui/CSE_ListSelector.frx
Normal file
Binary file not shown.
329
ui/CSE_ProgressBar.frm
Normal file
329
ui/CSE_ProgressBar.frm
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
VERSION 5.00
|
||||||
|
Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} CSE_ProgressBar
|
||||||
|
Caption = "Âûïîëíåíèå ïðîöåññà"
|
||||||
|
ClientHeight = 2850
|
||||||
|
ClientLeft = 45
|
||||||
|
ClientTop = 330
|
||||||
|
ClientWidth = 5280
|
||||||
|
OleObjectBlob = "CSE_ProgressBar.frx":0000
|
||||||
|
ShowModal = 0 'False
|
||||||
|
StartUpPosition = 2 'CenterScreen
|
||||||
|
End
|
||||||
|
Attribute VB_Name = "CSE_ProgressBar"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = True
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' =========== Progress Bar ================
|
||||||
|
' Shared module version: 20210827
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private thisWindow_&
|
||||||
|
|
||||||
|
Private isInterrupted_ As Boolean
|
||||||
|
Private canBeInterrupted_ As Boolean
|
||||||
|
|
||||||
|
Private minA_&
|
||||||
|
Private maxA_&
|
||||||
|
Private valueA_&
|
||||||
|
Private progressA_ As Double
|
||||||
|
|
||||||
|
Private minB_&
|
||||||
|
Private maxB_&
|
||||||
|
Private valueB_&
|
||||||
|
Private progressB_ As Double
|
||||||
|
|
||||||
|
Private tStart_&
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
|
||||||
|
|
||||||
|
'Windows API calls to remove the [x] from the top-right of the form
|
||||||
|
Private Declare PtrSafe Function FindWindow Lib "user32" _
|
||||||
|
Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
|
||||||
|
Private Declare PtrSafe Function GetWindowLong Lib "user32" _
|
||||||
|
Alias "GetWindowLongA" (ByVal nHwnd As Long, ByVal nIndex As Long) As Long
|
||||||
|
Private Declare PtrSafe Function SetWindowLong Lib "user32" _
|
||||||
|
Alias "SetWindowLongA" (ByVal nHwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
|
||||||
|
|
||||||
|
'Windows API calls to bring the progress bar form to the front of other modeless forms
|
||||||
|
Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As Long
|
||||||
|
Private Declare PtrSafe Function GetWindowThreadProcessId Lib "user32" (ByVal nHwnd As Long, ByRef lpdwProcessId As Long) As Long
|
||||||
|
Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal nHwnd As Long) As Long
|
||||||
|
|
||||||
|
Private Const UDATE_RATE = 0.005 ' ×àñòîòà îáíîâëåíèÿ - êàæäûé 0.5%
|
||||||
|
|
||||||
|
Private Sub UserForm_Initialize()
|
||||||
|
isInterrupted_ = False
|
||||||
|
canBeInterrupted_ = False
|
||||||
|
|
||||||
|
minA_ = 0
|
||||||
|
maxA_ = 100
|
||||||
|
valueA_ = 50
|
||||||
|
progressA_ = 50
|
||||||
|
|
||||||
|
Call Init("Çàãðóçêà...")
|
||||||
|
If Application.Name <> "Microsoft Visio" Then _
|
||||||
|
Call PositionInMiddle
|
||||||
|
Call RemoveCloseButton
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(sTitle$, _
|
||||||
|
Optional sHeader$ = vbNullString, _
|
||||||
|
Optional minVal& = 0, _
|
||||||
|
Optional maxVal& = 100, _
|
||||||
|
Optional curVal& = 0, _
|
||||||
|
Optional canInterrupt As Boolean = False)
|
||||||
|
canBeInterrupted_ = canInterrupt
|
||||||
|
btnInterrupt.Visible = canInterrupt
|
||||||
|
isInterrupted_ = False
|
||||||
|
|
||||||
|
If thisWindow_ = 0 Then
|
||||||
|
Me.Caption = "VBAProgressBar"
|
||||||
|
thisWindow_ = FindWindow(vbNullString, Me.Caption)
|
||||||
|
End If
|
||||||
|
|
||||||
|
tStart_ = GetTickCount()
|
||||||
|
|
||||||
|
minA_ = minVal
|
||||||
|
maxA_ = maxVal
|
||||||
|
|
||||||
|
minB_ = 0
|
||||||
|
maxB_ = 100
|
||||||
|
valueB_ = 0
|
||||||
|
|
||||||
|
Call EnableSecondBar(False)
|
||||||
|
Me.Title = sTitle
|
||||||
|
Me.HeaderLbl.Caption = sHeader
|
||||||
|
Me.DescriptionTB.Text = vbNullString
|
||||||
|
|
||||||
|
Call SetAInternal(curVal, bForceUpdate:=True)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function InitSecondBar(Optional minVal& = 0, Optional maxVal& = 100, Optional curVal& = 0)
|
||||||
|
Call EnableSecondBar(True)
|
||||||
|
minB_ = minVal
|
||||||
|
maxB_ = maxVal
|
||||||
|
Call SetBInternal(curVal, bForceUpdate:=True)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ShowModeless()
|
||||||
|
Call UpdateBar
|
||||||
|
Call Me.Show(vbModeless)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Sub btnInterrupt_Click()
|
||||||
|
HeaderLbl.Caption = "Èíèöèèðîâàíà îòìåíà, îæèäàéòå..."
|
||||||
|
isInterrupted_ = True
|
||||||
|
|
||||||
|
btnInterrupt.Enabled = False
|
||||||
|
Call RepaintEx
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub UserForm_QueryClose(ByRef nCancelCode As Integer, ByRef nCloseMode As Integer)
|
||||||
|
If nCloseMode = vbFormControlMenu And canBeInterrupted_ Then _
|
||||||
|
nCancelCode = True
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' ====== Access =====
|
||||||
|
Public Property Get Interrupted() As Boolean
|
||||||
|
Interrupted = isInterrupted_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Time() As Double
|
||||||
|
Time = GetTickCount() - tStart_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get TimeStr() As String
|
||||||
|
TimeStr = Format(Time / 1000, "# ##0.##") & " ñåê"
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Title() As String
|
||||||
|
Title = Me.Caption
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Description() As String
|
||||||
|
Description = DescriptionTB.Text
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Header() As String
|
||||||
|
Header = HeaderLbl.Caption
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get SecondBarEnabled() As Boolean
|
||||||
|
SecondBarEnabled = FrameSecBar.Visible
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get ValueA() As Long
|
||||||
|
ValueA = valueA_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get MinA() As Long
|
||||||
|
MinA = minA_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get MaxA() As Long
|
||||||
|
MaxA = maxA_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get ProgressA() As Double
|
||||||
|
If maxA_ = minA_ Then
|
||||||
|
ProgressA = 100
|
||||||
|
Else
|
||||||
|
ProgressA = VBA.Abs((valueA_ - minA_) / (maxA_ - minA_))
|
||||||
|
End If
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get ValueB() As Long
|
||||||
|
ValueB = valueB_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get MinB() As Long
|
||||||
|
MinB = minB_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get MaxB() As Long
|
||||||
|
MaxB = maxB_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get ProgressB() As Double
|
||||||
|
If maxB_ = minB_ Then
|
||||||
|
ProgressB = 100
|
||||||
|
Else
|
||||||
|
ProgressB = VBA.Abs((valueB_ - minB_) / (maxB_ - minB_))
|
||||||
|
End If
|
||||||
|
End Property
|
||||||
|
|
||||||
|
' ===== Modify =====
|
||||||
|
Public Property Let Title(ByVal newVal$)
|
||||||
|
Me.Caption = newVal
|
||||||
|
Call RemoveCloseButton
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Let Description(ByVal newVal$)
|
||||||
|
DescriptionTB.Text = newVal
|
||||||
|
Call RepaintEx
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Let Header(ByVal newVal$)
|
||||||
|
HeaderLbl.Caption = newVal
|
||||||
|
Call RepaintEx
|
||||||
|
End Property
|
||||||
|
|
||||||
|
' ====== Actions ===
|
||||||
|
Public Function IncrementA(Optional delta& = 1)
|
||||||
|
If Me.Visible Then _
|
||||||
|
Call SetAInternal(valueA_ + delta, bForceUpdate:=False)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function IncrementB(Optional delta& = 1)
|
||||||
|
If Me.Visible Then _
|
||||||
|
Call SetBInternal(valueB_ + delta, bForceUpdate:=False)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function HideSecondBar()
|
||||||
|
Call EnableSecondBar(False)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetA(ByVal newVal&, Optional bForceUpdate As Boolean = False)
|
||||||
|
If Me.Visible Then _
|
||||||
|
Call SetAInternal(newVal, bForceUpdate)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetB(ByVal newVal&, Optional bForceUpdate As Boolean = False)
|
||||||
|
If Me.Visible Then _
|
||||||
|
Call SetBInternal(newVal, bForceUpdate)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =======
|
||||||
|
Private Function EnableSecondBar(bEnable As Boolean)
|
||||||
|
FrameSecBar.Visible = bEnable
|
||||||
|
Call RepaintEx
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function PositionInMiddle()
|
||||||
|
Dim nTopOffset&: nTopOffset = (Application.UsableHeight / 2) - (Me.Height / 2)
|
||||||
|
Dim nLeftOffset&: nLeftOffset = (Application.UsableWidth / 2) - (Me.Width / 2)
|
||||||
|
|
||||||
|
Me.StartUpPosition = 0
|
||||||
|
Me.Top = Application.Top + IIf(nTopOffset > 0, nTopOffset, 0)
|
||||||
|
Me.Left = Application.Left + IIf(nLeftOffset > 0, nLeftOffset, 0)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SetAInternal(ByVal newVal&, bForceUpdate As Boolean)
|
||||||
|
valueA_ = newVal
|
||||||
|
If valueA_ > maxA_ Then _
|
||||||
|
valueA_ = maxA_
|
||||||
|
If valueA_ < minA_ Then _
|
||||||
|
valueA_ = minA_
|
||||||
|
Call SetProgressA(Me.ProgressA, bForceUpdate)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SetBInternal(ByVal newVal&, bForceUpdate As Boolean)
|
||||||
|
valueB_ = newVal
|
||||||
|
If valueB_ > maxB_ Then _
|
||||||
|
valueB_ = maxB_
|
||||||
|
If valueB_ < minB_ Then _
|
||||||
|
valueB_ = minB_
|
||||||
|
Call SetProgressB(Me.ProgressB, bForceUpdate)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SetProgressA(newProgress As Double, bForceUpdate As Boolean)
|
||||||
|
If Not bForceUpdate And Abs(newProgress - progressA_) < UDATE_RATE Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
progressA_ = newProgress
|
||||||
|
Call UpdateTime
|
||||||
|
Call UpdateBar
|
||||||
|
Call RepaintEx
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SetProgressB(newProgress As Double, bForceUpdate)
|
||||||
|
If Not bForceUpdate And Abs(newProgress - progressB_) < UDATE_RATE Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
progressB_ = newProgress
|
||||||
|
Call UpdateTime
|
||||||
|
Call UpdateBar
|
||||||
|
Call RepaintEx
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function UpdateBar()
|
||||||
|
fraInside.Width = VBA.Int(lblBack.Width * progressA_)
|
||||||
|
lblSecBar.Width = VBA.Int(FrameSecBar.Width * progressB_)
|
||||||
|
|
||||||
|
lblBack.Caption = Format(progressA_, "0%")
|
||||||
|
lblFront.Caption = Format(progressA_, "0%")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function RemoveCloseButton()
|
||||||
|
Const WS_SYSMENU& = &H80000
|
||||||
|
Const GWL_STYLE& = (-16)
|
||||||
|
Dim lStyle&: lStyle = GetWindowLong(thisWindow_, GWL_STYLE)
|
||||||
|
If lStyle And WS_SYSMENU > 0 Then _
|
||||||
|
Call SetWindowLong(thisWindow_, GWL_STYLE, (lStyle And Not WS_SYSMENU))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function BringToFront()
|
||||||
|
Dim lFocusThread&: lFocusThread = GetWindowThreadProcessId(GetForegroundWindow(), 0)
|
||||||
|
Dim lThisThread&: lThisThread = GetWindowThreadProcessId(thisWindow_, 0)
|
||||||
|
If lFocusThread = lThisThread Then
|
||||||
|
Call SetForegroundWindow(thisWindow_)
|
||||||
|
Else
|
||||||
|
DoEvents
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function UpdateTime()
|
||||||
|
TimeLabel.Caption = "Äëèòåëüíîñòü: " & TimeStr()
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function RepaintEx()
|
||||||
|
If Me.Visible Then
|
||||||
|
Call Me.Repaint
|
||||||
|
' BringToFront ' Ðàñêîììåíòèðîâàòü, ÷òîáû îêíî ñàìî âñïëûâàëî ïðè êàæäîì îáíîâëåíèè (óæàñíî áåñèò â ôîíîâîì ðåæèìå)
|
||||||
|
End If
|
||||||
|
DoEvents
|
||||||
|
End Function
|
BIN
ui/CSE_ProgressBar.frx
Normal file
BIN
ui/CSE_ProgressBar.frx
Normal file
Binary file not shown.
483
ui/Calendar/CSE_Calendar.frm
Normal file
483
ui/Calendar/CSE_Calendar.frm
Normal file
|
@ -0,0 +1,483 @@
|
||||||
|
VERSION 5.00
|
||||||
|
Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} CSE_Calendar
|
||||||
|
Caption = "Calendar"
|
||||||
|
ClientHeight = 4455
|
||||||
|
ClientLeft = 30
|
||||||
|
ClientTop = 390
|
||||||
|
ClientWidth = 4440
|
||||||
|
OleObjectBlob = "CSE_Calendar.frx":0000
|
||||||
|
StartUpPosition = 2 'CenterScreen
|
||||||
|
End
|
||||||
|
Attribute VB_Name = "CSE_Calendar"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = True
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' =========== Calendar picker native VBA ================
|
||||||
|
' Shared module version: 20210502
|
||||||
|
' Dependencies: z_CalendarUI, CSE_CallbackCalendar
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const GWL_STYLE = -16
|
||||||
|
Private Const WS_CAPTION = &HC00000
|
||||||
|
Private Const MOUSE_LEFT_BTN = 1
|
||||||
|
|
||||||
|
Private Const CALENDAR_DAYS_COUNT = 42
|
||||||
|
Private Const CALENDAR_WEEKDAY_COUNT = 7
|
||||||
|
Private Const CALENDAR_MONTH_COUNT = 12
|
||||||
|
|
||||||
|
Private Const CALENDAR_YEARS_PER_PAGE = 12
|
||||||
|
|
||||||
|
Private Type CalendarThemeColors
|
||||||
|
background_ As Long
|
||||||
|
foreground_ As Long
|
||||||
|
curDateBG_ As Long
|
||||||
|
curDateFG_ As Long
|
||||||
|
inactiveDates_ As Long
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Private Enum CalendarMode
|
||||||
|
CAL_MODE_DAY = 0
|
||||||
|
CAL_MODE_MONTH = 1
|
||||||
|
CAL_MODE_YEAR = 2
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function FindWindow Lib "user32" _
|
||||||
|
Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
|
||||||
|
Private Declare PtrSafe Function GetWindowLong Lib "user32" _
|
||||||
|
Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
|
||||||
|
Private Declare PtrSafe Function SetWindowLong Lib "user32" _
|
||||||
|
Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function DrawMenuBar Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
|
||||||
|
|
||||||
|
Public isCancelled_ As Boolean
|
||||||
|
|
||||||
|
Private curDate_ As Long
|
||||||
|
|
||||||
|
Private month_ As Long
|
||||||
|
Private year_ As Long
|
||||||
|
|
||||||
|
Private mode_ As CalendarMode
|
||||||
|
Private theme_ As TCalendarTheme
|
||||||
|
Private colors_ As CalendarThemeColors
|
||||||
|
|
||||||
|
Private firstYear_ As Long
|
||||||
|
Private lastYear_ As Long
|
||||||
|
|
||||||
|
Private xPos_ As Double
|
||||||
|
Private yPos_ As Double
|
||||||
|
|
||||||
|
Private callbacks_ As Collection
|
||||||
|
|
||||||
|
Private Sub UserForm_Initialize()
|
||||||
|
Call ResetBorders
|
||||||
|
Call HideTitleBar
|
||||||
|
Call InitLabelCallbacks
|
||||||
|
|
||||||
|
isCancelled_ = True
|
||||||
|
mode_ = CAL_MODE_DAY
|
||||||
|
|
||||||
|
Call PositionInMiddle
|
||||||
|
Call SetCurrentDateInternal(Int(Now))
|
||||||
|
Call SetTheme(T_CT_LIGHT)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(theDate&, aTheme As TCalendarTheme)
|
||||||
|
isCancelled_ = True
|
||||||
|
|
||||||
|
Call SetCurrentDateInternal(theDate)
|
||||||
|
Call ChangeMode(CAL_MODE_DAY)
|
||||||
|
Call UpdateTitle
|
||||||
|
Call SetTheme(aTheme)
|
||||||
|
Call ResetBorders
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get SelectedDate() As Long
|
||||||
|
SelectedDate = curDate_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Theme() As TCalendarTheme
|
||||||
|
Theme = theme_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function SetTheme(newValue As TCalendarTheme)
|
||||||
|
theme_ = newValue
|
||||||
|
Call UpdateThemeColors
|
||||||
|
Call RepaintAllColors
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ==== Events handlings ======
|
||||||
|
Public Function OnDayClicked(cLabel As MSForms.Label)
|
||||||
|
Call ReturnResult(cLabel.Tag)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function OnMonthClicked(cLabel As MSForms.Label)
|
||||||
|
month_ = TagToMonth(cLabel.Caption)
|
||||||
|
Call ChangeMode(CAL_MODE_DAY)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function OnYearClicked(cLabel As MSForms.Label)
|
||||||
|
If Len(Trim(cLabel.Caption)) = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
year_ = val(cLabel.Caption)
|
||||||
|
Call ChangeMode(CAL_MODE_MONTH)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Sub lblToday_Click()
|
||||||
|
If mode_ <> CAL_MODE_DAY Then
|
||||||
|
Call SetCurrentDateInternal(Int(Now))
|
||||||
|
Call ChangeMode(CAL_MODE_DAY)
|
||||||
|
Else
|
||||||
|
Call ReturnResult(Int(Now))
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub lblThemes_Click()
|
||||||
|
If theme_ < TCalendarTheme.[_Last] Then
|
||||||
|
Call SetTheme(theme_ + 1)
|
||||||
|
Else
|
||||||
|
Call SetTheme(TCalendarTheme.[_First])
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub lblUnload_Click()
|
||||||
|
isCancelled_ = True
|
||||||
|
Call Me.Hide
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub btnClose_Click()
|
||||||
|
If mode_ <> CAL_MODE_DAY Then
|
||||||
|
Call ChangeMode(mode_ - 1)
|
||||||
|
Else
|
||||||
|
isCancelled_ = True
|
||||||
|
Call Me.Hide
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub lblUP_Click()
|
||||||
|
Select Case mode_
|
||||||
|
Case CAL_MODE_DAY
|
||||||
|
Call IncrementMonth(-1)
|
||||||
|
|
||||||
|
Case CAL_MODE_MONTH
|
||||||
|
|
||||||
|
Case CAL_MODE_YEAR
|
||||||
|
lastYear_ = firstYear_ - 1
|
||||||
|
firstYear_ = firstYear_ - CALENDAR_YEARS_PER_PAGE
|
||||||
|
Call RepaintYearsColor
|
||||||
|
End Select
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub lblDOWN_Click()
|
||||||
|
Select Case mode_
|
||||||
|
Case CAL_MODE_DAY
|
||||||
|
Call IncrementMonth(1)
|
||||||
|
|
||||||
|
Case CAL_MODE_MONTH
|
||||||
|
|
||||||
|
Case CAL_MODE_YEAR
|
||||||
|
firstYear_ = lastYear_ + 1
|
||||||
|
lastYear_ = lastYear_ + CALENDAR_YEARS_PER_PAGE
|
||||||
|
Call RepaintYearsColor
|
||||||
|
End Select
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub UserForm_MouseDown(ByVal nBtn As Integer, ByVal nShift As Integer, ByVal mvX As Single, ByVal mvY As Single)
|
||||||
|
If nBtn = MOUSE_LEFT_BTN Then
|
||||||
|
xPos_ = mvX
|
||||||
|
yPos_ = mvY
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub UserForm_MouseMove(ByVal nBtn As Integer, ByVal nShift As Integer, ByVal mvX As Single, ByVal mvY As Single)
|
||||||
|
If nBtn And MOUSE_LEFT_BTN Then
|
||||||
|
Me.Left = Me.Left + (mvX - xPos_)
|
||||||
|
Me.Top = Me.Top + (mvY - yPos_)
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub Frame1_MouseDown(ByVal nBtn As Integer, ByVal nShift As Integer, ByVal mvX As Single, ByVal mvY As Single)
|
||||||
|
If nBtn = MOUSE_LEFT_BTN Then
|
||||||
|
xPos_ = mvX
|
||||||
|
yPos_ = mvY
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub lblTitleCurMY_Click()
|
||||||
|
'--> Handles the month to year multipage display
|
||||||
|
If mode_ = CAL_MODE_YEAR Then _
|
||||||
|
Exit Sub
|
||||||
|
Call ChangeMode(mode_ + 1)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' =========
|
||||||
|
Private Function ReturnResult(theDate&)
|
||||||
|
curDate_ = theDate
|
||||||
|
isCancelled_ = False
|
||||||
|
Call Me.Hide
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function PositionInMiddle()
|
||||||
|
Dim nTopOffset&: nTopOffset = (Application.UsableHeight / 2) - (Me.Height / 2)
|
||||||
|
Dim nLeftOffset&: nLeftOffset = (Application.UsableWidth / 2) - (Me.Width / 2)
|
||||||
|
|
||||||
|
Me.StartUpPosition = 0
|
||||||
|
Me.Top = Application.Top + IIf(nTopOffset > 0, nTopOffset, 0)
|
||||||
|
Me.Left = Application.Left + IIf(nLeftOffset > 0, nLeftOffset, 0)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function HideTitleBar()
|
||||||
|
Dim frameHandle As Long: frameHandle = FindWindow(vbNullString, Me.Caption)
|
||||||
|
Dim windowHandle As Long: windowHandle = GetWindowLong(frameHandle, GWL_STYLE)
|
||||||
|
windowHandle = windowHandle And (Not WS_CAPTION)
|
||||||
|
Call SetWindowLong(frameHandle, GWL_STYLE, windowHandle)
|
||||||
|
Call DrawMenuBar(frameHandle)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ResetBorders()
|
||||||
|
Dim aLabel As control
|
||||||
|
For Each aLabel In Me.Controls
|
||||||
|
If TypeOf aLabel Is MSForms.Label Then _
|
||||||
|
aLabel.BorderStyle = fmBorderStyleNone
|
||||||
|
Next
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function InitLabelCallbacks()
|
||||||
|
Dim aControl As control
|
||||||
|
Dim controlType$
|
||||||
|
Set callbacks_ = New Collection
|
||||||
|
For Each aControl In Me.Controls
|
||||||
|
If Not TypeOf aControl Is MSForms.Label Then _
|
||||||
|
GoTo NEXT_CONTROL
|
||||||
|
controlType = Left(aControl.Name, 1)
|
||||||
|
If Not controlType Like "[DMY]" Then _
|
||||||
|
GoTo NEXT_CONTROL
|
||||||
|
Call AddCallback(aControl)
|
||||||
|
NEXT_CONTROL:
|
||||||
|
Next aControl
|
||||||
|
|
||||||
|
Call AddCallback(lblUP)
|
||||||
|
Call AddCallback(lblDOWN)
|
||||||
|
Call AddCallback(lblThemes)
|
||||||
|
Call AddCallback(lblUnload)
|
||||||
|
Call AddCallback(lblTitleCurMY, bUnderline:=True)
|
||||||
|
Call AddCallback(lblToday, bUnderline:=True)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function AddCallback(target As control, Optional bUnderline As Boolean = False)
|
||||||
|
Dim newCallback As New CSE_CallbackCalendar
|
||||||
|
Call newCallback.Init(target, bUnderline)
|
||||||
|
Call callbacks_.Add(newCallback)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SetCurrentDateInternal(theDate&)
|
||||||
|
curDate_ = theDate
|
||||||
|
month_ = Month(curDate_)
|
||||||
|
year_ = Year(curDate_)
|
||||||
|
Call UpdateDays
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IncrementMonth(inc&)
|
||||||
|
year_ = year_ + ((month_ + inc + CALENDAR_MONTH_COUNT - 1) \ CALENDAR_MONTH_COUNT - 1)
|
||||||
|
month_ = 1 + ((CALENDAR_MONTH_COUNT + (month_ + inc - 1) Mod CALENDAR_MONTH_COUNT) Mod CALENDAR_MONTH_COUNT)
|
||||||
|
Call UpdateTitle
|
||||||
|
Call UpdateDays
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ChangeMode(newMode As CalendarMode)
|
||||||
|
If newMode = mode_ Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Me.MPmainDisplay.Value = newMode
|
||||||
|
Call SetArrowsVisibility(newMode <> CAL_MODE_MONTH)
|
||||||
|
mode_ = newMode
|
||||||
|
Select Case mode_
|
||||||
|
Case CAL_MODE_DAY
|
||||||
|
Call UpdateDays
|
||||||
|
|
||||||
|
Case CAL_MODE_MONTH
|
||||||
|
Call RepaintMonthsColors
|
||||||
|
|
||||||
|
Case CAL_MODE_YEAR
|
||||||
|
lastYear_ = year_ + 1
|
||||||
|
firstYear_ = lastYear_ - CALENDAR_YEARS_PER_PAGE + 1
|
||||||
|
Call RepaintYearsColor
|
||||||
|
End Select
|
||||||
|
Call UpdateTitle
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function UpdateDays()
|
||||||
|
'--> Populate the calendar
|
||||||
|
Dim firstDay&: firstDay = DateSerial(year_, month_, 1)
|
||||||
|
firstDay = firstDay - Weekday(firstDay, vbMonday) + 1
|
||||||
|
Dim lastDay&: lastDay = firstDay + CALENDAR_DAYS_COUNT - 1
|
||||||
|
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 1 To CALENDAR_DAYS_COUNT
|
||||||
|
With Me.Controls("D" & nItem)
|
||||||
|
.Caption = Day(firstDay + nItem - 1)
|
||||||
|
.Tag = firstDay + nItem - 1
|
||||||
|
End With
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
Call RepaintDaysColors
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function UpdateTitle()
|
||||||
|
Select Case mode_
|
||||||
|
Case CAL_MODE_DAY
|
||||||
|
lblTitleCurMY.Caption = Format(DateSerial(year_, month_, 1), "MMMM yyyy")
|
||||||
|
|
||||||
|
Case CAL_MODE_MONTH
|
||||||
|
lblTitleCurMY.Caption = year_
|
||||||
|
|
||||||
|
Case CAL_MODE_YEAR
|
||||||
|
lblTitleCurMY.Caption = firstYear_ & " - " & lastYear_
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function UpdateThemeColors()
|
||||||
|
With colors_
|
||||||
|
Select Case theme_
|
||||||
|
Case TCalendarTheme.T_CT_DARK
|
||||||
|
.background_ = RGB(69, 69, 69)
|
||||||
|
.foreground_ = RGB(252, 248, 248)
|
||||||
|
.curDateBG_ = RGB(246, 127, 8)
|
||||||
|
.curDateFG_ = RGB(0, 0, 0)
|
||||||
|
.inactiveDates_ = RGB(120, 120, 120)
|
||||||
|
|
||||||
|
Case TCalendarTheme.T_CT_RED
|
||||||
|
.background_ = RGB(87, 0, 0)
|
||||||
|
.foreground_ = RGB(203, 146, 146)
|
||||||
|
.curDateBG_ = RGB(122, 185, 247)
|
||||||
|
.curDateFG_ = RGB(0, 0, 0)
|
||||||
|
.inactiveDates_ = RGB(144, 70, 70)
|
||||||
|
|
||||||
|
Case TCalendarTheme.T_CT_BLUE
|
||||||
|
.background_ = RGB(42, 48, 92)
|
||||||
|
.foreground_ = RGB(179, 179, 179)
|
||||||
|
.curDateBG_ = RGB(122, 185, 247)
|
||||||
|
.curDateFG_ = RGB(0, 0, 0)
|
||||||
|
.inactiveDates_ = RGB(80, 80, 166)
|
||||||
|
|
||||||
|
Case TCalendarTheme.T_CT_LIGHT
|
||||||
|
.background_ = RGB(240, 240, 240)
|
||||||
|
.foreground_ = RGB(0, 0, 0)
|
||||||
|
.curDateBG_ = RGB(246, 127, 8)
|
||||||
|
.curDateFG_ = RGB(0, 0, 0)
|
||||||
|
.inactiveDates_ = RGB(200, 200, 200)
|
||||||
|
End Select
|
||||||
|
End With
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SetArrowsVisibility(isVisible As Boolean)
|
||||||
|
lblDOWN.Visible = isVisible
|
||||||
|
lblUP.Visible = isVisible
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TagToMonth(sTag$) As Long
|
||||||
|
Select Case sTag
|
||||||
|
Case "JAN": TagToMonth = 1
|
||||||
|
Case "FEB": TagToMonth = 2
|
||||||
|
Case "MAR": TagToMonth = 3
|
||||||
|
Case "APR": TagToMonth = 4
|
||||||
|
Case "MAY": TagToMonth = 5
|
||||||
|
Case "JUN": TagToMonth = 6
|
||||||
|
Case "JUL": TagToMonth = 7
|
||||||
|
Case "AUG": TagToMonth = 8
|
||||||
|
Case "SEP": TagToMonth = 9
|
||||||
|
Case "OCT": TagToMonth = 10
|
||||||
|
Case "NOV": TagToMonth = 11
|
||||||
|
Case "DEC": TagToMonth = 12
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function RepaintAllColors()
|
||||||
|
Me.BackColor = colors_.background_
|
||||||
|
FrameDay.BackColor = colors_.background_
|
||||||
|
FrameMonth.BackColor = colors_.background_
|
||||||
|
FrameYr.BackColor = colors_.background_
|
||||||
|
|
||||||
|
lblToday.ForeColor = colors_.foreground_
|
||||||
|
lblToday.BorderColor = colors_.foreground_
|
||||||
|
|
||||||
|
lblTitleCurMY.ForeColor = colors_.foreground_
|
||||||
|
lblTitleCurMY.BorderColor = colors_.foreground_
|
||||||
|
|
||||||
|
lblUnload.ForeColor = colors_.foreground_
|
||||||
|
lblThemes.ForeColor = colors_.foreground_
|
||||||
|
|
||||||
|
lblUP.ForeColor = colors_.foreground_
|
||||||
|
lblDOWN.ForeColor = colors_.foreground_
|
||||||
|
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 1 To CALENDAR_WEEKDAY_COUNT Step 1
|
||||||
|
Me.Controls("WD" & nItem).ForeColor = colors_.foreground_
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
Call RepaintDaysColors
|
||||||
|
Call RepaintMonthsColors
|
||||||
|
Call RepaintYearsColor
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function RepaintDaysColors()
|
||||||
|
Dim nItem&
|
||||||
|
Dim theDate&
|
||||||
|
For nItem = 1 To CALENDAR_DAYS_COUNT Step 1
|
||||||
|
With Me.Controls("D" & nItem)
|
||||||
|
theDate = .Tag
|
||||||
|
.BorderStyle = fmBorderStyleNone
|
||||||
|
If .Tag = curDate_ Then
|
||||||
|
.BackStyle = fmBackStyleOpaque
|
||||||
|
.BackColor = colors_.curDateBG_
|
||||||
|
.ForeColor = colors_.curDateFG_
|
||||||
|
ElseIf Month(.Tag) = month_ Then
|
||||||
|
.BackStyle = fmBackStyleTransparent
|
||||||
|
.BackColor = colors_.background_
|
||||||
|
.ForeColor = colors_.foreground_
|
||||||
|
Else
|
||||||
|
.BackStyle = fmBackStyleTransparent
|
||||||
|
.BackColor = colors_.background_
|
||||||
|
.ForeColor = colors_.inactiveDates_
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
Next nItem
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function RepaintMonthsColors()
|
||||||
|
Dim nMonth&
|
||||||
|
For nMonth = 1 To CALENDAR_MONTH_COUNT Step 1
|
||||||
|
With Me.Controls("M" & nMonth)
|
||||||
|
.BorderStyle = fmBorderStyleNone
|
||||||
|
If nMonth = month_ Then
|
||||||
|
.BackStyle = fmBackStyleOpaque
|
||||||
|
.BackColor = colors_.curDateBG_
|
||||||
|
.ForeColor = colors_.curDateFG_
|
||||||
|
Else
|
||||||
|
.BackStyle = fmBackStyleTransparent
|
||||||
|
.BackColor = colors_.background_
|
||||||
|
.ForeColor = colors_.foreground_
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
Next nMonth
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function RepaintYearsColor()
|
||||||
|
Dim nYear&
|
||||||
|
For nYear = 1 To CALENDAR_YEARS_PER_PAGE Step 1
|
||||||
|
With Me.Controls("Y" & nYear)
|
||||||
|
.BorderStyle = fmBorderStyleNone
|
||||||
|
.Caption = CStr(firstYear_ + nYear - 1)
|
||||||
|
If nYear + firstYear_ - 1 = year_ Then
|
||||||
|
.BackStyle = fmBackStyleOpaque
|
||||||
|
.BackColor = colors_.curDateBG_
|
||||||
|
.ForeColor = colors_.curDateFG_
|
||||||
|
Else
|
||||||
|
.BackStyle = fmBackStyleTransparent
|
||||||
|
.BackColor = colors_.background_
|
||||||
|
.ForeColor = colors_.foreground_
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
Next nYear
|
||||||
|
End Function
|
BIN
ui/Calendar/CSE_Calendar.frx
Normal file
BIN
ui/Calendar/CSE_Calendar.frx
Normal file
Binary file not shown.
53
ui/Calendar/CSE_CallbackCalendar.cls
Normal file
53
ui/Calendar/CSE_CallbackCalendar.cls
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CSE_CallbackCalendar"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' =========== Calendar picker native VBA - Label clicks callback ================
|
||||||
|
' Shared module version: 20210130
|
||||||
|
' Dependencies: z_CalendarUI, CSE_Calendar
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const CAL_MOVE_MARGIN = 2
|
||||||
|
|
||||||
|
Public WithEvents control_ As MSForms.Label
|
||||||
|
Attribute control_.VB_VarHelpID = -1
|
||||||
|
|
||||||
|
Private underline_ As Boolean
|
||||||
|
|
||||||
|
Public Function Init(aControl As MSForms.Label, Optional bUnderline As Boolean = False)
|
||||||
|
Set control_ = aControl
|
||||||
|
underline_ = bUnderline
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Sub control__Click()
|
||||||
|
Dim controlType$: controlType = Left(control_.Name, 1)
|
||||||
|
Select Case controlType
|
||||||
|
Case "D": Call CSE_Calendar.OnDayClicked(control_)
|
||||||
|
Case "M": Call CSE_Calendar.OnMonthClicked(control_)
|
||||||
|
Case "Y": Call CSE_Calendar.OnYearClicked(control_)
|
||||||
|
End Select
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub control__MouseMove(ByVal nBtn As Integer, ByVal nShift As Integer, ByVal mvX As Single, ByVal mvY As Single)
|
||||||
|
Dim bIsOutside As Boolean: bIsOutside = IsMoveOutside(mvX, mvY)
|
||||||
|
If underline_ Then
|
||||||
|
control_.Font.Underline = Not bIsOutside
|
||||||
|
control_.Font.Bold = Not bIsOutside
|
||||||
|
Else
|
||||||
|
control_.BorderStyle = IIf(bIsOutside, fmBorderStyleNone, fmBorderStyleSingle)
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' =======
|
||||||
|
Private Function IsMoveOutside(mvX As Single, mvY As Single)
|
||||||
|
IsMoveOutside = _
|
||||||
|
mvX >= control_.Width - CAL_MOVE_MARGIN Or _
|
||||||
|
mvX <= CAL_MOVE_MARGIN Or _
|
||||||
|
mvY <= CAL_MOVE_MARGIN Or _
|
||||||
|
mvY >= control_.Height - CAL_MOVE_MARGIN
|
||||||
|
End Function
|
49
ui/Calendar/z_CalendarUI.bas
Normal file
49
ui/Calendar/z_CalendarUI.bas
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
Attribute VB_Name = "z_CalendarUI"
|
||||||
|
' =========== Calendar picker native VBA ================
|
||||||
|
' Shared module version: 20210123
|
||||||
|
' Dependencies: CSE_Calendar, CSE_CallbackCalendar
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Enum TCalendarTheme
|
||||||
|
T_CT_INVALID = 0
|
||||||
|
[_First] = 1
|
||||||
|
|
||||||
|
T_CT_DARK = 1
|
||||||
|
T_CT_RED = 2
|
||||||
|
T_CT_BLUE = 3
|
||||||
|
T_CT_LIGHT = 4
|
||||||
|
|
||||||
|
[_Last] = 4
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Function PromptDate(initDate&, Optional ByRef cTheme As TCalendarTheme = T_CT_DARK) As Long
|
||||||
|
With CSE_Calendar
|
||||||
|
Call .Init(initDate, cTheme)
|
||||||
|
Call .Show
|
||||||
|
If .isCancelled_ Then _
|
||||||
|
Exit Function
|
||||||
|
PromptDate = .SelectedDate
|
||||||
|
cTheme = .Theme
|
||||||
|
End With
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CalendarTheme2String(eTheme As TCalendarTheme) As String
|
||||||
|
Select Case eTheme
|
||||||
|
Case T_CT_DARK: CalendarTheme2String = "DARK"
|
||||||
|
Case T_CT_RED: CalendarTheme2String = "RED"
|
||||||
|
Case T_CT_BLUE: CalendarTheme2String = "BLUE"
|
||||||
|
Case T_CT_LIGHT: CalendarTheme2String = "LIGHT"
|
||||||
|
Case Else: CalendarTheme2String = "INVALID"
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function String2CalendarTheme(sTheme$) As TCalendarTheme
|
||||||
|
Select Case sTheme
|
||||||
|
Case "DARK": String2CalendarTheme = T_CT_DARK
|
||||||
|
Case "RED": String2CalendarTheme = T_CT_RED
|
||||||
|
Case "BLUE": String2CalendarTheme = T_CT_BLUE
|
||||||
|
Case "LIGHT": String2CalendarTheme = T_CT_LIGHT
|
||||||
|
Case Else: String2CalendarTheme = T_CT_INVALID
|
||||||
|
End Select
|
||||||
|
End Function
|
2
ui/ribbonVSO/.rels
Normal file
2
ui/ribbonVSO/.rels
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail" Target="docProps/thumbnail.emf"/><Relationship Id="rId1" Type="http://schemas.microsoft.com/visio/2010/relationships/document" Target="visio/document.xml"/><Relationship Id="rId6" Type="http://schemas.microsoft.com/office/2007/relationships/ui/extensibility" Target="visio/customUI/customUI1.xml"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" Target="docProps/custom.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/></Relationships>
|
16
ui/ribbonVSO/customUI1.xml
Normal file
16
ui/ribbonVSO/customUI1.xml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
|
||||||
|
<ribbon>
|
||||||
|
<tabs>
|
||||||
|
<tab id="ReestrNPA" label="РЕЕСТР НПА" >
|
||||||
|
<group id="HT03_Data" label="Действия со схемой" >
|
||||||
|
<button id="CreateSchema" size="large"
|
||||||
|
label="Генерировать схему"
|
||||||
|
supertip="Создать схему из данных Реестра"
|
||||||
|
onAction="OnAction"
|
||||||
|
imageMso="MindMapImportData"/>
|
||||||
|
</group>
|
||||||
|
</tab>
|
||||||
|
</tabs>
|
||||||
|
</ribbon>
|
||||||
|
</customUI>
|
2
ui/ribbonWord/.rels
Normal file
2
ui/ribbonWord/.rels
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId4" Type="http://schemas.microsoft.com/office/2006/relationships/ui/extensibility" Target="customUI/customUI.xml"/></Relationships>
|
16
ui/ribbonWord/customUI.xml
Normal file
16
ui/ribbonWord/customUI.xml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
|
||||||
|
<ribbon>
|
||||||
|
<tabs>
|
||||||
|
<tab id="TabID" label="Tab Title" >
|
||||||
|
<group id="GroupID" label="Ссылки" >
|
||||||
|
<button id="NextLink" size="large" keytip="W"
|
||||||
|
label="Следующая ссылка"
|
||||||
|
supertip="Перейти к следующей необработанной ссылке [Ctrl+Shift+W]"
|
||||||
|
imageMso="PivotTableGroupSelection"
|
||||||
|
onAction="OnAction"/>
|
||||||
|
</group>
|
||||||
|
</tab>
|
||||||
|
</tabs>
|
||||||
|
</ribbon>
|
||||||
|
</customUI>
|
2
ui/ribbonXL/.rels
Normal file
2
ui/ribbonXL/.rels
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/><Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId4" Type="http://schemas.microsoft.com/office/2006/relationships/ui/extensibility" Target="customUI/customUI.xml"/></Relationships>
|
16
ui/ribbonXL/customUI.xml
Normal file
16
ui/ribbonXL/customUI.xml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
|
||||||
|
<ribbon>
|
||||||
|
<tabs>
|
||||||
|
<tab id="HT03_NPA" label="РЕЕСТР НПА" >
|
||||||
|
<group id="Group1" label="Функционал Excel модуля" >
|
||||||
|
<button id="HT03_NextDoc" visible="true" size="large"
|
||||||
|
label="Следющий документ"
|
||||||
|
supertip="Следующий необработанный документ"
|
||||||
|
imageMso="PivotTableGroupSelection"
|
||||||
|
onAction="OnAction"/>
|
||||||
|
</group>
|
||||||
|
</tab>
|
||||||
|
</tabs>
|
||||||
|
</ribbon>
|
||||||
|
</customUI>
|
103
utility/API_Config.cls
Normal file
103
utility/API_Config.cls
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_Config"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ================ Ðàáîòà êîíôèãóðàöèÿìè =============
|
||||||
|
' Shared module version: 20220727
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: API_JSON
|
||||||
|
' Required reference: Scripting, API_JSON
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const JSON_IDENT_SIZE_SPACES = 4
|
||||||
|
|
||||||
|
Private data_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Call Clear
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Contains(sKey$) As Boolean
|
||||||
|
Contains = data_.Exists(sKey)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetValue(sKey$, Optional iDefault As Variant = vbNullString) As Variant
|
||||||
|
If Not Contains(sKey) Then _
|
||||||
|
GoTo RETURN_DEFAULT
|
||||||
|
|
||||||
|
If VBA.VarType(data_(sKey)) = vbObject Then
|
||||||
|
Set GetValue = data_(sKey)
|
||||||
|
Exit Function
|
||||||
|
Else
|
||||||
|
GetValue = data_(sKey)
|
||||||
|
If GetValue <> "" Then _
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
RETURN_DEFAULT:
|
||||||
|
If VBA.VarType(iDefault) = vbObject Then
|
||||||
|
Set GetValue = iDefault
|
||||||
|
Else
|
||||||
|
GetValue = iDefault
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetValue(sKey$, newVal As Variant)
|
||||||
|
If VBA.VarType(newVal) = vbObject Then
|
||||||
|
Set data_.Item(sKey) = newVal
|
||||||
|
Else
|
||||||
|
data_.Item(sKey) = newVal
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RemoveKey(sKey$)
|
||||||
|
If data_.Exists(sKey) Then _
|
||||||
|
Call data_.Remove(sKey)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Clear()
|
||||||
|
Set data_ = New Scripting.Dictionary
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Scan configuration from JSON file
|
||||||
|
' Requires: sFile - path to valid UTF-8 JSON file
|
||||||
|
Public Function LoadFromFile(sFile$) As Boolean
|
||||||
|
Dim iParser As New API_JSON
|
||||||
|
Dim newData As Object: Set newData = iParser.LoadFromFile(sFile)
|
||||||
|
LoadFromFile = Not newData Is Nothing
|
||||||
|
If LoadFromFile Then _
|
||||||
|
Set data_ = newData
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Scan configuration from JSON string
|
||||||
|
Public Function LoadFromJSON(sJson$) As Boolean
|
||||||
|
LoadFromJSON = False
|
||||||
|
|
||||||
|
Dim iParser As New API_JSON
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
Set data_ = iParser.Parse(sJson)
|
||||||
|
|
||||||
|
LoadFromJSON = True
|
||||||
|
|
||||||
|
RETURN_FALSE:
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SaveToFile(sFile$, Optional bMultiline As Boolean = True) As Boolean
|
||||||
|
Dim iParser As New API_JSON
|
||||||
|
If bMultiline Then _
|
||||||
|
Call iParser.SetupMultiline(JSON_IDENT_SIZE_SPACES)
|
||||||
|
SaveToFile = iParser.SaveToFile(sFile, data_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SaveToJSON(Optional bMultiline As Boolean = False) As String
|
||||||
|
Dim iParser As New API_JSON
|
||||||
|
If bMultiline Then _
|
||||||
|
Call iParser.SetupMultiline(JSON_IDENT_SIZE_SPACES)
|
||||||
|
SaveToJSON = iParser.CreateJSON(data_)
|
||||||
|
End Function
|
73
utility/API_DistrManifest.cls
Normal file
73
utility/API_DistrManifest.cls
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_DistrManifest"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ======== Products distribution manifest ========
|
||||||
|
' Shared module version: 20220814
|
||||||
|
' Tested in:
|
||||||
|
' Depends on: API_JSON
|
||||||
|
' Required reference: Scripting, ex_Version
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const MANIFEST_JSON_IDENT = 4
|
||||||
|
|
||||||
|
Public data_ As Scripting.Dictionary
|
||||||
|
Private parser_ As New API_JSON
|
||||||
|
Private file_ As String
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Call parser_.SetupMultiline(MANIFEST_JSON_IDENT)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function LoadFrom(sFile$)
|
||||||
|
file_ = sFile
|
||||||
|
Set data_ = parser_.LoadFromFile(file_)
|
||||||
|
If data_ Is Nothing Then _
|
||||||
|
Set data_ = New Scripting.Dictionary
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function LoadServer()
|
||||||
|
Call LoadFrom(CP_TOOLS_SERVER & "\" & FILE_DISTRIBUTION_MANIFEST)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function LoadLocal()
|
||||||
|
Call LoadFrom(CP_TOOLS_LOCAL & "\" & FILE_DISTRIBUTION_MANIFEST)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function IsLoaded() As Boolean
|
||||||
|
IsLoaded = False
|
||||||
|
|
||||||
|
If data_ Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If data_.Count = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
IsLoaded = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Reload()
|
||||||
|
Call LoadFrom(file_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetVersion(sProduct$, sVersion$)
|
||||||
|
data_(sProduct) = sVersion
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetVersion(sProduct$) As String
|
||||||
|
If Not data_.Exists(sProduct) Then _
|
||||||
|
Exit Function
|
||||||
|
GetVersion = data_(sProduct)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Save() As Boolean
|
||||||
|
Save = SaveTo(file_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SaveTo(sFile$) As Boolean
|
||||||
|
SaveTo = parser_.SaveToFile(sFile, data_)
|
||||||
|
End Function
|
360
utility/API_GraphOrdering.cls
Normal file
360
utility/API_GraphOrdering.cls
Normal file
|
@ -0,0 +1,360 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_GraphOrdering"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ======= Graph sorting and ordering facilities =====
|
||||||
|
' Shared module version: 20210422
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: ex_Collection, CDS_Graph, API_LinkedComponents
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private graph_ As CDS_Graph
|
||||||
|
Private nodes_ As Collection
|
||||||
|
Private sourceOrder_ As Scripting.Dictionary ' Map of (nodeID, globalIndex)
|
||||||
|
Private nodeComponents_ As Scripting.Dictionary ' Map of (nodeID, componentIndex)
|
||||||
|
Private componentCount_ As Long
|
||||||
|
|
||||||
|
Private status_ As Scripting.Dictionary
|
||||||
|
Private ranks_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Private Const MAX_NODE_INDEX& = 100000
|
||||||
|
Private Const RANK_UNDEF = -1
|
||||||
|
|
||||||
|
Private Enum TVisitStatus
|
||||||
|
T_STATUS_UNVISITED = 0
|
||||||
|
T_STATUS_VISITING = 1
|
||||||
|
T_STATUS_VISITED = 2
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Function Init(iGraph As CDS_Graph)
|
||||||
|
Call SetGraph(iGraph)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Classic topological order (no edges pointing backward in order)
|
||||||
|
Public Function TopologicalOrder() As Collection
|
||||||
|
Set sourceOrder_ = Nothing
|
||||||
|
Set TopologicalOrder = RevertCollection(DefaultOrder)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Reversed topological order
|
||||||
|
Public Function ReverseTopologicalOrder() As Collection
|
||||||
|
Set sourceOrder_ = Nothing
|
||||||
|
Set ReverseTopologicalOrder = DefaultOrder
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Topological order where linked components are grouped together
|
||||||
|
Public Function TopologicalComponentOrder() As Collection
|
||||||
|
Set sourceOrder_ = Nothing
|
||||||
|
Dim iOrder As Collection: Set iOrder = RevertCollection(DefaultOrder)
|
||||||
|
Set TopologicalComponentOrder = GroupNodesDefault(iOrder)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Reversed topological order where linked components are grouped together
|
||||||
|
Public Function ReverseTopologicalComponentOrder() As Collection
|
||||||
|
Set sourceOrder_ = Nothing
|
||||||
|
Dim iOrder As Collection: Set iOrder = DefaultOrder
|
||||||
|
Set ReverseTopologicalComponentOrder = GroupNodesDefault(iOrder)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Sort topologically using BFS and ordering according to input
|
||||||
|
' Note: elements not present in the graph are excluded from output
|
||||||
|
Public Function SortLayers(iSourceOrder As Collection) As Collection
|
||||||
|
If iSourceOrder.Count = 0 Then
|
||||||
|
Set SortLayers = New Collection
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call InitSourceOrder(iSourceOrder)
|
||||||
|
Dim iNodesOrder As Collection: Set iNodesOrder = InduceWidthOrder
|
||||||
|
Dim iComponents As Collection: Set iComponents = SortComponents(iNodesOrder)
|
||||||
|
Set SortLayers = GroupNodesInduced(iNodesOrder, iComponents)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Sort topologically using DFS and ordering according to input
|
||||||
|
' Note: elements not present in the graph are excluded from output
|
||||||
|
Public Function SortDeep(iSourceOrder As Collection) As Collection
|
||||||
|
If iSourceOrder.Count = 0 Then
|
||||||
|
Set SortDeep = New Collection
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call InitSourceOrder(iSourceOrder)
|
||||||
|
Dim iNodesOrder As Collection: Set iNodesOrder = InduceDepthOrder
|
||||||
|
Dim iComponents As Collection: Set iComponents = SortComponents(iNodesOrder)
|
||||||
|
Set SortDeep = GroupNodesInduced(iNodesOrder, iComponents)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ==============
|
||||||
|
Private Function SetGraph(iGraph As CDS_Graph)
|
||||||
|
Set graph_ = iGraph
|
||||||
|
Set nodes_ = graph_.Nodes
|
||||||
|
Dim iProcessor As New API_LinkedComponents
|
||||||
|
Set nodeComponents_ = iProcessor.GetComponents(graph_)
|
||||||
|
componentCount_ = iProcessor.CountComponents
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function DefaultOrder()
|
||||||
|
Dim iOrder As New Collection
|
||||||
|
|
||||||
|
Call InitStatus
|
||||||
|
|
||||||
|
Dim iStack As Collection
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In nodes_
|
||||||
|
If status_(vNode) <> T_STATUS_UNVISITED Then _
|
||||||
|
GoTo NEXT_NODE
|
||||||
|
Set iStack = New Collection: Call iStack.Add(vNode)
|
||||||
|
Do While iStack.Count > 0
|
||||||
|
Dim vItem As Variant: vItem = iStack.Item(iStack.Count)
|
||||||
|
If status_(vItem) = T_STATUS_UNVISITED Then
|
||||||
|
status_(vItem) = T_STATUS_VISITING
|
||||||
|
Dim vChild As Variant
|
||||||
|
For Each vChild In graph_.nodes_(vItem).outputs_
|
||||||
|
If status_(vChild) = T_STATUS_UNVISITED Then _
|
||||||
|
Call iStack.Add(vChild)
|
||||||
|
Next vChild
|
||||||
|
Else
|
||||||
|
Call iStack.Remove(iStack.Count)
|
||||||
|
If status_(vItem) <> T_STATUS_VISITED Then
|
||||||
|
status_(vItem) = T_STATUS_VISITED
|
||||||
|
Call iOrder.Add(vItem)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
NEXT_NODE:
|
||||||
|
Next vNode
|
||||||
|
|
||||||
|
Set DefaultOrder = iOrder
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function InduceWidthOrder() As Collection
|
||||||
|
Dim iOrder As New Collection
|
||||||
|
|
||||||
|
Call InitRanks
|
||||||
|
|
||||||
|
Dim iQueue As Collection: Set iQueue = New Collection
|
||||||
|
Dim iKnown As New Scripting.Dictionary
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In CompleteInducedOrder
|
||||||
|
If ranks_(vNode) = 1 Then
|
||||||
|
Call iQueue.Add(vNode)
|
||||||
|
Call iKnown.Add(vNode, 0)
|
||||||
|
End If
|
||||||
|
Next vNode
|
||||||
|
|
||||||
|
Do While iQueue.Count > 0
|
||||||
|
vNode = iQueue.Item(1)
|
||||||
|
Call iQueue.Remove(1)
|
||||||
|
Call iOrder.Add(vNode)
|
||||||
|
|
||||||
|
If graph_.nodes_(vNode).outputs_.Count > 0 Then
|
||||||
|
Dim vChildren() As Variant: vChildren = ToArray(graph_.nodes_(vNode).outputs_)
|
||||||
|
Call SortChildren(vChildren)
|
||||||
|
Dim vChild As Variant
|
||||||
|
For Each vChild In vChildren
|
||||||
|
If Not iKnown.Exists(vChild) Then
|
||||||
|
If ranks_(vChild) = ranks_(vNode) + 1 Then
|
||||||
|
Call iQueue.Add(vChild)
|
||||||
|
Call iKnown.Add(vChild, 0)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Next vChild
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
|
||||||
|
Set InduceWidthOrder = iOrder
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function InduceDepthOrder() As Collection
|
||||||
|
Dim iOrder As New Collection
|
||||||
|
|
||||||
|
Call InitRanks
|
||||||
|
|
||||||
|
Dim iStack As Collection: Set iStack = New Collection
|
||||||
|
Dim iKnown As New Scripting.Dictionary
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In RevertCollection(CompleteInducedOrder)
|
||||||
|
If ranks_(vNode) = 1 Then
|
||||||
|
Call iStack.Add(vNode)
|
||||||
|
End If
|
||||||
|
Next vNode
|
||||||
|
|
||||||
|
Dim vChildren() As Variant
|
||||||
|
Dim vChild As Variant
|
||||||
|
Dim nChild&
|
||||||
|
Do While iStack.Count > 0
|
||||||
|
vNode = iStack.Item(iStack.Count)
|
||||||
|
Call iStack.Remove(iStack.Count)
|
||||||
|
If iKnown.Exists(vNode) Then _
|
||||||
|
GoTo NEXT_ITEM
|
||||||
|
|
||||||
|
Call iOrder.Add(vNode)
|
||||||
|
Call iKnown.Add(vNode, 0)
|
||||||
|
|
||||||
|
If graph_.nodes_(vNode).outputs_.Count > 0 Then
|
||||||
|
vChildren = ToArray(graph_.nodes_(vNode).outputs_)
|
||||||
|
Call SortChildren(vChildren)
|
||||||
|
For nChild = UBound(vChildren) To LBound(vChildren) Step -1
|
||||||
|
vChild = vChildren(nChild)
|
||||||
|
If Not iKnown.Exists(vChild) Then _
|
||||||
|
Call iStack.Add(vChild)
|
||||||
|
Next nChild
|
||||||
|
End If
|
||||||
|
NEXT_ITEM:
|
||||||
|
Loop
|
||||||
|
|
||||||
|
Set InduceDepthOrder = iOrder
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function InitStatus()
|
||||||
|
Set status_ = New Scripting.Dictionary
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In nodes_
|
||||||
|
status_(vNode) = T_STATUS_UNVISITED
|
||||||
|
Next vNode
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function InitRanks()
|
||||||
|
Call InitStatus
|
||||||
|
|
||||||
|
Set ranks_ = New Scripting.Dictionary
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In CompleteInducedOrder
|
||||||
|
ranks_(vNode) = RANK_UNDEF
|
||||||
|
Next vNode
|
||||||
|
|
||||||
|
For Each vNode In nodes_
|
||||||
|
Call PropagateRank(vNode, 1)
|
||||||
|
Next vNode
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function PropagateRank(vNode As Variant, nRank&)
|
||||||
|
If ranks_(vNode) >= nRank Then _
|
||||||
|
Exit Function
|
||||||
|
ranks_(vNode) = nRank
|
||||||
|
|
||||||
|
If graph_.nodes_(vNode).outputs_.Count = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
status_(vNode) = T_STATUS_VISITING
|
||||||
|
|
||||||
|
Dim vChildren() As Variant: vChildren = ToArray(graph_.nodes_(vNode).outputs_)
|
||||||
|
Call SortChildren(vChildren)
|
||||||
|
Dim vChild As Variant
|
||||||
|
For Each vChild In vChildren
|
||||||
|
If status_(vChild) <> T_STATUS_VISITING Then _
|
||||||
|
Call PropagateRank(vChild, nRank + 1)
|
||||||
|
Next vChild
|
||||||
|
|
||||||
|
status_(vNode) = T_STATUS_VISITED
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function InitSourceOrder(iSourceOrder As Collection)
|
||||||
|
Set sourceOrder_ = New Scripting.Dictionary
|
||||||
|
Dim aVal As Variant
|
||||||
|
Dim nIndex&: nIndex = 1
|
||||||
|
For Each aVal In iSourceOrder
|
||||||
|
If graph_.HasNode(aVal) Then
|
||||||
|
sourceOrder_(aVal) = nIndex
|
||||||
|
nIndex = nIndex + 1
|
||||||
|
End If
|
||||||
|
Next aVal
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CompleteInducedOrder() As Collection
|
||||||
|
Dim iOrder As New Collection
|
||||||
|
Dim vItem As Variant
|
||||||
|
For Each vItem In sourceOrder_
|
||||||
|
Call iOrder.Add(vItem)
|
||||||
|
Next vItem
|
||||||
|
|
||||||
|
If iOrder.Count = graph_.Size Then
|
||||||
|
Set CompleteInducedOrder = iOrder
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
For Each vItem In nodes_
|
||||||
|
If Not sourceOrder_.Exists(vItem) Then _
|
||||||
|
Call iOrder.Add(vItem)
|
||||||
|
Next vItem
|
||||||
|
|
||||||
|
Set CompleteInducedOrder = iOrder
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SortChildren(ByRef target() As Variant)
|
||||||
|
If UBound(target) < 1 Then _
|
||||||
|
Exit Function
|
||||||
|
Dim i&, j&
|
||||||
|
Dim vItem As Variant
|
||||||
|
For i = LBound(target) + 1 To UBound(target) Step 1
|
||||||
|
vItem = target(i)
|
||||||
|
Dim nIndex&: nIndex = GetNodeIndex(vItem)
|
||||||
|
j = i - 1
|
||||||
|
Do While j >= 0
|
||||||
|
If GetNodeIndex(target(j)) <= nIndex Then _
|
||||||
|
Exit Do
|
||||||
|
target(j + 1) = target(j)
|
||||||
|
j = j - 1
|
||||||
|
Loop
|
||||||
|
target(j + 1) = vItem
|
||||||
|
Next i
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GroupNodesDefault(iNodes As Collection) As Collection
|
||||||
|
Dim iResult As New Collection
|
||||||
|
Dim nComponent&
|
||||||
|
For nComponent = 0 To componentCount_ - 1 Step 1
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In iNodes
|
||||||
|
If Not sourceOrder_ Is Nothing Then _
|
||||||
|
If Not sourceOrder_.Exists(vNode) Then _
|
||||||
|
GoTo NEXT_NODE
|
||||||
|
If nodeComponents_(vNode) = nComponent Then _
|
||||||
|
Call iResult.Add(vNode)
|
||||||
|
NEXT_NODE:
|
||||||
|
Next vNode
|
||||||
|
Next nComponent
|
||||||
|
Set GroupNodesDefault = iResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GetNodeIndex(vNode As Variant) As Long
|
||||||
|
If sourceOrder_.Exists(vNode) Then
|
||||||
|
GetNodeIndex = sourceOrder_(vNode)
|
||||||
|
Else
|
||||||
|
GetNodeIndex = MAX_NODE_INDEX
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SortComponents(iNodesOrder As Collection) As Collection
|
||||||
|
Dim iOrder As New Collection
|
||||||
|
Dim nComponent&
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In iNodesOrder
|
||||||
|
nComponent = nodeComponents_(vNode)
|
||||||
|
Call SafeAddToCollection(nComponent, VBA.CStr(nComponent), iOrder)
|
||||||
|
Next vNode
|
||||||
|
Set SortComponents = iOrder
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GroupNodesInduced(iNodes As Collection, iComponents As Collection) As Collection
|
||||||
|
Dim iResult As New Collection
|
||||||
|
Dim nComponent As Variant
|
||||||
|
For Each nComponent In iComponents
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In iNodes
|
||||||
|
If Not sourceOrder_.Exists(vNode) Then _
|
||||||
|
GoTo NEXT_NODE
|
||||||
|
If nodeComponents_(vNode) = nComponent Then _
|
||||||
|
Call iResult.Add(vNode)
|
||||||
|
NEXT_NODE:
|
||||||
|
Next vNode
|
||||||
|
Next nComponent
|
||||||
|
Set GroupNodesInduced = iResult
|
||||||
|
End Function
|
||||||
|
|
618
utility/API_JSON.cls
Normal file
618
utility/API_JSON.cls
Normal file
|
@ -0,0 +1,618 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_JSON"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ======== JSON converter ========
|
||||||
|
' Shared module version: 20220813
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting
|
||||||
|
' This code is based on rethinking VBA-JSON, https://github.com/VBA-tools/VBA-JSON
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const ERR_PARSE_FAIL = vbObject + 2049
|
||||||
|
|
||||||
|
' JSON states esaping '/' as optional.
|
||||||
|
' Use this flag to escape them as '\/'
|
||||||
|
Public escapeSlash_ As Boolean
|
||||||
|
|
||||||
|
' JSON doesnt require escaping of non-AscII chars.
|
||||||
|
' Use this flag to enable \u codes escape mechanics
|
||||||
|
Public escapeUnicode_ As Boolean
|
||||||
|
|
||||||
|
Private multiline_ As Boolean
|
||||||
|
Private wsCharacter_ As String
|
||||||
|
Private wsCount_ As Long
|
||||||
|
|
||||||
|
Private json_ As String
|
||||||
|
Private inputLen_ As Long
|
||||||
|
Private position_ As Long
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
escapeSlash_ = False
|
||||||
|
escapeUnicode_ = False
|
||||||
|
Call DisableMultiline
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' {Integer|String} vWhitespace "Pretty" print json with given number of spaces per indentation (Integer) or given string
|
||||||
|
Public Function SetupMultiline(ByVal vWhitespace As Variant)
|
||||||
|
multiline_ = True
|
||||||
|
If VBA.VarType(vWhitespace) = VBA.vbString Then
|
||||||
|
wsCharacter_ = vWhitespace
|
||||||
|
wsCount_ = -1
|
||||||
|
Else
|
||||||
|
wsCharacter_ = vbNullString
|
||||||
|
wsCount_ = vWhitespace
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function DisableMultiline()
|
||||||
|
multiline_ = False
|
||||||
|
wsCharacter_ = vbNullString
|
||||||
|
wsCount_ = -1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Convert object (Dictionary/Collection/Array) to JSON
|
||||||
|
Public Function CreateJSON(ByRef vValue As Variant) As String
|
||||||
|
CreateJSON = Generator_Process(vValue)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Convert JSON string to object (Dictionary/Collection)
|
||||||
|
Public Function Parse(sJson$) As Object
|
||||||
|
json_ = VBA.Replace(VBA.Replace(VBA.Replace(sJson, VBA.vbCr, ""), VBA.vbLf, ""), VBA.vbTab, "")
|
||||||
|
position_ = 1
|
||||||
|
inputLen_ = VBA.Len(json_)
|
||||||
|
|
||||||
|
Call Parser_SkipWS
|
||||||
|
|
||||||
|
Select Case Parser_ViewAny()
|
||||||
|
Case "{": Set Parse = Parser_Object()
|
||||||
|
Case "[": Set Parse = Parser_Array()
|
||||||
|
Case Else: Call Parser_RaiseError("Expected '{' or '['")
|
||||||
|
End Select
|
||||||
|
|
||||||
|
If position_ <= inputLen_ Then _
|
||||||
|
Call Parser_RaiseError("Some part of input is out of main object scope")
|
||||||
|
|
||||||
|
json_ = vbNullString
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SaveToFile(sFile$, iData As Object) As Boolean
|
||||||
|
Const DO_OVERWRITE& = 2
|
||||||
|
|
||||||
|
SaveToFile = False
|
||||||
|
On Error GoTo EXIT_ERROR
|
||||||
|
|
||||||
|
Dim adoStream As Object: Set adoStream = CreateObject("ADODB.Stream")
|
||||||
|
adoStream.Charset = "utf-8"
|
||||||
|
Call adoStream.Open
|
||||||
|
Call adoStream.WriteText(CreateJSON(iData))
|
||||||
|
Call adoStream.SaveToFile(sFile, DO_OVERWRITE)
|
||||||
|
|
||||||
|
SaveToFile = True
|
||||||
|
|
||||||
|
EXIT_ERROR:
|
||||||
|
On Error GoTo 0
|
||||||
|
Call adoStream.Close
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function LoadFromFile(sFile$) As Object
|
||||||
|
Dim sJson$: sJson = ReadFile(sFile)
|
||||||
|
If sJson = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
Set LoadFromFile = Parse(sJson)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =============================================
|
||||||
|
Private Function ReadFile(sFile$) As String
|
||||||
|
Dim adoStream As Object: Set adoStream = CreateObject("ADODB.Stream")
|
||||||
|
adoStream.Charset = "utf-8"
|
||||||
|
Call adoStream.Open
|
||||||
|
|
||||||
|
On Error GoTo ERROR_FILE
|
||||||
|
Call adoStream.LoadFromFile(sFile)
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
ReadFile = adoStream.ReadText
|
||||||
|
|
||||||
|
ERROR_FILE:
|
||||||
|
Call adoStream.Close
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Generator_Process(ByRef vValue As Variant, Optional nIdent& = 0) As String
|
||||||
|
Select Case VBA.VarType(vValue)
|
||||||
|
Case VBA.vbNull: Generator_Process = "null"
|
||||||
|
Case VBA.vbBoolean: Generator_Process = IIf(vValue, "true", "false")
|
||||||
|
Case VBA.vbDate: Generator_Process = """" & CDbl(vValue) & """"
|
||||||
|
Case VBA.vbString
|
||||||
|
If IsLargeNumber(vValue) Then
|
||||||
|
Generator_Process = vValue ' Large number encoded as string
|
||||||
|
Else
|
||||||
|
Generator_Process = """" & EncodeStr(vValue) & """"
|
||||||
|
End If
|
||||||
|
|
||||||
|
Case VBA.vbArray To VBA.vbArray + VBA.vbByte: Generator_Process = Generator_Array(vValue, nIdent)
|
||||||
|
|
||||||
|
Case VBA.vbObject
|
||||||
|
If VBA.TypeName(vValue) = "Dictionary" Then
|
||||||
|
Generator_Process = Generator_Dictionary(vValue, nIdent)
|
||||||
|
ElseIf VBA.TypeName(vValue) = "Collection" Then
|
||||||
|
Generator_Process = Generator_Collection(vValue, nIdent)
|
||||||
|
Else ' TODO: call custom object function to generate JSON!
|
||||||
|
Call Err.Raise(1000, Description:="Unknown object type. Please use Dictionary or Collection")
|
||||||
|
End If
|
||||||
|
|
||||||
|
Case VBA.vbInteger, VBA.vbLong, VBA.vbSingle, VBA.vbDouble, VBA.vbCurrency, VBA.vbDecimal
|
||||||
|
Generator_Process = VBA.Replace(vValue, ",", ".") ' Number (use decimals for numbers)
|
||||||
|
Case Else ' vbEmpty, vbError, vbDataObject, vbByte, vbUserDefinedType
|
||||||
|
On Error Resume Next
|
||||||
|
Generator_Process = vValue
|
||||||
|
On Error GoTo 0
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Generator_Array(ByRef vValue As Variant, nIdent&) As String
|
||||||
|
Dim sBuffer$
|
||||||
|
Dim nBufferPosition&
|
||||||
|
Dim nBufferLen&
|
||||||
|
Call BufferAppend(sBuffer, "[", nBufferPosition, nBufferLen)
|
||||||
|
|
||||||
|
Dim nSize1&: nSize1 = ArraySize(vValue, 1)
|
||||||
|
If nSize1 <= 0 Then _
|
||||||
|
GoTo PRINT_TO_STR
|
||||||
|
Dim nIndex1Start&: nIndex1Start = LBound(vValue, 1)
|
||||||
|
|
||||||
|
Dim nSize2&: nSize2 = ArraySize(vValue, 2)
|
||||||
|
Dim nIndex2Start&
|
||||||
|
If nSize2 > 0 Then _
|
||||||
|
nIndex2Start = LBound(vValue, 2)
|
||||||
|
|
||||||
|
Dim sIndentLevel1$: sIndentLevel1 = GetWS(nIdent + 1)
|
||||||
|
Dim sIndentLevel2$: sIndentLevel2 = GetWS(nIdent + 2)
|
||||||
|
|
||||||
|
Dim bIsFirst: bIsFirst = True
|
||||||
|
Dim json_Converted$
|
||||||
|
Dim nIndex1&
|
||||||
|
For nIndex1 = nIndex1Start To nIndex1Start + nSize1 - 1 Step 1
|
||||||
|
If Not bIsFirst Then
|
||||||
|
Call BufferAppend(sBuffer, ",", nBufferPosition, nBufferLen)
|
||||||
|
Else
|
||||||
|
bIsFirst = False
|
||||||
|
End If
|
||||||
|
|
||||||
|
If nSize2 <= 0 Then ' 1D Array
|
||||||
|
json_Converted = Generator_Nullable(vValue(nIndex1), nIdent + 1)
|
||||||
|
If multiline_ Then _
|
||||||
|
json_Converted = vbNewLine & sIndentLevel1 & json_Converted
|
||||||
|
Call BufferAppend(sBuffer, json_Converted, nBufferPosition, nBufferLen)
|
||||||
|
Else ' 2D Array
|
||||||
|
If multiline_ Then _
|
||||||
|
Call BufferAppend(sBuffer, vbNewLine, nBufferPosition, nBufferLen)
|
||||||
|
Call BufferAppend(sBuffer, sIndentLevel1 & "[", nBufferPosition, nBufferLen)
|
||||||
|
|
||||||
|
Dim nIndex2&
|
||||||
|
Dim bIsFirst2 As Boolean: bIsFirst2 = True
|
||||||
|
For nIndex2 = nIndex2Start To nIndex2Start + nSize2 - 1 Step 1
|
||||||
|
If Not bIsFirst2 Then
|
||||||
|
Call BufferAppend(sBuffer, ",", nBufferPosition, nBufferLen)
|
||||||
|
Else
|
||||||
|
bIsFirst2 = False
|
||||||
|
End If
|
||||||
|
json_Converted = Generator_Nullable(vValue(nIndex1, nIndex2), nIdent + 2)
|
||||||
|
If multiline_ Then _
|
||||||
|
json_Converted = vbNewLine & sIndentLevel2 & json_Converted
|
||||||
|
Call BufferAppend(sBuffer, json_Converted, nBufferPosition, nBufferLen)
|
||||||
|
Next nIndex2
|
||||||
|
|
||||||
|
If multiline_ Then _
|
||||||
|
Call BufferAppend(sBuffer, vbNewLine, nBufferPosition, nBufferLen)
|
||||||
|
Call BufferAppend(sBuffer, sIndentLevel1 & "]", nBufferPosition, nBufferLen)
|
||||||
|
End If
|
||||||
|
Next nIndex1
|
||||||
|
|
||||||
|
PRINT_TO_STR:
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If multiline_ Then
|
||||||
|
Call BufferAppend(sBuffer, vbNewLine & GetWS(nIdent) & "]", nBufferPosition, nBufferLen)
|
||||||
|
Else
|
||||||
|
Call BufferAppend(sBuffer, "]", nBufferPosition, nBufferLen)
|
||||||
|
End If
|
||||||
|
Generator_Array = BufferToString(sBuffer, nBufferPosition)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Generator_Collection(ByRef vValue As Variant, nIdent&) As String
|
||||||
|
Dim sBuffer$
|
||||||
|
Dim nBufferPosition&
|
||||||
|
Dim nBufferLen&
|
||||||
|
Call BufferAppend(sBuffer, "[", nBufferPosition, nBufferLen)
|
||||||
|
|
||||||
|
Dim bIsFirst As Boolean: bIsFirst = True
|
||||||
|
Dim anElement As Variant
|
||||||
|
For Each anElement In vValue
|
||||||
|
If Not bIsFirst Then
|
||||||
|
Call BufferAppend(sBuffer, ",", nBufferPosition, nBufferLen)
|
||||||
|
Else
|
||||||
|
bIsFirst = False
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim json_Converted$: json_Converted = Generator_Nullable(anElement, nIdent + 1)
|
||||||
|
If multiline_ Then _
|
||||||
|
json_Converted = vbNewLine & GetWS(nIdent + 1) & json_Converted
|
||||||
|
Call BufferAppend(sBuffer, json_Converted, nBufferPosition, nBufferLen)
|
||||||
|
Next anElement
|
||||||
|
|
||||||
|
If multiline_ Then
|
||||||
|
Call BufferAppend(sBuffer, vbNewLine & GetWS(nIdent) & "]", nBufferPosition, nBufferLen)
|
||||||
|
Else
|
||||||
|
Call BufferAppend(sBuffer, "]", nBufferPosition, nBufferLen)
|
||||||
|
End If
|
||||||
|
Generator_Collection = BufferToString(sBuffer, nBufferPosition)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Generator_Dictionary(ByRef vValue As Variant, nIdent&) As String
|
||||||
|
Dim sBuffer$
|
||||||
|
Dim nBufferPosition&
|
||||||
|
Dim nBufferLen&
|
||||||
|
Call BufferAppend(sBuffer, "{", nBufferPosition, nBufferLen)
|
||||||
|
|
||||||
|
Dim bIsFirst As Boolean: bIsFirst = True
|
||||||
|
Dim aKey As Variant
|
||||||
|
For Each aKey In vValue.Keys
|
||||||
|
Dim json_Converted$: json_Converted = Generator_Process(vValue(aKey), nIdent + 1)
|
||||||
|
If json_Converted = vbNullString Then _
|
||||||
|
If IsUndefined(vValue(aKey)) Then _
|
||||||
|
GoTo NEXT_KEY ' For Objects, undefined (Empty/Nothing) is not added to object
|
||||||
|
|
||||||
|
If Not bIsFirst Then
|
||||||
|
Call BufferAppend(sBuffer, ",", nBufferPosition, nBufferLen)
|
||||||
|
Else
|
||||||
|
bIsFirst = False
|
||||||
|
End If
|
||||||
|
|
||||||
|
If multiline_ Then
|
||||||
|
json_Converted = vbNewLine & GetWS(nIdent + 1) & """" & aKey & """: " & json_Converted
|
||||||
|
Else
|
||||||
|
json_Converted = """" & aKey & """:" & json_Converted
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call BufferAppend(sBuffer, json_Converted, nBufferPosition, nBufferLen)
|
||||||
|
NEXT_KEY:
|
||||||
|
Next aKey
|
||||||
|
|
||||||
|
If multiline_ Then
|
||||||
|
Call BufferAppend(sBuffer, vbNewLine & GetWS(nIdent) & "}", nBufferPosition, nBufferLen)
|
||||||
|
Else
|
||||||
|
Call BufferAppend(sBuffer, "}", nBufferPosition, nBufferLen)
|
||||||
|
End If
|
||||||
|
Generator_Dictionary = BufferToString(sBuffer, nBufferPosition)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Generator_Nullable(ByVal vValue As Variant, nIdent&)
|
||||||
|
Generator_Nullable = Generator_Process(vValue, nIdent)
|
||||||
|
If Generator_Nullable = vbNullString Then _
|
||||||
|
If IsUndefined(vValue) Then _
|
||||||
|
Generator_Nullable = "null"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Parser_Object() As Scripting.Dictionary
|
||||||
|
If Parser_ConsumeChar() <> "{" Then _
|
||||||
|
Call Parser_RaiseError("Expected '{'")
|
||||||
|
|
||||||
|
Set Parser_Object = New Scripting.Dictionary
|
||||||
|
Do
|
||||||
|
Call Parser_SkipWS
|
||||||
|
Dim sNextChar$: sNextChar = Parser_ViewChar()
|
||||||
|
If sNextChar = "}" Then
|
||||||
|
Call Parser_Increment
|
||||||
|
Exit Function
|
||||||
|
ElseIf sNextChar = "," Then
|
||||||
|
Call Parser_Increment
|
||||||
|
Call Parser_SkipWS
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim sKey$: sKey = Parser_Key()
|
||||||
|
If Parser_ConsumeChar(1) <> ":" Then _
|
||||||
|
Call Parser_RaiseError("Expected ':'")
|
||||||
|
sNextChar = Parser_ViewChar()
|
||||||
|
If sNextChar = "[" Or sNextChar = "{" Then
|
||||||
|
Set Parser_Object.Item(sKey) = Parser_Value()
|
||||||
|
Else
|
||||||
|
Parser_Object.Item(sKey) = Parser_Value()
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Parser_Array() As Collection
|
||||||
|
If Parser_ConsumeChar() <> "[" Then _
|
||||||
|
Call Parser_RaiseError("Expected '['")
|
||||||
|
|
||||||
|
Set Parser_Array = New Collection
|
||||||
|
Do
|
||||||
|
Call Parser_SkipWS
|
||||||
|
Dim sChar$: sChar = Parser_ViewChar(1)
|
||||||
|
If sChar = "]" Then
|
||||||
|
Call Parser_Increment
|
||||||
|
Exit Function
|
||||||
|
ElseIf sChar = "," Then
|
||||||
|
Call Parser_Increment
|
||||||
|
End If
|
||||||
|
Call Parser_Array.Add(Parser_Value())
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Parser_Value() As Variant
|
||||||
|
Call Parser_SkipWS
|
||||||
|
Dim sValue$: sValue = Parser_ViewAny(1)
|
||||||
|
Select Case sValue
|
||||||
|
Case "{": Set Parser_Value = Parser_Object()
|
||||||
|
Case "[": Set Parser_Value = Parser_Array()
|
||||||
|
Case """", "'": Parser_Value = Parser_String()
|
||||||
|
Case Else
|
||||||
|
If VBA.InStr("+-0123456789", sValue) Then
|
||||||
|
Parser_Value = Parser_Numbers()
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
If inputLen_ - position_ + 1 > 4 Then
|
||||||
|
sValue = Parser_ViewAny(4)
|
||||||
|
If sValue = "true" Then
|
||||||
|
Parser_Value = True
|
||||||
|
Call Parser_Increment(4)
|
||||||
|
Exit Function
|
||||||
|
ElseIf sValue = "null" Then
|
||||||
|
Parser_Value = Null
|
||||||
|
Call Parser_Increment(4)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
If inputLen_ - position_ + 1 > 5 Then
|
||||||
|
sValue = Parser_ViewAny(5)
|
||||||
|
If sValue = "false" Then
|
||||||
|
Parser_Value = False
|
||||||
|
Call Parser_Increment(5)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call Parser_RaiseError("Expected 'STRING', 'NUMBER', null, true, false, '{', or '['")
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Parser_String() As String
|
||||||
|
Dim sBuffer$
|
||||||
|
Dim nBufferPosition&
|
||||||
|
Dim nBufferLen&
|
||||||
|
|
||||||
|
Dim sChar$
|
||||||
|
Dim json_Quote$: json_Quote = Parser_ConsumeChar()
|
||||||
|
Do While position_ <= inputLen_
|
||||||
|
sChar = Parser_ConsumeAny(1)
|
||||||
|
If sChar = json_Quote Then
|
||||||
|
Parser_String = BufferToString(sBuffer, nBufferPosition)
|
||||||
|
Exit Function
|
||||||
|
ElseIf sChar = "\" Then
|
||||||
|
' Escaped string, \\, or \/
|
||||||
|
sChar = Parser_ConsumeAny(1)
|
||||||
|
Select Case sChar
|
||||||
|
Case """", "\", "/", "'": Call BufferAppend(sBuffer, sChar, nBufferPosition, nBufferLen)
|
||||||
|
Case "b": Call BufferAppend(sBuffer, vbBack, nBufferPosition, nBufferLen)
|
||||||
|
Case "f": Call BufferAppend(sBuffer, vbFormFeed, nBufferPosition, nBufferLen)
|
||||||
|
Case "n": Call BufferAppend(sBuffer, vbCrLf, nBufferPosition, nBufferLen)
|
||||||
|
Case "r": Call BufferAppend(sBuffer, vbCr, nBufferPosition, nBufferLen)
|
||||||
|
Case "t": Call BufferAppend(sBuffer, vbTab, nBufferPosition, nBufferLen)
|
||||||
|
Case "u"
|
||||||
|
Dim sUTF8Char$: sUTF8Char = Parser_ViewAny(4)
|
||||||
|
Call BufferAppend(sBuffer, VBA.ChrW(VBA.val("&h" + sUTF8Char)), nBufferPosition, nBufferLen)
|
||||||
|
Call Parser_Increment(4)
|
||||||
|
Case Else: Call Parser_Increment(-1)
|
||||||
|
End Select
|
||||||
|
Else
|
||||||
|
Call BufferAppend(sBuffer, sChar, nBufferPosition, nBufferLen)
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Parser_Numbers() As Variant
|
||||||
|
Call Parser_SkipWS
|
||||||
|
Dim sChar$
|
||||||
|
Dim sValue$: sValue = vbNullString
|
||||||
|
Do While position_ <= inputLen_
|
||||||
|
sChar = Parser_ViewAny(1)
|
||||||
|
If VBA.InStr("+-0123456789.eE", sChar) Then
|
||||||
|
sValue = sValue & sChar
|
||||||
|
Call Parser_Increment(1)
|
||||||
|
Else
|
||||||
|
' Fix: Parse -> String, Convert -> String longer than 15/16 characters containing only numbers and decimal points -> Number
|
||||||
|
Dim bIsLarge As Boolean: bIsLarge = IIf(VBA.InStr(sValue, "."), VBA.Len(sValue) >= 17, VBA.Len(sValue) >= 16)
|
||||||
|
If bIsLarge Then
|
||||||
|
Parser_Numbers = sValue
|
||||||
|
Else
|
||||||
|
Parser_Numbers = VBA.val(sValue)
|
||||||
|
End If
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Parser_Key() As String
|
||||||
|
Dim sChar$: sChar = Parser_ViewAny(1)
|
||||||
|
If sChar = """" Or sChar = "'" Then
|
||||||
|
Parser_Key = Parser_String
|
||||||
|
Else
|
||||||
|
Call Parser_RaiseError("Expected '""' or '''")
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' View nChars from input
|
||||||
|
Private Function Parser_ViewAny(Optional nChars& = 1) As String
|
||||||
|
If position_ + nChars - 1 > inputLen_ Then _
|
||||||
|
Call Parser_RaiseError("Unexpected end of input")
|
||||||
|
Parser_ViewAny = VBA.Mid$(json_, position_, nChars)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' View nChar symboms (skipping spaces)
|
||||||
|
Private Function Parser_ViewChar(Optional nChars& = 1) As String
|
||||||
|
Dim nPosition&: nPosition = position_
|
||||||
|
Do While nPosition > 0 And nPosition <= inputLen_ And VBA.Mid$(json_, nPosition, 1) = " "
|
||||||
|
nPosition = nPosition + 1
|
||||||
|
Loop
|
||||||
|
If nPosition + nChars - 1 > inputLen_ Then _
|
||||||
|
Call Parser_RaiseError("Unexpected end of input")
|
||||||
|
Parser_ViewChar = VBA.Mid$(json_, nPosition, nChars)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Consumne nChars symbols from input
|
||||||
|
Private Function Parser_ConsumeAny(Optional nChars& = 1) As String
|
||||||
|
If position_ + nChars - 1 > inputLen_ Then _
|
||||||
|
Call Parser_RaiseError("Unexpected end of input")
|
||||||
|
Parser_ConsumeAny = VBA.Mid$(json_, position_, nChars)
|
||||||
|
Call Parser_Increment(nChars)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Consume nChars from input (skipping spaces)
|
||||||
|
Private Function Parser_ConsumeChar(Optional nChars& = 1) As String
|
||||||
|
Call Parser_SkipWS
|
||||||
|
If position_ + nChars - 1 > inputLen_ Then _
|
||||||
|
Call Parser_RaiseError("Unexpected end of input")
|
||||||
|
Parser_ConsumeChar = VBA.Mid$(json_, position_, nChars)
|
||||||
|
Call Parser_Increment(nChars)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Parser_Increment(Optional nIncrement& = 1)
|
||||||
|
position_ = position_ + nIncrement
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Parser_SkipWS()
|
||||||
|
Do While position_ <= inputLen_ And VBA.Mid$(json_, position_, 1) = " "
|
||||||
|
Call Parser_Increment
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Parser_RaiseError(sMessage$)
|
||||||
|
Dim nStart&: nStart = position_ - 10
|
||||||
|
If nStart <= 0 Then _
|
||||||
|
nStart = 1
|
||||||
|
|
||||||
|
Dim nEnd&: nEnd = position_ + 10
|
||||||
|
If nEnd > inputLen_ Then _
|
||||||
|
nEnd = inputLen_
|
||||||
|
|
||||||
|
Dim sErrorMsg$: sErrorMsg = "Error parsing JSON:" & VBA.vbNewLine & _
|
||||||
|
VBA.Mid$(json_, nStart, nEnd - nStart + 1) & VBA.vbNewLine & _
|
||||||
|
VBA.Space$(position_ - nStart) & "^" & VBA.vbNewLine & _
|
||||||
|
sMessage
|
||||||
|
Call Err.Raise(ERR_PARSE_FAIL, "JSONConverter", sErrorMsg)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ArraySize(target As Variant, Optional nDimension% = 1) As Long
|
||||||
|
On Error GoTo INVALID_SIZE
|
||||||
|
|
||||||
|
ArraySize = UBound(target, nDimension) - LBound(target, nDimension) + 1
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INVALID_SIZE:
|
||||||
|
ArraySize = -1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IsUndefined(ByVal vValue As Variant) As Boolean
|
||||||
|
' Empty / Nothing -> undefined
|
||||||
|
Select Case VBA.VarType(vValue)
|
||||||
|
Case VBA.vbEmpty
|
||||||
|
IsUndefined = True
|
||||||
|
Case VBA.vbObject
|
||||||
|
Select Case VBA.TypeName(vValue)
|
||||||
|
Case "Empty", "Nothing"
|
||||||
|
IsUndefined = True
|
||||||
|
End Select
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function EncodeStr(ByVal sText As Variant) As String
|
||||||
|
Dim sBuffer$: sBuffer = vbNullString
|
||||||
|
Dim nBufferPosition&
|
||||||
|
Dim nBufferLen&
|
||||||
|
|
||||||
|
Dim sChar$
|
||||||
|
Dim nCharCode&
|
||||||
|
Dim nPosition&
|
||||||
|
For nPosition = 1 To VBA.Len(sText)
|
||||||
|
sChar = VBA.Mid$(sText, nPosition, 1)
|
||||||
|
nCharCode = VBA.AscW(sChar)
|
||||||
|
|
||||||
|
' Convert negative complement into char code, see: https://support.microsoft.com/en-us/kb/272138
|
||||||
|
If nCharCode < 0 Then _
|
||||||
|
nCharCode = nCharCode + 65536
|
||||||
|
|
||||||
|
Select Case nCharCode
|
||||||
|
Case 34: sChar = "\"""
|
||||||
|
Case 92: sChar = "\\"
|
||||||
|
Case 47: If escapeSlash_ Then sChar = "\/"
|
||||||
|
Case 8: sChar = "\b"
|
||||||
|
Case 12: sChar = "\f"
|
||||||
|
Case 10: sChar = "\n"
|
||||||
|
Case 13: sChar = "\r"
|
||||||
|
Case 9: sChar = "\t"
|
||||||
|
Case 0 To 31, 127 To 65535: If escapeUnicode_ Then sChar = "\u" & VBA.Right$("0000" & VBA.Hex$(nCharCode), 4)
|
||||||
|
End Select
|
||||||
|
|
||||||
|
Call BufferAppend(sBuffer, sChar, nBufferPosition, nBufferLen)
|
||||||
|
Next nPosition
|
||||||
|
|
||||||
|
EncodeStr = BufferToString(sBuffer, nBufferPosition)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IsLargeNumber(sJson As Variant) As Boolean
|
||||||
|
Dim nLength&: nLength = VBA.Len(sJson)
|
||||||
|
If nLength < 16 Or nLength >= 100 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
IsLargeNumber = True
|
||||||
|
Dim nIndex&
|
||||||
|
For nIndex = 1 To nLength Step 1
|
||||||
|
Select Case VBA.Asc(VBA.Mid$(sJson, nIndex, 1))
|
||||||
|
Case 46, 48 To 57, 69, 101 ' Look for .|0-9|E|e
|
||||||
|
' Continue through characters
|
||||||
|
Case Else
|
||||||
|
IsLargeNumber = False
|
||||||
|
Exit Function
|
||||||
|
End Select
|
||||||
|
Next nIndex
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GetWS(nIdent&) As String
|
||||||
|
If Not multiline_ Then _
|
||||||
|
Exit Function
|
||||||
|
If wsCount_ <> -1 Then
|
||||||
|
GetWS = VBA.Space$(nIdent * wsCount_)
|
||||||
|
Else
|
||||||
|
GetWS = VBA.String$(nIdent, wsCharacter_)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function BufferAppend(ByRef sBuffer$, ByRef vAppendage As Variant, ByRef nBufferPosition&, ByRef nBufferLen&)
|
||||||
|
' Allocate a large empty string and then copy string at append position instead of allocating for each append
|
||||||
|
Dim nAppendLen&: nAppendLen = VBA.Len(vAppendage)
|
||||||
|
|
||||||
|
If nAppendLen + nBufferPosition > nBufferLen Then
|
||||||
|
Dim nAddedLength&: nAddedLength = IIf(nAppendLen > nBufferLen, nAppendLen, nBufferLen)
|
||||||
|
sBuffer = sBuffer & VBA.Space$(nAddedLength)
|
||||||
|
nBufferLen = nBufferLen + nAddedLength
|
||||||
|
End If
|
||||||
|
|
||||||
|
' Access and modify buffer using Mid$
|
||||||
|
Mid$(sBuffer, nBufferPosition + 1, nAppendLen) = CStr(vAppendage)
|
||||||
|
nBufferPosition = nBufferPosition + nAppendLen
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function BufferToString(ByRef sBuffer$, nBufferPosition&) As String
|
||||||
|
If nBufferPosition > 0 Then _
|
||||||
|
BufferToString = VBA.Left$(sBuffer, nBufferPosition)
|
||||||
|
End Function
|
61
utility/API_LinkedComponents.cls
Normal file
61
utility/API_LinkedComponents.cls
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_LinkedComponents"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ======= Graph linked components =====
|
||||||
|
' Shared module version: 20210411
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: CDS_Graph
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private graph_ As CDS_Graph
|
||||||
|
Private maxId_ As Long
|
||||||
|
Private components_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Public Property Get CountComponents() As Long
|
||||||
|
CountComponents = maxId_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function GetComponents(iGraph As CDS_Graph) As Scripting.Dictionary
|
||||||
|
maxId_ = 0
|
||||||
|
Set components_ = New Scripting.Dictionary
|
||||||
|
Set graph_ = iGraph
|
||||||
|
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In graph_.Nodes
|
||||||
|
If Not components_.Exists(vNode) Then
|
||||||
|
Call VisitNode(vNode)
|
||||||
|
maxId_ = maxId_ + 1
|
||||||
|
End If
|
||||||
|
Next vNode
|
||||||
|
Set GetComponents = components_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =========
|
||||||
|
Private Function VisitNode(vNode As Variant)
|
||||||
|
If components_.Exists(vNode) Then _
|
||||||
|
Exit Function
|
||||||
|
Call components_.Add(vNode, maxId_)
|
||||||
|
Call VisitParents(vNode)
|
||||||
|
Call VisitChildren(vNode)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function VisitParents(vNode As Variant)
|
||||||
|
Dim vParent As Variant
|
||||||
|
For Each vParent In graph_.nodes_(vNode).inputs_
|
||||||
|
Call VisitNode(vParent)
|
||||||
|
Next vParent
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function VisitChildren(vNode As Variant)
|
||||||
|
Dim vParent As Variant
|
||||||
|
For Each vParent In graph_.nodes_(vNode).outputs_
|
||||||
|
Call VisitNode(vParent)
|
||||||
|
Next vParent
|
||||||
|
End Function
|
101
utility/API_StrongComponents.cls
Normal file
101
utility/API_StrongComponents.cls
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_StrongComponents"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ===== Graph strongly linked components ====
|
||||||
|
' Shared module version: 20220408
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: CDS_Graph
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private graph_ As CDS_Graph
|
||||||
|
Private components_ As Collection
|
||||||
|
Private current_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Private visited_ As Scripting.Dictionary
|
||||||
|
Private reversedOrder_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Public Function GetComponents(iGraph As CDS_Graph) As Collection
|
||||||
|
Set graph_ = iGraph
|
||||||
|
Set components_ = New Collection
|
||||||
|
|
||||||
|
Call CalculateOrder
|
||||||
|
Call ScanComponents
|
||||||
|
|
||||||
|
Set GetComponents = components_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =============
|
||||||
|
Private Function CalculateOrder()
|
||||||
|
Set visited_ = New Scripting.Dictionary
|
||||||
|
Set reversedOrder_ = New Scripting.Dictionary
|
||||||
|
|
||||||
|
Dim vNode As Variant
|
||||||
|
For Each vNode In graph_.Nodes
|
||||||
|
If Not visited_.Exists(vNode) Then
|
||||||
|
Call VisitInversed(vNode)
|
||||||
|
End If
|
||||||
|
Next vNode
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ScanComponents()
|
||||||
|
Set visited_ = New Scripting.Dictionary
|
||||||
|
Dim nItem&
|
||||||
|
Dim vItems As Variant: vItems = reversedOrder_.Keys()
|
||||||
|
For nItem = UBound(vItems, 1) To LBound(vItems, 1) Step -1
|
||||||
|
Dim nodeID As Variant: nodeID = vItems(nItem)
|
||||||
|
If visited_.Exists(nodeID) Then _
|
||||||
|
GoTo NEXT_NODE
|
||||||
|
|
||||||
|
Set current_ = New Scripting.Dictionary
|
||||||
|
Call VisitNode(nodeID)
|
||||||
|
If current_.Count = 1 Then
|
||||||
|
If Not graph_.HasEdge(nodeID, nodeID) Then _
|
||||||
|
GoTo NEXT_NODE
|
||||||
|
End If
|
||||||
|
Call components_.Add(current_)
|
||||||
|
NEXT_NODE:
|
||||||
|
Next nItem
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function VisitInversed(nodeID As Variant)
|
||||||
|
Call visited_.Add(nodeID, 0)
|
||||||
|
Call VisitChildrenInversed(nodeID)
|
||||||
|
Call reversedOrder_.Add(nodeID, 0)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function VisitChildrenInversed(nodeID As Variant)
|
||||||
|
If graph_.nodes_(nodeID).inputs_.Count = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim parentID As Variant
|
||||||
|
For Each parentID In graph_.nodes_(nodeID).inputs_
|
||||||
|
If Not visited_.Exists(parentID) Then _
|
||||||
|
Call VisitInversed(parentID)
|
||||||
|
Next parentID
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function VisitNode(nodeID As Variant)
|
||||||
|
Call visited_.Add(nodeID, 0)
|
||||||
|
Call current_.Add(nodeID, 0)
|
||||||
|
Call VisitChildren(nodeID)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function VisitChildren(nodeID As Variant)
|
||||||
|
If graph_.nodes_(nodeID).outputs_.Count = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim childID As Variant
|
||||||
|
For Each childID In graph_.nodes_(nodeID).outputs_
|
||||||
|
If Not visited_.Exists(childID) Then _
|
||||||
|
Call VisitNode(childID)
|
||||||
|
Next childID
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
63
utility/API_Timer.cls
Normal file
63
utility/API_Timer.cls
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_Timer"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
'=========== Precise timer wrapper =============
|
||||||
|
' Shared module version: 20210528
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Type BigInt
|
||||||
|
lowPart As Long
|
||||||
|
highPart As Long
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As BigInt) As Long
|
||||||
|
Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As BigInt) As Long
|
||||||
|
|
||||||
|
Private Const TWO_32 = 4294967296#
|
||||||
|
|
||||||
|
Private start_ As BigInt
|
||||||
|
Private finish_ As BigInt
|
||||||
|
Private frequency_ As Double
|
||||||
|
|
||||||
|
Public Function Start()
|
||||||
|
Call QueryPerformanceCounter(start_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Time elapsed in milliseconds
|
||||||
|
Public Property Get TimeElapsed() As Double
|
||||||
|
Dim crStart As Double
|
||||||
|
Dim crStop As Double
|
||||||
|
QueryPerformanceCounter finish_
|
||||||
|
crStart = BigInt2Double(start_)
|
||||||
|
crStop = BigInt2Double(finish_)
|
||||||
|
TimeElapsed = 1000# * (crStop - crStart) / frequency_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get TimeStr() As String
|
||||||
|
TimeStr = VBA.Format(TimeElapsed, "# ##0.###") & " ms"
|
||||||
|
End Property
|
||||||
|
|
||||||
|
' ====
|
||||||
|
Private Function BigInt2Double(theInt As BigInt) As Double
|
||||||
|
Dim lowPart As Double
|
||||||
|
lowPart = theInt.lowPart
|
||||||
|
If lowPart < 0 Then
|
||||||
|
lowPart = lowPart + TWO_32
|
||||||
|
End If
|
||||||
|
BigInt2Double = theInt.highPart * TWO_32 + lowPart
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Dim freq As BigInt
|
||||||
|
Call QueryPerformanceFrequency(freq)
|
||||||
|
frequency_ = BigInt2Double(freq)
|
||||||
|
End Sub
|
67
utility/CDS_CompoundIntervals.cls
Normal file
67
utility/CDS_CompoundIntervals.cls
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_CompoundIntervals"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' === Óïîðÿäî÷åííûé íàáîð íåïåðåñåêàþùèõñÿ èíòåðâàëîâ ====
|
||||||
|
' Shared module version: 20210511
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: CDS_Interval
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public items_ As Collection ' of CDS_Interval
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Call Clear
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Property Get Count() As Long
|
||||||
|
Count = items_.Count
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get IsEmpty() As Boolean
|
||||||
|
IsEmpty = Count = 0
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function AddItem(nStart&, nFinish&)
|
||||||
|
If nFinish < nStart Then _
|
||||||
|
Exit Function
|
||||||
|
Call AddInternal(CreateInterval(nStart, nFinish))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Clear()
|
||||||
|
Set items_ = New Collection
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =======
|
||||||
|
Private Function AddInternal(ByRef newItem As CDS_Interval)
|
||||||
|
Dim nItem&: nItem = 1
|
||||||
|
Dim nextItem As CDS_Interval
|
||||||
|
For Each nextItem In items_
|
||||||
|
If nextItem.start_ > newItem.finish_ Then
|
||||||
|
Call items_.Add(newItem, Before:=nItem)
|
||||||
|
Exit Function
|
||||||
|
ElseIf nextItem.finish_ >= newItem.finish_ Then
|
||||||
|
If nextItem.start_ > newItem.start_ Then _
|
||||||
|
nextItem.start_ = newItem.start_
|
||||||
|
Exit Function
|
||||||
|
ElseIf nextItem.finish_ >= newItem.start_ Then
|
||||||
|
newItem.start_ = nextItem.start_
|
||||||
|
Call items_.Remove(nItem)
|
||||||
|
Call AddInternal(newItem)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
nItem = nItem + 1
|
||||||
|
Next nextItem
|
||||||
|
Call items_.Add(newItem)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CreateInterval(nStart&, nFinish&) As CDS_Interval
|
||||||
|
Dim iNewItem As New CDS_Interval: Call iNewItem.Init(nStart, nFinish)
|
||||||
|
Set CreateInterval = iNewItem
|
||||||
|
End Function
|
48
utility/CDS_Edge.cls
Normal file
48
utility/CDS_Edge.cls
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_Edge"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ========== Class Module for Graph edge ========
|
||||||
|
' Shared module version: 20210410
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: ex_VBA
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public source_ As Variant
|
||||||
|
Public dest_ As Variant
|
||||||
|
|
||||||
|
Public Function Init(vSource As Variant, vDestination As Variant)
|
||||||
|
source_ = vSource
|
||||||
|
dest_ = vDestination
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Clone() As CDS_Edge
|
||||||
|
Set Clone = New CDS_Edge
|
||||||
|
Call Clone.Init(source_, dest_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Reversed() As CDS_Edge
|
||||||
|
Dim iEdge As New CDS_Edge
|
||||||
|
Call iEdge.Init(dest_, source_)
|
||||||
|
Set Reversed = iEdge
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get ID() As String
|
||||||
|
ID = VBA.CStr(source_) & "-" & VBA.CStr(dest_)
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function Compare(rhs As Variant) As Double
|
||||||
|
Compare = CompareDeep(source_, rhs.source_)
|
||||||
|
If Compare = 0 Then _
|
||||||
|
Compare = CompareDeep(dest_, rhs.dest_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ToString(Optional nIndent& = 0) As String
|
||||||
|
ToString = "$Edge " & ID
|
||||||
|
End Function
|
76
utility/CDS_Factorizator.cls
Normal file
76
utility/CDS_Factorizator.cls
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_Factorizator"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ========= Module for floating point value groupings ===============
|
||||||
|
' Shared module version: 20210412
|
||||||
|
' Tested in: TestVisio
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting
|
||||||
|
|
||||||
|
' Note: elements should be comparable
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private gap_ As Double
|
||||||
|
Private values_ As Scripting.Dictionary
|
||||||
|
Private factors_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Public Function Init(dDividerGap As Double)
|
||||||
|
gap_ = dDividerGap
|
||||||
|
Set factors_ = New Scripting.Dictionary
|
||||||
|
Set values_ = New Scripting.Dictionary
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get Values() As Scripting.Dictionary
|
||||||
|
Set Values = values_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function Insert(vNewItem As Variant, dValue As Double) As Boolean
|
||||||
|
Insert = Not values_.Exists(vNewItem)
|
||||||
|
If Not Insert Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
values_(vNewItem) = dValue
|
||||||
|
factors_(vNewItem) = vNewItem
|
||||||
|
If values_.Count = 1 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim vItem As Variant
|
||||||
|
For Each vItem In values_
|
||||||
|
If vItem = vNewItem Then _
|
||||||
|
GoTo NEXT_ITEM
|
||||||
|
If VBA.Abs(values_(vItem) - dValue) > gap_ Then _
|
||||||
|
GoTo NEXT_ITEM
|
||||||
|
|
||||||
|
If FactorValueFor(vItem) >= FactorValueFor(vNewItem) Then
|
||||||
|
Call MergeFactors(factors_(vNewItem), factors_(vItem))
|
||||||
|
Else
|
||||||
|
Call MergeFactors(factors_(vItem), factors_(vNewItem))
|
||||||
|
End If
|
||||||
|
NEXT_ITEM:
|
||||||
|
Next vItem
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FactorFor(vItem As Variant) As Variant
|
||||||
|
If values_.Exists(vItem) Then _
|
||||||
|
FactorFor = factors_(vItem)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FactorValueFor(vItem As Variant) As Double
|
||||||
|
If values_.Exists(vItem) Then _
|
||||||
|
FactorValueFor = values_(factors_(vItem))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =====
|
||||||
|
Private Function MergeFactors(vOld As Variant, vNew As Variant)
|
||||||
|
Dim vItem As Variant
|
||||||
|
For Each vItem In factors_
|
||||||
|
If factors_(vItem) = vOld Then _
|
||||||
|
factors_(vItem) = vNew
|
||||||
|
Next vItem
|
||||||
|
End Function
|
172
utility/CDS_Graph.cls
Normal file
172
utility/CDS_Graph.cls
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_Graph"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Class Module for Graph management ========
|
||||||
|
' Shared module version: 20210419
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: ex_Collection, CDS_Edge, CDS_Node
|
||||||
|
' Required reference: Scripting
|
||||||
|
' Warning: node data should be basic type and be convertible to String (VBA.CStr)
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public nodes_ As Scripting.Dictionary
|
||||||
|
Public edges_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Call Clear
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Clear()
|
||||||
|
Set nodes_ = New Scripting.Dictionary
|
||||||
|
Set edges_ = New Scripting.Dictionary
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get Size() As Long
|
||||||
|
Size = nodes_.Count
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Nodes() As Collection
|
||||||
|
Set Nodes = New Collection
|
||||||
|
Dim vData As Variant
|
||||||
|
For Each vData In nodes_
|
||||||
|
Call Nodes.Add(vData)
|
||||||
|
Next vData
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function AddNode(vNode As Variant)
|
||||||
|
Call InternalAddNode(vNode)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function HasNode(vNode As Variant) As Boolean
|
||||||
|
HasNode = nodes_.Exists(vNode)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AddEdge(vSource As Variant, vDestination As Variant) As CDS_Edge
|
||||||
|
Call AddNode(vSource)
|
||||||
|
Call AddNode(vDestination)
|
||||||
|
|
||||||
|
Dim newEdge As New CDS_Edge: Call newEdge.Init(vSource, vDestination)
|
||||||
|
Dim sEdge$: sEdge = newEdge.ID
|
||||||
|
|
||||||
|
If edges_.Exists(sEdge) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Call edges_.Add(sEdge, newEdge)
|
||||||
|
Call nodes_(vSource).outputs_.Add(vDestination)
|
||||||
|
Call nodes_(vDestination).inputs_.Add(vSource)
|
||||||
|
|
||||||
|
Set AddEdge = newEdge
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function HasEdge(vSource As Variant, vDestination As Variant) As Boolean
|
||||||
|
Dim newEdge As New CDS_Edge: Call newEdge.Init(vSource, vDestination)
|
||||||
|
Dim sEdge$: sEdge = newEdge.ID
|
||||||
|
HasEdge = edges_.Exists(sEdge)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpandOutputs(ByRef target As Scripting.Dictionary)
|
||||||
|
Dim nCount&
|
||||||
|
Do While True
|
||||||
|
nCount = target.Count
|
||||||
|
Call ExpandOutsOnce(target)
|
||||||
|
If nCount = target.Count Then _
|
||||||
|
Exit Function
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpandInputs(ByRef target As Scripting.Dictionary)
|
||||||
|
Dim nCount&
|
||||||
|
Do While True
|
||||||
|
nCount = target.Count
|
||||||
|
Call ExpandInsOnce(target)
|
||||||
|
If nCount = target.Count Then _
|
||||||
|
Exit Function
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpandBiderctional(ByRef target As Scripting.Dictionary)
|
||||||
|
Dim nCount&
|
||||||
|
Do While True
|
||||||
|
nCount = target.Count
|
||||||
|
Call ExpandBiderctionalOnce(target)
|
||||||
|
If nCount = target.Count Then _
|
||||||
|
Exit Function
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpandOutsOnce(ByRef target As Scripting.Dictionary)
|
||||||
|
Dim nCount&: nCount = target.Count
|
||||||
|
Dim aKey As Variant
|
||||||
|
Dim vChild As Variant
|
||||||
|
For Each aKey In target
|
||||||
|
For Each vChild In nodes_(aKey).outputs_
|
||||||
|
If Not target.Exists(vChild) Then _
|
||||||
|
Call target.Add(vChild, 0)
|
||||||
|
Next vChild
|
||||||
|
nCount = nCount - 1
|
||||||
|
If nCount = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
Next aKey
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpandInsOnce(ByRef target As Scripting.Dictionary)
|
||||||
|
Dim nCount&: nCount = target.Count
|
||||||
|
Dim aKey As Variant
|
||||||
|
Dim vChild As Variant
|
||||||
|
For Each aKey In target
|
||||||
|
For Each vChild In nodes_(aKey).inputs_
|
||||||
|
If Not target.Exists(vChild) Then _
|
||||||
|
Call target.Add(vChild, 0)
|
||||||
|
Next vChild
|
||||||
|
nCount = nCount - 1
|
||||||
|
If nCount = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
Next aKey
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExpandBiderctionalOnce(ByRef target As Scripting.Dictionary, Optional nStart& = 1)
|
||||||
|
Dim nCount&: nCount = target.Count
|
||||||
|
Dim aKey As Variant
|
||||||
|
Dim vChild As Variant
|
||||||
|
For Each aKey In target
|
||||||
|
For Each vChild In nodes_(aKey).inputs_
|
||||||
|
If Not target.Exists(vChild) Then _
|
||||||
|
Call target.Add(vChild, 0)
|
||||||
|
Next vChild
|
||||||
|
For Each vChild In nodes_(aKey).outputs_
|
||||||
|
If Not target.Exists(vChild) Then _
|
||||||
|
Call target.Add(vChild, 0)
|
||||||
|
Next vChild
|
||||||
|
nCount = nCount - 1
|
||||||
|
If nCount = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
Next aKey
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FilterInternalEdges(iNodes As Scripting.Dictionary) As Collection
|
||||||
|
Dim iEdges As New Collection
|
||||||
|
Dim anEdge As CDS_Edge
|
||||||
|
Dim aKey As Variant
|
||||||
|
For Each aKey In edges_
|
||||||
|
Set anEdge = edges_(aKey)
|
||||||
|
If iNodes.Exists(anEdge.source_) And iNodes.Exists(anEdge.dest_) Then _
|
||||||
|
Call iEdges.Add(anEdge)
|
||||||
|
Next aKey
|
||||||
|
Set FilterInternalEdges = iEdges
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ==============
|
||||||
|
Private Function InternalAddNode(vNode As Variant)
|
||||||
|
If nodes_.Exists(vNode) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim newNode As New CDS_Node
|
||||||
|
newNode.data_ = vNode
|
||||||
|
Call nodes_.Add(vNode, newNode)
|
||||||
|
End Function
|
34
utility/CDS_Interval.cls
Normal file
34
utility/CDS_Interval.cls
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_Interval"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ======== Ìîäóëü-èíòåðâàë =============
|
||||||
|
' Shared module version: 20210430
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public start_ As Long
|
||||||
|
Public finish_ As Long
|
||||||
|
|
||||||
|
Public Function Init(nStart&, nFinish&)
|
||||||
|
start_ = nStart
|
||||||
|
finish_ = nFinish
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Clone() As CDS_Interval
|
||||||
|
Set Clone = New CDS_Interval
|
||||||
|
Call Clone.Init(start_, finish_)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Compare(rhs As Variant) As Double
|
||||||
|
Compare = start_ - rhs.start_
|
||||||
|
If Compare = 0 Then _
|
||||||
|
Compare = finish_ - rhs.finish_
|
||||||
|
End Function
|
19
utility/CDS_Node.cls
Normal file
19
utility/CDS_Node.cls
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_Node"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ========= Class Module for Graph node =======
|
||||||
|
' Shared module version: 20210409
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public data_ As Variant
|
||||||
|
Public outputs_ As New Collection
|
||||||
|
Public inputs_ As New Collection
|
33
utility/CDS_NodeSH.cls
Normal file
33
utility/CDS_NodeSH.cls
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_NodeSH"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' =============== Static hierarchy Node =====================
|
||||||
|
' Shared module version: 20210430
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public id_ As Long
|
||||||
|
Public rank_ As Long
|
||||||
|
Public parent_ As CDS_NodeSH
|
||||||
|
Public children_ As Collection ' of CDS_NodeSH
|
||||||
|
Public descendantsCount_ As Long
|
||||||
|
|
||||||
|
Public data_ As Object ' Payload should be filled in by the caller
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Set children_ = New Collection
|
||||||
|
descendantsCount_ = 0
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(nID&, nRank&)
|
||||||
|
id_ = nID
|
||||||
|
rank_ = nRank
|
||||||
|
End Function
|
78
utility/CDS_StaticHierarchy.cls
Normal file
78
utility/CDS_StaticHierarchy.cls
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "CDS_StaticHierarchy"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ================= Static hierarchy =======================
|
||||||
|
' Shared module version: 20210518
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on: CDS_NodeSH
|
||||||
|
' Required reference: Scripting
|
||||||
|
' =========
|
||||||
|
' Example of valid structure
|
||||||
|
' 1
|
||||||
|
' 1.1
|
||||||
|
' 1.2
|
||||||
|
' 1.2.1
|
||||||
|
' 1.2.1.1
|
||||||
|
' 1.3
|
||||||
|
' 2
|
||||||
|
' 0
|
||||||
|
' ======
|
||||||
|
' Example of invalid structure ( increase level > 1 )
|
||||||
|
' 1
|
||||||
|
' 1.1.3
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public nodes_ As Collection
|
||||||
|
|
||||||
|
Private levels_ As Scripting.Dictionary
|
||||||
|
Private previousRank_ As Long
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
Set nodes_ = New Collection
|
||||||
|
Set levels_ = New Scripting.Dictionary
|
||||||
|
previousRank_ = -1
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Property Get MaxDepth() As Long
|
||||||
|
MaxDepth = levels_.Count
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Size() As Long
|
||||||
|
Size = nodes_.Count
|
||||||
|
End Property
|
||||||
|
|
||||||
|
' Requires nRank <= previousLevel + 1
|
||||||
|
Public Function PushItem(nRank&) As CDS_NodeSH
|
||||||
|
If nodes_.Count > 0 Then
|
||||||
|
If previousRank_ < nRank Then _
|
||||||
|
If previousRank_ <> nRank - 1 Then _
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim iNewNode As New CDS_NodeSH: Call iNewNode.Init(nodes_.Count + 1, nRank)
|
||||||
|
Set levels_(nRank) = iNewNode
|
||||||
|
previousRank_ = nRank
|
||||||
|
If levels_.Exists(nRank - 1) Then
|
||||||
|
Set iNewNode.parent_ = levels_(nRank - 1)
|
||||||
|
Call levels_(nRank - 1).children_.Add(iNewNode)
|
||||||
|
Call IncrementDescendants(nRank - 1)
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call nodes_.Add(iNewNode)
|
||||||
|
Set PushItem = iNewNode
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =======
|
||||||
|
Private Function IncrementDescendants(nTarget&)
|
||||||
|
Dim nRank As Variant
|
||||||
|
For Each nRank In levels_
|
||||||
|
If nRank <= nTarget Then _
|
||||||
|
levels_(nRank).descendantsCount_ = levels_(nRank).descendantsCount_ + 1
|
||||||
|
Next nRank
|
||||||
|
End Function
|
138
utility/ex_Collection.bas
Normal file
138
utility/ex_Collection.bas
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
Attribute VB_Name = "ex_Collection"
|
||||||
|
' ======= Collection functions extensions ===============
|
||||||
|
' Shared module version: 20210708
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Function InCollection(sKey$, coll As Collection) As Boolean
|
||||||
|
On Error GoTo RESTORE_ERR
|
||||||
|
Call coll.Item(sKey)
|
||||||
|
InCollection = True
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
RESTORE_ERR:
|
||||||
|
InCollection = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Returns dictionary of indicies of first occurence for every element in array @aSource
|
||||||
|
' Note: duplicate elements are ignored
|
||||||
|
Public Function CollectionToIndex(iSource As Collection) As Scripting.Dictionary
|
||||||
|
Dim indicies As New Scripting.Dictionary
|
||||||
|
Dim val As Variant
|
||||||
|
Dim nIndex&: nIndex = 1
|
||||||
|
For Each val In iSource
|
||||||
|
If Not indicies.Exists(val) Then _
|
||||||
|
Call indicies.Add(val, nIndex)
|
||||||
|
nIndex = nIndex + 1
|
||||||
|
Next val
|
||||||
|
Set CollectionToIndex = indicies
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RevertCollection(target As Collection) As Collection
|
||||||
|
Set RevertCollection = New Collection
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = target.Count To 1 Step -1
|
||||||
|
Call RevertCollection.Add(target.Item(nItem))
|
||||||
|
Next nItem
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function IsSubsetOf(target As Collection, superset As Collection) As Boolean
|
||||||
|
IsSubsetOf = False
|
||||||
|
|
||||||
|
If target Is Nothing Or superset Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim val As Variant
|
||||||
|
For Each val In target
|
||||||
|
If Not InCollection(CStr(val), superset) Then _
|
||||||
|
Exit Function
|
||||||
|
Next val
|
||||||
|
|
||||||
|
IsSubsetOf = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SafeAddToCollection(aValue As Variant, sKey$, coll As Collection) As Boolean
|
||||||
|
' Äîáàâëåíèå â êîëëíåöèþ - âîçâðàùàåò False åñëè óæå åñòü
|
||||||
|
On Error GoTo ON_ERROR
|
||||||
|
Call coll.Add(aValue, sKey)
|
||||||
|
SafeAddToCollection = True
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
ON_ERROR:
|
||||||
|
SafeAddToCollection = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SafeMergeCollection(cSource As Collection, cDestination As Collection) As Boolean
|
||||||
|
SafeMergeCollection = False
|
||||||
|
|
||||||
|
If cSource Is Nothing Or cDestination Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim val As Variant
|
||||||
|
For Each val In cSource
|
||||||
|
Call SafeAddToCollection(CStr(val), CStr(val), cDestination)
|
||||||
|
Next val
|
||||||
|
|
||||||
|
SafeMergeCollection = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ToArray(cSource As Collection) As Variant
|
||||||
|
If cSource Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim arResult() As Variant
|
||||||
|
If cSource.Count > 0 Then
|
||||||
|
ReDim arResult(0 To cSource.Count - 1)
|
||||||
|
Dim i&
|
||||||
|
For i = 0 To cSource.Count - 1
|
||||||
|
arResult(i) = cSource(i + 1)
|
||||||
|
Next i
|
||||||
|
Else
|
||||||
|
ReDim arResult(0 To 0)
|
||||||
|
End If
|
||||||
|
ToArray = arResult()
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ToLongArray(cSource As Collection) As Long()
|
||||||
|
If cSource Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim arResult() As Long
|
||||||
|
If cSource.Count > 0 Then
|
||||||
|
ReDim arResult(0 To cSource.Count - 1)
|
||||||
|
Dim i&
|
||||||
|
For i = 0 To cSource.Count - 1
|
||||||
|
arResult(i) = cSource(i + 1)
|
||||||
|
Next i
|
||||||
|
Else
|
||||||
|
ReDim arResult(0 To 0)
|
||||||
|
End If
|
||||||
|
ToLongArray = arResult()
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FromArray(aSource As Variant) As Collection
|
||||||
|
If Not IsArrayAllocated(aSource) Then _
|
||||||
|
Exit Function
|
||||||
|
Dim cResult As New Collection
|
||||||
|
Dim val As Variant
|
||||||
|
If UBound(aSource) <> LBound(aSource) Then
|
||||||
|
For Each val In aSource
|
||||||
|
Call cResult.Add(val)
|
||||||
|
Next val
|
||||||
|
End If
|
||||||
|
Set FromArray = cResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CollectionToLines(target As Collection) As String
|
||||||
|
If target Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
Dim anItem As Variant
|
||||||
|
For Each anItem In target
|
||||||
|
If CollectionToLines <> vbNullString Then _
|
||||||
|
CollectionToLines = CollectionToLines & vbNewLine
|
||||||
|
CollectionToLines = CollectionToLines & CStr(anItem)
|
||||||
|
Next anItem
|
||||||
|
End Function
|
298
utility/ex_Color.bas
Normal file
298
utility/ex_Color.bas
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
Attribute VB_Name = "ex_Color"
|
||||||
|
'================ Ìîäóëü äëÿ ðàáîòû ñ öâåòàìè â ðàçíûõ ïðèëîæåíèÿõ =============
|
||||||
|
' Shared module version: 20210430
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Enum ColorType
|
||||||
|
CT_RGB = &H0
|
||||||
|
CT_AUTO = &HFF
|
||||||
|
CT_SYSTEM = &H80
|
||||||
|
CT_THEME_LOW = &HD0
|
||||||
|
CT_THEME_HIGH = &HDF
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Type ColorDetails
|
||||||
|
type_ As Byte
|
||||||
|
clrIndex_ As WdThemeColorIndex
|
||||||
|
tintShade_ As Double
|
||||||
|
rgb_ As Long
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Public Enum HardColor
|
||||||
|
HC_INVALID = -1
|
||||||
|
HC_WHITE = &HFFFFFF
|
||||||
|
HC_BLACK = &H0
|
||||||
|
HC_RED = &HFF
|
||||||
|
HC_GREEN = &HFF00
|
||||||
|
HC_BLUE = &HFF0000
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Declare PtrSafe Function _
|
||||||
|
ColorAdjustLuma Lib "shlwapi.dll" ( _
|
||||||
|
ByVal nRGB As Long, _
|
||||||
|
ByVal nLumaAdjust As Long, _
|
||||||
|
ByVal bScale As Long) As Long
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long
|
||||||
|
|
||||||
|
Private Enum AppType
|
||||||
|
T_APP_UNDEF = 0
|
||||||
|
[_First] = 1
|
||||||
|
|
||||||
|
T_APP_WORD = 1
|
||||||
|
T_APP_EXCEL = 2
|
||||||
|
T_APP_VISIO = 3
|
||||||
|
T_APP_POWER_POINT = 4
|
||||||
|
|
||||||
|
[_Last] = 4
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Function ColorGetLuma(nRGB&) As Long
|
||||||
|
Dim clrRed%, clrGreen%, clrBlue%
|
||||||
|
Call LongColor3Components(nRGB, clrRed, clrGreen, clrBlue)
|
||||||
|
ColorGetLuma = Math_Sym(0.2126 * clrRed + 0.7152 * clrGreen + 0.0722 * clrBlue)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ColorGetRGB(nColorID&, aDocument As Object) As Long
|
||||||
|
ColorGetRGB = QueryColor(nColorID, aDocument).rgb_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ConvertRGBtoString(nRGB&) As String
|
||||||
|
Dim nRed%, nGreen%, nBlue%
|
||||||
|
Call LongColor3Components(nRGB, nRed, nGreen, nBlue)
|
||||||
|
ConvertRGBtoString = "RGB(" & nRed & "," & nGreen & "," & nBlue & ")"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ConvertStringToRGB(sColor$) As Long
|
||||||
|
ConvertStringToRGB = HC_INVALID
|
||||||
|
|
||||||
|
Dim nLen&: nLen = VBA.Len(sColor)
|
||||||
|
Dim nPos&: nPos = 1
|
||||||
|
Dim nRed%: nRed = ExtractFirstInteger(sColor, nPos)
|
||||||
|
If nPos > nLen Or nRed = HC_INVALID Or nRed > 255 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
nPos = nPos + 1
|
||||||
|
Dim nGreen%: nGreen = ExtractFirstInteger(sColor, nPos)
|
||||||
|
If nPos > nLen Or nGreen = HC_INVALID Or nGreen > 255 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
nPos = nPos + 1
|
||||||
|
Dim nBlue%: nBlue = ExtractFirstInteger(sColor, nPos)
|
||||||
|
If nPos > nLen Or nBlue = HC_INVALID Or nBlue > 255 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
ConvertStringToRGB = RGB(nRed, nGreen, nBlue)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function QueryColor(nColorID&, aDocument As Object) As ColorDetails
|
||||||
|
Dim sHexColor$: sHexColor = VBA.Right$(VBA.String$(7, "0") & VBA.Hex$(nColorID), 8)
|
||||||
|
QueryColor.type_ = CByte("&H" & VBA.Left$(sHexColor, 2))
|
||||||
|
Select Case QueryColor.type_
|
||||||
|
Case CT_RGB: QueryColor.rgb_ = nColorID
|
||||||
|
Case CT_SYSTEM: QueryColor.rgb_ = SysColorToRGB(nColorID)
|
||||||
|
Case CT_THEME_LOW To CT_THEME_HIGH: QueryColor = QueryThemeColor(QueryColor.type_, sHexColor, aDocument)
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =========
|
||||||
|
Private Function LongColor3Components(nColor&, ByRef c1%, ByRef c2%, ByRef c3%)
|
||||||
|
c1 = Math_Sym(nColor And &HFF&)
|
||||||
|
c2 = Math_Sym((nColor And &HFF00&) / &H100&)
|
||||||
|
c3 = Math_Sym((nColor And &HFF0000) / &H10000)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Math_Sym(ByVal nValue As Double, Optional ByVal dFactor As Double = 1) As Double
|
||||||
|
Math_Sym = Fix(nValue * dFactor + 0.5 * Sgn(nValue)) / dFactor
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ExtractApplicationType(target As Object) As AppType
|
||||||
|
If target Is Nothing Then
|
||||||
|
ExtractApplicationType = T_APP_UNDEF
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Select Case target.Application.Name
|
||||||
|
Case "Microsoft Word": ExtractApplicationType = T_APP_WORD
|
||||||
|
Case "Microsoft Excel": ExtractApplicationType = T_APP_EXCEL
|
||||||
|
Case "Microsoft PowerPoint": ExtractApplicationType = T_APP_POWER_POINT
|
||||||
|
Case "Microsoft Visio": ExtractApplicationType = T_APP_VISIO
|
||||||
|
Case Else: ExtractApplicationType = T_APP_UNDEF
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ExtractFirstInteger(sText$, ByRef nPos&) As Integer
|
||||||
|
On Error GoTo ERR_OVERFLOW
|
||||||
|
|
||||||
|
Dim bStarted As Boolean: bStarted = False
|
||||||
|
Dim nLen&: nLen = VBA.Len(sText)
|
||||||
|
Dim sDigit$
|
||||||
|
Do
|
||||||
|
If nPos > nLen Then _
|
||||||
|
Exit Function
|
||||||
|
sDigit = VBA.Mid(sText, nPos, 1)
|
||||||
|
If VBA.IsNumeric(sDigit) Then
|
||||||
|
bStarted = True
|
||||||
|
ExtractFirstInteger = ExtractFirstInteger * 10 + VBA.CLng(sDigit)
|
||||||
|
ElseIf bStarted Then _
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
nPos = nPos + 1
|
||||||
|
Loop
|
||||||
|
|
||||||
|
ERR_OVERFLOW:
|
||||||
|
On Error GoTo 0
|
||||||
|
ExtractFirstInteger = HC_INVALID
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SysColorToRGB(nColor&) As Long
|
||||||
|
SysColorToRGB = GetSysColor(nColor And &HFFFFFF)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function QueryThemeColor(clrType As Byte, hexColor$, aDocument As Object) As ColorDetails
|
||||||
|
QueryThemeColor.type_ = clrType And &HF0
|
||||||
|
QueryThemeColor.clrIndex_ = clrType And &HF
|
||||||
|
|
||||||
|
Dim bDarkness As Byte: bDarkness = CByte("&H" & Mid$(hexColor, 5, 2))
|
||||||
|
If bDarkness <> &HFF Then
|
||||||
|
QueryThemeColor.tintShade_ = VBA.Round(-1 + bDarkness / &HFF, 2)
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim bLightness As Byte: bLightness = CByte("&H" & Mid$(hexColor, 7, 2))
|
||||||
|
If bLightness <> &HFF Then
|
||||||
|
QueryThemeColor.tintShade_ = VBA.Round(1 - bLightness / &HFF, 2)
|
||||||
|
End If
|
||||||
|
|
||||||
|
QueryThemeColor.rgb_ = GetThemeRGB(QueryThemeColor.clrIndex_, QueryThemeColor.tintShade_, aDocument)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GetThemeRGB(clrIndex As WdThemeColorIndex, tintShade As Double, aDocument As Object) As Long
|
||||||
|
Dim schemeID As MsoThemeColorSchemeIndex
|
||||||
|
Dim schemeRGB&
|
||||||
|
Select Case ExtractApplicationType(aDocument)
|
||||||
|
Case T_APP_UNDEF
|
||||||
|
schemeID = WordColor(clrIndex)
|
||||||
|
schemeRGB = ActiveDocument.DocumentTheme.ThemeColorScheme(schemeID).RGB
|
||||||
|
Case T_APP_WORD
|
||||||
|
schemeID = WordColor(clrIndex)
|
||||||
|
schemeRGB = aDocument.DocumentTheme.ThemeColorScheme(schemeID).RGB
|
||||||
|
Case T_APP_VISIO
|
||||||
|
schemeRGB = aDocument.DocumentTheme.ThemeColorScheme(clrIndex).RGB
|
||||||
|
Case T_APP_EXCEL
|
||||||
|
schemeID = ExcelColor(clrIndex)
|
||||||
|
schemeRGB = aDocument.DocumentTheme.ThemeColorScheme(schemeID).RGB
|
||||||
|
Case T_APP_POWER_POINT
|
||||||
|
schemeRGB = aDocument.DocumentTheme.ThemeColorScheme(clrIndex).RGB
|
||||||
|
End Select
|
||||||
|
GetThemeRGB = ColorAdjustLuma(schemeRGB, Math_Sym(tintShade, 1000) * 1000, True)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function WordColor(clrID As WdThemeColorIndex) As MsoThemeColorSchemeIndex
|
||||||
|
Select Case clrID
|
||||||
|
Case wdThemeColorMainDark1: WordColor = msoThemeDark1
|
||||||
|
Case wdThemeColorMainLight1: WordColor = msoThemeLight1
|
||||||
|
Case wdThemeColorMainDark2: WordColor = msoThemeDark2
|
||||||
|
Case wdThemeColorMainLight2: WordColor = msoThemeLight2
|
||||||
|
Case wdThemeColorAccent1: WordColor = msoThemeAccent1
|
||||||
|
Case wdThemeColorAccent2: WordColor = msoThemeAccent2
|
||||||
|
Case wdThemeColorAccent3: WordColor = msoThemeAccent3
|
||||||
|
Case wdThemeColorAccent4: WordColor = msoThemeAccent4
|
||||||
|
Case wdThemeColorAccent5: WordColor = msoThemeAccent5
|
||||||
|
Case wdThemeColorAccent6: WordColor = msoThemeAccent6
|
||||||
|
Case wdThemeColorHyperlink: WordColor = msoThemeHyperlink
|
||||||
|
Case wdThemeColorHyperlinkFollowed: WordColor = msoThemeFollowedHyperlink
|
||||||
|
Case wdThemeColorBackground1: WordColor = msoThemeLight1
|
||||||
|
Case wdThemeColorText1: WordColor = msoThemeDark1
|
||||||
|
Case wdThemeColorBackground2: WordColor = msoThemeLight2
|
||||||
|
Case wdThemeColorText2: WordColor = msoThemeDark2
|
||||||
|
Case Else:
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ExcelColor(clrID As XlThemeColor) As MsoThemeColorSchemeIndex
|
||||||
|
Select Case clrID
|
||||||
|
Case xlThemeColorDark1: ExcelColor = msoThemeDark1
|
||||||
|
Case xlThemeColorLight1: ExcelColor = msoThemeLight1
|
||||||
|
Case xlThemeColorDark2: ExcelColor = msoThemeDark2
|
||||||
|
Case xlThemeColorLight2: ExcelColor = msoThemeLight2
|
||||||
|
Case xlThemeColorAccent1: ExcelColor = msoThemeAccent1
|
||||||
|
Case xlThemeColorAccent2: ExcelColor = msoThemeAccent2
|
||||||
|
Case xlThemeColorAccent3: ExcelColor = msoThemeAccent3
|
||||||
|
Case xlThemeColorAccent4: ExcelColor = msoThemeAccent4
|
||||||
|
Case xlThemeColorAccent5: ExcelColor = msoThemeAccent5
|
||||||
|
Case xlThemeColorAccent6: ExcelColor = msoThemeAccent6
|
||||||
|
Case xlThemeColorHyperlink: ExcelColor = msoThemeHyperlink
|
||||||
|
Case xlThemeColorFollowedHyperlink: ExcelColor = msoThemeFollowedHyperlink
|
||||||
|
Case Else:
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'
|
||||||
|
'
|
||||||
|
''----------------------Êîíâåðòèðîâàíèå ------------------------------------------------
|
||||||
|
'Function Convert_RGB2CMYK(colr&) As Long
|
||||||
|
''RGB -> CMYK
|
||||||
|
' Dim red As Integer, blue As Integer, green As Integer
|
||||||
|
' Dim c As Double, m As Double, y As Double, k As Double, minCMY As Double
|
||||||
|
' Dim cyan As Integer, magenta As Integer, yellow As Integer, key As Integer
|
||||||
|
'
|
||||||
|
' Call LongColor3Components(colr, red, green, blue)
|
||||||
|
' If red = 0 And green = 0 And blue = 0 Then
|
||||||
|
' key = 100
|
||||||
|
' GoTo RESULTING
|
||||||
|
' End If
|
||||||
|
'
|
||||||
|
' c = 1 - (red / 255)
|
||||||
|
' m = 1 - (green / 255)
|
||||||
|
' y = 1 - (blue / 255)
|
||||||
|
' k = Math_Min(c, Math_Min(m, y))
|
||||||
|
'
|
||||||
|
' cyan = Math_Sym(100 * (c - k) / (1 - k))
|
||||||
|
' magenta = Math_Sym(100 * (m - k) / (1 - k))
|
||||||
|
' yellow = Math_Sym(100 * (y - k) / (1 - k))
|
||||||
|
' key = Math_Sym(100 * k)
|
||||||
|
'
|
||||||
|
'RESULTING:
|
||||||
|
' Convert_RGB2CMYK = cyan * 101 ^ 3 + 101 ^ 2 * magenta + 101 * yellow + key
|
||||||
|
'End Function
|
||||||
|
'
|
||||||
|
'
|
||||||
|
''--------------------------Îáðàáîòêà öâåòîâûõ ññûëîê ------------------------
|
||||||
|
'
|
||||||
|
'Private Function SysColorToRGB(ByVal SysColor&) As Long
|
||||||
|
''Âîçâðàùàåò RGB ñèñòåìíîãî öâåòà
|
||||||
|
' SysColor = SysColor And &HFFFFFF
|
||||||
|
' SysColorToRGB = GetSysColor(SysColor)
|
||||||
|
'End Function
|
||||||
|
'
|
||||||
|
'Private Function QueryThemeColor(ColourTypeByte As Byte, _
|
||||||
|
' ColourToTestHex As String, app As Object) _
|
||||||
|
' As ColorDetails
|
||||||
|
'
|
||||||
|
' Const Unchanged As Byte = &HFF
|
||||||
|
'
|
||||||
|
' Dim LightnessByte As Byte
|
||||||
|
' Dim DarknessByte As Byte
|
||||||
|
'
|
||||||
|
' LightnessByte = CByte("&H" & Mid$(ColourToTestHex, 7, 2))
|
||||||
|
' DarknessByte = CByte("&H" & Mid$(ColourToTestHex, 5, 2))
|
||||||
|
'
|
||||||
|
' QueryThemeColor.ColourType = ColourTypeByte And &HF0
|
||||||
|
' QueryThemeColor.ThemeColorIndex = ColourTypeByte And &HF
|
||||||
|
'
|
||||||
|
' If DarknessByte <> Unchanged Then
|
||||||
|
' QueryThemeColor.TintAndShade = Round(-1 + DarknessByte / &HFF, 2)
|
||||||
|
' End If
|
||||||
|
'
|
||||||
|
' If LightnessByte <> Unchanged Then
|
||||||
|
' QueryThemeColor.TintAndShade = Round(1 - LightnessByte / &HFF, 2)
|
||||||
|
' End If
|
||||||
|
'
|
||||||
|
' QueryThemeColor.RGB = GetThemeRGB(QueryThemeColor.ThemeColorIndex, _
|
||||||
|
' QueryThemeColor.TintAndShade, app)
|
||||||
|
'
|
||||||
|
'End Function
|
||||||
|
|
345
utility/ex_DataPreparation.bas
Normal file
345
utility/ex_DataPreparation.bas
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
Attribute VB_Name = "ex_DataPreparation"
|
||||||
|
' ======== Data manipulation tools ============
|
||||||
|
' Shared module version: 20220217
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Function ApplyCommonAbbreviations(sText$) As String
|
||||||
|
Dim sResult$: sResult = sText
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, Chr(9), " ")
|
||||||
|
sResult = VBA.Replace(sResult, Chr(160), " ")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "(с изменениями и дополнениями)", "")
|
||||||
|
sResult = VBA.Replace(sResult, " ", " ")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "Российская Федерация", "РФ")
|
||||||
|
sResult = VBA.Replace(sResult, "Россия", "РФ")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральный закон", "ФЗ")
|
||||||
|
sResult = VBA.Replace(sResult, "Постановление Правительства", "ПП")
|
||||||
|
sResult = VBA.Replace(sResult, "Распоряжение Правительства", "РП")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "Российской Федерации", "РФ")
|
||||||
|
sResult = VBA.Replace(sResult, "России", "РФ")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального закона", "ФЗ")
|
||||||
|
sResult = VBA.Replace(sResult, "Постановления Правительства", "ПП")
|
||||||
|
sResult = VBA.Replace(sResult, "Распоряжения Правительства", "РП")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство внутренних дел", "МВД")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство иностранных дел", "МИД")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство обороны", "Минобороны")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство юстиции", "Минюст")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство здравоохранения", "Минздрав")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство культуры", "Минкультуры")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство образования и науки", "Минобрнауки")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство природных ресурсов и экологии", "Минприроды")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство промышленности и торговли", "Минпромторг")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство Российской Федерации по развитию Дальнего Востока", "Минвостокразвития РФ")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство регионального развития", "Минрегион")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство связи и массовых коммуникаций", "Минкомсвязь")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство сельского хозяйства", "Минсельхоз")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство спорта", "Минспорт")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство транспорта", "Минтранса")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство труда и социальной защиты", "Минтруд")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство финансов", "Минфин")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство экономического развития", "Минэкономразвития")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство энергетики", "Минэнерго")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерство строительства и жилищно-коммунального хозяйства", "Минстрой")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства внутренних дел", "МВД")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства иностранных дел", "МИД")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства обороны", "Минобороны")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства юстиции", "Минюста")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства здравоохранения", "Минздрава")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства культуры", "Минкультуры")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства образования и науки", "Минобрнауки")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства природных ресурсов и экологии", "Минприроды")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства промышленности и торговли", "Минпромторга")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства Российской Федерации по развитию Дальнего Востока", "Минвостокразвития РФ")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства регионального развития", "Минрегиона")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства связи и массовых коммуникаций", "Минкомсвязи")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства сельского хозяйства", "Минсельхоза")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства спорта", "Минспорта")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства транспорта", "Минтранса")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства труда и социальной защиты", "Минтруда")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства финансов", "Минфина")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства экономического развития", "Минэкономразвития")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства энергетики", "Минэнерго")
|
||||||
|
sResult = VBA.Replace(sResult, "Министерства строительства и жилищно-коммунального хозяйства", "Минстроя")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по техническому и экспортному контролю", "ФСТЭК")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба безопасности", "ФСБ")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба Российской Федерации по контролю за оборотом наркотиков", "ФСКН РФ")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба охраны", "ФСО")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по гидрометеорологии и мониторингу окружающей среды", "Росгидромет")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по экологическому, технологическому и атомному надзору", "Ростехнадзор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по надзору в сфере природопользования", "Росприроднадзор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по ветеринарному и фитосанитарному надзору", "Россельхознадзор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по надзору в сфере транспорта", "Ространснадзор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по труду и занятости", "Роструд")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по регулированию алкогольного рынка", "Росалкогольрегулирование")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по надзору в сфере связи, информационных технологий и массовых коммуникаций", "Роскомнадзор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по аккредитации", "Росаккредитация")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба государственной статистики", "Росстат")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по интеллектуальной собственности", "Роспатент")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по финансовому мониторингу", "Росфинмониторинг")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба государственной регистрации, кадастра и картографии", "Росреестр")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по надзору в сфере защиты прав потребителей и благополучия человека", "Роспотребнадзор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по надзору в сфере здравоохранения", "Росздравнадзор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по надзору в сфере образования и науки", "Рособзнадзор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба по экологическому, технологическому и атомному надзору", "Ростехнадзор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство по техническому регулированию и метрологии", "Росстандарт")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство водных ресурсов", "Росводресурсы")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство лесного хозяйства", "Рослесхоз")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство по недропользованию", "Роснедра")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство по рыболовству", "Росрыболовство")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство воздушного транспорта", "Росавиация")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство железнодорожного транспорта", "Росжелдор")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство морского и речного транспорта", "Росморречфлот")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство по управлению государственным имуществом", "Росимущество")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство по печати и массовым коммуникациям", "Роспечать")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство связи", "Россвязь")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство по туризму", "Ростуризм")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство по государственным резервам", "Росрезерв")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство по делам молодёжи", "Росмолодёжь")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное агентство по делам национальностей", "ФАДН")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная налоговая служба", "ФНС")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная таможенная служба", "ФТС")
|
||||||
|
sResult = VBA.Replace(sResult, "Государственная фельдъегерская служба", "ГФС")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная антимонопольная служба", "ФАС")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное медико-биологическое агентство", "ФМБА")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное архивное агентство", "Росархив")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное космическое агентство", "Роскосмос")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральное дорожное агентство", "Росавтодор")
|
||||||
|
sResult = VBA.Replace(sResult, "Пенсионный фонд", "ПФ")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральный фонд обязательного медицинского страхования", "ФОМС")
|
||||||
|
sResult = VBA.Replace(sResult, "Фонд социального страхования", "ФСС")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по техническому и экспортному контролю", "ФСТЭК")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы безопасности", "ФСБ")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы Российской Федерации по контролю за оборотом наркотиков", "ФСКН РФ")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы охраны", "ФСО")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по гидрометеорологии и мониторингу окружающей среды", "Росгидромета")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по экологическому, технологическому и атомному надзору", "Ростехнадзора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по надзору в сфере природопользования", "Росприроднадзора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по ветеринарному и фитосанитарному надзору", "Россельхознадзора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по надзору в сфере транспорта", "Ространснадзора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по труду и занятости", "Роструда")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по регулированию алкогольного рынка", "Росалкогольрегулирования")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по надзору в сфере связи, информационных технологий и массовых коммуникаций", "Роскомнадзора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по аккредитации", "Росаккредитации")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы государственной статистики", "Росстата")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по интеллектуальной собственности", "Роспатента")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по финансовому мониторингу", "Росфинмониторинга")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы государственной регистрации, кадастра и картографии", "Росреестра")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по надзору в сфере защиты прав потребителей и благополучия человека", "Роспотребнадзора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по надзору в сфере здравоохранения", "Росздравнадзора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по надзору в сфере образования и науки", "Рособзнадзора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы по экологическому, технологическому и атомному надзору", "Ростехнадзора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства по техническому регулированию и метрологии", "Росстандарта")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства водных ресурсов", "Росводресурсов")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства лесного хозяйства", "Рослесхоза")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства по недропользованию", "Роснедр")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства по рыболовству", "Росрыболовства")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства воздушного транспорта", "Росавиации")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства железнодорожного транспорта", "Росжелдора")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства морского и речного транспорта", "Росморречфлота")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства по управлению государственным имуществом", "Росимущества")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства по печати и массовым коммуникациям", "Роспечати")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства связи", "Россвязи")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства по туризму", "Ростуризма")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства по государственным резервам", "Росрезерва")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства по делам молодёжи", "Росмолодёжи")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального агентства по делам национальностей", "ФАДН")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной налоговой службы", "ФНС")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной таможенной службы", "ФТС")
|
||||||
|
sResult = VBA.Replace(sResult, "Государственной фельдъегерской службы", "ГФС")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной антимонопольной службы", "ФАС")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального медико-биологического агентства", "ФМБА")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального архивного агентства", "Росархива")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального космического агентства", "Роскосмоса")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального дорожного агентства", "Росавтодора")
|
||||||
|
sResult = VBA.Replace(sResult, "Пенсионного фонда", "ПФ")
|
||||||
|
sResult = VBA.Replace(sResult, "Федерального фонда обязательного медицинского страхования", "ФОМС")
|
||||||
|
sResult = VBA.Replace(sResult, "Фонда социального страхования", "ФСС")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "Российская академия наук", "РАН")
|
||||||
|
sResult = VBA.Replace(sResult, "Российской академии наук", "РАН")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральная служба", "ФС")
|
||||||
|
sResult = VBA.Replace(sResult, "Федеральной службы", "ФС")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "сельское хозяйство", "с/х")
|
||||||
|
sResult = VBA.Replace(sResult, "сельского хозяйства", "с/х")
|
||||||
|
sResult = VBA.Replace(sResult, "сельскому хозяйству", "с/х")
|
||||||
|
sResult = VBA.Replace(sResult, "сельском хозяйстве", "с/х")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, "железнодорожный", "ж/д")
|
||||||
|
sResult = VBA.Replace(sResult, "железнодорожного", "ж/д")
|
||||||
|
sResult = VBA.Replace(sResult, "железнодорожному", "ж/д")
|
||||||
|
sResult = VBA.Replace(sResult, "железнодорожном", "ж/д")
|
||||||
|
sResult = VBA.Replace(sResult, "железнодорожная", "ж/д")
|
||||||
|
sResult = VBA.Replace(sResult, "железнодорожной", "ж/д")
|
||||||
|
sResult = VBA.Replace(sResult, "железнодорожную", "ж/д")
|
||||||
|
sResult = VBA.Replace(sResult, "железнодорожное", "ж/д")
|
||||||
|
sResult = VBA.Replace(sResult, "железнодорожным", "ж/д")
|
||||||
|
|
||||||
|
sResult = VBA.Replace(sResult, " ", " ")
|
||||||
|
|
||||||
|
ApplyCommonAbbreviations = sResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SafeFileName(sText$) As String
|
||||||
|
Dim sResult$: sResult = sText
|
||||||
|
sResult = VBA.Replace(sResult, "*", "x")
|
||||||
|
sResult = VBA.Replace(sResult, "?", "7")
|
||||||
|
sResult = VBA.Replace(sResult, "/", "-")
|
||||||
|
sResult = VBA.Replace(sResult, "\", "-")
|
||||||
|
sResult = VBA.Replace(sResult, "|", "l")
|
||||||
|
sResult = VBA.Replace(sResult, ":", " ")
|
||||||
|
sResult = VBA.Replace(sResult, "<", "")
|
||||||
|
sResult = VBA.Replace(sResult, ">", "")
|
||||||
|
sResult = VBA.Replace(sResult, """", "'")
|
||||||
|
sResult = VBA.Replace(sResult, "' ", "'")
|
||||||
|
sResult = VBA.Replace(sResult, Chr(11), " ")
|
||||||
|
sResult = VBA.Replace(sResult, Chr(13), " ")
|
||||||
|
sResult = VBA.Replace(sResult, Chr(160), " ")
|
||||||
|
sResult = VBA.Replace(sResult, Chr(9), " ")
|
||||||
|
sResult = VBA.Replace(sResult, Chr(7), " ")
|
||||||
|
sResult = VBA.Replace(sResult, " ", " ")
|
||||||
|
SafeFileName = sResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function QuotedStr(sText$) As String
|
||||||
|
QuotedStr = """" & sText & """"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function TrimWhitespace(ByVal target$) As String
|
||||||
|
Dim sResult$: sResult = target
|
||||||
|
Dim bChanged As Boolean: bChanged = True
|
||||||
|
Do While bChanged
|
||||||
|
If VBA.Len(sResult) < 1 Then _
|
||||||
|
Exit Do
|
||||||
|
bChanged = False
|
||||||
|
|
||||||
|
If IsWhitespace(VBA.Left(sResult, 1)) Then
|
||||||
|
sResult = VBA.Right(sResult, Len(sResult) - 1)
|
||||||
|
bChanged = True
|
||||||
|
End If
|
||||||
|
|
||||||
|
If VBA.Len(sResult) < 1 Then _
|
||||||
|
Exit Do
|
||||||
|
If IsWhitespace(VBA.Right(sResult, 1)) Then
|
||||||
|
sResult = VBA.Left(sResult, VBA.Len(sResult) - 1)
|
||||||
|
bChanged = True
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
|
||||||
|
TrimWhitespace = sResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function TrimEndPunctuation(ByVal target$) As String
|
||||||
|
Dim sResult$: sResult = target
|
||||||
|
Dim isChanged As Boolean: isChanged = True
|
||||||
|
Do While isChanged
|
||||||
|
isChanged = False
|
||||||
|
|
||||||
|
If VBA.Len(sResult) < 1 Then _
|
||||||
|
GoTo END_LOOP
|
||||||
|
If IsPunctuation(VBA.Right(sResult, 1)) Then
|
||||||
|
sResult = VBA.Left(sResult, VBA.Len(sResult) - 1)
|
||||||
|
isChanged = True
|
||||||
|
End If
|
||||||
|
|
||||||
|
END_LOOP:
|
||||||
|
Loop
|
||||||
|
|
||||||
|
TrimEndPunctuation = sResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SubstituteWhitespace(ByVal target$) As String
|
||||||
|
SubstituteWhitespace = VBA.Replace(target, Chr(160), " ")
|
||||||
|
SubstituteWhitespace = VBA.Replace(SubstituteWhitespace, Chr(7), " ")
|
||||||
|
SubstituteWhitespace = VBA.Replace(SubstituteWhitespace, Chr(10), " ")
|
||||||
|
SubstituteWhitespace = VBA.Replace(SubstituteWhitespace, Chr(13), " ")
|
||||||
|
SubstituteWhitespace = VBA.Replace(SubstituteWhitespace, Chr(9), " ")
|
||||||
|
SubstituteWhitespace = VBA.Replace(SubstituteWhitespace, Chr(11), " ")
|
||||||
|
SubstituteWhitespace = VBA.Replace(SubstituteWhitespace, " ", " ")
|
||||||
|
SubstituteWhitespace = Trim(SubstituteWhitespace)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FixSpecialSymbols(ByVal target$) As String
|
||||||
|
FixSpecialSymbols = VBA.Replace(target, Chr(160), " ")
|
||||||
|
FixSpecialSymbols = VBA.Replace(FixSpecialSymbols, Chr(7), " ")
|
||||||
|
FixSpecialSymbols = VBA.Replace(FixSpecialSymbols, Chr(10), " ")
|
||||||
|
FixSpecialSymbols = VBA.Replace(FixSpecialSymbols, Chr(13), " ")
|
||||||
|
FixSpecialSymbols = VBA.Replace(FixSpecialSymbols, Chr(9), " ")
|
||||||
|
FixSpecialSymbols = VBA.Replace(FixSpecialSymbols, Chr(11), " ")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CapitalizeFirstLetter(target$) As String
|
||||||
|
If VBA.Len(target) > 1 Then
|
||||||
|
CapitalizeFirstLetter = VBA.UCase(VBA.Left(target, 1)) & VBA.Right(target, VBA.Len(target) - 1)
|
||||||
|
Else
|
||||||
|
CapitalizeFirstLetter = target
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function IsAlphaNumeric(sText$) As Boolean
|
||||||
|
IsAlphaNumeric = VBA.Len(sText) > 0 And Not sText Like "*[!a-zA-Zа-яА-ЯёЁ0-9]*"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ================
|
||||||
|
Private Function IsWhitespace(sSymbol$) As Boolean
|
||||||
|
IsWhitespace = True
|
||||||
|
If sSymbol = " " Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = Chr(7) Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = Chr(9) Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = Chr(13) Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = Chr(11) Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = Chr(10) Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = Chr(160) Then
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
IsWhitespace = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IsPunctuation(sSymbol$) As Boolean
|
||||||
|
IsPunctuation = True
|
||||||
|
If sSymbol = "." Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = "," Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = "-" Then _
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ChrW(&H2013) Then _
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ChrW(&H2012) Then _
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ChrW(&H2010) Then _
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ";" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = " " Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = "!" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = ":" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = "?" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = """" Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf sSymbol = "Chr(85)" Then
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
IsPunctuation = False
|
||||||
|
End Function
|
29
utility/ex_Hash.bas
Normal file
29
utility/ex_Hash.bas
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
Attribute VB_Name = "ex_Hash"
|
||||||
|
' ======== MD5 hash functions ========
|
||||||
|
' Shared module version: 20210408
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Function MD5AsLong(sText$) As Long
|
||||||
|
Dim sMD5$: sMD5 = MD5AsString(sText)
|
||||||
|
MD5AsLong = Abs(CLng("&H" & Right(sMD5, 8)))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function MD5AsString(sText$) As String
|
||||||
|
' Uses late binding for mscorlib 4.0 64-bit
|
||||||
|
If sText = vbNullString Then _
|
||||||
|
sText = ""
|
||||||
|
Dim oText As Object: Set oText = CreateObject("System.Text.UTF8Encoding")
|
||||||
|
Dim inBytes() As Byte: inBytes = oText.GetBytes_4(sText)
|
||||||
|
|
||||||
|
Dim oMD5 As Object: Set oMD5 = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
|
||||||
|
Dim outBytes() As Byte: outBytes = oMD5.ComputeHash_2(inBytes)
|
||||||
|
|
||||||
|
Dim nPos&
|
||||||
|
For nPos = 1 To VBA.LenB(outBytes) Step 1
|
||||||
|
MD5AsString = MD5AsString & LCase(Right("0" & VBA.Hex(VBA.AscB(VBA.MidB(outBytes, nPos, 1))), 2))
|
||||||
|
Next
|
||||||
|
End Function
|
74
utility/ex_MSHook.bas
Normal file
74
utility/ex_MSHook.bas
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
Attribute VB_Name = "ex_MSHook"
|
||||||
|
' =========== Microsoft event hooks ================
|
||||||
|
' Shared module version: 20220703
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function FindWindow Lib "user32" _
|
||||||
|
Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
|
||||||
|
Private Declare PtrSafe Function GetWindowLong Lib "user32" _
|
||||||
|
Alias "GetWindowLongA" (ByVal nHwnd As Long, ByVal nIndex As Long) As Long
|
||||||
|
Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As Long
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function SetWindowsHookEx Lib "user32" _
|
||||||
|
Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpFn As LongPtr, ByVal hmod As LongPtr, ByVal dwThreadId As Long) As LongPtr
|
||||||
|
Private Declare PtrSafe Function CallNextHookEx Lib "user32" _
|
||||||
|
(ByVal hHook As LongPtr, ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As LongPtr
|
||||||
|
Private Declare PtrSafe Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As LongPtr) As Long
|
||||||
|
|
||||||
|
Public Type LongPoint
|
||||||
|
x_ As Long
|
||||||
|
y_ As Long
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Public Type MSLLHOOKSTRUCT
|
||||||
|
point_ As LongPoint
|
||||||
|
direction_ As Long
|
||||||
|
flags_ As Long
|
||||||
|
time_ As Long
|
||||||
|
info_ As Long
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Public g_mouseHook As LongPtr
|
||||||
|
Public g_hookData As MSLLHOOKSTRUCT
|
||||||
|
Public g_hookCallback As Object
|
||||||
|
Public g_hookCBFunction As String
|
||||||
|
|
||||||
|
Private Const HC_ACTION = 0
|
||||||
|
Private Const WH_MOUSE_LL = 14
|
||||||
|
Private Const WM_MOUSEWHEEL = &H20A
|
||||||
|
Private Const GWL_HINSTANCE = (-6)
|
||||||
|
|
||||||
|
Private Const USERFORM_CLASSNAME = "ThunderDFrame"
|
||||||
|
|
||||||
|
Public Function HookEnable(oCallback As Object, sCallbackFunc$)
|
||||||
|
If g_mouseHook > 0 Then _
|
||||||
|
Exit Function
|
||||||
|
Set g_hookCallback = oCallback
|
||||||
|
g_hookCBFunction = sCallbackFunc
|
||||||
|
g_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf HookProcess, _
|
||||||
|
GetWindowLong(FindWindow(USERFORM_CLASSNAME, g_hookCallback.Caption), GWL_HINSTANCE), 0)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function HookDisable()
|
||||||
|
If g_mouseHook = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
Call UnhookWindowsHookEx(g_mouseHook)
|
||||||
|
g_mouseHook = 0
|
||||||
|
Set g_hookCallback = Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function HookProcess(ByVal nCode As Long, ByVal wParam As Long, ByRef lParam As MSLLHOOKSTRUCT) As LongPtr
|
||||||
|
If nCode <> HC_ACTION Or wParam <> WM_MOUSEWHEEL Then
|
||||||
|
HookProcess = CallNextHookEx(0, nCode, wParam, ByVal lParam)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
If GetForegroundWindow <> FindWindow(USERFORM_CLASSNAME, g_hookCallback.Caption) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Call CallByName(g_hookCallback, g_hookCBFunction, VbMethod, lParam.direction_)
|
||||||
|
HookProcess = True
|
||||||
|
End Function
|
108
utility/ex_Regex.bas
Normal file
108
utility/ex_Regex.bas
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
Attribute VB_Name = "ex_Regex"
|
||||||
|
' ======= General functions for String regex manipulations ========
|
||||||
|
' Shared module version: 20210411
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: VBScript_RegExp_55
|
||||||
|
|
||||||
|
' ====== API params =============
|
||||||
|
' sTarget - string to execute the regex on
|
||||||
|
' sPattern - the regular expression with at least 1 capture '()'
|
||||||
|
' nMatch - the index of the match you want to return (default: 0)
|
||||||
|
' nSubMatch - the index of the submatch you want to return (default: 0)
|
||||||
|
|
||||||
|
' Regex Execute returns collection of Match
|
||||||
|
' Match API: FirstIndex (0-indexed), Length, Value
|
||||||
|
|
||||||
|
' More info: https://vremya-ne-zhdet.ru/vba-excel/regulyarnyye-vyrazheniya/
|
||||||
|
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const REGEXP_SPECIAL$ = "[\?\.\$\^\(\)\[\]\{\}\\]"
|
||||||
|
|
||||||
|
Public Function RegexCountMatches(sTarget$, sPattern$) As String
|
||||||
|
'Returns the number of matches found for a given regex
|
||||||
|
Dim regEx As New VBScript_RegExp_55.RegExp
|
||||||
|
regEx.Pattern = sPattern
|
||||||
|
regEx.Global = True
|
||||||
|
Dim matches As Object
|
||||||
|
If regEx.Test(sTarget) Then
|
||||||
|
Set matches = regEx.Execute(sTarget)
|
||||||
|
RegexCountMatches = matches.Count
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Function RegexExecute(sTarget$, sPattern$, Optional bOnlyFirstMatch As Boolean = False) As Object
|
||||||
|
'Executes a Regular Expression on a provided string and returns all matches
|
||||||
|
Dim regEx As New VBScript_RegExp_55.RegExp
|
||||||
|
regEx.Pattern = sPattern
|
||||||
|
regEx.Global = Not (bOnlyFirstMatch)
|
||||||
|
If regEx.Test(sTarget) Then
|
||||||
|
Set RegexExecute = regEx.Execute(sTarget)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RegexExecuteGet(sTarget$, sPattern$, Optional nMatch& = 0, Optional nSubMatch& = 0) As String
|
||||||
|
'Executes a Regular Expression on a provided string and returns a selected submatch
|
||||||
|
Dim regEx As New VBScript_RegExp_55.RegExp
|
||||||
|
regEx.Pattern = sPattern
|
||||||
|
regEx.Global = nMatch <> 0 Or nSubMatch <> 0
|
||||||
|
|
||||||
|
If regEx.Test(sTarget) Then
|
||||||
|
Dim matches As Object: Set matches = regEx.Execute(sTarget)
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
RegexExecuteGet = matches(nMatch).SubMatches(nSubMatch)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RegexTest(sTarget$, sPattern$) As Boolean
|
||||||
|
' Test if sTarget matches regExp
|
||||||
|
On Error GoTo EXIT_FUNC
|
||||||
|
|
||||||
|
Dim regEx As New VBScript_RegExp_55.RegExp
|
||||||
|
regEx.Pattern = sTarget
|
||||||
|
regEx.Global = False
|
||||||
|
RegexTest = regEx.Test(sPattern)
|
||||||
|
|
||||||
|
On Error GoTo 0
|
||||||
|
EXIT_FUNC:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RegexMaskFrom(sTarget$) As String
|
||||||
|
' Creating regex mask from string - using \ to shield regular symbols
|
||||||
|
RegexMaskFrom = RegexReplace(sTarget, "(" + REGEXP_SPECIAL + ")", "\$1")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RegexTextFromMask(mask$) As String
|
||||||
|
' Remove shielding from symbols
|
||||||
|
RegexTextFromMask = RegexReplace(mask, "\\" + "(" + REGEXP_SPECIAL + ")", "$1")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function RegexReplace(sTarget$, sPattern$, replaceStr$, Optional replaceLimit& = -1) As String
|
||||||
|
Attribute RegexReplace.VB_Description = "Replace a pattern within a string with the provided replacement string based on all captures of the specified regular expression"
|
||||||
|
Attribute RegexReplace.VB_ProcData.VB_Invoke_Func = " \n9"
|
||||||
|
'Replaces a string using Regular Expressions
|
||||||
|
'replaceStr - the string with which the sPattern pattern substrings are to be replaced with
|
||||||
|
'replaceLimit - by default unlimited (-1). Providing value will limit the number of performed replacements
|
||||||
|
If replaceLimit = 0 Then
|
||||||
|
RegexReplace = sTarget
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim regEx As New VBScript_RegExp_55.RegExp
|
||||||
|
regEx.Pattern = sPattern
|
||||||
|
regEx.Global = IIf(replaceLimit = -1, True, False)
|
||||||
|
If replaceLimit <> -1 And replaceLimit <> 1 Then
|
||||||
|
RegexReplace = sTarget
|
||||||
|
Dim i&
|
||||||
|
For i = 1 To replaceLimit
|
||||||
|
RegexReplace = RegexReplace(RegexReplace, sPattern, replaceStr, 1)
|
||||||
|
Next i
|
||||||
|
Else
|
||||||
|
RegexReplace = regEx.Replace(sTarget, replaceStr)
|
||||||
|
End If
|
||||||
|
End Function
|
180
utility/ex_Time.bas
Normal file
180
utility/ex_Time.bas
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
Attribute VB_Name = "ex_Time"
|
||||||
|
' ======== Time conversion ========
|
||||||
|
' Shared module version: 20210630
|
||||||
|
' Tested in:
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
''
|
||||||
|
' VBA-UTC v1.0.6
|
||||||
|
' (c) Tim Hall - https://github.com/VBA-tools/VBA-UtcConverter
|
||||||
|
' UTC/ISO 8601 Converter for VBA
|
||||||
|
' @license MIT (http://www.opensource.org/licenses/mit-license.php)
|
||||||
|
'' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
|
||||||
|
|
||||||
|
Private Declare PtrSafe Function UTC_GetTimeZoneInfo Lib "kernel32" Alias "GetTimeZoneInformation" _
|
||||||
|
(utc_lpTimeZoneInformation As TTimeZone) As Long
|
||||||
|
Private Declare PtrSafe Function UTC_SystemTimeToLocalTime Lib "kernel32" Alias "SystemTimeToTzSpecificLocalTime" _
|
||||||
|
(utc_lpTimeZoneInformation As TTimeZone, utc_lpUniversalTime As TSystemTime, utc_lpLocalTime As TSystemTime) As Long
|
||||||
|
Private Declare PtrSafe Function UTC_LocalTimeToSystemTime Lib "kernel32" Alias "TzSpecificLocalTimeToSystemTime" _
|
||||||
|
(utc_lpTimeZoneInformation As TTimeZone, utc_lpLocalTime As TSystemTime, utc_lpUniversalTime As TSystemTime) As Long
|
||||||
|
|
||||||
|
Private Type TSystemTime
|
||||||
|
utc_wYear As Integer
|
||||||
|
utc_wMonth As Integer
|
||||||
|
utc_wDayOfWeek As Integer
|
||||||
|
utc_wDay As Integer
|
||||||
|
utc_wHour As Integer
|
||||||
|
utc_wMinute As Integer
|
||||||
|
utc_wSecond As Integer
|
||||||
|
utc_wMilliseconds As Integer
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Private Type TTimeZone
|
||||||
|
utc_Bias As Long
|
||||||
|
utc_StandardName(0 To 31) As Integer
|
||||||
|
utc_StandardDate As TSystemTime
|
||||||
|
utc_StandardBias As Long
|
||||||
|
utc_DaylightName(0 To 31) As Integer
|
||||||
|
utc_DaylightDate As TSystemTime
|
||||||
|
utc_DaylightBias As Long
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Private Const ERR_UTC_PARSE_FAIL = vbObjectError + 1025
|
||||||
|
Private Const ERR_UTC_CONVERSION_FAIL = vbObjectError + 1026
|
||||||
|
Private Const ERR_ISO_PARSE_FAIL = vbObjectError + 1027
|
||||||
|
Private Const ERR_ISO_CONVERSION_FAIL = vbObjectError + 1028
|
||||||
|
|
||||||
|
' Parse UTC date to local date
|
||||||
|
Public Function ParseUtc(utc_UtcDate As Date) As Date
|
||||||
|
On Error GoTo ON_ERROR
|
||||||
|
|
||||||
|
Dim utc_TimeZoneInfo As TTimeZone
|
||||||
|
Dim utc_LocalDate As TSystemTime
|
||||||
|
|
||||||
|
Call UTC_GetTimeZoneInfo(utc_TimeZoneInfo)
|
||||||
|
Call UTC_SystemTimeToLocalTime(utc_TimeZoneInfo, UTC_DateToSystemTime(utc_UtcDate), utc_LocalDate)
|
||||||
|
|
||||||
|
ParseUtc = UTC_SystemTimeToDate(utc_LocalDate)
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
ON_ERROR:
|
||||||
|
Call Err.Raise(ERR_UTC_PARSE_FAIL, "UtcConverter.ParseUtc", "UTC parsing error: " & Err.Number & " - " & Err.Description)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Convert local date to UTC date
|
||||||
|
Public Function ConvertToUtc(utc_LocalDate As Date) As Date
|
||||||
|
On Error GoTo ON_ERROR
|
||||||
|
|
||||||
|
Dim utc_TimeZoneInfo As TTimeZone
|
||||||
|
Dim utc_UtcDate As TSystemTime
|
||||||
|
|
||||||
|
UTC_GetTimeZoneInfo utc_TimeZoneInfo
|
||||||
|
UTC_LocalTimeToSystemTime utc_TimeZoneInfo, UTC_DateToSystemTime(utc_LocalDate), utc_UtcDate
|
||||||
|
|
||||||
|
ConvertToUtc = UTC_SystemTimeToDate(utc_UtcDate)
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
ON_ERROR:
|
||||||
|
Call Err.Raise(ERR_UTC_PARSE_FAIL, "UtcConverter.ConvertToUtc", "UTC conversion error: " & Err.Number & " - " & Err.Description)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Parse ISO 8601 date string to local date
|
||||||
|
Public Function ParseIso(sISOTime$) As Date
|
||||||
|
On Error GoTo ON_ERROR
|
||||||
|
|
||||||
|
Dim utc_Parts() As String
|
||||||
|
Dim utc_DateParts() As String
|
||||||
|
Dim utc_TimeParts() As String
|
||||||
|
Dim utc_OffsetIndex&
|
||||||
|
Dim utc_HasOffset As Boolean
|
||||||
|
Dim utc_NegativeOffset As Boolean
|
||||||
|
Dim utc_OffsetParts() As String
|
||||||
|
Dim utc_Offset As Date
|
||||||
|
|
||||||
|
utc_Parts = VBA.Split(sISOTime, "T")
|
||||||
|
utc_DateParts = VBA.Split(utc_Parts(0), "-")
|
||||||
|
ParseIso = VBA.DateSerial(VBA.CInt(utc_DateParts(0)), VBA.CInt(utc_DateParts(1)), VBA.CInt(utc_DateParts(2)))
|
||||||
|
|
||||||
|
If UBound(utc_Parts) > 0 Then
|
||||||
|
If VBA.InStr(utc_Parts(1), "Z") Then
|
||||||
|
utc_TimeParts = VBA.Split(VBA.Replace(utc_Parts(1), "Z", ""), ":")
|
||||||
|
Else
|
||||||
|
utc_OffsetIndex = VBA.InStr(1, utc_Parts(1), "+")
|
||||||
|
If utc_OffsetIndex = 0 Then
|
||||||
|
utc_NegativeOffset = True
|
||||||
|
utc_OffsetIndex = VBA.InStr(1, utc_Parts(1), "-")
|
||||||
|
End If
|
||||||
|
|
||||||
|
If utc_OffsetIndex > 0 Then
|
||||||
|
utc_HasOffset = True
|
||||||
|
utc_TimeParts = VBA.Split(VBA.Left$(utc_Parts(1), utc_OffsetIndex - 1), ":")
|
||||||
|
utc_OffsetParts = VBA.Split(VBA.Right$(utc_Parts(1), Len(utc_Parts(1)) - utc_OffsetIndex), ":")
|
||||||
|
|
||||||
|
Select Case UBound(utc_OffsetParts)
|
||||||
|
Case 0
|
||||||
|
utc_Offset = TimeSerial(VBA.CInt(utc_OffsetParts(0)), 0, 0)
|
||||||
|
Case 1
|
||||||
|
utc_Offset = TimeSerial(VBA.CInt(utc_OffsetParts(0)), VBA.CInt(utc_OffsetParts(1)), 0)
|
||||||
|
Case 2
|
||||||
|
' VBA.Val does not use regional settings, use for seconds to avoid decimal/comma issues
|
||||||
|
utc_Offset = TimeSerial(VBA.CInt(utc_OffsetParts(0)), VBA.CInt(utc_OffsetParts(1)), Int(VBA.Val(utc_OffsetParts(2))))
|
||||||
|
End Select
|
||||||
|
|
||||||
|
If utc_NegativeOffset Then: utc_Offset = -utc_Offset
|
||||||
|
Else
|
||||||
|
utc_TimeParts = VBA.Split(utc_Parts(1), ":")
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
Select Case UBound(utc_TimeParts)
|
||||||
|
Case 0
|
||||||
|
ParseIso = ParseIso + VBA.TimeSerial(VBA.CInt(utc_TimeParts(0)), 0, 0)
|
||||||
|
Case 1
|
||||||
|
ParseIso = ParseIso + VBA.TimeSerial(VBA.CInt(utc_TimeParts(0)), VBA.CInt(utc_TimeParts(1)), 0)
|
||||||
|
Case 2
|
||||||
|
' VBA.Val does not use regional settings, use for seconds to avoid decimal/comma issues
|
||||||
|
ParseIso = ParseIso + VBA.TimeSerial(VBA.CInt(utc_TimeParts(0)), VBA.CInt(utc_TimeParts(1)), Int(VBA.Val(utc_TimeParts(2))))
|
||||||
|
End Select
|
||||||
|
|
||||||
|
ParseIso = ParseUtc(ParseIso)
|
||||||
|
|
||||||
|
If utc_HasOffset Then
|
||||||
|
ParseIso = ParseIso - utc_Offset
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
ON_ERROR:
|
||||||
|
Err.Raise ERR_ISO_PARSE_FAIL, "UtcConverter.ParseIso", "ISO 8601 parsing error for " & sISOTime & ": " & Err.Number & " - " & Err.Description
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Convert local date to ISO 8601 string
|
||||||
|
Public Function ConvertToIso(utc_LocalDate As Date) As String
|
||||||
|
On Error GoTo ON_ERROR
|
||||||
|
ConvertToIso = VBA.Format$(ConvertToUtc(utc_LocalDate), "yyyy-mm-ddTHH:mm:ss.000Z")
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
ON_ERROR:
|
||||||
|
Call Err.Raise(ERR_ISO_CONVERSION_FAIL, "UtcConverter.ConvertToIso", "ISO 8601 conversion error: " & Err.Number & " - " & Err.Description)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =============================================
|
||||||
|
Private Function UTC_DateToSystemTime(utc_Value As Date) As TSystemTime
|
||||||
|
UTC_DateToSystemTime.utc_wYear = VBA.Year(utc_Value)
|
||||||
|
UTC_DateToSystemTime.utc_wMonth = VBA.Month(utc_Value)
|
||||||
|
UTC_DateToSystemTime.utc_wDay = VBA.Day(utc_Value)
|
||||||
|
UTC_DateToSystemTime.utc_wHour = VBA.Hour(utc_Value)
|
||||||
|
UTC_DateToSystemTime.utc_wMinute = VBA.Minute(utc_Value)
|
||||||
|
UTC_DateToSystemTime.utc_wSecond = VBA.Second(utc_Value)
|
||||||
|
UTC_DateToSystemTime.utc_wMilliseconds = 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function UTC_SystemTimeToDate(utc_Value As TSystemTime) As Date
|
||||||
|
UTC_SystemTimeToDate = DateSerial(utc_Value.utc_wYear, utc_Value.utc_wMonth, utc_Value.utc_wDay) + _
|
||||||
|
TimeSerial(utc_Value.utc_wHour, utc_Value.utc_wMinute, utc_Value.utc_wSecond)
|
||||||
|
End Function
|
||||||
|
|
469
utility/ex_VBA.bas
Normal file
469
utility/ex_VBA.bas
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
Attribute VB_Name = "ex_VBA"
|
||||||
|
' ======== VBA Extensions for objects and messaging ========
|
||||||
|
' Shared module version: 20220623
|
||||||
|
' Tested in: TestCommons
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Const VBA_INVALID_SIZE = -1
|
||||||
|
|
||||||
|
Public Enum SystemErrors
|
||||||
|
SYS_ERR_TYPE_MISMATCH = 13
|
||||||
|
SYS_ERR_INVALID_OPERATION = 17
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Function ArraySize(target As Variant, Optional nDimension% = 1) As Long
|
||||||
|
On Error GoTo INVALID_SIZE
|
||||||
|
|
||||||
|
ArraySize = UBound(target, nDimension) - LBound(target, nDimension) + 1
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INVALID_SIZE:
|
||||||
|
ArraySize = VBA_INVALID_SIZE
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Returns dictionary of indicies of first occurence for every element in array @aSource
|
||||||
|
' Note: duplicate elements are ignored
|
||||||
|
Public Function ArrayToIndex(iSource As Variant) As Scripting.Dictionary
|
||||||
|
If Not IsArrayAllocated(iSource) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim indicies As New Scripting.Dictionary
|
||||||
|
Dim vValue As Variant
|
||||||
|
Dim nIndex&: nIndex = LBound(iSource, 1)
|
||||||
|
For Each vValue In iSource
|
||||||
|
If Not indicies.Exists(vValue) Then _
|
||||||
|
Call indicies.Add(vValue, nIndex)
|
||||||
|
nIndex = nIndex + 1
|
||||||
|
Next vValue
|
||||||
|
Set ArrayToIndex = indicies
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function IsArrayAllocated(target As Variant) As Boolean
|
||||||
|
IsArrayAllocated = False
|
||||||
|
If Not IsArray(target) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
Dim nSize&: nSize = UBound(target, 1)
|
||||||
|
If Err.Number <> 0 Then
|
||||||
|
Call Err.Clear
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
IsArrayAllocated = LBound(target) <= UBound(target)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FixForwardedParams(params As Variant) As Variant
|
||||||
|
FixForwardedParams = params
|
||||||
|
If IsArrayAllocated(params) Then _
|
||||||
|
If VBA.IsArray(params(0)) Then _
|
||||||
|
If ArraySize(params, 1) = 1 Then _
|
||||||
|
FixForwardedParams = params(0)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CColl(ParamArray params() As Variant) As Collection
|
||||||
|
Set CColl = New Collection
|
||||||
|
Dim val As Variant
|
||||||
|
For Each val In params
|
||||||
|
Call CColl.Add(val)
|
||||||
|
Next val
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CSet(ParamArray params() As Variant) As Scripting.Dictionary
|
||||||
|
Set CSet = New Scripting.Dictionary
|
||||||
|
Dim vValue As Variant
|
||||||
|
For Each vValue In params
|
||||||
|
CSet.Item(vValue) = 0
|
||||||
|
Next vValue
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CDict(ParamArray params() As Variant) As Scripting.Dictionary
|
||||||
|
If UBound(params) > 0 Then _
|
||||||
|
If UBound(params) Mod 2 <> 1 Then _
|
||||||
|
Call Err.Raise(vbObjectError, Description:="Dictionary params should be key and value pairs")
|
||||||
|
|
||||||
|
Set CDict = New Scripting.Dictionary
|
||||||
|
If UBound(params) <= 0 Then _
|
||||||
|
Exit Function
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 0 To UBound(params) / 2 Step 1
|
||||||
|
Call CDict.Add(params(2 * nItem), params(2 * nItem + 1))
|
||||||
|
Next nItem
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CollectionToDictionary(iSource As Collection) As Scripting.Dictionary
|
||||||
|
Dim iDict As New Scripting.Dictionary
|
||||||
|
Dim nIndex&: nIndex = 1
|
||||||
|
Dim vValue As Variant
|
||||||
|
For Each vValue In iSource
|
||||||
|
If Not iDict.Exists(vValue) Then _
|
||||||
|
Call iDict.Add(vValue, nIndex)
|
||||||
|
nIndex = nIndex + 1
|
||||||
|
Next vValue
|
||||||
|
Set CollectionToDictionary = iDict
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Add unique (key, value) pairs from Source to Destination without deep copy
|
||||||
|
Public Function DictionaryAbsorbShallow(iDestination As Scripting.Dictionary, iSource As Scripting.Dictionary)
|
||||||
|
Dim aKey As Variant
|
||||||
|
For Each aKey In iSource
|
||||||
|
If Not iDestination.Exists(aKey) Then _
|
||||||
|
Call iDestination.Add(aKey, iSource(aKey))
|
||||||
|
Next aKey
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Convert paramarray into unique list, where value equals 1-based index into input array
|
||||||
|
' Duplicates are skipped
|
||||||
|
Public Function UniqueList(ParamArray params() As Variant) As Scripting.Dictionary
|
||||||
|
Set UniqueList = New Scripting.Dictionary
|
||||||
|
Dim vValue As Variant
|
||||||
|
Dim nIndex&: nIndex = 1
|
||||||
|
For Each vValue In params
|
||||||
|
If Not UniqueList.Exists(vValue) Then _
|
||||||
|
Call UniqueList.Add(vValue, nIndex)
|
||||||
|
nIndex = nIndex + 1
|
||||||
|
Next vValue
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Compare two floats / integers and return difference or 0 if Precision is not met
|
||||||
|
' Note: positive precision indicates number of positions after decimal
|
||||||
|
' while negative precision indicates positions before decimal
|
||||||
|
Public Function CompareApproximate(val1 As Variant, val2 As Variant, nDecimalPrecision&) As Double
|
||||||
|
CompareApproximate = val1 - val2
|
||||||
|
If CompareApproximate = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
If VBA.Log(VBA.Abs(CompareApproximate)) / VBA.Log(10) < -nDecimalPrecision Then _
|
||||||
|
CompareApproximate = 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Compate two Variants
|
||||||
|
' Integer values will resolve into their difference
|
||||||
|
' Other types will return 0 if objects are equal and unspecified number otherwise
|
||||||
|
' Object type will try to call Compare() function if it is available for the type
|
||||||
|
' otherwise objects will by compared using ObjPtr()
|
||||||
|
' If there is no way to compare values the Error SYS_ERR_INVALID_OPERATION will be raised
|
||||||
|
' Warning: multidimensional arrays are not supported!
|
||||||
|
Public Function CompareDeep(val1 As Variant, val2 As Variant) As Double
|
||||||
|
If TryMinus(val1, val2, CompareDeep) Then
|
||||||
|
Exit Function
|
||||||
|
ElseIf TryEquality(val1, val2) Then
|
||||||
|
CompareDeep = 0
|
||||||
|
Exit Function
|
||||||
|
ElseIf TryInequality(val1, val2) Then
|
||||||
|
CompareDeep = 1
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim vType As VBA.VbVarType: vType = VBA.VarType(val1)
|
||||||
|
If vType <> VBA.VarType(val2) Then
|
||||||
|
GoTo UNCOMPARABLE
|
||||||
|
ElseIf vType >= vbArray And vType <= vbArray + vbByte Then
|
||||||
|
CompareDeep = CompareDeepArray(val1, val2)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim sType$: sType = VBA.TypeName(val1)
|
||||||
|
If sType <> VBA.TypeName(val2) Then _
|
||||||
|
GoTo UNCOMPARABLE
|
||||||
|
|
||||||
|
On Error GoTo UNCOMPARABLE
|
||||||
|
Select Case sType
|
||||||
|
Case "Collection": CompareDeep = CompareDeepCollection(val1, val2)
|
||||||
|
Case "Dictionary": CompareDeep = CompareDeepDictionary(val1, val2)
|
||||||
|
Case Else: CompareDeep = CompareDeepObjects(val1, val2)
|
||||||
|
End Select
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
UNCOMPARABLE:
|
||||||
|
Call Err.Raise(SYS_ERR_INVALID_OPERATION)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExtractTimestamp(sLine$) As Double
|
||||||
|
Dim sStamp$: sStamp = ExtractDatePrefix(sLine)
|
||||||
|
If sStamp = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo INVALID_FORMAT
|
||||||
|
ExtractTimestamp = DateValue(sStamp) + TimeValue(sStamp)
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
INVALID_FORMAT:
|
||||||
|
Call Err.Clear
|
||||||
|
ExtractTimestamp = 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function TrimTimestamp(sLine$) As String
|
||||||
|
TrimTimestamp = sLine
|
||||||
|
Dim sStamp$: sStamp = ExtractDatePrefix(sLine)
|
||||||
|
If sStamp = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
If ExtractTimestamp(sStamp) = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
If VBA.Len(sLine) > VBA.Len(sStamp) + 2 Then
|
||||||
|
TrimTimestamp = VBA.Right(sLine, VBA.Len(sLine) - VBA.Len(sStamp) - 2)
|
||||||
|
Else
|
||||||
|
TrimTimestamp = vbNullString
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CommonPrefixLength(sText1$, sText2$, Optional nMode As VbCompareMethod = vbBinaryCompare) As Long
|
||||||
|
Dim nPrefixEnd&: nPrefixEnd = 0
|
||||||
|
Dim nLen&: nLen = VBA.Len(sText1)
|
||||||
|
nLen = IIf(nLen < VBA.Len(sText2), VBA.Len(sText2), nLen)
|
||||||
|
Do While nPrefixEnd < nLen
|
||||||
|
If VBA.StrComp(VBA.Mid(sText1, nPrefixEnd + 1, 1), _
|
||||||
|
VBA.Mid(sText2, nPrefixEnd + 1, 1), nMode) <> 0 Then _
|
||||||
|
Exit Do
|
||||||
|
nPrefixEnd = nPrefixEnd + 1
|
||||||
|
Loop
|
||||||
|
CommonPrefixLength = nPrefixEnd
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Formatting function
|
||||||
|
' Expected message format: "test {1} argument enumerating 1-based"
|
||||||
|
' Note: params array is 0-base enumerated
|
||||||
|
Public Function Fmt(sMsg$, ParamArray params() As Variant) As String
|
||||||
|
Dim vValue As Variant
|
||||||
|
Dim nIndex&: nIndex = 1
|
||||||
|
Fmt = sMsg
|
||||||
|
|
||||||
|
Dim unwrapped As Variant: unwrapped = params
|
||||||
|
unwrapped = FixForwardedParams(unwrapped)
|
||||||
|
For Each vValue In unwrapped
|
||||||
|
Fmt = VBA.Replace(Fmt, "{" & nIndex & "}", PrettyPrint(vValue))
|
||||||
|
nIndex = nIndex + 1
|
||||||
|
Next vValue
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Warning: multidimensional arrays are not supported!
|
||||||
|
Public Function PrettyPrint(vValue As Variant, Optional nIndent& = 0) As String
|
||||||
|
Select Case VBA.VarType(vValue)
|
||||||
|
Case VBA.vbEmpty: PrettyPrint = "[Empty]"
|
||||||
|
Case VBA.vbNull: PrettyPrint = "[Null]"
|
||||||
|
Case VBA.vbString: PrettyPrint = vValue
|
||||||
|
Case VBA.vbArray To VBA.vbArray + VBA.vbByte: PrettyPrint = PrintArray(vValue, nIndent)
|
||||||
|
Case VBA.vbObject
|
||||||
|
If vValue Is Nothing Then
|
||||||
|
PrettyPrint = "[Nothing]"
|
||||||
|
ElseIf VBA.TypeName(vValue) = "Collection" Then
|
||||||
|
PrettyPrint = PrintCollection(vValue, nIndent)
|
||||||
|
ElseIf VBA.TypeName(vValue) = "Dictionary" Then
|
||||||
|
PrettyPrint = PrintDictionary(vValue, nIndent)
|
||||||
|
Else
|
||||||
|
PrettyPrint = PrintCustomObject(vValue, nIndent)
|
||||||
|
End If
|
||||||
|
Case Else: PrettyPrint = Var2Str(vValue)
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function Indent(Optional nIndent&)
|
||||||
|
Indent = VBA.String$(nIndent, " ")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ==========
|
||||||
|
Private Function ExtractDatePrefix(sLine$) As String
|
||||||
|
If sLine = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
Dim nEnd&: nEnd = VBA.InStr(1, sLine, " ")
|
||||||
|
If nEnd <> 0 Then _
|
||||||
|
nEnd = VBA.InStr(nEnd + 1, sLine, " ")
|
||||||
|
If nEnd = 0 Then
|
||||||
|
nEnd = VBA.Len(sLine)
|
||||||
|
Else
|
||||||
|
nEnd = nEnd - 1
|
||||||
|
End If
|
||||||
|
|
||||||
|
If VBA.Mid(sLine, nEnd, 1) = ":" Then _
|
||||||
|
nEnd = nEnd - 1
|
||||||
|
ExtractDatePrefix = VBA.Left(sLine, nEnd)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Var2Str(target As Variant) As String
|
||||||
|
On Error GoTo CANNOT_CONVERT
|
||||||
|
Var2Str = CStr(target)
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
CANNOT_CONVERT:
|
||||||
|
Var2Str = "[T" & VarType(target) & "] "
|
||||||
|
On Error GoTo NON_STRING
|
||||||
|
If target Is Nothing Then
|
||||||
|
Var2Str = Var2Str & "Nothing"
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
NON_STRING:
|
||||||
|
Var2Str = Var2Str & "non-stringable"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TryEquality(val1 As Variant, val2 As Variant) As Boolean
|
||||||
|
On Error Resume Next
|
||||||
|
TryEquality = val1 = val2
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TryInequality(val1 As Variant, val2 As Variant) As Boolean
|
||||||
|
On Error Resume Next
|
||||||
|
TryInequality = val1 <> val2
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function TryMinus(val1 As Variant, val2 As Variant, ByRef dDiff As Double) As Boolean
|
||||||
|
On Error GoTo CANNOT_DIFF
|
||||||
|
|
||||||
|
dDiff = val1 - val2
|
||||||
|
TryMinus = True
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
CANNOT_DIFF:
|
||||||
|
TryMinus = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CompareDeepArray(val1 As Variant, val2 As Variant) As Double
|
||||||
|
If Not IsArrayAllocated(val1) Then
|
||||||
|
CompareDeepArray = IIf(IsArrayAllocated(val2), 1, 0)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
CompareDeepArray = UBound(val1) - UBound(val2)
|
||||||
|
If CompareDeepArray <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
CompareDeepArray = LBound(val1) - LBound(val2)
|
||||||
|
If CompareDeepArray <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo UNCOMPARABLE_ELEMENTS
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = LBound(val1) To UBound(val2) Step 1
|
||||||
|
If CompareDeep(val1(nItem), val2(nItem)) <> 0 Then
|
||||||
|
CompareDeepArray = 1
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
CompareDeepArray = 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
UNCOMPARABLE_ELEMENTS:
|
||||||
|
CompareDeepArray = 1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CompareDeepCollection(val1 As Variant, val2 As Variant) As Double
|
||||||
|
Dim nCount&: nCount = val1.Count
|
||||||
|
CompareDeepCollection = nCount - val2.Count
|
||||||
|
If CompareDeepCollection <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo UNCOMPARABLE_ELEMENTS
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 1 To nCount Step 1
|
||||||
|
If CompareDeep(val1(nItem), val2(nItem)) <> 0 Then
|
||||||
|
CompareDeepCollection = 1
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
CompareDeepCollection = 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
UNCOMPARABLE_ELEMENTS:
|
||||||
|
CompareDeepCollection = 1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CompareDeepDictionary(val1 As Variant, val2 As Variant) As Double
|
||||||
|
Dim nCount&: nCount = val1.Count
|
||||||
|
CompareDeepDictionary = nCount - val2.Count
|
||||||
|
If CompareDeepDictionary <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo UNCOMPARABLE_ELEMENTS
|
||||||
|
Dim vKey As Variant
|
||||||
|
For Each vKey In val1
|
||||||
|
If Not val2.Exists(vKey) Then _
|
||||||
|
GoTo UNCOMPARABLE_ELEMENTS
|
||||||
|
If CompareDeep(val1(vKey), val2(vKey)) <> 0 Then
|
||||||
|
CompareDeepDictionary = 1
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next vKey
|
||||||
|
|
||||||
|
CompareDeepDictionary = 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
UNCOMPARABLE_ELEMENTS:
|
||||||
|
CompareDeepDictionary = 1
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CompareDeepObjects(val1 As Variant, val2 As Variant) As Double
|
||||||
|
On Error Resume Next
|
||||||
|
CompareDeepObjects = val1.Compare(val2)
|
||||||
|
If Err.Number = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
Call Err.Clear
|
||||||
|
CompareDeepObjects = IIf(ObjPtr(val1) = ObjPtr(val2), 0, 1)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function PrintCollection(target As Variant, nIndent&) As String
|
||||||
|
If target.Count = 0 Then
|
||||||
|
PrintCollection = "$Collection []"
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
PrintCollection = "$Collection [" & vbNewLine
|
||||||
|
Dim nItem&: nItem = 0
|
||||||
|
Dim anItem As Variant
|
||||||
|
For Each anItem In target
|
||||||
|
nItem = nItem + 1
|
||||||
|
PrintCollection = PrintCollection & _
|
||||||
|
Indent(nIndent + 1) & PrettyPrint(anItem, nIndent + 1) & _
|
||||||
|
IIf(nItem <> target.Count, ",", "") & vbNewLine
|
||||||
|
Next anItem
|
||||||
|
PrintCollection = PrintCollection & Indent(nIndent) & "]"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function PrintDictionary(target As Variant, nIndent&) As String
|
||||||
|
If target.Count = 0 Then
|
||||||
|
PrintDictionary = "$Dictionary []"
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
PrintDictionary = "$Dictionary [" & vbNewLine
|
||||||
|
Dim nItem&: nItem = 0
|
||||||
|
Dim aKey As Variant
|
||||||
|
For Each aKey In target
|
||||||
|
nItem = nItem + 1
|
||||||
|
PrintDictionary = PrintDictionary & _
|
||||||
|
Indent(nIndent + 1) & aKey & ": " & _
|
||||||
|
PrettyPrint(target(aKey), nIndent + 1) & _
|
||||||
|
IIf(nItem <> target.Count, ",", "") & vbNewLine
|
||||||
|
Next aKey
|
||||||
|
PrintDictionary = PrintDictionary & Indent(nIndent) & "]"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function PrintCustomObject(vValue As Variant, nIndent&) As String
|
||||||
|
On Error GoTo PRINT_TYPE
|
||||||
|
PrintCustomObject = vValue.ToString(nIndent)
|
||||||
|
Exit Function
|
||||||
|
PRINT_TYPE:
|
||||||
|
PrintCustomObject = "[" & VBA.TypeName(vValue) & "]"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function PrintArray(target As Variant, nIndent&) As String
|
||||||
|
If Not IsArrayAllocated(target) Then
|
||||||
|
PrintArray = "$Array []"
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
PrintArray = "$Array [" & vbNewLine
|
||||||
|
Dim nItem&
|
||||||
|
Dim nLast&: nLast = UBound(target, 1)
|
||||||
|
For nItem = LBound(target, 1) To nLast
|
||||||
|
PrintArray = PrintArray & _
|
||||||
|
Indent(nIndent + 1) & PrettyPrint(target(nItem), nIndent + 1) & _
|
||||||
|
IIf(nItem <> nLast, ",", "") & vbNewLine
|
||||||
|
Next nItem
|
||||||
|
PrintArray = PrintArray & Indent(nIndent) & "]"
|
||||||
|
End Function
|
||||||
|
|
130
utility/ex_Version.bas
Normal file
130
utility/ex_Version.bas
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
Attribute VB_Name = "ex_Version"
|
||||||
|
' ======== Versioning helpers ========
|
||||||
|
' Shared module version: 20221031
|
||||||
|
' Tested in:
|
||||||
|
' Depends on: API_DistrManifest
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Const CP_TOOLS_LOCAL = "C:\Tools"
|
||||||
|
Public Const CP_UPDATE_SCRIPT = "UpdateConceptProducts"
|
||||||
|
Public Const CP_VERSION_MSG_DELAY = 2000
|
||||||
|
|
||||||
|
Public Const CP_TOOLS_SERVER = "\\fs1.concept.ru\Exchange\ConceptDistr"
|
||||||
|
Public Const FILE_DISTRIBUTION_MANIFEST = "distribution_manifest.json"
|
||||||
|
Public Const CONCEPT_INSTALLER = "install.bat"
|
||||||
|
|
||||||
|
Public Const ADDIN_NAME_CONCEPT = "CONCEPT.dotm"
|
||||||
|
Public Const ADDIN_NAME_NPA = "_Concept-NPA.dotm"
|
||||||
|
Public Const ADDIN_NAME_MARKUP = "MARKUP.dotm"
|
||||||
|
Public Const ADDIN_NAME_PARSER = "Parsers.dotm"
|
||||||
|
Public Const ADDIN_NAME_MAKET = "_Maket.dotm"
|
||||||
|
|
||||||
|
Public Function VersionValidate(sProduct$, sCurrentVersion$)
|
||||||
|
Dim iLocal As API_DistrManifest: Set iLocal = VersionLocalManifest
|
||||||
|
Dim sLocalVersion$: sLocalVersion = iLocal.GetVersion(sProduct)
|
||||||
|
If sLocalVersion = vbNullString Then
|
||||||
|
Debug.Print "Cannot access local manifest"
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim iServer As API_DistrManifest: Set iServer = VersionServerManifest
|
||||||
|
Dim sServerVersion$: sServerVersion = iServer.GetVersion(sProduct)
|
||||||
|
|
||||||
|
Dim bResult As Boolean: bResult = True
|
||||||
|
If VersionCompare(sLocalVersion, sCurrentVersion) > 0 Then _
|
||||||
|
bResult = False
|
||||||
|
If sServerVersion <> vbNullString Then _
|
||||||
|
If VersionCompare(sServerVersion, sCurrentVersion) > 0 Then _
|
||||||
|
bResult = False
|
||||||
|
|
||||||
|
If bResult Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
If sServerVersion = vbNullString Then _
|
||||||
|
sServerVersion = "íå äîñòóïíà"
|
||||||
|
Dim sMsg$: sMsg = "Âíèìàíèå! Èñïîëüçóåìàÿ âåðñèÿ ïðîäóêòà óñòàðåëà!" & vbNewLine & _
|
||||||
|
"Ñîçäàéòå ôàéë â íîâîé âåðñèè ïðîäóêòà" & vbNewLine & _
|
||||||
|
"Ïðè íåîáõîäèìîñòè îáíîâèòå ïðîäóêò ñ ñåðâåðà" & vbNewLine & _
|
||||||
|
"ID ïðîäóêòà: " & sProduct & vbNewLine & _
|
||||||
|
"Âåðñèÿ ôàéëà: " & sCurrentVersion & vbNewLine & _
|
||||||
|
"Âåðñèÿ ëîêàëüíàÿ: " & sLocalVersion & vbNewLine & _
|
||||||
|
"Âåðñèÿ ñåðâåðíàÿ: " & sServerVersion
|
||||||
|
Call MsgBox(sMsg, vbOKOnly + vbExclamation, "Version control")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VersionCreateFile(sPath$, sVersion$) As Boolean
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
Dim sFile$: sFile = sPath & "\" & "VERSION"
|
||||||
|
Dim nFileID&: nFileID = FreeFile
|
||||||
|
Open sFile For Output As nFileID
|
||||||
|
Print #nFileID, sVersion
|
||||||
|
Close #nFileID
|
||||||
|
|
||||||
|
VersionCreateFile = True
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
RETURN_FALSE:
|
||||||
|
VersionCreateFile = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VersionCompare(ver1$, ver2$) As Long
|
||||||
|
Dim vals1() As String: vals1 = VBA.Split(ver1, ".")
|
||||||
|
Dim vals2() As String: vals2 = VBA.Split(ver2, ".")
|
||||||
|
VersionCompare = CLng(vals1(0)) - CLng(vals2(0))
|
||||||
|
If VersionCompare <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
VersionCompare = CLng(vals1(1)) - CLng(vals2(1))
|
||||||
|
If VersionCompare <> 0 Then _
|
||||||
|
Exit Function
|
||||||
|
VersionCompare = CLng(vals1(2)) - CLng(vals2(2))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VersionLocalManifest() As API_DistrManifest
|
||||||
|
Static s_Manifest As API_DistrManifest
|
||||||
|
|
||||||
|
If s_Manifest Is Nothing Then
|
||||||
|
Set s_Manifest = New API_DistrManifest
|
||||||
|
Call s_Manifest.LoadLocal
|
||||||
|
End If
|
||||||
|
|
||||||
|
Set VersionLocalManifest = s_Manifest
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VersionServerManifest() As API_DistrManifest
|
||||||
|
Static s_Manifest As API_DistrManifest
|
||||||
|
|
||||||
|
If s_Manifest Is Nothing Then
|
||||||
|
Set s_Manifest = New API_DistrManifest
|
||||||
|
Call s_Manifest.LoadServer
|
||||||
|
End If
|
||||||
|
|
||||||
|
Set VersionServerManifest = s_Manifest
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VersionUpdateProducts(Optional sParams$ = "all")
|
||||||
|
VersionUpdateProducts = False
|
||||||
|
|
||||||
|
Dim fso As New Scripting.FileSystemObject
|
||||||
|
Dim sScriptPath$: sScriptPath = CP_TOOLS_LOCAL & "\script\" & CP_UPDATE_SCRIPT & ".ps1"
|
||||||
|
If Not fso.FileExists(sScriptPath) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
Dim sExec$: sExec = "PowerShell -Command """ & _
|
||||||
|
"." & " " & "'" & sScriptPath & "'" & ";" & _
|
||||||
|
CP_UPDATE_SCRIPT & " " & "'" & CP_TOOLS_SERVER & "'" & " " & sParams & _
|
||||||
|
""""
|
||||||
|
Dim iShell As Object: Set iShell = CreateObject("WScript.Shell")
|
||||||
|
VersionUpdateProducts = iShell.Run(sExec, waitOnReturn:=True) = 0
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
RETURN_FALSE:
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =======
|
||||||
|
Private Function LocalWordAddin(sName$) As String
|
||||||
|
LocalWordAddin = VBA.Environ$("APPDATA") & "\Microsoft\Word\STARTUP" & "\" & sName
|
||||||
|
End Function
|
30
utility/z_QuickSort.bas
Normal file
30
utility/z_QuickSort.bas
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
Attribute VB_Name = "z_QuickSort"
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Function QuickSort(ByRef ids() As Variant, ByVal low&, ByVal high&, values As Collection)
|
||||||
|
Do While low < high
|
||||||
|
Dim pivot&: pivot = QPartition(ids, low, high, values)
|
||||||
|
Call QuickSort(ids, low, pivot - 1, values)
|
||||||
|
low = pivot + 1
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function QPartition(ByRef ids() As Variant, low&, high&, values As Collection) As Long
|
||||||
|
Dim pivot As Variant: pivot = ids(high)
|
||||||
|
Dim smallest&: smallest = low - 1
|
||||||
|
Dim n&
|
||||||
|
Dim tmp$
|
||||||
|
For n = low To high - 1
|
||||||
|
If values(CStr(ids(n))) < values(CStr(pivot)) Then
|
||||||
|
smallest = smallest + 1
|
||||||
|
tmp = ids(smallest)
|
||||||
|
ids(smallest) = ids(n)
|
||||||
|
ids(n) = tmp
|
||||||
|
End If
|
||||||
|
Next n
|
||||||
|
smallest = smallest + 1
|
||||||
|
tmp = ids(smallest)
|
||||||
|
ids(smallest) = ids(high)
|
||||||
|
ids(high) = tmp
|
||||||
|
QPartition = smallest
|
||||||
|
End Function
|
102
visio/API_ShapeStorage.cls
Normal file
102
visio/API_ShapeStorage.cls
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_ShapeStorage"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ===== Visio shape storage manager simulating grid ========
|
||||||
|
' Shared module version: 20210707
|
||||||
|
' Depends on:
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const SS_AREA_RATIO = 20# ' target sum widht / height ratio
|
||||||
|
Private Const SS_STORAGE_GAP = 0.1 ' gap between shapes in inches
|
||||||
|
Private Const SS_STORAGE_MAX_LVL = 1024 ' maximum number of rows
|
||||||
|
|
||||||
|
Private Const SS_PRECISION = 3
|
||||||
|
|
||||||
|
Private originX_ As Double
|
||||||
|
Private originY_ As Double
|
||||||
|
|
||||||
|
Private areaHeight_ As Double
|
||||||
|
|
||||||
|
Private lvlCount_ As Long
|
||||||
|
|
||||||
|
Private levelHeight_(1 To SS_STORAGE_MAX_LVL) As Double
|
||||||
|
Private levelX_(1 To SS_STORAGE_MAX_LVL) As Double
|
||||||
|
Private levelY_(1 To SS_STORAGE_MAX_LVL) As Double
|
||||||
|
|
||||||
|
Public items_ As Scripting.Dictionary
|
||||||
|
|
||||||
|
Public Function Init(xOrigin As Double, yOrigin As Double)
|
||||||
|
originX_ = xOrigin
|
||||||
|
originY_ = yOrigin
|
||||||
|
areaHeight_ = 0
|
||||||
|
lvlCount_ = 0
|
||||||
|
|
||||||
|
Set items_ = New Scripting.Dictionary
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get TargetPage() As Visio.Page
|
||||||
|
If items_.Count > 0 Then _
|
||||||
|
Set TargetPage = items_.Items(1).Parent
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function Store(target As Visio.Shape) As Boolean
|
||||||
|
Store = Not items_.Exists(target.ID)
|
||||||
|
If Not Store Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Call items_.Add(target.ID, target)
|
||||||
|
|
||||||
|
Dim nLvl&: nLvl = GetLevelForNew(target.Cells("Height"))
|
||||||
|
target.Cells("PinX") = levelX_(nLvl) + target.Cells("Width") / 2#
|
||||||
|
target.Cells("PinY") = levelY_(nLvl) + target.Cells("Height") / 2#
|
||||||
|
|
||||||
|
Call IncrementLevelWidth(nLvl, target.Cells("Width"))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GiveBack(target As Visio.Shape)
|
||||||
|
If items_.Exists(target.ID) Then _
|
||||||
|
Call items_.Remove(target.ID)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
'=============
|
||||||
|
Private Function GetLevelForNew(dHeight As Double) As Long
|
||||||
|
Dim minLvl&: minLvl = 0
|
||||||
|
Dim minX As Double: minX = 100000000#
|
||||||
|
Dim nLvl&
|
||||||
|
For nLvl = 1 To lvlCount_ Step 1
|
||||||
|
If VBA.Round(levelHeight_(nLvl), SS_PRECISION) + SS_STORAGE_GAP >= VBA.Round(dHeight, SS_PRECISION) And _
|
||||||
|
VBA.Round(minX, SS_PRECISION) > VBA.Round(levelX_(nLvl), SS_PRECISION) Then
|
||||||
|
minLvl = nLvl
|
||||||
|
minX = levelX_(nLvl)
|
||||||
|
End If
|
||||||
|
Next nLvl
|
||||||
|
|
||||||
|
If minLvl = 0 Then
|
||||||
|
GetLevelForNew = CreateNewLevel(dHeight)
|
||||||
|
ElseIf levelX_(minLvl) > SS_AREA_RATIO * VBA.Round(areaHeight_, SS_PRECISION) And lvlCount_ <= SS_STORAGE_MAX_LVL Then
|
||||||
|
GetLevelForNew = CreateNewLevel(dHeight)
|
||||||
|
Else
|
||||||
|
GetLevelForNew = minLvl
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function CreateNewLevel(dHeight As Double) As Long
|
||||||
|
lvlCount_ = lvlCount_ + 1
|
||||||
|
levelHeight_(lvlCount_) = dHeight
|
||||||
|
levelY_(lvlCount_) = originY_ + areaHeight_
|
||||||
|
levelX_(lvlCount_) = originX_
|
||||||
|
areaHeight_ = areaHeight_ + dHeight + SS_STORAGE_GAP
|
||||||
|
|
||||||
|
CreateNewLevel = lvlCount_
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IncrementLevelWidth(nLvl&, incWidth As Double)
|
||||||
|
levelX_(nLvl) = levelX_(nLvl) + SS_STORAGE_GAP + incWidth
|
||||||
|
End Function
|
45
visio/API_UndoWrapper.cls
Normal file
45
visio/API_UndoWrapper.cls
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_UndoWrapper"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ====== Class for managing Visio undo scopes ==========
|
||||||
|
' Shared module version: 20210329
|
||||||
|
' Tested in: TestVisio
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private app_ As Visio.Application
|
||||||
|
Private scopeID_ As Long
|
||||||
|
Private isActive_ As Boolean
|
||||||
|
|
||||||
|
Private Sub Class_Initialize()
|
||||||
|
scopeID_ = 0
|
||||||
|
isActive_ = False
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(targetApp As Visio.Application)
|
||||||
|
Set app_ = targetApp
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function BeginScope(sName$)
|
||||||
|
If isActive_ Then
|
||||||
|
Debug.Print "Scope already running"
|
||||||
|
Exit Function
|
||||||
|
Else
|
||||||
|
isActive_ = True
|
||||||
|
scopeID_ = app_.BeginUndoScope(sName)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function EndScope(Optional bCommit As Boolean = True)
|
||||||
|
If isActive_ Then
|
||||||
|
isActive_ = False
|
||||||
|
Call app_.EndUndoScope(scopeID_, bCommit)
|
||||||
|
End If
|
||||||
|
End Function
|
432
visio/z_CCVsoExtension.bas
Normal file
432
visio/z_CCVsoExtension.bas
Normal file
|
@ -0,0 +1,432 @@
|
||||||
|
Attribute VB_Name = "z_CCVsoExtension"
|
||||||
|
' ============== Module for additional functionality for CIHT CONCEPT ====================
|
||||||
|
' Shared module version: 20220714
|
||||||
|
' Tested in: TestVisio
|
||||||
|
' Depends on: CDS_Factorizator, z_VsoUtilities, CDS_Graph, API_VsoWrapper, z_VsoGraph
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const CC_DEFAULT_CONNECTOR_NAME = "Ñîåäèíèòåëü"
|
||||||
|
|
||||||
|
Public Sub CC_DispatchCommand(sCommand$)
|
||||||
|
Select Case sCommand
|
||||||
|
Case "WidthAdd10": Call CC_WidthAdd10
|
||||||
|
Case "WidthSub10": Call CC_WidthSub10
|
||||||
|
Case "TopAlignment": Call CC_TopAlignment
|
||||||
|
Case "LeftAlignment": Call CC_LeftAlignment
|
||||||
|
Case "ExpandStraight": Call CC_ExpandStraight
|
||||||
|
Case "ExpandReverse": Call CC_ExpandReverse
|
||||||
|
Case "GotoLinkBegin": Call CC_GotoLinkBegin
|
||||||
|
Case "GotoLinkEnd": Call CC_GotoLinkEnd
|
||||||
|
Case "IterateSimilar": Call CC_IterateSimilar
|
||||||
|
Case "RedirectConnects": Call CC_RedirectConnectors
|
||||||
|
Case "CreateConnects": Call CC_CreateConnectors
|
||||||
|
Case "Convert1251": Call CC_Convert1251
|
||||||
|
End Select
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_SelectByID()
|
||||||
|
Dim target As Visio.Shape
|
||||||
|
Dim sName$: sName = VBA.InputBox("Input ID")
|
||||||
|
If sName = vbNullString Then _
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
If Not IsNumeric(sName) Then
|
||||||
|
Call MsgBox("Invalid ID", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
Set target = Visio.Application.ActivePage.Shapes.ItemFromID(CLng(sName))
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If target Is Nothing Then
|
||||||
|
Call MsgBox("Invalid ID", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim iSelection As Visio.Selection: Set iSelection = Visio.Application.ActiveWindow.Selection
|
||||||
|
Call iSelection.DeselectAll
|
||||||
|
Call iSelection.Select(target, visSelect)
|
||||||
|
Visio.Application.ActiveWindow.Selection = iSelection
|
||||||
|
Call Visio.Application.ActiveWindow.CenterViewOnShape(target, visCenterViewSelectShape)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_GotoLinkBegin()
|
||||||
|
Dim iConnector As Visio.Shape: Set iConnector = GetSelectedConnector
|
||||||
|
If iConnector Is Nothing Then _
|
||||||
|
Exit Sub
|
||||||
|
Dim iTarget As Visio.Shape: Set iTarget = VsoGetConnectedShape(iConnector, bBegin:=True)
|
||||||
|
Call VsoCenterViewOn(iTarget)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_GotoLinkEnd()
|
||||||
|
Dim iConnector As Visio.Shape: Set iConnector = GetSelectedConnector
|
||||||
|
If iConnector Is Nothing Then _
|
||||||
|
Exit Sub
|
||||||
|
Dim iTarget As Visio.Shape: Set iTarget = VsoGetConnectedShape(iConnector, bBegin:=False)
|
||||||
|
Call VsoCenterViewOn(iTarget)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_WidthAdd10()
|
||||||
|
If ActiveWindow.Selection.Count = 0 Then
|
||||||
|
Call MsgBox("Âûäåëèòå ôèãóðó", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
Call GlobalUndo.BeginScope("Óâåëè÷åíèå øèðèíû")
|
||||||
|
ThisDocument.DiagramServicesEnabled = visServiceStructureBasic
|
||||||
|
|
||||||
|
Call VsoIncrementWidth(ActiveWindow.Selection(1), 10)
|
||||||
|
|
||||||
|
ThisDocument.DiagramServicesEnabled = 0
|
||||||
|
Call GlobalUndo.EndScope
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_WidthSub10()
|
||||||
|
If ActiveWindow.Selection.Count = 0 Then
|
||||||
|
Call MsgBox("Âûäåëèòå ôèãóðó", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
Call GlobalUndo.BeginScope("Óìåíüøåíèå øèðèíû")
|
||||||
|
ThisDocument.DiagramServicesEnabled = visServiceStructureBasic
|
||||||
|
|
||||||
|
Call VsoIncrementWidth(ActiveWindow.Selection(1), -10)
|
||||||
|
|
||||||
|
ThisDocument.DiagramServicesEnabled = 0
|
||||||
|
Call GlobalUndo.EndScope
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_IterateSimilar()
|
||||||
|
Attribute CC_IterateSimilar.VB_ProcData.VB_Invoke_Func = "F"
|
||||||
|
Dim iSelected As Visio.Selection: Set iSelected = Visio.Application.ActiveWindow.Selection
|
||||||
|
Dim iShape As Visio.Shape
|
||||||
|
Dim nItem&: nItem = 1
|
||||||
|
Do
|
||||||
|
If nItem > iSelected.Count Then _
|
||||||
|
Exit Do
|
||||||
|
Set iShape = iSelected.Item(nItem)
|
||||||
|
If iShape.OneD Then
|
||||||
|
Call iSelected.Select(iShape, visDeselect)
|
||||||
|
Else
|
||||||
|
nItem = nItem + 1
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
|
||||||
|
If iSelected.Count = 0 Then
|
||||||
|
Call MsgBox("Select at least one shape", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim iTarget As Visio.Shape: Set iTarget = iSelected.Item(1)
|
||||||
|
|
||||||
|
Dim idList As Scripting.Dictionary: Set idList = ExtractShapeIDs(iTarget)
|
||||||
|
If idList Is Nothing Then
|
||||||
|
Call MsgBox("Selected shape is missing IDs", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim filteredIDs As Collection: Set filteredIDs = FilterPageIDs(Visio.Application.ActivePage, idList)
|
||||||
|
|
||||||
|
Call iSelected.DeselectAll
|
||||||
|
Dim nStart&: nStart = -1
|
||||||
|
For nItem = 1 To filteredIDs.Count Step 1
|
||||||
|
Set iShape = filteredIDs.Item(nItem)
|
||||||
|
If nStart <> -1 Then _
|
||||||
|
Call iSelected.Select(iShape, visSelect)
|
||||||
|
If iShape.ID = iTarget.ID Then
|
||||||
|
nStart = nItem
|
||||||
|
If nStart = filteredIDs.Count Then _
|
||||||
|
nStart = 1
|
||||||
|
End If
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
For nItem = 1 To nStart Step 1
|
||||||
|
Call iSelected.Select(filteredIDs.Item(nItem), visSelect)
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
Visio.Application.ActiveWindow.Selection = iSelected
|
||||||
|
Call Visio.Application.ActiveWindow.CenterViewOnShape(iSelected.Item(1), visCenterViewSelectShape)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' Create connections from all selected to last selected
|
||||||
|
Public Sub CC_CreateConnectors()
|
||||||
|
Dim iSelected As Visio.Selection: Set iSelected = ThisDocument.Application.ActiveWindow.Selection
|
||||||
|
If iSelected.Count < 2 Then
|
||||||
|
Call MsgBox("Select at least two shapes", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call GlobalUndo.BeginScope("Ñîçäàòü ñâÿçè")
|
||||||
|
|
||||||
|
Dim shapeTo As Visio.Shape: Set shapeTo = iSelected(iSelected.Count)
|
||||||
|
Dim shapeFrom As Visio.Shape
|
||||||
|
Dim conShp As Visio.Shape
|
||||||
|
Dim iMaster As Visio.Master: Set iMaster = FindMaster(ThisDocument, CC_DEFAULT_CONNECTOR_NAME)
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 1 To iSelected.Count - 1 Step 1
|
||||||
|
Set shapeFrom = iSelected(nItem)
|
||||||
|
If shapeFrom.Connects.Count <> 0 Then _
|
||||||
|
GoTo NEXT_ITEM
|
||||||
|
If iMaster Is Nothing Then
|
||||||
|
Call shapeFrom.AutoConnect(shapeTo, visAutoConnectDirNone)
|
||||||
|
Else
|
||||||
|
Call shapeFrom.AutoConnect(shapeTo, visAutoConnectDirNone, iMaster)
|
||||||
|
End If
|
||||||
|
NEXT_ITEM:
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
Call GlobalUndo.EndScope
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' Move connectors end to last selected
|
||||||
|
Public Sub CC_RedirectConnectors()
|
||||||
|
Dim iSelected As Visio.Selection: Set iSelected = ThisDocument.Application.ActiveWindow.Selection
|
||||||
|
If iSelected.Count < 2 Then
|
||||||
|
Call MsgBox("Select at least one connector and one shape", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim shapeTo As Visio.Shape: Set shapeTo = iSelected(iSelected.Count)
|
||||||
|
If shapeTo.Connects.Count <> 0 Then
|
||||||
|
Call MsgBox("Select valid shape last (not connector!)", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call GlobalUndo.BeginScope("Ïåðåíåñòè êîíöû")
|
||||||
|
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = 1 To iSelected.Count - 1 Step 1
|
||||||
|
Call iSelected(nItem).CellsU("EndX").GlueTo(shapeTo.CellsU("PinX"))
|
||||||
|
Next nItem
|
||||||
|
|
||||||
|
Call GlobalUndo.EndScope
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_TopAlignment()
|
||||||
|
Dim dGap As Double: dGap = PromptAlignGap
|
||||||
|
If dGap = 0 Then _
|
||||||
|
Exit Sub
|
||||||
|
Dim iShapes As Collection: Set iShapes = VsoActiveShapes(ThisDocument.Application)
|
||||||
|
If iShapes.Count = 0 Then _
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
Dim vsoUI As New API_VsoWrapper: Call vsoUI.SetDocument(ThisDocument)
|
||||||
|
Call vsoUI.PauseUI
|
||||||
|
ThisDocument.DiagramServicesEnabled = visServiceStructureBasic
|
||||||
|
Call GlobalUndo.BeginScope("Âûðàâíèâàíèå")
|
||||||
|
|
||||||
|
Call VsoAlignShapes(iShapes, dGap, bAlignTop:=True)
|
||||||
|
|
||||||
|
Call GlobalUndo.EndScope
|
||||||
|
ThisDocument.DiagramServicesEnabled = 0
|
||||||
|
Call vsoUI.ResumeUI
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_LeftAlignment()
|
||||||
|
Dim dGap As Double: dGap = PromptAlignGap
|
||||||
|
If dGap = 0 Then _
|
||||||
|
Exit Sub
|
||||||
|
Dim iShapes As Collection: Set iShapes = VsoActiveShapes(ThisDocument.Application)
|
||||||
|
If iShapes.Count = 0 Then _
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
Dim vsoUI As New API_VsoWrapper: Call vsoUI.SetDocument(ThisDocument)
|
||||||
|
Call vsoUI.PauseUI
|
||||||
|
ThisDocument.DiagramServicesEnabled = visServiceStructureBasic
|
||||||
|
Call GlobalUndo.BeginScope("Âûðàâíèâàíèå")
|
||||||
|
|
||||||
|
Call VsoAlignShapes(iShapes, dGap, bAlignTop:=False)
|
||||||
|
|
||||||
|
Call GlobalUndo.EndScope
|
||||||
|
ThisDocument.DiagramServicesEnabled = 0
|
||||||
|
Call vsoUI.ResumeUI
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_ExpandReverse()
|
||||||
|
If ThisDocument.Application.ActiveWindow.Selection.Count = 0 Then
|
||||||
|
Call MsgBox("Âûäåëèòå Ýëåìåíò", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim iSelected As Collection: Set iSelected = VsoActiveShapes(ThisDocument.Application)
|
||||||
|
Dim iGraph As CDS_Graph: Set iGraph = ScanGraph(iSelected, bReverseLinks:=True)
|
||||||
|
Call VsoSelectShapesIDs(iGraph.Nodes, ThisDocument.Application.ActivePage)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_ExpandStraight()
|
||||||
|
If ThisDocument.Application.ActiveWindow.Selection.Count = 0 Then
|
||||||
|
Call MsgBox("Âûäåëèòå Ýëåìåíò", vbExclamation)
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim iSelected As Collection: Set iSelected = VsoActiveShapes(ThisDocument.Application)
|
||||||
|
Dim iGraph As CDS_Graph: Set iGraph = ScanGraph(iSelected, bReverseLinks:=False)
|
||||||
|
Call VsoSelectShapesIDs(iGraph.Nodes, ThisDocument.Application.ActivePage)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub CC_Convert1251()
|
||||||
|
Dim iShapes As Collection: Set iShapes = VsoActiveShapes(ThisDocument.Application)
|
||||||
|
If iShapes.Count = 0 Then _
|
||||||
|
Exit Sub
|
||||||
|
|
||||||
|
Dim aShape As Visio.Shape
|
||||||
|
For Each aShape In iShapes
|
||||||
|
Dim sText$: sText = aShape.Text
|
||||||
|
Dim sNewText$: sNewText = Convert1251(aShape.Text)
|
||||||
|
If sText <> sNewText Then _
|
||||||
|
aShape.Text = sNewText
|
||||||
|
Next aShape
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function VsoAlignShapes(target As Collection, dGap As Double, bAlignTop As Boolean)
|
||||||
|
Dim iFactors As New CDS_Factorizator
|
||||||
|
Call iFactors.Init(Application.ConvertResult(dGap, "mm", "in"))
|
||||||
|
|
||||||
|
Dim sCellPosition$: sCellPosition = IIf(bAlignTop, "PinY", "PinX")
|
||||||
|
Dim sDimension$: sDimension = IIf(bAlignTop, "Height", "Width")
|
||||||
|
Dim k&: k = IIf(bAlignTop, 1, -1)
|
||||||
|
|
||||||
|
Dim aShape As Visio.Shape
|
||||||
|
For Each aShape In target
|
||||||
|
If VsoIsMovable(aShape) Then _
|
||||||
|
Call iFactors.Insert(aShape.ID, k * (aShape.CellsU(sCellPosition) + k * aShape.CellsU(sDimension) / 2#))
|
||||||
|
Next aShape
|
||||||
|
|
||||||
|
For Each aShape In target
|
||||||
|
Dim vFactor As Variant: vFactor = iFactors.FactorFor(aShape.ID)
|
||||||
|
If vFactor = Empty Then _
|
||||||
|
GoTo NEXT_SHAPE
|
||||||
|
If vFactor = aShape.ID Then _
|
||||||
|
GoTo NEXT_SHAPE
|
||||||
|
|
||||||
|
Dim targetPos As Double: targetPos = iFactors.FactorValueFor(vFactor)
|
||||||
|
aShape.CellsU(sCellPosition) = k * (targetPos - aShape.CellsU(sDimension) / 2#)
|
||||||
|
NEXT_SHAPE:
|
||||||
|
Next aShape
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExtractShapeIDs(target As Visio.Shape) As Scripting.Dictionary
|
||||||
|
Dim sText$: sText = target.Text
|
||||||
|
If sText = vbNullString And target.Shapes.Count > 1 Then _
|
||||||
|
sText = target.Shapes(1).Text
|
||||||
|
If sText = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim sPrefix$: sPrefix = ExtractShapePrefix(sText)
|
||||||
|
If sPrefix = vbNullString Then
|
||||||
|
Set ExtractShapeIDs = New Scripting.Dictionary
|
||||||
|
Call ExtractShapeIDs.Add(VBA.Trim(sText), VBA.Trim(sText))
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Set ExtractShapeIDs = SplitPrefix(sPrefix)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FilterPageIDs(iPage As Visio.Page, iFilter As Scripting.Dictionary) As Collection
|
||||||
|
Dim iResult As New Collection
|
||||||
|
Dim idList As Scripting.Dictionary
|
||||||
|
Dim iShape As Visio.Shape
|
||||||
|
For Each iShape In iPage.Shapes
|
||||||
|
Set idList = ExtractShapeIDs(iShape)
|
||||||
|
If Not idList Is Nothing Then
|
||||||
|
Dim iKey As Variant
|
||||||
|
For Each iKey In idList.Keys
|
||||||
|
Dim sValue$: sValue = idList(iKey)
|
||||||
|
If iFilter.Exists(sValue) Then
|
||||||
|
Call iResult.Add(iShape)
|
||||||
|
Exit For
|
||||||
|
End If
|
||||||
|
Next iKey
|
||||||
|
End If
|
||||||
|
Next iShape
|
||||||
|
Set FilterPageIDs = iResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ========
|
||||||
|
Private Function GetSelectedConnector() As Visio.Shape
|
||||||
|
Dim iSelected As Visio.Selection: Set iSelected = Application.ActiveWindow.Selection
|
||||||
|
If iSelected.Count <> 1 Then
|
||||||
|
Call MsgBox("Âûáåðèòå ñîåäèíèòåëü", vbExclamation)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Dim iConnector As Visio.Shape: Set iConnector = iSelected(1)
|
||||||
|
If iConnector.Connects.Count = 0 Then
|
||||||
|
Call MsgBox("Âûáðàííûé ñîåäèíèòåëü äîëæåí áûòü ïðèêðåëïåí ê ôèãóðàì", vbExclamation)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Set GetSelectedConnector = iConnector
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function PromptAlignGap() As Double
|
||||||
|
Dim sInput$: sInput = InputBox("Ââåäèòå ïîðîãîâîå ðàçëè÷èå â ìèëëèìåòðàõ", Default:="10")
|
||||||
|
If sInput = vbNullString Then _
|
||||||
|
Exit Function
|
||||||
|
If Not IsNumeric(sInput) Then
|
||||||
|
Call MsgBox("Ââåäèòå ÷èñëî", vbExclamation)
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
PromptAlignGap = CDbl(sInput)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function Convert1251(sText$) As String
|
||||||
|
If sText = "" Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim nChr&
|
||||||
|
For nChr = 1 To VBA.Len(sText)
|
||||||
|
Dim aChr$: aChr = VBA.Mid$(sText, nChr, 1)
|
||||||
|
Dim uniChr&: uniChr = VBA.AscW(aChr)
|
||||||
|
If uniChr < 256 And uniChr > 127 Then _
|
||||||
|
aChr = VBA.Chr(uniChr)
|
||||||
|
Convert1251 = Convert1251 & aChr
|
||||||
|
Next nChr
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ExtractShapePrefix(sText$) As String
|
||||||
|
Dim nChr&
|
||||||
|
For nChr = 1 To VBA.Len(sText) Step 1
|
||||||
|
If VBA.Mid$(sText, nChr, 1) Like "[à-ÿ¸a-zA-Z]" Then
|
||||||
|
nChr = nChr - 1
|
||||||
|
Exit For
|
||||||
|
End If
|
||||||
|
Next nChr
|
||||||
|
If nChr = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim sResult$: sResult = VBA.Left(sText, nChr)
|
||||||
|
If VBA.InStr(1, sResult, " ", vbTextCompare) = 0 Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
ExtractShapePrefix = VBA.Trim(VBA.Left(sResult, VBA.InStrRev(sResult, " ")))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SplitPrefix(sPrefix$) As Scripting.Dictionary
|
||||||
|
Dim iResult As New Scripting.Dictionary
|
||||||
|
If VBA.InStr(1, sPrefix, "(") = 0 Then
|
||||||
|
Call iResult.Add(sPrefix, sPrefix)
|
||||||
|
Set SplitPrefix = iResult
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim nStart&: nStart = -1
|
||||||
|
Dim nChr&
|
||||||
|
For nChr = 1 To VBA.Len(sPrefix) Step 1
|
||||||
|
Dim aChr$: aChr = VBA.Mid$(sPrefix, nChr, 1)
|
||||||
|
If aChr = "(" Then
|
||||||
|
nStart = nChr
|
||||||
|
ElseIf aChr = ")" Then
|
||||||
|
If nStart <> -1 And nStart + 1 < nChr Then
|
||||||
|
Dim sID$: sID = VBA.Trim(VBA.Mid$(sPrefix, nStart + 1, nChr - nStart - 1))
|
||||||
|
If sID <> vbNullString Then
|
||||||
|
On Error Resume Next
|
||||||
|
Call iResult.Add(sID, sID)
|
||||||
|
On Error GoTo 0
|
||||||
|
End If
|
||||||
|
nStart = -1
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Next nChr
|
||||||
|
If iResult.Count = 0 Then _
|
||||||
|
Call iResult.Add(sPrefix, sPrefix)
|
||||||
|
Set SplitPrefix = iResult
|
||||||
|
End Function
|
132
visio/z_VsoGraph.bas
Normal file
132
visio/z_VsoGraph.bas
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
Attribute VB_Name = "z_VsoGraph"
|
||||||
|
' ========= Extension functions for Visio ========
|
||||||
|
' Shared module version: 20210414
|
||||||
|
' Depends on: CDS_Graph
|
||||||
|
' Required reference: Scripting
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Enum TConnectorDirection
|
||||||
|
T_CD_NONE = 0
|
||||||
|
T_CD_STRAIGHT = 1
|
||||||
|
T_CD_REVERSE = 2
|
||||||
|
T_CD_MUTUAL = 3
|
||||||
|
End Enum
|
||||||
|
|
||||||
|
Public Type ItemConnector
|
||||||
|
begin_ As Visio.Shape
|
||||||
|
end_ As Visio.Shape
|
||||||
|
dir_ As TConnectorDirection
|
||||||
|
End Type
|
||||||
|
|
||||||
|
Public Function ScanConnector(iArrow As Visio.Shape) As ItemConnector
|
||||||
|
ScanConnector.dir_ = GetConnectorDirection(iArrow)
|
||||||
|
If iArrow.Connects.Count = 2 Then
|
||||||
|
Set ScanConnector.begin_ = iArrow.Connects(1).ToSheet
|
||||||
|
Set ScanConnector.end_ = iArrow.Connects(2).ToSheet
|
||||||
|
ElseIf iArrow.Connects.Count = 1 Then
|
||||||
|
If iArrow.CellsU("BeginX").Formula Like "*GLUE*" Then
|
||||||
|
Set ScanConnector.begin_ = iArrow.Connects(1).ToSheet
|
||||||
|
Else
|
||||||
|
Set ScanConnector.end_ = iArrow.Connects(1).ToSheet
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ScanGraph(ByRef iSeed As Collection, bReverseLinks As Boolean) As CDS_Graph
|
||||||
|
Dim iVisited As New Scripting.Dictionary
|
||||||
|
Dim iGraph As New CDS_Graph
|
||||||
|
Do While iSeed.Count > 0
|
||||||
|
Dim aShape As Visio.Shape: Set aShape = iSeed.Item(1)
|
||||||
|
Call iSeed.Remove(1)
|
||||||
|
If aShape.OneD Then _
|
||||||
|
GoTo NEXT_SHAPE
|
||||||
|
|
||||||
|
Dim nShape&: nShape = aShape.ID
|
||||||
|
If iVisited.Exists(nShape) Then _
|
||||||
|
GoTo NEXT_SHAPE
|
||||||
|
|
||||||
|
Call iVisited.Add(nShape, 0)
|
||||||
|
Call iGraph.AddNode(nShape)
|
||||||
|
Call ScanConnectorsFor(aShape, iSeed, iGraph, bReverseLinks)
|
||||||
|
|
||||||
|
NEXT_SHAPE:
|
||||||
|
Loop
|
||||||
|
Set ScanGraph = iGraph
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoGetConnectedShape(vConnector As Visio.Shape, bBegin As Boolean) As Visio.Shape
|
||||||
|
Dim iConnector As ItemConnector: iConnector = ScanConnector(vConnector)
|
||||||
|
If bBegin Then
|
||||||
|
Set VsoGetConnectedShape = IIf(iConnector.dir_ <> T_CD_REVERSE, iConnector.begin_, iConnector.end_)
|
||||||
|
Else
|
||||||
|
Set VsoGetConnectedShape = IIf(iConnector.dir_ <> T_CD_REVERSE, iConnector.end_, iConnector.begin_)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =====
|
||||||
|
Private Function IsArrow(nEndType&) As Boolean
|
||||||
|
Select Case nEndType
|
||||||
|
Case 1 To 8: IsArrow = True
|
||||||
|
Case 12 To 19: IsArrow = True
|
||||||
|
Case 39 To 40: IsArrow = True
|
||||||
|
Case 43 To 45: IsArrow = True
|
||||||
|
Case Else: IsArrow = False
|
||||||
|
End Select
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GetConnectorDirection(target As Visio.Shape) As TConnectorDirection
|
||||||
|
Dim bBegin As Boolean: bBegin = IsArrow(target.CellsU("BeginArrow"))
|
||||||
|
Dim bFinish As Boolean: bFinish = IsArrow(target.CellsU("EndArrow"))
|
||||||
|
|
||||||
|
If bBegin Then
|
||||||
|
GetConnectorDirection = IIf(bFinish, T_CD_MUTUAL, T_CD_REVERSE)
|
||||||
|
Else
|
||||||
|
GetConnectorDirection = IIf(bFinish, T_CD_STRAIGHT, T_CD_NONE)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ScanConnectorsFor(target As Visio.Shape, ByRef iSeed As Collection, ByRef iGraph As CDS_Graph, bReverseLinks As Boolean)
|
||||||
|
Dim iConnect As Visio.Connect
|
||||||
|
For Each iConnect In target.FromConnects
|
||||||
|
If iConnect.FromSheet.Connects.Count <> 2 Then _
|
||||||
|
GoTo NEXT_CONNECT
|
||||||
|
|
||||||
|
Dim theInfo As ItemConnector: theInfo = ScanConnector(iConnect.FromSheet)
|
||||||
|
If bReverseLinks Then
|
||||||
|
If theInfo.dir_ = T_CD_STRAIGHT Then
|
||||||
|
theInfo.dir_ = T_CD_REVERSE
|
||||||
|
ElseIf theInfo.dir_ = T_CD_REVERSE Then
|
||||||
|
theInfo.dir_ = T_CD_STRAIGHT
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
If GraphAddEdge(iGraph, theInfo, theInfo.begin_ = target) Then
|
||||||
|
Dim destShape As Visio.Shape: Set destShape = IIf(theInfo.begin_ = target, theInfo.end_, theInfo.begin_)
|
||||||
|
Call iSeed.Add(destShape)
|
||||||
|
End If
|
||||||
|
NEXT_CONNECT:
|
||||||
|
Next iConnect
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GraphAddEdge(ByRef graph As CDS_Graph, iConnector As ItemConnector, fixedBegin As Boolean) As Boolean
|
||||||
|
GraphAddEdge = False
|
||||||
|
Select Case iConnector.dir_
|
||||||
|
Case T_CD_MUTUAL, T_CD_NONE:
|
||||||
|
Call graph.AddEdge(iConnector.begin_.ID, iConnector.end_.ID)
|
||||||
|
Call graph.AddEdge(iConnector.end_.ID, iConnector.begin_.ID)
|
||||||
|
GraphAddEdge = True
|
||||||
|
|
||||||
|
Case T_CD_REVERSE:
|
||||||
|
If Not fixedBegin Then
|
||||||
|
Call graph.AddEdge(iConnector.end_.ID, iConnector.begin_.ID)
|
||||||
|
GraphAddEdge = True
|
||||||
|
End If
|
||||||
|
|
||||||
|
Case T_CD_STRAIGHT:
|
||||||
|
If fixedBegin Then
|
||||||
|
Call graph.AddEdge(iConnector.begin_.ID, iConnector.end_.ID)
|
||||||
|
GraphAddEdge = True
|
||||||
|
End If
|
||||||
|
End Select
|
||||||
|
End Function
|
303
visio/z_VsoUtilities.bas
Normal file
303
visio/z_VsoUtilities.bas
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
Attribute VB_Name = "z_VsoUtilities"
|
||||||
|
' ====== Extension functions for Visio =====
|
||||||
|
' Shared module version: 20220624
|
||||||
|
' Tested in: TestVisio
|
||||||
|
' Depends on: API_UndoWrapper
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Private Const CELLSU_STRUCTURE_TYPE = "User.msvStructureType"
|
||||||
|
Private Const CELLSU_CONTAINER_RESIZE = "User.msvSDContainerResize"
|
||||||
|
|
||||||
|
Private Const RESIZE_FIT_TO_CONTENTS = 2
|
||||||
|
Private Const SORT_PRECISION = 1# / 25.4 ' mm to inches
|
||||||
|
|
||||||
|
Public Function GlobalUndo() As API_UndoWrapper
|
||||||
|
Static s_Wrapper As API_UndoWrapper
|
||||||
|
|
||||||
|
If s_Wrapper Is Nothing Then
|
||||||
|
Set s_Wrapper = New API_UndoWrapper
|
||||||
|
Call s_Wrapper.Init(Visio.Application)
|
||||||
|
End If
|
||||||
|
|
||||||
|
Set GlobalUndo = s_Wrapper
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function FindMaster(iSource As Visio.Document, sMaster$) As Visio.Master
|
||||||
|
On Error Resume Next
|
||||||
|
Set FindMaster = iSource.Masters.Item(sMaster)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SetupFixedGridPage(target As Visio.Page)
|
||||||
|
With target.PageSheet
|
||||||
|
.CellsU("XRulerOrigin").Formula = "=GUARD(0)"
|
||||||
|
.CellsU("YRulerOrigin").Formula = "=GUARD(0)"
|
||||||
|
.CellsU("XGridOrigin").Formula = "=GUARD(XRulerOrigin)"
|
||||||
|
.CellsU("YGridOrigin").Formula = "=GUARD(YRulerOrigin)"
|
||||||
|
End With
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CellsExists(target As Visio.Shape, sCellName$) As Boolean
|
||||||
|
CellsExists = target.CellExistsU(sCellName, visExistsAnywhere)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CellsGetValue(target As Visio.Shape, sCellName$) As Variant
|
||||||
|
If Not CellsExists(target, sCellName) Then _
|
||||||
|
Exit Function
|
||||||
|
CellsGetValue = target.CellsU(sCellName)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CellsSetValue(target As Visio.Shape, sCellName$, vNewValue As Variant)
|
||||||
|
If Not CellsExists(target, sCellName) Then _
|
||||||
|
Exit Function
|
||||||
|
target.CellsU(sCellName) = vNewValue
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CellsGetFormula(target As Visio.Shape, sCellName$) As String
|
||||||
|
If Not CellsExists(target, sCellName) Then _
|
||||||
|
Exit Function
|
||||||
|
CellsGetFormula = target.CellsU(sCellName).Formula
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function CellsSetFormula(target As Visio.Shape, sCellName$, sFormula$)
|
||||||
|
If Not CellsExists(target, sCellName) Then _
|
||||||
|
Exit Function
|
||||||
|
target.CellsU(sCellName).Formula = sFormula
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetContainingShape(target As Visio.Shape) As Visio.Shape
|
||||||
|
Dim vContainer As Variant
|
||||||
|
For Each vContainer In target.MemberOfContainers
|
||||||
|
Set GetContainingShape = target.ContainingPage.Shapes.ItemFromID(vContainer)
|
||||||
|
Exit Function
|
||||||
|
Next vContainer
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoShapeExists(sShapeName$, iWhere As Visio.Page) As Boolean
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
|
||||||
|
Dim iShape As Visio.Shape: Set iShape = iWhere.Shapes(sShapeName)
|
||||||
|
VsoShapeExists = Not iShape Is Nothing
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
RETURN_FALSE:
|
||||||
|
On Error GoTo 0
|
||||||
|
VsoShapeExists = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoIsListContainer(target As Visio.Shape) As Boolean
|
||||||
|
VsoIsListContainer = CellsGetFormula(target, CELLSU_STRUCTURE_TYPE) = """List"""
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoIsMovable(target As Visio.Shape) As Boolean
|
||||||
|
If target.OneD Then
|
||||||
|
VsoIsMovable = False
|
||||||
|
ElseIf CellsExists(target, CELLSU_CONTAINER_RESIZE) Then
|
||||||
|
VsoIsMovable = CellsGetValue(target, CELLSU_CONTAINER_RESIZE) = RESIZE_FIT_TO_CONTENTS
|
||||||
|
Else
|
||||||
|
Dim shpContainer As Visio.Shape: Set shpContainer = GetContainingShape(target)
|
||||||
|
If shpContainer Is Nothing Then
|
||||||
|
VsoIsMovable = True
|
||||||
|
Else
|
||||||
|
VsoIsMovable = Not VsoIsMovable(shpContainer)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoIsConnected(iSource As Visio.Shape, iDestination As Visio.Shape) As Boolean
|
||||||
|
VsoIsConnected = False
|
||||||
|
If iSource.OneD Then _
|
||||||
|
Exit Function
|
||||||
|
Dim iOuts() As Long: iOuts = iSource.ConnectedShapes(visConnectedShapesOutgoingNodes, "")
|
||||||
|
Dim nItem&
|
||||||
|
For nItem = LBound(iOuts) To UBound(iOuts) Step 1
|
||||||
|
If iOuts(nItem) = iDestination.ID Then
|
||||||
|
VsoIsConnected = True
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next nItem
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoApplyColorTo(target As Visio.Shape, nStart&, nEnd&, sColor$)
|
||||||
|
Dim iChars As Visio.Characters: Set iChars = target.Characters
|
||||||
|
iChars.Begin = nStart
|
||||||
|
iChars.End = nEnd
|
||||||
|
iChars.CharProps(visCharacterColor) = 1
|
||||||
|
|
||||||
|
iChars.End = nStart + 1
|
||||||
|
Dim nRow&
|
||||||
|
Dim nPrevious&: nPrevious = -1
|
||||||
|
Do While iChars.End <= nEnd And iChars.Begin <> iChars.End
|
||||||
|
nRow = iChars.CharPropsRow(visBiasLetVisioChoose)
|
||||||
|
If nRow <> nPrevious Then
|
||||||
|
nPrevious = nRow
|
||||||
|
target.CellsSRC(visSectionCharacter, nRow, visCharacterColor).FormulaU = sColor
|
||||||
|
End If
|
||||||
|
|
||||||
|
iChars.Begin = iChars.End
|
||||||
|
iChars.End = iChars.End + 1
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Extracts selected shapes or all shapes on ActivePage if selection is empty
|
||||||
|
Public Function VsoActiveShapes(iSource As Visio.Application) As Collection
|
||||||
|
Dim iSelected As Visio.Selection: Set iSelected = iSource.ActiveWindow.Selection
|
||||||
|
Dim iShapes As New Collection
|
||||||
|
Dim aShape As Visio.Shape
|
||||||
|
If iSelected.Count <> 0 Then
|
||||||
|
For Each aShape In iSelected
|
||||||
|
Call iShapes.Add(aShape)
|
||||||
|
Next aShape
|
||||||
|
Else
|
||||||
|
For Each aShape In iSource.ActivePage.Shapes
|
||||||
|
Call iShapes.Add(aShape)
|
||||||
|
Next aShape
|
||||||
|
End If
|
||||||
|
Set VsoActiveShapes = iShapes
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoGetSelectedShapes(vsoAppl As Visio.Application)
|
||||||
|
Dim iShapes As New Collection
|
||||||
|
Dim iSelection As Visio.Selection: Set iSelection = vsoAppl.ActiveWindow.Selection
|
||||||
|
Dim iShape As Visio.Shape
|
||||||
|
For Each iShape In iSelection
|
||||||
|
Call iShapes.Add(iShape)
|
||||||
|
Next iShape
|
||||||
|
Set VsoGetSelectedShapes = iShapes
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoSelectShapes(iShapes As Collection, iPage As Visio.Page)
|
||||||
|
ActiveWindow.Page = iPage
|
||||||
|
Dim iSelection As Visio.Selection: Set iSelection = ActiveWindow.Selection
|
||||||
|
Call iSelection.DeselectAll
|
||||||
|
Dim iShape As Visio.Shape
|
||||||
|
For Each iShape In iShapes
|
||||||
|
Call iSelection.Select(iShape, visSelect)
|
||||||
|
Next iShape
|
||||||
|
ActiveWindow.Selection = iSelection
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoSelectShapesIDs(iShapeIDs As Collection, iPage As Visio.Page)
|
||||||
|
Dim iSelection As Visio.Selection: Set iSelection = iPage.Application.ActiveWindow.Selection
|
||||||
|
Call iSelection.DeselectAll
|
||||||
|
Dim nShapeID As Variant
|
||||||
|
For Each nShapeID In iShapeIDs
|
||||||
|
Call iSelection.Select(iPage.Shapes.ItemFromID(CLng(nShapeID)), visSelect)
|
||||||
|
Next nShapeID
|
||||||
|
iPage.Application.ActiveWindow.Selection = iSelection
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoClearPage(target As Visio.Page)
|
||||||
|
Do While target.Shapes.Count > 0
|
||||||
|
Call target.Shapes(1).Delete
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoShowWholePage(target As Visio.Page)
|
||||||
|
target.Application.ActiveWindow.Page = target
|
||||||
|
target.Application.ActiveWindow.ViewFit = visFitPage
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoCenterViewOn(target As Visio.Shape)
|
||||||
|
Call target.Application.ActiveWindow.ScrollViewTo(target.CellsU("PinX"), target.CellsU("PinY"))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoIncrementWidth(target As Visio.Shape, valueInMM&)
|
||||||
|
If target.OneD Then _
|
||||||
|
Exit Function
|
||||||
|
If TryIncrementContained(target, valueInMM) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Dim dWidth As Double: dWidth = target.CellsU("Width")
|
||||||
|
If VsoIsListContainer(target) Then
|
||||||
|
Dim iMember As Visio.Shape
|
||||||
|
Dim memberID As Variant
|
||||||
|
For Each memberID In target.ContainerProperties.GetListMembers
|
||||||
|
Set iMember = target.ContainingPage.Shapes.ItemFromID(memberID)
|
||||||
|
Call iMember.Resize(visResizeDirE, valueInMM, visMillimeters)
|
||||||
|
Next memberID
|
||||||
|
End If
|
||||||
|
If dWidth = target.CellsU("Width") Then _
|
||||||
|
Call target.Resize(visResizeDirE, valueInMM, visMillimeters)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function VsoCalculateFillingFor(target As Visio.Page) As Double
|
||||||
|
' returns percentage of used space on the page
|
||||||
|
' does not account for shapes overlapping
|
||||||
|
Dim sumAreas As Double: sumAreas = 0
|
||||||
|
Dim aShape As Visio.Shape
|
||||||
|
For Each aShape In target.Shapes
|
||||||
|
If VsoIsMovable(aShape) Then _
|
||||||
|
sumAreas = sumAreas + aShape.CellsU("Width") * aShape.CellsU("Height")
|
||||||
|
Next aShape
|
||||||
|
|
||||||
|
Dim dSheetArea As Double
|
||||||
|
dSheetArea = target.PageSheet.CellsU("PageWidth") * target.PageSheet.CellsU("PageHeight")
|
||||||
|
|
||||||
|
If dSheetArea <> 0 Then _
|
||||||
|
VsoCalculateFillingFor = sumAreas / dSheetArea
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Sort by topmost and leftmost position of a shape
|
||||||
|
Public Function VsoGeometricSort(ByRef target() As Long, iPage As Visio.Page)
|
||||||
|
If Not IsArrayAllocated(target) Then _
|
||||||
|
Exit Function
|
||||||
|
Call GeometricQuickSort(iPage, target, LBound(target, 1), UBound(target, 1))
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ==========
|
||||||
|
Private Function TryIncrementContained(target As Visio.Shape, valueInMM&) As Boolean
|
||||||
|
TryIncrementContained = False
|
||||||
|
Dim wrapper As Visio.Shape: Set wrapper = GetContainingShape(target)
|
||||||
|
If wrapper Is Nothing Then _
|
||||||
|
Exit Function
|
||||||
|
If Not VsoIsListContainer(wrapper) Then _
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
Call VsoIncrementWidth(wrapper, valueInMM)
|
||||||
|
TryIncrementContained = True
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GeometricQuickSort(iPage As Visio.Page, ByRef target() As Long, ByVal nLow&, ByVal nHigh&)
|
||||||
|
Do While nLow < nHigh
|
||||||
|
Dim nPivot&: nPivot = GSPartition(iPage, target, nLow, nHigh)
|
||||||
|
Call GeometricQuickSort(iPage, target, nLow, nPivot - 1)
|
||||||
|
nLow = nPivot + 1
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GSPartition(iPage As Visio.Page, ByRef target() As Long, nLow&, nHigh&) As Long
|
||||||
|
Dim iPivot As Visio.Shape: Set iPivot = iPage.Shapes.ItemFromID(target(nHigh))
|
||||||
|
Dim nSmallest&: nSmallest = nLow - 1
|
||||||
|
Dim iTempValue As Variant
|
||||||
|
|
||||||
|
Dim n&
|
||||||
|
For n = nLow To nHigh - 1
|
||||||
|
Dim iShape As Visio.Shape: Set iShape = iPage.Shapes.ItemFromID(target(n))
|
||||||
|
If GeometricCompare(iShape, iPivot) < 0 Then
|
||||||
|
nSmallest = nSmallest + 1
|
||||||
|
iTempValue = target(nSmallest)
|
||||||
|
target(nSmallest) = target(n)
|
||||||
|
target(n) = iTempValue
|
||||||
|
End If
|
||||||
|
Next n
|
||||||
|
|
||||||
|
nSmallest = nSmallest + 1
|
||||||
|
iTempValue = target(nSmallest)
|
||||||
|
target(nSmallest) = target(nHigh)
|
||||||
|
target(nHigh) = iTempValue
|
||||||
|
|
||||||
|
GSPartition = nSmallest
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function GeometricCompare(iShape1 As Visio.Shape, iShape2 As Visio.Shape) As Double
|
||||||
|
GeometricCompare = iShape2.CellsU("PinY") + iShape2.CellsU("Height") / 2# - iShape1.CellsU("PinY") - iShape1.CellsU("Height") / 2#
|
||||||
|
If Abs(GeometricCompare) > SORT_PRECISION Then _
|
||||||
|
Exit Function
|
||||||
|
GeometricCompare = iShape1.CellsU("PinX") - iShape1.CellsU("Width") / 2# - iShape2.CellsU("PinX") + iShape2.CellsU("Width") / 2#
|
||||||
|
If Abs(GeometricCompare) > SORT_PRECISION Then _
|
||||||
|
Exit Function
|
||||||
|
GeometricCompare = 0
|
||||||
|
End Function
|
||||||
|
|
219
word/API_WordEditGuard.cls
Normal file
219
word/API_WordEditGuard.cls
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
VERSION 1.0 CLASS
|
||||||
|
BEGIN
|
||||||
|
MultiUse = -1 'True
|
||||||
|
END
|
||||||
|
Attribute VB_Name = "API_WordEditGuard"
|
||||||
|
Attribute VB_GlobalNameSpace = False
|
||||||
|
Attribute VB_Creatable = False
|
||||||
|
Attribute VB_PredeclaredId = False
|
||||||
|
Attribute VB_Exposed = False
|
||||||
|
' ========= Îáîëî÷êà äëÿ ðåäàêòèðîâàíèÿ äîêóìåíòà â Ìàñòåðå Òåêñòîâ ===================
|
||||||
|
' Shared module version: 20220613
|
||||||
|
' Tested in:
|
||||||
|
' Depends on: API_WordWrapper
|
||||||
|
' Required reference: Word
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
' Note: should synchronize with DocumentEditor
|
||||||
|
Private Const ERROR_HIGHLIGHT_COLOR = wdViolet
|
||||||
|
|
||||||
|
Private Const AUTOMATON_USER_NAME = "Ìàñòåð òåêñòîâ"
|
||||||
|
Private Const AUTOMATON_USER_INIT = "ÌÒ"
|
||||||
|
|
||||||
|
Private Const IGNORE_BM_PREFIX = "zIGNORE_"
|
||||||
|
Private Const AUTO_COMMENT_PREFIX = "AR"
|
||||||
|
|
||||||
|
Private document_ As Word.Document
|
||||||
|
Private word_ As API_WordWrapper
|
||||||
|
|
||||||
|
Private userName_ As String
|
||||||
|
Private userInitials_ As String
|
||||||
|
Private trackRevisions_ As Boolean
|
||||||
|
Private includeHiddenText_ As Boolean
|
||||||
|
Private includeFieldCodes_ As Boolean
|
||||||
|
Private deletedTextMark_ As WdDeletedTextMark
|
||||||
|
Private noProofing_ As Boolean
|
||||||
|
|
||||||
|
Private Sub Class_Terminate()
|
||||||
|
Set document_ = Nothing
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Function Init(target As API_WordWrapper)
|
||||||
|
Set document_ = target.Document
|
||||||
|
Set word_ = target
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function InitDoc(target As Word.Document)
|
||||||
|
Set document_ = target
|
||||||
|
Set word_ = Nothing
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Property Get Document() As Word.Document
|
||||||
|
Set Document = document_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Property Get Wrapper() As API_WordWrapper
|
||||||
|
Set Wrapper = word_
|
||||||
|
End Property
|
||||||
|
|
||||||
|
Public Function BeginEdit(bTrackRevisions As Boolean)
|
||||||
|
With document_
|
||||||
|
userName_ = .Application.UserName
|
||||||
|
userInitials_ = .Application.UserInitials
|
||||||
|
trackRevisions_ = .TrackRevisions
|
||||||
|
includeHiddenText_ = .Range.TextRetrievalMode.IncludeHiddenText
|
||||||
|
includeFieldCodes_ = .Range.TextRetrievalMode.IncludeFieldCodes
|
||||||
|
deletedTextMark_ = .Application.Options.DeletedTextMark
|
||||||
|
noProofing_ = .Range.NoProofing
|
||||||
|
|
||||||
|
.ActiveWindow.View.ReadingLayout = False
|
||||||
|
.Range.NoProofing = True
|
||||||
|
.Application.ScreenUpdating = False
|
||||||
|
End With
|
||||||
|
|
||||||
|
Call HideIgnored
|
||||||
|
Call ClearExistingFormat
|
||||||
|
|
||||||
|
On Error Resume Next ' Ñðàáàòûâàåò, åñëè îôèñ íå 2013 è ïàðàìåòðà Options.UseLocalUserInfo íå ñóùåñòâóåò
|
||||||
|
With document_
|
||||||
|
.TrackRevisions = bTrackRevisions
|
||||||
|
.Range.TextRetrievalMode.IncludeHiddenText = False
|
||||||
|
.Range.TextRetrievalMode.IncludeFieldCodes = False
|
||||||
|
.Application.Options.DeletedTextMark = wdDeletedTextMarkHidden
|
||||||
|
|
||||||
|
.Application.UserName = AUTOMATON_USER_NAME
|
||||||
|
.Application.UserInitials = AUTOMATON_USER_INIT
|
||||||
|
.Application.Options.UseLocalUserInfo = True
|
||||||
|
End With
|
||||||
|
On Error GoTo 0
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function EndEdit()
|
||||||
|
If userName_ = vbNullString Then _
|
||||||
|
userName_ = VBA.Environ("username")
|
||||||
|
|
||||||
|
On Error Resume Next
|
||||||
|
With document_
|
||||||
|
.TrackRevisions = trackRevisions_
|
||||||
|
Call UnHideIgnored
|
||||||
|
|
||||||
|
.Application.UserName = userName_
|
||||||
|
.Application.UserInitials = userInitials_
|
||||||
|
.Range.TextRetrievalMode.IncludeHiddenText = includeHiddenText_
|
||||||
|
.Range.TextRetrievalMode.IncludeFieldCodes = includeFieldCodes_
|
||||||
|
.Application.Options.DeletedTextMark = deletedTextMark_
|
||||||
|
.Range.NoProofing = noProofing_
|
||||||
|
|
||||||
|
.Application.ScreenUpdating = True
|
||||||
|
Call .Application.ScreenRefresh
|
||||||
|
End With
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function SaveAndShowDiff()
|
||||||
|
Dim oldName$: oldName = document_.FullName
|
||||||
|
Dim newName$: newName = oldName
|
||||||
|
newName = VBA.Left(newName, VBA.Len(newName) - VBA.Len(".docx"))
|
||||||
|
newName = newName & "_" & VBA.Format(VBA.Now(), "MMddhhmm") & ".docx"
|
||||||
|
Call document_.SaveAs(newName)
|
||||||
|
|
||||||
|
Dim iWrap As New API_WordWrapper: Call iWrap.OpenDocument(oldName)
|
||||||
|
Call document_.Application.CompareDocuments(iWrap.Document, document_, _
|
||||||
|
Destination:=wdCompareDestinationNew, _
|
||||||
|
CompareFields:=False, _
|
||||||
|
RevisedAuthor:=AUTOMATON_USER_NAME)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function BookmarkIgnored(target As Word.Range) As String
|
||||||
|
Dim nIndex&: nIndex = document_.Bookmarks.Count + 1
|
||||||
|
Dim sBookmark$: sBookmark = IGNORE_BM_PREFIX & nIndex
|
||||||
|
Do While document_.Bookmarks.Exists(sBookmark)
|
||||||
|
nIndex = nIndex - 1
|
||||||
|
sBookmark = IGNORE_BM_PREFIX & nIndex
|
||||||
|
Loop
|
||||||
|
|
||||||
|
BookmarkIgnored = sBookmark
|
||||||
|
If target.Bookmarks.Add(sBookmark, target) Is Nothing Then _
|
||||||
|
Call Err.Raise(vbObjectError)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ==============
|
||||||
|
Private Function HideIgnored()
|
||||||
|
' Ñêðûâàåì èãíîðèðóåìûå ÷àñòè òåêñòà
|
||||||
|
Dim trackRev As Boolean: trackRev = document_.TrackRevisions
|
||||||
|
document_.TrackRevisions = False
|
||||||
|
|
||||||
|
Dim aBookmark As Word.Bookmark
|
||||||
|
For Each aBookmark In document_.Bookmarks
|
||||||
|
If IsBookmarkIgnored(aBookmark.Name) Then _
|
||||||
|
aBookmark.Range.Font.Hidden = True
|
||||||
|
Next aBookmark
|
||||||
|
|
||||||
|
document_.TrackRevisions = trackRev
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function UnHideIgnored()
|
||||||
|
' Îòîáðàæàåì èãíîðèðóåìûå ÷àñòè òåêñòà
|
||||||
|
Dim trackRev As Boolean: trackRev = document_.TrackRevisions
|
||||||
|
document_.TrackRevisions = False
|
||||||
|
|
||||||
|
Dim aBookmark As Word.Bookmark
|
||||||
|
For Each aBookmark In document_.Bookmarks
|
||||||
|
If IsBookmarkIgnored(aBookmark.Name) Then _
|
||||||
|
aBookmark.Range.Font.Hidden = False
|
||||||
|
Next aBookmark
|
||||||
|
|
||||||
|
document_.TrackRevisions = trackRev
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function IsBookmarkIgnored(sBookmark$) As Boolean
|
||||||
|
IsBookmarkIgnored = sBookmark Like IGNORE_BM_PREFIX & "*"
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ClearExistingFormat()
|
||||||
|
' Î÷èùàåì ôîðìàòèðîâàíèå â äîêóìåíòå
|
||||||
|
Dim trackRev As Boolean: trackRev = document_.TrackRevisions
|
||||||
|
document_.TrackRevisions = False
|
||||||
|
|
||||||
|
' Î÷èùàåì âûäåëåíèå öâåòîì ññûëîê íà òåðìèíû
|
||||||
|
Dim rFind As Word.Range: Set rFind = document_.Range
|
||||||
|
Dim nStart&: nStart = -1
|
||||||
|
With rFind.Find
|
||||||
|
.Forward = True
|
||||||
|
.Font.Hidden = False
|
||||||
|
.Highlight = True
|
||||||
|
.Format = True
|
||||||
|
.MatchPhrase = False
|
||||||
|
.MatchCase = False
|
||||||
|
.MatchWholeWord = False
|
||||||
|
.MatchWildcards = False
|
||||||
|
End With
|
||||||
|
|
||||||
|
Do While rFind.Find.Execute
|
||||||
|
If rFind.HighlightColorIndex = ERROR_HIGHLIGHT_COLOR Then
|
||||||
|
rFind.HighlightColorIndex = wdNoHighlight
|
||||||
|
End If
|
||||||
|
|
||||||
|
Call rFind.Collapse(wdCollapseEnd)
|
||||||
|
|
||||||
|
If nStart = rFind.Start Then ' Ãàðàíòèðóåì, ÷òî íå çàöèêëèìñÿ
|
||||||
|
Debug.Print "Î÷èñòêà òåêñòà çàöèêëèëàñü"
|
||||||
|
Exit Do
|
||||||
|
Else
|
||||||
|
nStart = rFind.Start
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
|
||||||
|
Dim i&: i = 1
|
||||||
|
Do While True
|
||||||
|
If i > document_.Comments.Count Then _
|
||||||
|
Exit Do
|
||||||
|
|
||||||
|
If VBA.Left(document_.Comments(i).Range.Text, 2) = AUTO_COMMENT_PREFIX Then
|
||||||
|
document_.Comments(i).Delete
|
||||||
|
Else
|
||||||
|
i = i + 1
|
||||||
|
End If
|
||||||
|
Loop
|
||||||
|
|
||||||
|
document_.TrackRevisions = trackRev
|
||||||
|
End Function
|
226
word/ex_Word.bas
Normal file
226
word/ex_Word.bas
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
Attribute VB_Name = "ex_Word"
|
||||||
|
'================ Word helper functions =============
|
||||||
|
' Shared module version: 20220911
|
||||||
|
' Tested in: TestWord
|
||||||
|
' Depends on:
|
||||||
|
' Required reference:
|
||||||
|
Option Private Module
|
||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Public Function WordIntersectRanges(rng1 As Word.Range, rng2 As Word.Range) As Word.Range
|
||||||
|
If rng1.Document <> rng2.Document Then _
|
||||||
|
Exit Function
|
||||||
|
If rng1.Start >= rng2.End Or rng2.Start >= rng1.End Then _
|
||||||
|
Exit Function
|
||||||
|
If rng1.Start >= rng2.Start Then
|
||||||
|
If rng2.End > rng1.Start Then _
|
||||||
|
Set WordIntersectRanges = rng1.Document.Range(rng1.Start, IIf(rng1.End > rng2.End, rng2.End, rng1.End))
|
||||||
|
Else
|
||||||
|
If rng1.End > rng2.Start Then _
|
||||||
|
Set WordIntersectRanges = rng1.Document.Range(rng2.Start, IIf(rng1.End > rng2.End, rng2.End, rng1.End))
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function WordPutText(sText$, iDoc As Word.Document, nWhere&, Optional aStyle$ = vbNullString) As Word.Range
|
||||||
|
Set WordPutText = iDoc.Range(nWhere, nWhere)
|
||||||
|
With WordPutText
|
||||||
|
Call .InsertAfter(sText)
|
||||||
|
If aStyle <> vbNullString Then _
|
||||||
|
.Style = aStyle
|
||||||
|
End With
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function WordAppendTo(sText$, ByRef rWhere As Word.Range, Optional aStyle$ = vbNullString) As Word.Range
|
||||||
|
Const ASCII_NEWLINE = 13
|
||||||
|
|
||||||
|
If rWhere.Characters.Count > 0 Then _
|
||||||
|
If AscW(rWhere.Characters.Last) = ASCII_NEWLINE Then _
|
||||||
|
Call rWhere.MoveEnd(wdCharacter, -1)
|
||||||
|
|
||||||
|
Call rWhere.InsertAfter(sText)
|
||||||
|
Dim insertedRng As Word.Range: Set insertedRng = rWhere.Duplicate
|
||||||
|
insertedRng.Start = rWhere.End - VBA.Len(sText)
|
||||||
|
If aStyle <> vbNullString Then _
|
||||||
|
insertedRng.Style = aStyle
|
||||||
|
Set WordAppendTo = insertedRng
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function WordAddLine(sText$, rWhere As Word.Range, Optional aStyle$ = vbNullString) As Word.Range
|
||||||
|
Dim nEnd&: nEnd = rWhere.End
|
||||||
|
If rWhere.Tables.Count > 0 Then
|
||||||
|
If rWhere.Cells.Count > 0 Then
|
||||||
|
Dim theLastCell As Word.Cell: Set theLastCell = rWhere.Cells(rWhere.Cells.Count)
|
||||||
|
If nEnd >= theLastCell.Range.End - 1 Then
|
||||||
|
Set WordAddLine = AddLineLastInCell(sText, theLastCell, aStyle)
|
||||||
|
GoTo SAFE_EXIT
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim nParaEnd&: nParaEnd = rWhere.Paragraphs.Last.Range.End
|
||||||
|
If rWhere.End = rWhere.Document.Range.End Then
|
||||||
|
Call rWhere.InsertAfter(vbNewLine)
|
||||||
|
Set WordAddLine = WordPutText(sText, rWhere.Document, rWhere.End - 1, aStyle)
|
||||||
|
Call WordAddLine.MoveEnd(wdCharacter, 1)
|
||||||
|
GoTo SAFE_EXIT
|
||||||
|
End If
|
||||||
|
|
||||||
|
If nEnd <> nParaEnd Then
|
||||||
|
If rWhere.Next(wdCharacter, 1).End = nParaEnd Then
|
||||||
|
Call rWhere.MoveEnd(wdCharacter, 1)
|
||||||
|
Else
|
||||||
|
Call rWhere.InsertAfter(vbNewLine)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Set WordAddLine = WordPutText(sText & vbNewLine, rWhere.Document, rWhere.End, aStyle)
|
||||||
|
|
||||||
|
SAFE_EXIT:
|
||||||
|
rWhere.End = nEnd
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function WordFindAndReplace(iDoc As Word.Document, findText$, ReplaceText$, _
|
||||||
|
Optional mCase As Boolean = False, _
|
||||||
|
Optional wildCard As Boolean = False, _
|
||||||
|
Optional wholeWord As Boolean = True) As Long
|
||||||
|
Dim findRange As Word.Range: Set findRange = iDoc.Range
|
||||||
|
Dim nResult&: nResult = 0
|
||||||
|
With findRange.Find
|
||||||
|
.Text = findText
|
||||||
|
.MatchCase = mCase
|
||||||
|
.MatchWholeWord = wholeWord
|
||||||
|
.MatchWildcards = wildCard
|
||||||
|
.Replacement.Text = ReplaceText
|
||||||
|
.Format = True
|
||||||
|
.Font.Hidden = False
|
||||||
|
|
||||||
|
Do While .Execute(Replace:=1)
|
||||||
|
nResult = nResult + 1
|
||||||
|
Call findRange.Collapse(Direction:=wdCollapseEnd)
|
||||||
|
Loop
|
||||||
|
End With
|
||||||
|
WordFindAndReplace = nResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function WordDeleteHyperlinks(iDoc As Word.Document) As Long
|
||||||
|
On Error Resume Next
|
||||||
|
WordDeleteHyperlinks = 0
|
||||||
|
Do Until iDoc.Hyperlinks.Count = 0
|
||||||
|
Call iDoc.Hyperlinks(1).Delete
|
||||||
|
WordDeleteHyperlinks = WordDeleteHyperlinks + 1
|
||||||
|
Loop
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function WordStyleExists(iDoc As Word.Document, target$) As Boolean
|
||||||
|
WordStyleExists = True
|
||||||
|
On Error GoTo RET_FALSE
|
||||||
|
Dim aStyle As Word.Style: Set aStyle = iDoc.Styles(target)
|
||||||
|
Exit Function
|
||||||
|
|
||||||
|
RET_FALSE:
|
||||||
|
WordStyleExists = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' Requires: wordApp should be Application, responsible for both documents (if any or both are open)
|
||||||
|
Public Function WordCopyStyle(wordApp As Word.Application, sSource$, sDestination$, sStyle$) As Boolean
|
||||||
|
WordCopyStyle = True
|
||||||
|
On Error GoTo RETURN_FALSE
|
||||||
|
Call wordApp.OrganizerCopy(sSource, sDestination, sStyle, wdOrganizerObjectStyles)
|
||||||
|
On Error GoTo 0
|
||||||
|
Exit Function
|
||||||
|
RETURN_FALSE:
|
||||||
|
WordCopyStyle = False
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function WordStyleIsUsed(iDoc As Word.Document, aStyle As Word.Style) As Boolean
|
||||||
|
On Error Resume Next
|
||||||
|
Dim findRange As Word.Range: Set findRange = iDoc.Range
|
||||||
|
With findRange.Find
|
||||||
|
.Text = vbNullString
|
||||||
|
.Format = True
|
||||||
|
.Style = aStyle
|
||||||
|
WordStyleIsUsed = .Execute
|
||||||
|
End With
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function WordAdjustRange(target As Word.Range) As Word.Range
|
||||||
|
Dim rExpansion As Word.Range: Set rExpansion = target.Duplicate
|
||||||
|
If Not rExpansion.Characters.First Like "[" & " " & Chr(160) & "]" Then
|
||||||
|
Call rExpansion.StartOf(wdWord, wdExtend)
|
||||||
|
Call rExpansion.MoveStartWhile("""'«»“”", wdBackward)
|
||||||
|
End If
|
||||||
|
|
||||||
|
If rExpansion.Start <> rExpansion.End Then _
|
||||||
|
Call rExpansion.EndOf(wdWord, wdExtend)
|
||||||
|
|
||||||
|
If Not rExpansion.Characters.Last Like "[" & " " & Chr(160) & "]" Then
|
||||||
|
Call rExpansion.MoveEndWhile("""'«»“”", wdForward)
|
||||||
|
Call rExpansion.MoveEndWhile(" " & Chr(160), wdForward)
|
||||||
|
End If
|
||||||
|
|
||||||
|
Set WordAdjustRange = rExpansion
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AcceptAuthorsRevision(oAuthors As Collection, target As Word.Document) As Long
|
||||||
|
AcceptAuthorsRevision = 0
|
||||||
|
Dim itemChange As Word.Revision
|
||||||
|
For Each itemChange In target.Revisions
|
||||||
|
Dim sAuthor As Variant
|
||||||
|
For Each sAuthor In oAuthors
|
||||||
|
If sAuthor = itemChange.Author Then
|
||||||
|
Call itemChange.Accept
|
||||||
|
AcceptAuthorsRevision = AcceptAuthorsRevision + 1
|
||||||
|
GoTo NEXT_ITEM
|
||||||
|
End If
|
||||||
|
Next sAuthor
|
||||||
|
NEXT_ITEM:
|
||||||
|
Next itemChange
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function AcceptFormatRevision(iDoc As Word.Document) As Long
|
||||||
|
AcceptFormatRevision = 0
|
||||||
|
Dim itemChange As Word.Revision
|
||||||
|
Dim theType As WdRevisionType
|
||||||
|
For Each itemChange In iDoc.Revisions
|
||||||
|
theType = itemChange.Type
|
||||||
|
If theType = wdRevisionParagraphProperty Or _
|
||||||
|
theType = wdRevisionStyle Or _
|
||||||
|
theType = wdRevisionProperty Then
|
||||||
|
itemChange.Accept
|
||||||
|
AcceptFormatRevision = AcceptFormatRevision + 1
|
||||||
|
End If
|
||||||
|
Next itemChange
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function StyleByName(sStyle$, iDoc As Word.Document) As Word.Style
|
||||||
|
On Error GoTo RETURN_NOTHING
|
||||||
|
Set StyleByName = iDoc.Styles(sStyle)
|
||||||
|
RETURN_NOTHING:
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function ExtractCommentText(target As Word.Range) As String
|
||||||
|
If target.Comments.Count <> 0 Then
|
||||||
|
ExtractCommentText = target.Comments(1).Range.Text
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim theDoc As Word.Document: Set theDoc = target.Document
|
||||||
|
Dim aComment As Word.Comment
|
||||||
|
For Each aComment In theDoc.Comments
|
||||||
|
If target.InRange(aComment.Scope) Then
|
||||||
|
ExtractCommentText = aComment.Range.Text
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
Next aComment
|
||||||
|
ExtractCommentText = vbNullString
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' =====
|
||||||
|
Private Function AddLineLastInCell(sText$, whereCell As Word.Cell, aStyle$) As Word.Range
|
||||||
|
Dim nWhere&: nWhere = whereCell.Range.End - 1
|
||||||
|
If whereCell.Range.Characters.Count > 1 Then
|
||||||
|
Call WordPutText(vbNewLine, whereCell.Range.Document, nWhere)
|
||||||
|
nWhere = nWhere + 1
|
||||||
|
End If
|
||||||
|
Set AddLineLastInCell = WordPutText(sText, whereCell.Range.Document, whereCell.Range.End - 1, aStyle)
|
||||||
|
End Function
|
||||||
|
|
Loading…
Reference in New Issue
Block a user