When the registry empties, the table is unlisted and its cells are cleared.
The sheet is then free of tables, which is what SetupTranslationsTable reads:
it treats every table it finds on that sheet as a registry.
Internal members (not exported)
Factory
Seal #
seal
Seal the instance so setup-only setters can no longer be used.
Signature:
Public Sub Seal()
GuardNotSealed #
guard-not-sealed
Raise when a setup-only setter is used after sealing.
Signature:
Private Sub GuardNotSealed(ByVal propName As String)
PublicState
Wksh #
worksheet-set
Bind the watcher to a registry worksheet.
Signature:
Public Property Set Wksh(ByVal sh As Worksheet)
Parameters:
sh: Worksheet. The worksheet to assign.
EnsureWorksheet #
ensure-worksheet
Guard that the registry worksheet reference is initialised before use.
Signature:
Private Function EnsureWorksheet() As Worksheet
ColumnRegistration
AddColumns #
add-columns
Register or refresh watched columns for the supplied ListObject.
Signature:
Public Sub AddColumns(ByVal source As ListObject)
Reads the three rows above the header in one call and the header row in one
call, keeps the columns whose tag is one of the three known tags, and rewrites
the registry rows for this table. A table with no tagged column creates
nothing. The flag of a column that was already registered is carried over, so
a rebuild does not lose a pending change.
Parameters:
source: ListObject. Table to inspect for update tags.
AddSheet #
add-sheet
Register tagged columns for every ListObject on the provided worksheet.
Signature:
Public Sub AddSheet(ByVal targetSheet As Worksheet)
Every table on the sheet is read, and the registry is written once at the end
rather than once per table.
RemoveLo #
remove-lo
Remove registry entries associated with the supplied ListObject.
Signature:
Public Sub RemoveLo(ByVal target As ListObject)
MergeTableEntries #
merge-table-entries
Replace the registry rows belonging to one table with the supplied rows.
Signature:
Private Sub MergeTableEntries(ByVal tableName As String, _
ByVal wanted As Collection, _
ByVal source As ListObject)
Parameters:
tableName: String. Source table whose rows are being replaced.
wanted: Collection. Rows to keep for that table, empty to drop them all.
source: ListObject. Table the defined names point at, Nothing when dropping.
ColumnLabelMatchesTable #
column-label-matches-table
Determine whether a registry column label belongs to the provided ListObject name.
Signature:
Private Function ColumnLabelMatchesTable(ByVal columnLabel As String, _
ByVal tableName As String) As Boolean
ReadTagBlock #
read-tag-block
Read the rows above the header row in one call.
Signature:
Private Sub ReadTagBlock(ByVal headerRow As Range, ByRef block As Variant, ByRef tagRows As Long)
Parameters:
headerRow: Range. Header row of the source table.
block: Variant. Output, the values of the rows above the header.
tagRows: Long. Output, how many rows the block holds.
TagForColumn #
tag-for-column
Read the metadata tag of a column, walking up from the header row.
Signature:
Private Function TagForColumn(ByVal block As Variant, _
ByVal tagRows As Long, _
ByVal columnCount As Long, _
ByVal columnIndex As Long) As String
The walk answers with the first cell holding one of the three known tags. A
section title or a note sitting between the tag row and the header is stepped
over, and a value that is not a tag never wins. Analysis tables are stacked,
so the row three above a header can belong to the table above it; looking for
a known tag removes that risk with it.
ShouldWatchColumn #
should-watch-column
Determine whether a column should be monitored based on its tag value.
Signature:
Private Function ShouldWatchColumn(ByVal tagValue As String) As Boolean
Parameters:
tagValue: String. Tag retrieved from worksheet metadata.
RegistryWriting
BeginBatch #
begin-batch
Load the registry rows into memory so a build pass can change them.
Signature:
Private Sub BeginBatch()
EndBatch #
end-batch
Write the registry rows back in one call and drop the in-memory index.
Signature:
Private Sub EndBatch()
ReadRegistryEntries #
read-registry-entries
Read every registry row into a collection, in one call.
Signature:
Private Function ReadRegistryEntries() As Collection
WriteRegistryEntries #
write-registry-entries
Write the supplied rows over the registry table in one call.
Signature:
Private Sub WriteRegistryEntries(ByVal entries As Collection)
An empty registry leaves no table behind: the ListObject is unlisted and its
cells are cleared. A table always keeps one data row, so an empty table would
mean a blank row every reader has to step over.
ResolveRegistry #
resolve-registry
Locate the registry table, building it when asked.
Signature:
Private Function ResolveRegistry(ByVal createWhenMissing As Boolean) As ListObject
CreateRegistryTable #
create-registry-table
Build an empty registry ListObject with the expected headers.
Signature:
Private Function CreateRegistryTable(ByVal sh As Worksheet) As ListObject
ensure-registry-headers
Guarantee the registry ListObject exposes the expected header names.
Signature:
Private Sub EnsureRegistryHeaders(ByVal lo As ListObject)
RemoveRegistryTable #
remove-registry-table
Take the registry table off the sheet and clear the cells it held.
Signature:
Private Sub RemoveRegistryTable()
NamedRanges
EnsureNamedRange #
ensure-named-range
Create or refresh the workbook defined name pointing to the watched column.
Signature:
Private Sub EnsureNamedRange(ByVal source As ListObject, _
ByVal columnName As String, _
ByVal rangeName As String)
RemoveNamedRange #
remove-named-range
Delete a single defined name if it exists in the workbook.
Signature:
Private Sub RemoveNamedRange(ByVal rangeName As String)
RemoveSheetNamedRanges #
remove-sheet-named-ranges
Delete every watcher defined name belonging to the registry sheet, in one pass.
Signature:
Private Sub RemoveSheetNamedRanges()
StatusTracking
CheckUpdate #
check-update
Evaluate a range change against the registered watchers and flag updates.
Signature:
Public Sub CheckUpdate(ByVal tagReference As Variant, ByVal Target As Range)
Every watched column the change covers is flagged, so a paste across two
watched columns flips two flags.
The handler only ever writes a flag. Cleanup belongs to AddColumns and
AddSheet, which run at build time: a defined name can fail to resolve for a
moment while a source table resizes or a sheet is renamed, and deleting a row
on that would take the registration away for good.
The cost is four to five worksheet calls whatever the registry holds. A table
nobody watches is left after two of them, and the flag cell is written every
time a watched column is covered.
Parameters:
tagReference: Variant. Kept for the call shape; the table is read from Target.
Target: Range. The changed cells to evaluate.
MarkWideTarget #
mark-wide-target
Flag the watched tables a whole-row or whole-column change reaches.
Signature:
Private Sub MarkWideTarget(ByVal changed As Range)
Selecting a worksheet row or column gives a target with no ListObject. Only
the watched tables on that sheet are asked, one Intersect each.
MarkColumns #
mark-columns
Flag every watched column of one table that the change covers.
Signature:
Private Sub MarkColumns(ByVal lo As ListObject, ByVal changed As Range)
The header row is read on every edit rather than cached. It is one worksheet
call, and it stays right when the user inserts or deletes a column in the
source table; a cached position would need an invalidation signal that costs a
call to read anyway.
MarkKey #
mark-key
Write the updated flag for one watched column, on every change.
Signature:
Private Sub MarkKey(ByVal tableKey As String, ByVal headerText As String)
THE CELL IS WRITTEN EVERY TIME, and that is the whole point of this member.
It used to skip the write whenever the in-memory flag already read "yes":
If KeyedText(this.statusByKey, key) = STATUS_UPDATED Then Exit Sub
The flag then lived in memory and never reached the worksheet, and the
worksheet is what every reader of the registry actually reads.
SetupTranslationsTable.ProcessRegistryTable takes the status straight off the
cell, so a column whose flag stayed in memory was read as "not updated" and
its translations were skipped. The saving was one write per edit against the
four to five worksheet calls an edit already costs.
CleanupHelpers
ClearUp #
clear-up
Reset all tracked columns to the default (not updated) status.
Signature:
Public Sub ClearUp()
switch-tags
Set every registry status flag on the sheet to the supplied value.
Signature:
Private Sub SwitchTags(ByVal value As String)
Every table on the sheet carrying an "updated" column is reset, including the
ones this class never built. The master setup registry is hand-built and a
workbook made before the single-table layout carries its own registries; both
have to reset when the translations button is pressed.
StatusColumnOf #
status-column-of
Return the flag column of a table that carries one.
Signature:
Private Function StatusColumnOf(ByVal lo As ListObject) As Range
InMemoryIndex
EnsureIndex #
ensure-index
Build the in-memory index the first time something needs it.
Signature:
Private Sub EnsureIndex()
ResetIndex #
reset-index
Drop the in-memory index so the next read rebuilds it from the sheet.
Signature:
Private Sub ResetIndex()
The acknowledged set is left alone. It answers for the session, and the index
is dropped whenever the registry is written.
LoadIndex #
load-index
Read the whole registry in one call and build the lookups from it.
Signature:
Private Sub LoadIndex()
RefreshStatusIndex #
refresh-status-index
Put the same flag on every key the index holds.
Signature:
Private Sub RefreshStatusIndex(ByVal value As String)
SetStatus #
set-status
Record the flag of one key.
Signature:
Private Sub SetStatus(ByVal key As String, ByVal value As String)
ReadStatus #
read-status
Read a flag cell, answering the default for anything that is not "yes".
Signature:
Private Function ReadStatus(ByVal valueText As String) As String
RegistryColumnPosition #
registry-column-position
Find a registry column by header name, falling back to its published position.
Signature:
Private Function RegistryColumnPosition(ByVal lo As ListObject, _
ByVal headerName As String, _
ByVal fallbackColumn As Long) As Long
ColumnData #
column-data
Return the data body of one registry column by position.
Signature:
Private Function ColumnData(ByVal lo As ListObject, ByVal position As Long) As Range
Utilities
RegistryName #
registry-name
Compose the name of the registry table hosted on this sheet.
Signature:
Private Function RegistryName() As String
SheetKey #
sheet-key
Return the normalised worksheet name, read and normalised once.
Signature:
Private Function SheetKey() As String
BuildColumnLabel #
build-column-label
Compose the descriptive label stored in the registry for a watched column.
Signature:
Private Function BuildColumnLabel(ByVal tableName As String, ByVal columnName As String) As String
TableNameFromLabel #
table-name-from-label
Retrieve the table portion from a table-column label.
Signature:
Private Function TableNameFromLabel(ByVal columnLabel As String) As String
ExtractColumnNameFromLabel #
extract-column-name-from-label
Retrieve the column portion from a table-column label.
Signature:
Private Function ExtractColumnNameFromLabel(ByVal columnLabel As String) As String
BuildRangeName #
build-range-name
Generate the workbook defined name for a watched column, including table context.
Signature:
Private Function BuildRangeName(ByVal tableKey As String, ByVal columnKey As String) As String
NormalizeIdentifier #
normalize-identifier
Produce a safe workbook-level identifier from free-form text.
Signature:
Private Function NormalizeIdentifier(ByVal valueText As String) As String
Letters are lower-cased, digits are kept, and everything else becomes a single
underscore, with leading and trailing underscores dropped. One pass writes the
answer in place with Mid$, the way HiddenNames.SanitizeNameId does it.
NormalizeValue #
normalize-value
Normalise text by trimming spaces and applying lower case.
Signature:
Private Function NormalizeValue(ByVal valueText As String) As String
SafeText #
safe-text
Read a cell value as text, answering an empty string for an error value.
Signature:
Private Function SafeText(ByVal value As Variant) As String
CellText #
cell-text
Read one cell out of a bulk .Value2 result.
Signature:
Private Function CellText(ByVal block As Variant, _
ByVal rowIndex As Long, _
ByVal columnIndex As Long, _
ByVal rowTotal As Long, _
ByVal columnTotal As Long) As String
A single cell read through .Value2 answers a scalar rather than a 2D array, so
both shapes are handled here.
KeyedAdd #
keyed-add
Add an item under a key, leaving the first entry in place.
Signature:
Private Sub KeyedAdd(ByVal target As Collection, ByVal item As Variant, ByVal key As String)
KeyedRemove #
keyed-remove
Remove the item held under a key, when there is one.
Signature:
Private Sub KeyedRemove(ByVal target As Collection, ByVal key As String)
KeyedHas #
keyed-has
Report whether a key holds an item.
Signature:
Private Function KeyedHas(ByVal source As Collection, ByVal key As String) As Boolean
KeyedText #
keyed-text
Read the text held under a key, answering an empty string when there is none.
Signature:
Private Function KeyedText(ByVal source As Collection, ByVal key As String) As String
KeyedNumber #
keyed-number
Read the number held under a key, answering zero when there is none.
Signature:
Private Function KeyedNumber(ByVal source As Collection, ByVal key As String) As Long
ThrowError #
throw-error
Raise a ProjectError aligned error from the current class.
Signature:
Private Sub ThrowError(ByVal errNumber As ProjectError, ByVal errorMessage As String)