← Macro & add-in library

Search & navigation · R10.1.1 form preflight

Assembly Part Search Tool

A SolidWorks VBA search and navigation tool for large assemblies, with ranked text matching and temporary visibility isolation.

ENGINEERING CONTRIBUTION

Designed the component index, ranked search, result interaction, isolation/restore workflow, and form integration contract.

Prerequisites

  • SolidWorks on Windows with an active assembly and SolidWorks VBA/API references.
  • Microsoft Forms controls and the matching frmPartSearch form resources.
  • The Windows API declarations and pointer types must match the host VBA architecture.

Additional setup

  • Requires the matching frmPartSearch .frm/.frx resources and form event wiring.

SOURCE WALKTHROUGH

How the workflow fits together.

  1. 01

    Preflight the host and form

    main connects to the active assembly, clears stale wheel hooks, builds the component index, and separately loads and validates the form before displaying it.

  2. 02

    Rank component names

    Candidate scoring combines starts-with and contains matches, token coverage and edit-distance similarity. The result map links each list row to a component; the configured display limit is 100 results.

  3. 03

    Navigate and isolate

    Form callbacks select, preview and zoom components. Isolation stores visibility state; switching selection exits the prior isolate, verifies the new selection and reapplies isolation.

  4. 04

    Restore transient state

    Cleanup restores visibility and removes temporary window subclassing. APS_EmergencyCleanup provides a recovery entry point.

Output & model changes

  • Changes selection, component visibility and view zoom in the active assembly.
  • Temporarily subclasses form/focus windows for mouse-wheel events.
  • APS_ENABLE_FILE_LOG enables file logging, including model/session details.

CODE & ENTRY POINTS

Read the implementation.

Find a procedure, follow an API call or download the module for your SolidWorks setup.

AssemblyPartSearch.bas

Standard module containing ranked component search, result-to-component mapping, visibility snapshots, isolate transitions and temporary mouse-wheel window subclassing.

VBA · 4,505 lines

Download .bas

Entry points
API & integration calls
Selected line 1
Attribute VB_Name = "modAssemblySearch"
Option Explicit

'===========================================================
' ASSEMBLY PART SEARCH - APS_R10_1_1_FORM_PREFLIGHT
' SOLIDWORKS VBA
'
' BUILD: 2026-09-22 R10.1.1 FORM PREFLIGHT + BULK FAST ISOLATE + R9 WHEEL
'
' IMPORTANT CHANGES IN R8
' ----------------------------------------------------------
' 1) Dynamic isolate mode is NEVER cleared by
'    IsCommandEnabled(). The previous code incorrectly
'    treated command availability as isolate state.
'
' 2) Dynamic isolate switch is:
'       Exit old isolate
'       Select NEW list item(s)
'       Verify selection count
'       Isolate NEW selection
'       Reselect
'       Zoom to NEW selection
'
' 3) ListBox.Change does NOT launch isolate operations.
'    MouseUp / KeyUp are used after MSForms selection has
'    fully settled.
'
' 4) Mouse wheel uses temporary subclassing only.
'
' 5) Both the UserForm window AND the currently focused
'    child window are subclassed while the pointer is over
'    the result list. WM_MOUSEWHEEL is normally delivered to
'    the focus window, so this covers both routing paths.
'===========================================================


'===========================================================
' SEARCH SETTINGS
'===========================================================

Private Const APS_BUILD_ID As String = "APS_R10_1_1_FORM_PREFLIGHT_2026-09-22"

Private Const APS_MAX_RESULTS As Long = 100
Private Const APS_MIN_FUZZY_LENGTH As Long = 3
Private Const APS_MIN_DISPLAY_SCORE As Double = 300
Private Const APS_SKIP_FUZZY_SCORE As Double = 800

Private Const APS_ENABLE_FILE_LOG As Boolean = True


'===========================================================
' FAST ISOLATE VISIBILITY CONSTANTS
'
' swComponentVisibilityState_e:
'   Hidden  = 0
'   Visible = 1
'
' R9 FAST ISOLATE does NOT use native IAssemblyDoc.Isolate.
'===========================================================

Private Const APS_COMPONENT_HIDDEN As Long = 0
Private Const APS_COMPONENT_VISIBLE As Long = 1
Private Const APS_VISIBILITY_UNKNOWN As Long = -9999
Private Const APS_MAX_PARENT_DEPTH As Long = 64


'===========================================================
' SOLIDWORKS OBJECTS
'===========================================================

Private APS_swApp As SldWorks.SldWorks
Private APS_swModel As SldWorks.ModelDoc2
Private APS_swAssy As SldWorks.AssemblyDoc


'===========================================================
' COMPONENT INDEX
'===========================================================

Private APS_Components() As SldWorks.Component2

Private APS_BaseName() As String
Private APS_LeafName() As String
Private APS_FullName() As String

Private APS_BaseNorm() As String
Private APS_LeafNorm() As String
Private APS_FullNorm() As String

Private APS_BaseCompact() As String
Private APS_LeafCompact() As String
Private APS_FullCompact() As String

Public APS_ComponentCount As Long


'===========================================================
' ALL COMPONENT INSTANCES
'
' APS_Components() contains searchable PART instances only.
' APS_AllComponents() also contains subassembly components.
'
' All-component storage is required so Fast Isolate can
' snapshot and restore the exact pre-isolate visibility
' state, including hidden subassembly parents.
'===========================================================

Private APS_AllComponents() As SldWorks.Component2
Private APS_AllIsPart() As Boolean
Private APS_AllCount As Long


'===========================================================
' FAST ISOLATE STATE
'===========================================================

Private APS_FastIsolateActive As Boolean
Private APS_FastSnapshotValid As Boolean

Private APS_OriginalVisibility() As Long
Private APS_FastCurrentVisiblePart() As Boolean

Private APS_LastIsolateSelectionSignature As String


'===========================================================
' RESULT MAP
'===========================================================

Private APS_ResultMap() As Long


'===========================================================
' UI / RE-ENTRY STATE
'===========================================================

Public APS_UpdatingResults As Boolean
Public APS_PreviewComponentIndex As Long

Private APS_SelectionBusy As Boolean
Private APS_IsolationBusy As Boolean
Private APS_DynamicIsolateBusy As Boolean


'===========================================================
' FAST ISOLATE MODE
'
' Fast Isolate is a temporary visibility layer maintained
' by this macro:
'
'   Enter once:
'       snapshot component visibility
'       hide non-selected part instances
'
'   Switch:
'       hide only parts leaving the selection
'       show only parts entering the selection
'
'   Exit:
'       restore the exact visibility snapshot
'
' No native ExitIsolate/Isolate cycle occurs during browsing.
'===========================================================


'===========================================================
' RIGHT CLICK SELECTION MEMORY
'===========================================================

Private APS_RightClickSelection() As Boolean
Private APS_RightClickListCount As Long
Private APS_RightClickSelectedCount As Long


'===========================================================
' DEBUG STATE
'===========================================================

Private APS_LogFilePath As String
Private APS_LogSessionStarted As Boolean
Private APS_CurrentOperation As String
Private APS_CurrentStep As String
Private APS_LastSubclassError As String


'===========================================================
' MOUSE WHEEL SUBCLASS STATE
'
' We subclass:
'   A) the UserForm top-level window
'   B) the currently focused child window, when it belongs
'      to the UserForm
'
' This captures WM_MOUSEWHEEL whether it is delivered first
' to the focused MSForms child or propagated to the form.
'===========================================================

Private Const APS_GWLP_WNDPROC As Long = -4
Private Const APS_WM_MOUSEWHEEL As Long = &H20A
Private Const APS_WHEEL_DELTA As Long = 120
Private Const APS_SCROLL_LINES As Long = 3

Private APS_FormHwnd As LongPtr
Private APS_FormOriginalWndProc As LongPtr
Private APS_FormSubclassInstalled As Boolean

Private APS_FocusHwnd As LongPtr
Private APS_FocusOriginalWndProc As LongPtr
Private APS_FocusSubclassInstalled As Boolean

Private APS_MouseOverList As Boolean
Private APS_WheelBusy As Boolean
Private APS_WheelRemainder As Long
Private APS_WheelTarget As Object

Private APS_WheelEventCount As Long
Private APS_LastWheelDelta As Long
Private APS_WheelShuttingDown As Boolean


'===========================================================
' CONTEXT MENU
'===========================================================

Private Const APS_MF_STRING As Long = &H0&
Private Const APS_MF_SEPARATOR As Long = &H800&

Private Const APS_TPM_LEFTALIGN As Long = &H0&
Private Const APS_TPM_RIGHTBUTTON As Long = &H2&
Private Const APS_TPM_NONOTIFY As Long = &H80&
Private Const APS_TPM_RETURNCMD As Long = &H100&

Private Const APS_WM_NULL As Long = 0

Private Const APS_MENU_ISOLATE As Long = 11001
Private Const APS_MENU_EXIT_ISOLATE As Long = 11002
Private Const APS_MENU_DIAGNOSTICS As Long = 11003


'===========================================================
' WINDOWS POINT
'===========================================================

Private Type APS_POINTAPI
    X As Long
    Y As Long
End Type


'===========================================================
' WINDOWS API - SUBCLASS / WHEEL
'===========================================================

Private Declare PtrSafe Function APS_FindWindow _
    Lib "user32" _
    Alias "FindWindowA" ( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As LongPtr

Private Declare PtrSafe Function APS_GetFocus _
    Lib "user32" _
    Alias "GetFocus" () As LongPtr

Private Declare PtrSafe Function APS_IsWindow _
    Lib "user32" _
    Alias "IsWindow" ( _
        ByVal hWnd As LongPtr) As Long

Private Declare PtrSafe Function APS_IsChild _
    Lib "user32" _
    Alias "IsChild" ( _
        ByVal hWndParent As LongPtr, _
        ByVal hWnd As LongPtr) As Long

Private Declare PtrSafe Function APS_GetClassName _
    Lib "user32" _
    Alias "GetClassNameA" ( _
        ByVal hWnd As LongPtr, _
        ByVal lpClassName As String, _
        ByVal nMaxCount As Long) As Long

#If Win64 Then

Private Declare PtrSafe Function APS_SetWindowLongPtr _
    Lib "user32" _
    Alias "SetWindowLongPtrA" ( _
        ByVal hWnd As LongPtr, _
        ByVal nIndex As Long, _
        ByVal dwNewLong As LongPtr) As LongPtr

#Else

Private Declare PtrSafe Function APS_SetWindowLongPtr _
    Lib "user32" _
    Alias "SetWindowLongA" ( _
        ByVal hWnd As LongPtr, _
        ByVal nIndex As Long, _
        ByVal dwNewLong As LongPtr) As LongPtr

#End If

Private Declare PtrSafe Function APS_CallWindowProc _
    Lib "user32" _
    Alias "CallWindowProcA" ( _
        ByVal lpPrevWndFunc As LongPtr, _
        ByVal hWnd As LongPtr, _
        ByVal Msg As Long, _
        ByVal wParam As LongPtr, _
        ByVal lParam As LongPtr) As LongPtr

Private Declare PtrSafe Function APS_GetLastError _
    Lib "kernel32" _
    Alias "GetLastError" () As Long

Private Declare PtrSafe Sub APS_SetLastError _
    Lib "kernel32" _
    Alias "SetLastError" ( _
        ByVal dwErrCode As Long)


'===========================================================
' WINDOWS API - POPUP MENU
'===========================================================

Private Declare PtrSafe Function APS_CreatePopupMenu _
    Lib "user32" _
    Alias "CreatePopupMenu" () As LongPtr

Private Declare PtrSafe Function APS_DestroyMenu _
    Lib "user32" _
    Alias "DestroyMenu" ( _
        ByVal hMenu As LongPtr) As Long

Private Declare PtrSafe Function APS_AppendMenu _
    Lib "user32" _
    Alias "AppendMenuA" ( _
        ByVal hMenu As LongPtr, _
        ByVal uFlags As Long, _
        ByVal uIDNewItem As LongPtr, _
        ByVal lpNewItem As String) As Long

Private Declare PtrSafe Function APS_TrackPopupMenu _
    Lib "user32" _
    Alias "TrackPopupMenu" ( _
        ByVal hMenu As LongPtr, _
        ByVal uFlags As Long, _
        ByVal X As Long, _
        ByVal Y As Long, _
        ByVal nReserved As Long, _
        ByVal hWnd As LongPtr, _
        ByVal prcRect As LongPtr) As Long

Private Declare PtrSafe Function APS_GetCursorPos _
    Lib "user32" _
    Alias "GetCursorPos" ( _
        ByRef lpPoint As APS_POINTAPI) As Long

Private Declare PtrSafe Function APS_GetForegroundWindow _
    Lib "user32" _
    Alias "GetForegroundWindow" () As LongPtr

Private Declare PtrSafe Function APS_SetForegroundWindow _
    Lib "user32" _
    Alias "SetForegroundWindow" ( _
        ByVal hWnd As LongPtr) As Long

Private Declare PtrSafe Function APS_PostMessage _
    Lib "user32" _
    Alias "PostMessageA" ( _
        ByVal hWnd As LongPtr, _
        ByVal wMsg As Long, _
        ByVal wParam As LongPtr, _
        ByVal lParam As LongPtr) As Long


'===========================================================
' LOGGING
'===========================================================

Private Sub APS_StartDebugSession()

    Dim ff As Integer

    On Error Resume Next

    APS_LogFilePath = _
        Environ$("TEMP") & _
        "\AssemblyPartSearch_Debug.log"

    APS_LogSessionStarted = True

    If APS_ENABLE_FILE_LOG Then

        ff = FreeFile

        Open APS_LogFilePath For Append As #ff

        Print #ff, ""
        Print #ff, String$(80, "=")
        Print #ff, "NEW SESSION: " & Format$(Now, "yyyy-mm-dd hh:nn:ss")
        Print #ff, "BUILD: " & APS_BUILD_ID
        Print #ff, String$(80, "=")

        Close #ff

    End If

    APS_Log "SESSION", "Diagnostic session started"
    APS_Log "SESSION", "BUILD=" & APS_BUILD_ID
    APS_Log "SESSION", "Log file=" & APS_LogFilePath

    On Error GoTo 0

End Sub


Public Sub APS_Log( _
    ByVal category As String, _
    ByVal message As String)

    Dim outputLine As String
    Dim ff As Integer

    On Error Resume Next

    If Not APS_LogSessionStarted Then

        APS_LogFilePath = _
            Environ$("TEMP") & _
            "\AssemblyPartSearch_Debug.log"

        APS_LogSessionStarted = True

    End If

    outputLine = _
        Format$(Now, "hh:nn:ss") & _
        " | " & category & _
        " | " & message

    Debug.Print outputLine

    If APS_ENABLE_FILE_LOG Then

        ff = FreeFile

        Open APS_LogFilePath For Append As #ff
        Print #ff, outputLine
        Close #ff

    End If

    On Error GoTo 0

End Sub


Private Sub APS_SetStep( _
    ByVal operationName As String, _
    ByVal stepName As String)

    APS_CurrentOperation = operationName
    APS_CurrentStep = stepName

    APS_Log operationName, "STEP: " & stepName

End Sub


Private Sub APS_LogError( _
    ByVal procedureName As String, _
    ByVal stepName As String, _
    ByVal errorNumber As Long, _
    ByVal errorDescription As String, _
    ByVal errorSource As String)

    APS_Log _
        "ERROR", _
        procedureName & _
        " | Step=" & stepName & _
        " | Err=" & CStr(errorNumber) & _
        " | Hex=" & Hex$(errorNumber) & _
        " | Description=" & errorDescription & _
        " | Source=" & errorSource

End Sub


Private Sub APS_ShowDiagnosticError( _
    ByVal procedureName As String, _
    ByVal stepName As String, _
    ByVal errorNumber As Long, _
    ByVal errorDescription As String)

    MsgBox _
        "Assembly Part Search error." & _
        vbCrLf & vbCrLf & _
        "Procedure: " & procedureName & _
        vbCrLf & _
        "Step: " & stepName & _
        vbCrLf & _
        "Error: " & CStr(errorNumber) & _
        vbCrLf & _
        "Description: " & errorDescription & _
        vbCrLf & vbCrLf & _
        "Log:" & vbCrLf & APS_LogFilePath, _
        vbExclamation, _
        "Assembly Part Search"

End Sub


Public Sub APS_PrintDiagnostics()

    APS_Log "DIAGNOSTICS", "BUILD=" & APS_BUILD_ID
    APS_Log "DIAGNOSTICS", "ComponentCount=" & CStr(APS_ComponentCount)
    APS_Log "DIAGNOSTICS", "UpdatingResults=" & CStr(APS_UpdatingResults)
    APS_Log "DIAGNOSTICS", "SelectionBusy=" & CStr(APS_SelectionBusy)
    APS_Log "DIAGNOSTICS", "IsolationBusy=" & CStr(APS_IsolationBusy)
    APS_Log "DIAGNOSTICS", "DynamicIsolateBusy=" & CStr(APS_DynamicIsolateBusy)
    APS_Log "DIAGNOSTICS", "FastIsolateActive=" & CStr(APS_FastIsolateActive)
    APS_Log "DIAGNOSTICS", "FastSnapshotValid=" & CStr(APS_FastSnapshotValid)
    APS_Log "DIAGNOSTICS", "AllComponentCount=" & CStr(APS_AllCount)
    APS_Log "DIAGNOSTICS", "IsolateSignature=" & APS_LastIsolateSelectionSignature

    APS_Log "DIAGNOSTICS", "FormSubclassInstalled=" & CStr(APS_FormSubclassInstalled)
    APS_Log "DIAGNOSTICS", "FormHwnd=" & CStr(APS_FormHwnd)
    APS_Log "DIAGNOSTICS", "FormClass=" & APS_GetWindowClassName(APS_FormHwnd)

    APS_Log "DIAGNOSTICS", "FocusSubclassInstalled=" & CStr(APS_FocusSubclassInstalled)
    APS_Log "DIAGNOSTICS", "FocusHwnd=" & CStr(APS_FocusHwnd)
    APS_Log "DIAGNOSTICS", "FocusClass=" & APS_GetWindowClassName(APS_FocusHwnd)

    APS_Log "DIAGNOSTICS", "WheelEventCount=" & CStr(APS_WheelEventCount)
    APS_Log "DIAGNOSTICS", "LastWheelDelta=" & CStr(APS_LastWheelDelta)

    APS_Log "DIAGNOSTICS", "CurrentOperation=" & APS_CurrentOperation
    APS_Log "DIAGNOSTICS", "CurrentStep=" & APS_CurrentStep

    If Len(APS_LastSubclassError) > 0 Then
        APS_Log "DIAGNOSTICS", "LastSubclassError=" & APS_LastSubclassError
    End If

End Sub


'===========================================================
' MAIN
'===========================================================

Public Sub main()

    Dim stepName As String

    Dim savedErrNumber As Long
    Dim savedErrDescription As String
    Dim savedErrSource As String

    Dim formValidationMessage As String

    On Error GoTo MainError

    APS_StartDebugSession

    APS_Log "MAIN", "BUILD=" & APS_BUILD_ID

    stepName = "Remove previous wheel subclass"
    APS_SetStep "MAIN", stepName
    APS_DisableWheelSubclass

    stepName = "Connect to SOLIDWORKS"
    APS_SetStep "MAIN", stepName
    Set APS_swApp = Application.SldWorks

    stepName = "Get active document"
    APS_SetStep "MAIN", stepName
    Set APS_swModel = APS_swApp.ActiveDoc

    If APS_swModel Is Nothing Then

        MsgBox _
            "Open a SOLIDWORKS assembly first.", _
            vbExclamation, _
            "Assembly Part Search"

        Exit Sub

    End If

    If APS_swModel.GetType <> swDocASSEMBLY Then

        MsgBox _
            "The active document must be a SOLIDWORKS assembly.", _
            vbExclamation, _
            "Assembly Part Search"

        Exit Sub

    End If

    Set APS_swAssy = APS_swModel

    APS_UpdatingResults = False
    APS_PreviewComponentIndex = -1

    APS_SelectionBusy = False
    APS_IsolationBusy = False
    APS_DynamicIsolateBusy = False

    APS_FastIsolateActive = False
    APS_FastSnapshotValid = False
    APS_LastIsolateSelectionSignature = ""

    APS_WheelEventCount = 0
    APS_LastWheelDelta = 0

    stepName = "Build component index"
    APS_SetStep "MAIN", stepName

    If Not APS_BuildComponentIndex() Then

        MsgBox _
            "No searchable part components were found.", _
            vbInformation, _
            "Assembly Part Search"

        Exit Sub

    End If

    APS_Log _
        "MAIN", _
        "Indexed parts=" & CStr(APS_ComponentCount)

    '-------------------------------------------------------
    ' FORM RESOURCE PREFLIGHT
    '
    ' Load is intentionally separated from Show.
    '
    ' If Microsoft Forms cannot instantiate the UserForm or
    ' one of its stored controls, the log now reports:
    '
    '   MAIN | STEP: Load frmPartSearch resource
    '
    ' rather than vaguely blaming Show.
    '-------------------------------------------------------

    stepName = "Load frmPartSearch resource"
    APS_SetStep "MAIN", stepName

    Load frmPartSearch

    APS_Log _
        "FORM_PREFLIGHT", _
        "frmPartSearch resource loaded successfully"

    stepName = "Validate frmPartSearch controls"
    APS_SetStep "MAIN", stepName

    If Not APS_ValidatePartSearchForm( _
        frmPartSearch, _
        formValidationMessage) Then

        Err.Raise _
            vbObjectError + 2201, _
            "APS_ValidatePartSearchForm", _
            formValidationMessage

    End If

    APS_Log _
        "FORM_PREFLIGHT", _
        "Required controls validated"

    stepName = "Show form"
    APS_SetStep "MAIN", stepName

    frmPartSearch.Show vbModeless

    APS_Log _
        "MAIN", _
        "Search form shown"

    Exit Sub

MainError:

    'CRITICAL:
    'Cache Err BEFORE logging/file I/O. The prior build could
    'display Error 0 because APS_LogError changed Err state.
    savedErrNumber = Err.Number
    savedErrDescription = Err.Description
    savedErrSource = Err.Source

    APS_LogError _
        "MAIN", _
        stepName, _
        savedErrNumber, _
        savedErrDescription, _
        savedErrSource

    APS_ShowDiagnosticError _
        "MAIN", _
        stepName, _
        savedErrNumber, _
        savedErrDescription

End Sub



'===========================================================
' FORM PREFLIGHT VALIDATION
'===========================================================

Private Function APS_ValidatePartSearchForm( _
    ByVal frm As Object, _
    ByRef validationMessage As String) As Boolean

    Dim ctl As Object

    APS_ValidatePartSearchForm = False
    validationMessage = ""

    On Error GoTo ValidationError

    Set ctl = frm.Controls("txtSearch")
    If TypeName(ctl) <> "TextBox" Then
        validationMessage = _
            "txtSearch exists but is not a TextBox."
        Exit Function
    End If

    Set ctl = frm.Controls("lstResults")
    If TypeName(ctl) <> "ListBox" Then
        validationMessage = _
            "lstResults exists but is not a ListBox."
        Exit Function
    End If

    Set ctl = frm.Controls("lblStatus")
    If TypeName(ctl) <> "Label" Then
        validationMessage = _
            "lblStatus exists but is not a Label."
        Exit Function
    End If

    Set ctl = frm.Controls("chkZoom")
    If TypeName(ctl) <> "CheckBox" Then
        validationMessage = _
            "chkZoom exists but is not a CheckBox."
        Exit Function
    End If

    Set ctl = frm.Controls("cmdRefresh")
    If TypeName(ctl) <> "CommandButton" Then
        validationMessage = _
            "cmdRefresh exists but is not a CommandButton."
        Exit Function
    End If

    Set ctl = frm.Controls("cmdClose")
    If TypeName(ctl) <> "CommandButton" Then
        validationMessage = _
            "cmdClose exists but is not a CommandButton."
        Exit Function
    End If

    APS_ValidatePartSearchForm = True

    Exit Function

ValidationError:

    validationMessage = _
        "UserForm control validation failed." & _
        " Missing or damaged control near: " & _
        Err.Description & _
        " (Err " & CStr(Err.Number) & ")."

End Function


'===========================================================
' EMERGENCY CLEANUP
'===========================================================

Public Sub APS_EmergencyCleanup()

    APS_Log "CLEANUP", "Emergency cleanup START"

    On Error Resume Next

    APS_DisableWheelSubclass

    If APS_FastSnapshotValid Then
        APS_RestoreFastVisibilitySnapshot "EMERGENCY_CLEANUP"
    End If

    APS_FastIsolateActive = False
    APS_FastSnapshotValid = False
    APS_LastIsolateSelectionSignature = ""

    APS_IsolationBusy = False
    APS_DynamicIsolateBusy = False
    APS_SelectionBusy = False
    APS_UpdatingResults = False

    APS_Log "CLEANUP", "Emergency cleanup END"

    On Error GoTo 0

End Sub


'===========================================================
' BUILD COMPONENT INDEX
'===========================================================

Public Function APS_BuildComponentIndex() As Boolean

    Dim vComponents As Variant
    Dim swComp As SldWorks.Component2

    Dim i As Long
    Dim idx As Long
    Dim allIdx As Long
    Dim maxIndex As Long

    Dim pathName As String
    Dim baseName As String
    Dim leafName As String
    Dim fullName As String

    Dim isPart As Boolean

    Dim started As Double
    Dim elapsed As Double

    Dim stepName As String

    On Error GoTo BuildError

    started = Timer

    APS_BuildComponentIndex = False
    APS_ComponentCount = 0
    APS_AllCount = 0

    Set APS_swApp = Application.SldWorks
    Set APS_swModel = APS_swApp.ActiveDoc

    If APS_swModel Is Nothing Then Exit Function
    If APS_swModel.GetType <> swDocASSEMBLY Then Exit Function

    Set APS_swAssy = APS_swModel

    stepName = "GetComponents(False)"
    APS_SetStep "BUILD_INDEX", stepName

    vComponents = APS_swAssy.GetComponents(False)

    If IsEmpty(vComponents) Then Exit Function
    If Not IsArray(vComponents) Then Exit Function

    maxIndex = UBound(vComponents) - LBound(vComponents)

    ReDim APS_Components(0 To maxIndex)

    ReDim APS_BaseName(0 To maxIndex)
    ReDim APS_LeafName(0 To maxIndex)
    ReDim APS_FullName(0 To maxIndex)

    ReDim APS_BaseNorm(0 To maxIndex)
    ReDim APS_LeafNorm(0 To maxIndex)
    ReDim APS_FullNorm(0 To maxIndex)

    ReDim APS_BaseCompact(0 To maxIndex)
    ReDim APS_LeafCompact(0 To maxIndex)
    ReDim APS_FullCompact(0 To maxIndex)

    ReDim APS_AllComponents(0 To maxIndex)
    ReDim APS_AllIsPart(0 To maxIndex)

    ReDim APS_ResultMap(0 To APS_MAX_RESULTS - 1)

    APS_ResetResultMap

    idx = 0
    allIdx = 0

    For i = LBound(vComponents) To UBound(vComponents)

        Set swComp = vComponents(i)

        If Not swComp Is Nothing Then

            isPart = APS_IsPartComponent(swComp)

            Set APS_AllComponents(allIdx) = swComp
            APS_AllIsPart(allIdx) = isPart
            allIdx = allIdx + 1

            If isPart Then

                pathName = APS_SafeGetPath(swComp)
                fullName = APS_SafeGetComponentName(swComp)

                baseName = APS_GetFileBaseName(pathName)
                leafName = APS_GetLeafComponentName(fullName)

                If Len(baseName) = 0 Then
                    baseName = APS_RemoveInstanceSuffix(leafName)
                End If

                Set APS_Components(idx) = swComp

                APS_BaseName(idx) = baseName
                APS_LeafName(idx) = leafName
                APS_FullName(idx) = fullName

                APS_BaseNorm(idx) = APS_NormalizeText(baseName)
                APS_LeafNorm(idx) = APS_NormalizeText(leafName)
                APS_FullNorm(idx) = APS_NormalizeText(fullName)

                APS_BaseCompact(idx) = APS_CompactText(baseName)
                APS_LeafCompact(idx) = APS_CompactText(leafName)
                APS_FullCompact(idx) = APS_CompactText(fullName)

                idx = idx + 1

            End If

        End If

    Next i

    APS_ComponentCount = idx
    APS_AllCount = allIdx

    If APS_ComponentCount > 0 Then
        ReDim APS_FastCurrentVisiblePart(0 To APS_ComponentCount - 1)
    End If

    APS_PreviewComponentIndex = -1

    APS_BuildComponentIndex = _
        (APS_ComponentCount > 0)

    elapsed = Timer - started

    If elapsed < 0 Then
        elapsed = elapsed + 86400
    End If

    APS_Log _
        "BUILD_INDEX", _
        "Parts=" & CStr(APS_ComponentCount) & _
        " | All components=" & CStr(APS_AllCount) & _
        " | Time=" & Format$(elapsed, "0.000") & " sec"

    Exit Function

BuildError:

    APS_LogError _
        "APS_BuildComponentIndex", _
        stepName, _
        Err.Number, _
        Err.Description, _
        Err.Source

    APS_BuildComponentIndex = False

End Function

Private Sub APS_ResetResultMap()

    Dim i As Long

    For i = 0 To APS_MAX_RESULTS - 1
        APS_ResultMap(i) = -1
    Next i

End Sub


Private Function APS_IsPartComponent( _
    ByVal swComp As SldWorks.Component2) As Boolean

    Dim pathName As String
    Dim swDoc As SldWorks.ModelDoc2

    APS_IsPartComponent = False

    On Error Resume Next

    pathName = LCase$(swComp.GetPathName)

    If Right$(pathName, 7) = ".sldprt" Then

        APS_IsPartComponent = True

        On Error GoTo 0

        Exit Function

    End If

    Set swDoc = swComp.GetModelDoc2

    If Not swDoc Is Nothing Then

        If swDoc.GetType = swDocPART Then
            APS_IsPartComponent = True
        End If

    End If

    On Error GoTo 0

End Function


Private Function APS_IsIndexedAssemblyActive() As Boolean

    Dim activeModel As SldWorks.ModelDoc2

    APS_IsIndexedAssemblyActive = False

    If APS_swApp Is Nothing Then Exit Function
    If APS_swModel Is Nothing Then Exit Function

    Set activeModel = APS_swApp.ActiveDoc

    If activeModel Is Nothing Then Exit Function
    If activeModel.GetType <> swDocASSEMBLY Then Exit Function

    On Error Resume Next

    APS_IsIndexedAssemblyActive = _
        (StrComp( _
            activeModel.GetTitle, _
            APS_swModel.GetTitle, _
            vbTextCompare) = 0)

    On Error GoTo 0

End Function


'===========================================================
' SEARCH
'===========================================================

Public Sub APS_UpdateSearchResults( _
    ByVal frm As Object, _
    ByVal rawQuery As String)

    Dim queryNorm As String

    Dim scores(0 To APS_MAX_RESULTS - 1) As Double
    Dim indices(0 To APS_MAX_RESULTS - 1) As Long

    Dim i As Long
    Dim j As Long

    Dim score As Double
    Dim resultCount As Long

    Dim started As Double
    Dim elapsed As Double

    Dim stepName As String

    If APS_UpdatingResults Then Exit Sub

    If Not APS_IsIndexedAssemblyActive() Then

        frm.lstResults.Clear
        frm.lstResults.Visible = False

        frm.lblStatus.Caption = _
            "Active assembly changed - click Refresh"

        Exit Sub

    End If

    On Error GoTo SearchError

    started = Timer

    APS_UpdatingResults = True

    queryNorm = APS_NormalizeText(rawQuery)

    APS_Log "SEARCH", "Query=""" & rawQuery & """"

    frm.lstResults.Clear

    If Len(queryNorm) = 0 Then

        frm.lstResults.Visible = False

        frm.lblStatus.Caption = _
            CStr(APS_ComponentCount) & " parts indexed"

        APS_PreviewComponentIndex = -1
        APS_UpdatingResults = False

        Exit Sub

    End If

    For i = 0 To APS_MAX_RESULTS - 1

        scores(i) = -1
        indices(i) = -1
        APS_ResultMap(i) = -1

    Next i

    stepName = "Score components"

    For i = 0 To APS_ComponentCount - 1

        score = APS_ScoreCandidate(rawQuery, i)

        If score >= APS_MIN_DISPLAY_SCORE Then

            APS_InsertRankedResult _
                score, _
                i, _
                scores, _
                indices

        End If

    Next i

    resultCount = 0

    For j = 0 To APS_MAX_RESULTS - 1

        If indices(j) >= 0 Then

            APS_ResultMap(resultCount) = indices(j)

            frm.lstResults.AddItem _
                APS_BaseName(indices(j)) & _
                "    -    " & _
                APS_FullName(indices(j))

            resultCount = resultCount + 1

        End If

    Next j

    If resultCount = 0 Then

        frm.lstResults.Visible = False
        frm.lblStatus.Caption = "No close matches"

        APS_PreviewComponentIndex = -1
        APS_UpdatingResults = False

        Exit Sub

    End If

    'Highest-ranked result is selected by default.
    frm.lstResults.Visible = True

    frm.lstResults.Selected(0) = True
    frm.lstResults.ListIndex = 0
    frm.lstResults.TopIndex = 0

    If resultCount = 1 Then

        frm.lblStatus.Caption = "1 match"

    Else

        frm.lblStatus.Caption = _
            CStr(resultCount) & " best matches"

    End If

    APS_UpdatingResults = False

    APS_Log _
        "SEARCH", _
        "Results=" & CStr(resultCount) & _
        " | Top=""" & _
        APS_BaseName(APS_ResultMap(0)) & """"

    'CRITICAL:
    'No command-enabled isolate-state probing here.
    If APS_FastIsolateActive Then

        APS_DynamicSwitchIsolate _
            frm, _
            frm.chkZoom.Value

    Else

        APS_PreviewBestResult _
            frm.chkZoom.Value

    End If

    elapsed = Timer - started

    If elapsed < 0 Then
        elapsed = elapsed + 86400
    End If

    APS_Log _
        "SEARCH", _
        "Completed in " & _
        Format$(elapsed, "0.000") & _
        " sec"

    Exit Sub

SearchError:

    APS_UpdatingResults = False

    APS_LogError _
        "APS_UpdateSearchResults", _
        stepName, _
        Err.Number, _
        Err.Description, _
        Err.Source

    frm.lblStatus.Caption = _
        "Search error - see diagnostics"

End Sub


'===========================================================
' SEARCH SCORING
'===========================================================

Private Function APS_ScoreCandidate( _
    ByVal query As String, _
    ByVal componentIndex As Long) As Double

    Dim q As String
    Dim qc As String

    Dim bestScore As Double
    Dim score As Double
    Dim similarity As Double
    Dim tokenScore As Double

    q = APS_NormalizeText(query)
    qc = APS_CompactText(query)

    If Len(q) = 0 Then Exit Function

    bestScore = 0

    'Exact.
    If q = APS_BaseNorm(componentIndex) Then
        APS_ScoreCandidate = 1000
        Exit Function
    End If

    If qc = APS_BaseCompact(componentIndex) Then
        APS_ScoreCandidate = 995
        Exit Function
    End If

    If q = APS_LeafNorm(componentIndex) Then
        APS_ScoreCandidate = 990
        Exit Function
    End If

    'Prefix.
    score = _
        APS_StartsWithScore( _
            q, _
            APS_BaseNorm(componentIndex), _
            950)

    If score > bestScore Then bestScore = score

    score = _
        APS_StartsWithScore( _
            q, _
            APS_LeafNorm(componentIndex), _
            935)

    If score > bestScore Then bestScore = score

    score = _
        APS_StartsWithScore( _
            q, _
            APS_FullNorm(componentIndex), _
            900)

    If score > bestScore Then bestScore = score

    'Contains.
    score = _
        APS_ContainsScore( _
            q, _
            APS_BaseNorm(componentIndex), _
            900)

    If score > bestScore Then bestScore = score

    score = _
        APS_ContainsScore( _
            q, _
            APS_LeafNorm(componentIndex), _
            875)

    If score > bestScore Then bestScore = score

    score = _
        APS_ContainsScore( _
            q, _
            APS_FullNorm(componentIndex), _
            830)

    If score > bestScore Then bestScore = score

    'Separator-independent.
    If Len(qc) > 0 Then

        If qc = APS_LeafCompact(componentIndex) Then
            If bestScore < 925 Then bestScore = 925
        End If

        If InStr( _
            1, _
            APS_BaseCompact(componentIndex), _
            qc, _
            vbTextCompare) > 0 Then

            If bestScore < 870 Then bestScore = 870

        End If

        If InStr( _
            1, _
            APS_LeafCompact(componentIndex), _
            qc, _
            vbTextCompare) > 0 Then

            If bestScore < 850 Then bestScore = 850

        End If

        If InStr( _
            1, _
            APS_FullCompact(componentIndex), _
            qc, _
            vbTextCompare) > 0 Then

            If bestScore < 810 Then bestScore = 810

        End If

    End If

    'Token coverage.
    tokenScore = _
        APS_TokenCoverageScore( _
            q, _
            APS_BaseNorm(componentIndex) & _
            " " & _
            APS_LeafNorm(componentIndex) & _
            " " & _
            APS_FullNorm(componentIndex))

    If tokenScore >= 1 Then
        score = 825
    Else
        score = tokenScore * 720
    End If

    If score > bestScore Then bestScore = score

    'Fuzzy only when conventional matching is not already
    'strong.
    If bestScore < APS_SKIP_FUZZY_SCORE Then

        If Len(qc) >= APS_MIN_FUZZY_LENGTH Then

            similarity = _
                APS_StringSimilarity( _
                    qc, _
                    APS_BaseCompact(componentIndex))

            score = similarity * 760

            If score > bestScore Then bestScore = score

            similarity = _
                APS_StringSimilarity( _
                    qc, _
                    APS_LeafCompact(componentIndex))

            score = similarity * 730

            If score > bestScore Then bestScore = score

            similarity = _
                APS_BestTokenSimilarity( _
                    q, _
                    APS_BaseNorm(componentIndex) & _
                    " " & _
                    APS_LeafNorm(componentIndex))

            score = similarity * 710

            If score > bestScore Then bestScore = score

        End If

    End If

    APS_ScoreCandidate = bestScore

End Function


Private Function APS_StartsWithScore( _
    ByVal query As String, _
    ByVal candidate As String, _
    ByVal baseScore As Double) As Double

    If Len(query) = 0 Then Exit Function
    If Len(candidate) < Len(query) Then Exit Function

    If Left$(candidate, Len(query)) = query Then

        APS_StartsWithScore = _
            baseScore - _
            ((Len(candidate) - Len(query)) * 0.05)

    End If

End Function


Private Function APS_ContainsScore( _
    ByVal query As String, _
    ByVal candidate As String, _
    ByVal baseScore As Double) As Double

    Dim position As Long

    If Len(query) = 0 Then Exit Function

    position = _
        InStr( _
            1, _
            candidate, _
            query, _
            vbTextCompare)

    If position > 0 Then

        APS_ContainsScore = _
            baseScore - _
            ((position - 1) * 0.5)

    End If

End Function


Private Function APS_TokenCoverageScore( _
    ByVal query As String, _
    ByVal candidate As String) As Double

    Dim tokens() As String
    Dim token As Variant

    Dim totalCount As Long
    Dim hitCount As Long

    tokens = Split(query, " ")

    For Each token In tokens

        If Len(CStr(token)) > 0 Then

            totalCount = totalCount + 1

            If InStr( _
                1, _
                candidate, _
                CStr(token), _
                vbTextCompare) > 0 Then

                hitCount = hitCount + 1

            End If

        End If

    Next token

    If totalCount > 0 Then

        APS_TokenCoverageScore = _
            CDbl(hitCount) / _
            CDbl(totalCount)

    End If

End Function


Private Function APS_BestTokenSimilarity( _
    ByVal query As String, _
    ByVal candidate As String) As Double

    Dim qTokens() As String
    Dim cTokens() As String

    Dim i As Long
    Dim j As Long

    Dim similarity As Double
    Dim bestForToken As Double

    Dim totalSimilarity As Double
    Dim tokenCount As Long

    qTokens = Split(query, " ")
    cTokens = Split(candidate, " ")

    For i = LBound(qTokens) To UBound(qTokens)

        If Len(qTokens(i)) > 0 Then

            bestForToken = 0

            For j = LBound(cTokens) To UBound(cTokens)

                If Len(cTokens(j)) > 0 Then

                    similarity = _
                        APS_StringSimilarity( _
                            qTokens(i), _
                            cTokens(j))

                    If similarity > bestForToken Then
                        bestForToken = similarity
                    End If

                End If

            Next j

            totalSimilarity = _
                totalSimilarity + _
                bestForToken

            tokenCount = tokenCount + 1

        End If

    Next i

    If tokenCount > 0 Then

        APS_BestTokenSimilarity = _
            totalSimilarity / _
            tokenCount

    End If

End Function


Private Function APS_StringSimilarity( _
    ByVal a As String, _
    ByVal b As String) As Double

    Dim distance As Long
    Dim maximumLength As Long

    If Len(a) = 0 And Len(b) = 0 Then

        APS_StringSimilarity = 1
        Exit Function

    End If

    If Len(a) = 0 Or Len(b) = 0 Then Exit Function

    If Len(a) > Len(b) Then
        maximumLength = Len(a)
    Else
        maximumLength = Len(b)
    End If

    distance = _
        APS_LevenshteinDistance( _
            a, _
            b)

    APS_StringSimilarity = _
        1 - _
        (CDbl(distance) / CDbl(maximumLength))

End Function


Private Function APS_LevenshteinDistance( _
    ByVal source As String, _
    ByVal target As String) As Long

    Dim sourceLength As Long
    Dim targetLength As Long

    Dim previousRow() As Long
    Dim currentRow() As Long

    Dim i As Long
    Dim j As Long

    Dim charCost As Long

    Dim deleteCost As Long
    Dim insertCost As Long
    Dim substituteCost As Long

    sourceLength = Len(source)
    targetLength = Len(target)

    If sourceLength = 0 Then

        APS_LevenshteinDistance = targetLength
        Exit Function

    End If

    If targetLength = 0 Then

        APS_LevenshteinDistance = sourceLength
        Exit Function

    End If

    ReDim previousRow(0 To targetLength)
    ReDim currentRow(0 To targetLength)

    For j = 0 To targetLength
        previousRow(j) = j
    Next j

    For i = 1 To sourceLength

        currentRow(0) = i

        For j = 1 To targetLength

            If Mid$(source, i, 1) = _
               Mid$(target, j, 1) Then

                charCost = 0

            Else

                charCost = 1

            End If

            deleteCost = previousRow(j) + 1
            insertCost = currentRow(j - 1) + 1

            substituteCost = _
                previousRow(j - 1) + _
                charCost

            currentRow(j) = _
                APS_MinimumOfThree( _
                    deleteCost, _
                    insertCost, _
                    substituteCost)

        Next j

        For j = 0 To targetLength
            previousRow(j) = currentRow(j)
        Next j

    Next i

    APS_LevenshteinDistance = _
        previousRow(targetLength)

End Function


Private Function APS_MinimumOfThree( _
    ByVal a As Long, _
    ByVal b As Long, _
    ByVal c As Long) As Long

    Dim result As Long

    result = a

    If b < result Then result = b
    If c < result Then result = c

    APS_MinimumOfThree = result

End Function


Private Sub APS_InsertRankedResult( _
    ByVal newScore As Double, _
    ByVal newIndex As Long, _
    ByRef scores() As Double, _
    ByRef indices() As Long)

    Dim position As Long
    Dim movePosition As Long

    For position = _
        LBound(scores) _
        To UBound(scores)

        If newScore > scores(position) Then

            For movePosition = _
                UBound(scores) _
                To position + 1 _
                Step -1

                scores(movePosition) = _
                    scores(movePosition - 1)

                indices(movePosition) = _
                    indices(movePosition - 1)

            Next movePosition

            scores(position) = newScore
            indices(position) = newIndex

            Exit Sub

        End If

    Next position

End Sub


'===========================================================
' NORMAL DYNAMIC PREVIEW
'===========================================================

Public Sub APS_PreviewBestResult( _
    Optional ByVal zoomToPart As Boolean = False)

    Dim componentIndex As Long

    Dim activeModel As SldWorks.ModelDoc2
    Dim swComp As SldWorks.Component2

    Dim success As Boolean
    Dim stepName As String

    If APS_SelectionBusy Then Exit Sub
    If Not APS_IsIndexedAssemblyActive() Then Exit Sub
    If APS_ComponentCount <= 0 Then Exit Sub

    componentIndex = APS_ResultMap(0)

    If componentIndex < 0 Or _
       componentIndex >= APS_ComponentCount Then Exit Sub

    If componentIndex = APS_PreviewComponentIndex Then

        If zoomToPart Then
            APS_ZoomToCurrentSelection
        End If

        Exit Sub

    End If

    APS_SelectionBusy = True

    On Error GoTo PreviewError

    Set activeModel = APS_swApp.ActiveDoc
    Set swComp = APS_Components(componentIndex)

    If swComp Is Nothing Then GoTo Finished

    activeModel.ClearSelection2 True

    stepName = "Component2.Select4"

    success = _
        swComp.Select4( _
            False, _
            Nothing, _
            False)

    APS_Log _
        "PREVIEW", _
        "Select=" & _
        APS_BaseName(componentIndex) & _
        " | Success=" & _
        CStr(success)

    If success Then

        APS_PreviewComponentIndex = componentIndex

        If zoomToPart Then

            stepName = "ViewZoomToSelection"
            activeModel.ViewZoomToSelection

        Else

            activeModel.GraphicsRedraw2

        End If

    End If

Finished:

    APS_SelectionBusy = False
    Exit Sub

PreviewError:

    APS_LogError _
        "APS_PreviewBestResult", _
        stepName, _
        Err.Number, _
        Err.Description, _
        Err.Source

    APS_SelectionBusy = False

End Sub


'===========================================================
' SELECT CURRENT LIST RESULTS IN SOLIDWORKS
'
' Returns number of components that Select4 actually
' reported as selected.
'===========================================================

Private Function APS_SelectListResultsInSolidWorks( _
    ByVal frm As Object, _
    Optional ByVal zoomAfterSelect As Boolean = False, _
    Optional ByVal updateStatus As Boolean = True) As Long

    Dim activeModel As SldWorks.ModelDoc2
    Dim swComp As SldWorks.Component2

    Dim i As Long
    Dim componentIndex As Long

    Dim selectedCount As Long
    Dim firstSelection As Boolean
    Dim success As Boolean
    Dim singleComponentIndex As Long

    Dim stepName As String

    APS_SelectListResultsInSolidWorks = 0

    If APS_UpdatingResults Then Exit Function
    If APS_SelectionBusy Then Exit Function
    If Not APS_IsIndexedAssemblyActive() Then Exit Function

    APS_SelectionBusy = True

    On Error GoTo SelectionError

    Set activeModel = APS_swApp.ActiveDoc

    activeModel.ClearSelection2 True

    firstSelection = True
    selectedCount = 0
    singleComponentIndex = -1

    For i = 0 To frm.lstResults.ListCount - 1

        If frm.lstResults.Selected(i) Then

            componentIndex = APS_ResultMap(i)

            If componentIndex >= 0 And _
               componentIndex < APS_ComponentCount Then

                Set swComp = _
                    APS_Components(componentIndex)

                If Not swComp Is Nothing Then

                    stepName = _
                        "Select4 " & _
                        APS_BaseName(componentIndex)

                    success = _
                        swComp.Select4( _
                            Not firstSelection, _
                            Nothing, _
                            False)

                    APS_Log _
                        "SELECTION", _
                        APS_BaseName(componentIndex) & _
                        " | Select4=" & _
                        CStr(success)

                    If success Then

                        selectedCount = selectedCount + 1
                        singleComponentIndex = componentIndex
                        firstSelection = False

                    End If

                End If

            End If

        End If

    Next i

    If selectedCount = 1 Then
        APS_PreviewComponentIndex = singleComponentIndex
    Else
        APS_PreviewComponentIndex = -1
    End If

    If selectedCount > 0 Then

        If zoomAfterSelect Then

            stepName = "ViewZoomToSelection"
            activeModel.ViewZoomToSelection

        Else

            activeModel.GraphicsRedraw2

        End If

    End If

    If updateStatus Then

        If selectedCount = 0 Then

            frm.lblStatus.Caption = _
                "No components selected"

        ElseIf selectedCount = 1 Then

            frm.lblStatus.Caption = _
                "1 component selected"

        Else

            frm.lblStatus.Caption = _
                CStr(selectedCount) & _
                " components selected"

        End If

    End If

    APS_SelectListResultsInSolidWorks = selectedCount

Finished:

    APS_SelectionBusy = False
    Exit Function

SelectionError:

    APS_LogError _
        "APS_SelectListResultsInSolidWorks", _
        stepName, _
        Err.Number, _
        Err.Description, _
        Err.Source

    APS_SelectionBusy = False

End Function


Public Sub APS_SyncListSelectionToSolidWorks( _
    ByVal frm As Object, _
    Optional ByVal zoomAfterSelect As Boolean = False)

    Dim selectedCount As Long

    selectedCount = _
        APS_SelectListResultsInSolidWorks( _
            frm, _
            zoomAfterSelect, _
            True)

End Sub


Public Function APS_CountSelectedResults( _
    ByVal frm As Object) As Long

    Dim i As Long
    Dim selectedCount As Long

    For i = 0 To frm.lstResults.ListCount - 1

        If frm.lstResults.Selected(i) Then
            selectedCount = selectedCount + 1
        End If

    Next i

    APS_CountSelectedResults = selectedCount

End Function


Public Sub APS_ZoomToCurrentSelection()

    Dim activeModel As SldWorks.ModelDoc2

    If APS_SelectionBusy Then Exit Sub
    If Not APS_IsIndexedAssemblyActive() Then Exit Sub

    On Error GoTo ZoomError

    Set activeModel = APS_swApp.ActiveDoc

    activeModel.ViewZoomToSelection

    Exit Sub

ZoomError:

    APS_LogError _
        "APS_ZoomToCurrentSelection", _
        "ViewZoomToSelection", _
        Err.Number, _
        Err.Description, _
        Err.Source

End Sub


'===========================================================
' SELECTION SIGNATURE
'===========================================================

Private Function APS_GetSelectionSignature( _
    ByVal frm As Object) As String

    Dim i As Long
    Dim componentIndex As Long

    Dim result As String

    For i = 0 To frm.lstResults.ListCount - 1

        If frm.lstResults.Selected(i) Then

            componentIndex = APS_ResultMap(i)

            If componentIndex >= 0 And _
               componentIndex < APS_ComponentCount Then

                result = _
                    result & _
                    "|" & _
                    CStr(componentIndex)

            End If

        End If

    Next i

    APS_GetSelectionSignature = result

End Function


'===========================================================
' CURRENT LIST SELECTION DISPATCHER
'===========================================================

Public Sub APS_ApplyCurrentListSelection( _
    ByVal frm As Object, _
    Optional ByVal zoomAfterSelect As Boolean = True)

    If APS_UpdatingResults Then Exit Sub
    If APS_DynamicIsolateBusy Then Exit Sub
    If APS_IsolationBusy Then Exit Sub

    If APS_FastIsolateActive Then

        APS_DynamicSwitchIsolate _
            frm, _
            zoomAfterSelect

    Else

        APS_SyncListSelectionToSolidWorks _
            frm, _
            zoomAfterSelect

    End If

End Sub


 '===========================================================
' FAST ISOLATE - LOW LEVEL VISIBILITY HELPERS
'===========================================================

Private Function APS_GetComponentVisibilitySafe( _
    ByVal swComp As SldWorks.Component2) As Long

    APS_GetComponentVisibilitySafe = _
        APS_VISIBILITY_UNKNOWN

    If swComp Is Nothing Then Exit Function

    On Error Resume Next
    Err.Clear

    APS_GetComponentVisibilitySafe = _
        swComp.Visible

    If Err.Number <> 0 Then

        APS_GetComponentVisibilitySafe = _
            APS_VISIBILITY_UNKNOWN

        Err.Clear

    End If

    On Error GoTo 0

End Function


Private Function APS_SetComponentVisibilitySafe( _
    ByVal swComp As SldWorks.Component2, _
    ByVal visibilityState As Long) As Boolean

    APS_SetComponentVisibilitySafe = False

    If swComp Is Nothing Then Exit Function

    On Error Resume Next
    Err.Clear

    swComp.Visible = visibilityState

    If Err.Number = 0 Then
        APS_SetComponentVisibilitySafe = True
    Else
        Err.Clear
    End If

    On Error GoTo 0

End Function


Private Sub APS_SetFeatureTreeUpdates( _
    ByVal enabled As Boolean)

    Dim swFeatureMgr As SldWorks.FeatureManager

    On Error Resume Next

    If APS_swModel Is Nothing Then Exit Sub

    Set swFeatureMgr = APS_swModel.FeatureManager

    If swFeatureMgr Is Nothing Then Exit Sub

    If enabled Then

        swFeatureMgr.EnableFeatureTree = True
        swFeatureMgr.EnableFeatureTreeWindow = True
        swFeatureMgr.UpdateFeatureTree

    Else

        swFeatureMgr.EnableFeatureTreeWindow = False
        swFeatureMgr.EnableFeatureTree = False

    End If

    On Error GoTo 0

End Sub



Private Sub APS_SetGraphicsUpdates( _
    ByVal enabled As Boolean)

    Dim swView As SldWorks.ModelView

    On Error Resume Next

    If APS_swModel Is Nothing Then Exit Sub

    Set swView = APS_swModel.ActiveView

    If swView Is Nothing Then Exit Sub

    swView.EnableGraphicsUpdate = enabled

    On Error GoTo 0

End Sub


'===========================================================
' BULK COMPONENT SELECTION
'
' Uses IModelDocExtension.MultiSelect2 so hundreds of
' components are selected in one API call instead of one
' Select4 call per component.
'===========================================================

Private Function APS_BulkSelectComponents( _
    ByRef components() As SldWorks.Component2) As Long

    Dim swExt As SldWorks.ModelDocExtension

    APS_BulkSelectComponents = 0

    On Error GoTo BulkSelectError

    If APS_swModel Is Nothing Then Exit Function

    Set swExt = APS_swModel.Extension

    If swExt Is Nothing Then Exit Function

    APS_swModel.ClearSelection2 True

    APS_BulkSelectComponents = _
        swExt.MultiSelect2( _
            components, _
            False, _
            Nothing)

    Exit Function

BulkSelectError:

    APS_LogError _
        "APS_BulkSelectComponents", _
        "MultiSelect2", _
        Err.Number, _
        Err.Description, _
        Err.Source

End Function


'===========================================================
' BULK HIDE NON-SELECTED PART INSTANCES
'
' This is the key R10 optimization:
'
'   R9:
'       437 x Component2.Visible = Hidden
'
'   R10:
'       1 x MultiSelect2(array of components)
'       1 x IModelDoc2.HideComponent2
'
'===========================================================

Private Function APS_BulkHideUnselectedParts( _
    ByRef desiredVisible() As Boolean, _
    ByRef targetCount As Long, _
    ByRef selectedCount As Long) As Boolean

    Dim targets() As SldWorks.Component2

    Dim i As Long
    Dim idx As Long

    targetCount = 0
    selectedCount = 0

    APS_BulkHideUnselectedParts = False

    If APS_ComponentCount <= 0 Then Exit Function

    'Count only currently visible non-selected part instances.
    For i = 0 To APS_ComponentCount - 1

        If Not desiredVisible(i) Then

            If APS_GetComponentVisibilitySafe( _
                APS_Components(i)) <> _
                APS_COMPONENT_HIDDEN Then

                targetCount = targetCount + 1

            End If

        End If

    Next i

    If targetCount = 0 Then

        APS_BulkHideUnselectedParts = True
        Exit Function

    End If

    ReDim targets(0 To targetCount - 1)

    idx = 0

    For i = 0 To APS_ComponentCount - 1

        If Not desiredVisible(i) Then

            If APS_GetComponentVisibilitySafe( _
                APS_Components(i)) <> _
                APS_COMPONENT_HIDDEN Then

                Set targets(idx) = APS_Components(i)

                idx = idx + 1

            End If

        End If

    Next i

    selectedCount = _
        APS_BulkSelectComponents( _
            targets)

    APS_Log _
        "FAST_ISOLATE", _
        "Bulk hide selection | Target=" & _
        CStr(targetCount) & _
        " | Selected=" & _
        CStr(selectedCount)

    If selectedCount <= 0 Then Exit Function

    APS_swModel.HideComponent2

    APS_swModel.ClearSelection2 True

    APS_BulkHideUnselectedParts = _
        (selectedCount = targetCount)

End Function


'===========================================================
' BULK RESTORE PART VISIBILITY
'
' At exit:
'   - one ShowComponent2 call for parts that were visible
'   - one HideComponent2 call for parts that were hidden
'
' Subassembly-parent visibility is restored separately; the
' number of parent components is typically very small.
'===========================================================

Private Function APS_BulkRestorePartVisibility( _
    ByRef shownCount As Long, _
    ByRef hiddenCount As Long) As Boolean

    Dim showTargets() As SldWorks.Component2
    Dim hideTargets() As SldWorks.Component2

    Dim i As Long
    Dim showTargetCount As Long
    Dim hideTargetCount As Long

    Dim showIdx As Long
    Dim hideIdx As Long

    Dim selectedCount As Long
    Dim failed As Boolean

    APS_BulkRestorePartVisibility = False

    If APS_AllCount <= 0 Then Exit Function

    For i = 0 To APS_AllCount - 1

        If APS_AllIsPart(i) Then

            Select Case APS_OriginalVisibility(i)

                Case APS_COMPONENT_VISIBLE
                    showTargetCount = showTargetCount + 1

                Case APS_COMPONENT_HIDDEN
                    hideTargetCount = hideTargetCount + 1

            End Select

        End If

    Next i

    If showTargetCount > 0 Then
        ReDim showTargets(0 To showTargetCount - 1)
    End If

    If hideTargetCount > 0 Then
        ReDim hideTargets(0 To hideTargetCount - 1)
    End If

    For i = 0 To APS_AllCount - 1

        If APS_AllIsPart(i) Then

            Select Case APS_OriginalVisibility(i)

                Case APS_COMPONENT_VISIBLE

                    Set showTargets(showIdx) = _
                        APS_AllComponents(i)

                    showIdx = showIdx + 1

                Case APS_COMPONENT_HIDDEN

                    Set hideTargets(hideIdx) = _
                        APS_AllComponents(i)

                    hideIdx = hideIdx + 1

            End Select

        End If

    Next i

    If showTargetCount > 0 Then

        selectedCount = _
            APS_BulkSelectComponents( _
                showTargets)

        APS_Log _
            "FAST_ISOLATE_EXIT", _
            "Bulk show | Target=" & _
            CStr(showTargetCount) & _
            " | Selected=" & _
            CStr(selectedCount)

        If selectedCount > 0 Then
            APS_swModel.ShowComponent2
            shownCount = selectedCount
        End If

        If selectedCount <> showTargetCount Then
            failed = True
        End If

    End If

    If hideTargetCount > 0 Then

        selectedCount = _
            APS_BulkSelectComponents( _
                hideTargets)

        APS_Log _
            "FAST_ISOLATE_EXIT", _
            "Bulk hide restore | Target=" & _
            CStr(hideTargetCount) & _
            " | Selected=" & _
            CStr(selectedCount)

        If selectedCount > 0 Then
            APS_swModel.HideComponent2
            hiddenCount = selectedCount
        End If

        If selectedCount <> hideTargetCount Then
            failed = True
        End If

    End If

    APS_swModel.ClearSelection2 True

    APS_BulkRestorePartVisibility = Not failed

End Function


Private Function APS_TakeFastVisibilitySnapshot() As Boolean

    Dim i As Long
    Dim state As Long
    Dim unknownCount As Long

    APS_TakeFastVisibilitySnapshot = False
    APS_FastSnapshotValid = False

    If APS_AllCount <= 0 Then Exit Function

    ReDim APS_OriginalVisibility(0 To APS_AllCount - 1)

    For i = 0 To APS_AllCount - 1

        state = _
            APS_GetComponentVisibilitySafe( _
                APS_AllComponents(i))

        APS_OriginalVisibility(i) = state

        If state = APS_VISIBILITY_UNKNOWN Then
            unknownCount = unknownCount + 1
        End If

    Next i

    APS_FastSnapshotValid = True
    APS_TakeFastVisibilitySnapshot = True

    APS_Log _
        "FAST_ISOLATE", _
        "Visibility snapshot captured | Components=" & _
        CStr(APS_AllCount) & _
        " | Unknown=" & CStr(unknownCount)

End Function


Private Function APS_RestoreFastVisibilitySnapshot( _
    ByVal operationName As String) As Boolean

    Dim i As Long
    Dim state As Long

    Dim shownCount As Long
    Dim hiddenCount As Long

    Dim parentRestoredCount As Long
    Dim parentFailedCount As Long

    Dim bulkSuccess As Boolean

    Dim started As Double
    Dim elapsed As Double

    APS_RestoreFastVisibilitySnapshot = False

    If Not APS_FastSnapshotValid Then

        APS_Log _
            operationName, _
            "No Fast Isolate visibility snapshot exists"

        APS_RestoreFastVisibilitySnapshot = True

        Exit Function

    End If

    If APS_AllCount <= 0 Then Exit Function

    started = Timer

    APS_SetFeatureTreeUpdates False
    APS_SetGraphicsUpdates False

    On Error GoTo RestoreError

    '-------------------------------------------------------
    ' BULK RESTORE ALL PART INSTANCES
    '-------------------------------------------------------

    bulkSuccess = _
        APS_BulkRestorePartVisibility( _
            shownCount, _
            hiddenCount)

    '-------------------------------------------------------
    ' RESTORE SUBASSEMBLY-PARENT VISIBILITY
    '
    'There are usually very few of these compared with the
    'hundreds/thousands of leaf parts, so individual writes
    'are inexpensive here.
    '-------------------------------------------------------

    For i = 0 To APS_AllCount - 1

        If Not APS_AllIsPart(i) Then

            state = APS_OriginalVisibility(i)

            If state = APS_COMPONENT_HIDDEN Or _
               state = APS_COMPONENT_VISIBLE Then

                If APS_SetComponentVisibilitySafe( _
                    APS_AllComponents(i), _
                    state) Then

                    parentRestoredCount = _
                        parentRestoredCount + 1

                Else

                    parentFailedCount = _
                        parentFailedCount + 1

                End If

            End If

        End If

    Next i

    APS_SetGraphicsUpdates True
    APS_SetFeatureTreeUpdates True

    APS_swModel.ClearSelection2 True
    APS_swModel.GraphicsRedraw2

    elapsed = Timer - started

    If elapsed < 0 Then
        elapsed = elapsed + 86400
    End If

    APS_Log _
        operationName, _
        "BULK visibility restored" & _
        " | PartsShown=" & CStr(shownCount) & _
        " | PartsHidden=" & CStr(hiddenCount) & _
        " | Parents=" & CStr(parentRestoredCount) & _
        " | ParentFailed=" & CStr(parentFailedCount) & _
        " | Time=" & _
        Format$(elapsed, "0.000") & " sec"

    APS_RestoreFastVisibilitySnapshot = _
        bulkSuccess And _
        (parentFailedCount = 0)

    Exit Function

RestoreError:

    APS_SetGraphicsUpdates True
    APS_SetFeatureTreeUpdates True

    On Error Resume Next
    APS_swModel.ClearSelection2 True
    APS_swModel.GraphicsRedraw2
    On Error GoTo 0

    APS_LogError _
        "APS_RestoreFastVisibilitySnapshot", _
        operationName, _
        Err.Number, _
        Err.Description, _
        Err.Source

End Function

Private Sub APS_ShowAncestorChain( _
    ByVal swComp As SldWorks.Component2)

    Dim swParent As SldWorks.Component2
    Dim depth As Long

    On Error Resume Next

    Set swParent = swComp.GetParent

    On Error GoTo 0

    For depth = 1 To APS_MAX_PARENT_DEPTH

        If swParent Is Nothing Then Exit For

        APS_SetComponentVisibilitySafe _
            swParent, _
            APS_COMPONENT_VISIBLE

        Set swComp = swParent

        On Error Resume Next

        Set swParent = swComp.GetParent

        On Error GoTo 0

    Next depth

End Sub


Private Sub APS_BuildSelectedPartMask( _
    ByVal frm As Object, _
    ByRef desiredVisible() As Boolean, _
    ByRef requestedCount As Long)

    Dim i As Long
    Dim componentIndex As Long

    requestedCount = 0

    If APS_ComponentCount <= 0 Then Exit Sub

    ReDim desiredVisible(0 To APS_ComponentCount - 1)

    For i = 0 To frm.lstResults.ListCount - 1

        If frm.lstResults.Selected(i) Then

            componentIndex = APS_ResultMap(i)

            If componentIndex >= 0 And _
               componentIndex < APS_ComponentCount Then

                If Not desiredVisible(componentIndex) Then

                    desiredVisible(componentIndex) = True
                    requestedCount = requestedCount + 1

                End If

            End If

        End If

    Next i

End Sub


Private Function APS_EnterFastVisibilityMode( _
    ByVal frm As Object, _
    ByRef requestedCount As Long) As Boolean

    Dim desiredVisible() As Boolean

    Dim i As Long

    Dim bulkTargetCount As Long
    Dim bulkSelectedCount As Long

    Dim shownCount As Long
    Dim failedCount As Long

    Dim bulkHideSuccess As Boolean

    Dim started As Double
    Dim elapsed As Double

    APS_EnterFastVisibilityMode = False

    APS_BuildSelectedPartMask _
        frm, _
        desiredVisible, _
        requestedCount

    If requestedCount <= 0 Then Exit Function

    If Not APS_TakeFastVisibilitySnapshot() Then
        Exit Function
    End If

    If APS_ComponentCount <= 0 Then Exit Function

    ReDim APS_FastCurrentVisiblePart( _
        0 To APS_ComponentCount - 1)

    started = Timer

    APS_SetFeatureTreeUpdates False
    APS_SetGraphicsUpdates False

    On Error GoTo EnterError

    '-------------------------------------------------------
    ' BULK HIDE EVERY NON-SELECTED VISIBLE PART
    '
    'This replaces hundreds/thousands of individual
    'Component2.Visible writes with one MultiSelect2 call
    'and one HideComponent2 call.
    '-------------------------------------------------------

    bulkHideSuccess = _
        APS_BulkHideUnselectedParts( _
            desiredVisible, _
            bulkTargetCount, _
            bulkSelectedCount)

    If Not bulkHideSuccess Then

        APS_Log _
            "FAST_ISOLATE", _
            "WARNING: bulk hide selected " & _
            CStr(bulkSelectedCount) & _
            " of " & _
            CStr(bulkTargetCount)

        failedCount = _
            Abs(bulkTargetCount - bulkSelectedCount)

    End If

    'Track current fast-isolate mask.
    For i = 0 To APS_ComponentCount - 1
        APS_FastCurrentVisiblePart(i) = desiredVisible(i)
    Next i

    'Selected parts are only a very small set, so individual
    'show calls are inexpensive and safely handle a part that
    'was originally hidden.
    For i = 0 To APS_ComponentCount - 1

        If desiredVisible(i) Then

            APS_ShowAncestorChain _
                APS_Components(i)

            If APS_SetComponentVisibilitySafe( _
                APS_Components(i), _
                APS_COMPONENT_VISIBLE) Then

                shownCount = shownCount + 1

            Else

                failedCount = failedCount + 1

            End If

        End If

    Next i

    APS_swModel.ClearSelection2 True

    APS_SetGraphicsUpdates True
    APS_SetFeatureTreeUpdates True

    APS_swModel.GraphicsRedraw2

    elapsed = Timer - started

    If elapsed < 0 Then
        elapsed = elapsed + 86400
    End If

    APS_Log _
        "FAST_ISOLATE", _
        "ENTER BULK visibility layer" & _
        " | BulkHideTarget=" & _
        CStr(bulkTargetCount) & _
        " | BulkSelected=" & _
        CStr(bulkSelectedCount) & _
        " | SelectedShown=" & _
        CStr(shownCount) & _
        " | Failed=" & _
        CStr(failedCount) & _
        " | Time=" & _
        Format$(elapsed, "0.000") & " sec"

    APS_EnterFastVisibilityMode = _
        (failedCount = 0)

    Exit Function

EnterError:

    APS_SetGraphicsUpdates True
    APS_SetFeatureTreeUpdates True

    On Error Resume Next
    APS_swModel.ClearSelection2 True
    APS_swModel.GraphicsRedraw2
    On Error GoTo 0

    APS_LogError _
        "APS_EnterFastVisibilityMode", _
        "Enter bulk visibility layer", _
        Err.Number, _
        Err.Description, _
        Err.Source

End Function

Private Function APS_SwitchFastVisibility( _
    ByVal frm As Object, _
    ByRef requestedCount As Long, _
    ByRef hideCalls As Long, _
    ByRef showCalls As Long) As Boolean

    Dim desiredVisible() As Boolean
    Dim i As Long
    Dim failedCount As Long

    APS_SwitchFastVisibility = False

    APS_BuildSelectedPartMask _
        frm, _
        desiredVisible, _
        requestedCount

    If requestedCount <= 0 Then Exit Function
    If Not APS_FastSnapshotValid Then Exit Function

    For i = 0 To APS_ComponentCount - 1

        If APS_FastCurrentVisiblePart(i) And _
           Not desiredVisible(i) Then

            If APS_SetComponentVisibilitySafe( _
                APS_Components(i), _
                APS_COMPONENT_HIDDEN) Then

                hideCalls = hideCalls + 1

            Else

                failedCount = failedCount + 1

            End If

            APS_FastCurrentVisiblePart(i) = False

        End If

    Next i

    For i = 0 To APS_ComponentCount - 1

        If desiredVisible(i) And _
           Not APS_FastCurrentVisiblePart(i) Then

            APS_ShowAncestorChain _
                APS_Components(i)

            If APS_SetComponentVisibilitySafe( _
                APS_Components(i), _
                APS_COMPONENT_VISIBLE) Then

                showCalls = showCalls + 1

            Else

                failedCount = failedCount + 1

            End If

            APS_FastCurrentVisiblePart(i) = True

        End If

    Next i

    APS_SwitchFastVisibility = _
        (failedCount = 0)

End Function


'===========================================================
' FAST ISOLATE - INITIAL ENTRY
'===========================================================

Public Sub APS_IsolateSelected( _
    ByVal frm As Object, _
    Optional ByVal zoomAfterIsolate As Boolean = True)

    Dim activeModel As SldWorks.ModelDoc2

    Dim requestedCount As Long
    Dim selectedInSW As Long

    Dim currentSignature As String
    Dim fastEnterSuccess As Boolean

    Dim stepName As String

    Dim started As Double
    Dim elapsed As Double

    If APS_IsolationBusy Then Exit Sub
    If APS_DynamicIsolateBusy Then Exit Sub

    If Not APS_IsIndexedAssemblyActive() Then

        frm.lblStatus.Caption = _
            "Active assembly changed - click Refresh"

        Exit Sub

    End If

    requestedCount = APS_CountSelectedResults(frm)

    If requestedCount <= 0 Then

        frm.lblStatus.Caption = _
            "Select one or more components first"

        Exit Sub

    End If

    currentSignature = APS_GetSelectionSignature(frm)

    If Len(currentSignature) = 0 Then Exit Sub

    If APS_FastIsolateActive Then

        APS_DynamicSwitchIsolate _
            frm, _
            zoomAfterIsolate

        Exit Sub

    End If

    APS_IsolationBusy = True
    APS_DisableWheelSubclass

    On Error GoTo IsolateError

    started = Timer

    Set activeModel = APS_swApp.ActiveDoc

    stepName = "Create Fast Isolate visibility layer"
    APS_SetStep "FAST_ISOLATE", stepName

    fastEnterSuccess = _
        APS_EnterFastVisibilityMode( _
            frm, _
            requestedCount)

    If Not fastEnterSuccess Then

        frm.lblStatus.Caption = _
            "Fast Isolate could not create visibility layer"

        If APS_FastSnapshotValid Then

            APS_RestoreFastVisibilitySnapshot _
                "FAST_ISOLATE_ROLLBACK"

        End If

        APS_FastSnapshotValid = False

        GoTo Finished

    End If

    APS_FastIsolateActive = True

    APS_LastIsolateSelectionSignature = _
        currentSignature

    stepName = "Select visible isolated components"

    selectedInSW = _
        APS_SelectListResultsInSolidWorks( _
            frm, _
            False, _
            False)

    If selectedInSW <> requestedCount Then

        APS_Log _
            "FAST_ISOLATE", _
            "WARNING selection mismatch | Requested=" & _
            CStr(requestedCount) & _
            " | Selected=" & CStr(selectedInSW)

    End If

    If zoomAfterIsolate And _
       selectedInSW > 0 Then

        stepName = "ViewZoomToSelection"
        activeModel.ViewZoomToSelection

    Else

        activeModel.GraphicsRedraw2

    End If

    If requestedCount = 1 Then

        frm.lblStatus.Caption = _
            "1 component fast-isolated"

    Else

        frm.lblStatus.Caption = _
            CStr(requestedCount) & _
            " components fast-isolated"

    End If

    elapsed = Timer - started

    If elapsed < 0 Then
        elapsed = elapsed + 86400
    End If

    APS_Log _
        "FAST_ISOLATE", _
        "MODE ON | Signature=" & _
        currentSignature & _
        " | Total time=" & _
        Format$(elapsed, "0.000") & " sec"

Finished:

    APS_IsolationBusy = False
    Exit Sub

IsolateError:

    APS_SetFeatureTreeUpdates True

    APS_LogError _
        "APS_IsolateSelected", _
        stepName, _
        Err.Number, _
        Err.Description, _
        Err.Source

    If APS_FastSnapshotValid Then

        APS_RestoreFastVisibilitySnapshot _
            "FAST_ISOLATE_ERROR_ROLLBACK"

    End If

    APS_FastIsolateActive = False
    APS_FastSnapshotValid = False
    APS_LastIsolateSelectionSignature = ""

    APS_IsolationBusy = False

    frm.lblStatus.Caption = _
        "Fast Isolate error at: " & _
        stepName

End Sub


'===========================================================
' FAST ISOLATE - DYNAMIC SWITCH
'===========================================================

Public Sub APS_DynamicSwitchIsolate( _
    ByVal frm As Object, _
    Optional ByVal zoomAfterSwitch As Boolean = True)

    Dim activeModel As SldWorks.ModelDoc2

    Dim requestedCount As Long
    Dim selectedInSW As Long

    Dim currentSignature As String

    Dim hideCalls As Long
    Dim showCalls As Long

    Dim visibilitySuccess As Boolean

    Dim started As Double
    Dim elapsed As Double

    Dim stepName As String

    If APS_DynamicIsolateBusy Then Exit Sub
    If APS_IsolationBusy Then Exit Sub
    If Not APS_FastIsolateActive Then Exit Sub
    If Not APS_IsIndexedAssemblyActive() Then Exit Sub

    requestedCount = APS_CountSelectedResults(frm)

    If requestedCount <= 0 Then Exit Sub

    currentSignature = APS_GetSelectionSignature(frm)

    If Len(currentSignature) = 0 Then Exit Sub

    If currentSignature = _
       APS_LastIsolateSelectionSignature Then

        APS_Log _
            "FAST_SWITCH", _
            "Signature unchanged - select/zoom only"

        APS_SyncListSelectionToSolidWorks _
            frm, _
            zoomAfterSwitch

        Exit Sub

    End If

    APS_DynamicIsolateBusy = True
    APS_DisableWheelSubclass

    On Error GoTo DynamicError

    started = Timer

    Set activeModel = APS_swApp.ActiveDoc

    APS_Log _
        "FAST_SWITCH", _
        "START | Old=" & _
        APS_LastIsolateSelectionSignature & _
        " | New=" & currentSignature

    stepName = "Apply visibility delta"

    visibilitySuccess = _
        APS_SwitchFastVisibility( _
            frm, _
            requestedCount, _
            hideCalls, _
            showCalls)

    If Not visibilitySuccess Then

        APS_Log _
            "FAST_SWITCH", _
            "WARNING: one or more visibility changes failed"

    End If

    APS_LastIsolateSelectionSignature = _
        currentSignature

    stepName = "Select new visible set"

    selectedInSW = _
        APS_SelectListResultsInSolidWorks( _
            frm, _
            False, _
            False)

    If zoomAfterSwitch And _
       selectedInSW > 0 Then

        stepName = "ViewZoomToSelection"
        activeModel.ViewZoomToSelection

    Else

        activeModel.GraphicsRedraw2

    End If

    If requestedCount = 1 Then

        frm.lblStatus.Caption = _
            "1 component fast-isolated"

    Else

        frm.lblStatus.Caption = _
            CStr(requestedCount) & _
            " components fast-isolated"

    End If

    elapsed = Timer - started

    If elapsed < 0 Then
        elapsed = elapsed + 86400
    End If

    APS_Log _
        "FAST_SWITCH", _
        "COMPLETE | HideCalls=" & _
        CStr(hideCalls) & _
        " | ShowCalls=" & CStr(showCalls) & _
        " | Selected=" & CStr(selectedInSW) & _
        " | Time=" & _
        Format$(elapsed, "0.0000") & " sec"

Finished:

    APS_DynamicIsolateBusy = False
    Exit Sub

DynamicError:

    APS_LogError _
        "APS_DynamicSwitchIsolate", _
        stepName, _
        Err.Number, _
        Err.Description, _
        Err.Source

    APS_PrintDiagnostics

    APS_DynamicIsolateBusy = False

    frm.lblStatus.Caption = _
        "Fast switch error at: " & _
        stepName

End Sub


'===========================================================
' FAST ISOLATE - EXIT
'===========================================================

Public Sub APS_ExitIsolate( _
    ByVal frm As Object)

    Dim success As Boolean

    If APS_IsolationBusy Then Exit Sub
    If APS_DynamicIsolateBusy Then Exit Sub

    APS_IsolationBusy = True
    APS_DisableWheelSubclass

    APS_Log _
        "FAST_ISOLATE", _
        "Exit requested"

    success = _
        APS_RestoreFastVisibilitySnapshot( _
            "FAST_ISOLATE_EXIT")

    If success Then

        APS_FastIsolateActive = False
        APS_FastSnapshotValid = False
        APS_LastIsolateSelectionSignature = ""

        If APS_ComponentCount > 0 Then

            ReDim APS_FastCurrentVisiblePart( _
                0 To APS_ComponentCount - 1)

        End If

        frm.lblStatus.Caption = _
            "Exited Fast Isolate - original visibility restored"

        APS_Log _
            "FAST_ISOLATE", _
            "MODE OFF"

    Else

        frm.lblStatus.Caption = _
            "Fast Isolate restore had errors - see diagnostics"

        APS_PrintDiagnostics

    End If

    APS_IsolationBusy = False

End Sub


Public Function APS_IsMacroIsolationActive() As Boolean

    APS_IsMacroIsolationActive = _
        APS_FastIsolateActive

End Function


'===========================================================
' RIGHT CLICK SELECTION
'===========================================================

Public Sub APS_CaptureRightClickSelection( _
    ByVal frm As Object)

    Dim i As Long

    APS_RightClickListCount = _
        frm.lstResults.ListCount

    APS_RightClickSelectedCount = 0

    If APS_RightClickListCount <= 0 Then Exit Sub

    ReDim APS_RightClickSelection( _
        0 To APS_RightClickListCount - 1)

    For i = 0 To APS_RightClickListCount - 1

        APS_RightClickSelection(i) = _
            frm.lstResults.Selected(i)

        If APS_RightClickSelection(i) Then
            APS_RightClickSelectedCount = _
                APS_RightClickSelectedCount + 1
        End If

    Next i

End Sub


Public Sub APS_RestoreRightClickSelection( _
    ByVal frm As Object)

    Dim i As Long

    If APS_RightClickSelectedCount <= 1 Then Exit Sub

    If frm.lstResults.ListCount <> _
       APS_RightClickListCount Then Exit Sub

    APS_UpdatingResults = True

    For i = 0 To APS_RightClickListCount - 1

        frm.lstResults.Selected(i) = _
            APS_RightClickSelection(i)

    Next i

    APS_UpdatingResults = False

    APS_SyncListSelectionToSolidWorks _
        frm, _
        False

End Sub


'===========================================================
' RIGHT CLICK MENU
'===========================================================

Public Sub APS_ShowContextMenu( _
    ByVal frm As Object)

    Dim hMenu As LongPtr
    Dim ownerHwnd As LongPtr

    Dim pt As APS_POINTAPI

    Dim selectedCount As Long
    Dim commandID As Long

    Dim isolateCaption As String

    On Error GoTo MenuError

    APS_DisableWheelSubclass

    selectedCount = _
        APS_CountSelectedResults(frm)

    If selectedCount = 0 Then

        If frm.lstResults.ListCount <= 0 Then Exit Sub

        APS_UpdatingResults = True

        frm.lstResults.Selected(0) = True
        frm.lstResults.ListIndex = 0

        APS_UpdatingResults = False

        selectedCount = 1

        APS_SyncListSelectionToSolidWorks _
            frm, _
            False

    End If

    hMenu = APS_CreatePopupMenu

    If hMenu = 0 Then Exit Sub

    If selectedCount = 1 Then

        isolateCaption = _
            "Fast Isolate Selected"

    Else

        isolateCaption = _
            "Fast Isolate Selected (" & _
            CStr(selectedCount) & ")"

    End If

    APS_AppendMenu _
        hMenu, _
        APS_MF_STRING, _
        APS_MENU_ISOLATE, _
        isolateCaption

    APS_AppendMenu _
        hMenu, _
        APS_MF_SEPARATOR, _
        0, _
        vbNullString

    APS_AppendMenu _
        hMenu, _
        APS_MF_STRING, _
        APS_MENU_EXIT_ISOLATE, _
        "Exit Fast Isolate"

    APS_AppendMenu _
        hMenu, _
        APS_MF_SEPARATOR, _
        0, _
        vbNullString

    APS_AppendMenu _
        hMenu, _
        APS_MF_STRING, _
        APS_MENU_DIAGNOSTICS, _
        "Print Diagnostics"

    If APS_GetCursorPos(pt) = 0 Then GoTo CleanUp

    ownerHwnd = APS_GetForegroundWindow

    If ownerHwnd = 0 Then GoTo CleanUp

    APS_SetForegroundWindow ownerHwnd

    commandID = _
        APS_TrackPopupMenu( _
            hMenu, _
            APS_TPM_LEFTALIGN Or _
            APS_TPM_RIGHTBUTTON Or _
            APS_TPM_NONOTIFY Or _
            APS_TPM_RETURNCMD, _
            pt.X, _
            pt.Y, _
            0, _
            ownerHwnd, _
            0)

    APS_PostMessage _
        ownerHwnd, _
        APS_WM_NULL, _
        0, _
        0

    Select Case commandID

        Case APS_MENU_ISOLATE

            APS_IsolateSelected _
                frm, _
                frm.chkZoom.Value

        Case APS_MENU_EXIT_ISOLATE

            APS_ExitIsolate frm

        Case APS_MENU_DIAGNOSTICS

            APS_PrintDiagnostics

    End Select

CleanUp:

    If hMenu <> 0 Then
        APS_DestroyMenu hMenu
    End If

    Exit Sub

MenuError:

    APS_LogError _
        "APS_ShowContextMenu", _
        "Context Menu", _
        Err.Number, _
        Err.Description, _
        Err.Source

    Resume CleanUp

End Sub


'===========================================================
' MOUSE WHEEL
'
' NO WINDOWS HOOKS.
'
' While pointer is over lstResults:
'   - subclass the UserForm WndProc
'   - subclass the focused child WndProc, if the focus child
'     belongs to the UserForm
'
' Only WM_MOUSEWHEEL is intercepted.
'===========================================================

Public Sub APS_EnableWheelSubclass( _
    ByVal formCaption As String, _
    ByVal target As Object)

    Dim currentFormHwnd As LongPtr
    Dim currentFocusHwnd As LongPtr

    On Error GoTo SubclassError

    If APS_WheelShuttingDown Then Exit Sub

    currentFormHwnd = _
        APS_FindWindow( _
            vbNullString, _
            formCaption)

    If currentFormHwnd = 0 Then

        APS_LastSubclassError = _
            "Could not locate UserForm HWND"

        APS_Log _
            "WHEEL", _
            APS_LastSubclassError

        Exit Sub

    End If

    If APS_FormSubclassInstalled Then

        If APS_FormHwnd <> currentFormHwnd Then

            APS_Log _
                "WHEEL", _
                "Form HWND changed; rebuilding wheel routing"

            APS_DisableWheelSubclass

        End If

    End If

    APS_MouseOverList = True

    Set APS_WheelTarget = target

    If Not APS_FormSubclassInstalled Then

        APS_FormHwnd = currentFormHwnd

        APS_FormOriginalWndProc = _
            APS_InstallSubclass( _
                APS_FormHwnd)

        If APS_FormOriginalWndProc <> 0 Then

            APS_FormSubclassInstalled = True

            APS_Log _
                "WHEEL", _
                "Form subclass installed | hwnd=" & _
                CStr(APS_FormHwnd) & _
                " | class=" & _
                APS_GetWindowClassName(APS_FormHwnd)

        Else

            APS_LastSubclassError = _
                "Could not subclass UserForm window"

            APS_Log _
                "WHEEL", _
                APS_LastSubclassError

            Exit Sub

        End If

    End If

    currentFocusHwnd = _
        APS_GetValidFormFocusHwnd( _
            APS_FormHwnd)

    APS_EnsureFocusWheelSubclass _
        currentFocusHwnd

    Exit Sub

SubclassError:

    APS_LastSubclassError = _
        "APS_EnableWheelSubclass | Err=" & _
        CStr(Err.Number) & _
        " | " & _
        Err.Description

    APS_Log _
        "WHEEL", _
        APS_LastSubclassError

    APS_MouseOverList = False

End Sub


Public Sub APS_WheelTargetLeave()

    APS_MouseOverList = False

End Sub


Public Sub APS_RefreshWheelFocus( _
    ByVal formCaption As String, _
    ByVal target As Object)

    If APS_WheelShuttingDown Then Exit Sub

    If Not APS_FormSubclassInstalled Then

        APS_EnableWheelSubclass _
            formCaption, _
            target

        Exit Sub

    End If

    APS_MouseOverList = True

    Set APS_WheelTarget = target

    APS_EnsureFocusWheelSubclass _
        APS_GetValidFormFocusHwnd( _
            APS_FormHwnd)

End Sub


Private Function APS_GetValidFormFocusHwnd( _
    ByVal formHwnd As LongPtr) As LongPtr

    Dim currentFocusHwnd As LongPtr

    APS_GetValidFormFocusHwnd = 0

    If formHwnd = 0 Then Exit Function

    currentFocusHwnd = APS_GetFocus

    If currentFocusHwnd = 0 Then Exit Function

    If currentFocusHwnd = formHwnd Then

        APS_GetValidFormFocusHwnd = _
            currentFocusHwnd

        Exit Function

    End If

    If APS_IsChild( _
        formHwnd, _
        currentFocusHwnd) <> 0 Then

        APS_GetValidFormFocusHwnd = _
            currentFocusHwnd

    End If

End Function


Private Sub APS_EnsureFocusWheelSubclass( _
    ByVal newFocusHwnd As LongPtr)

    If APS_WheelShuttingDown Then Exit Sub

    If newFocusHwnd = 0 Or _
       newFocusHwnd = APS_FormHwnd Then

        APS_RemoveFocusWheelSubclass

        Exit Sub

    End If

    If APS_FocusSubclassInstalled Then

        If APS_FocusHwnd = newFocusHwnd Then
            Exit Sub
        End If

        APS_RemoveFocusWheelSubclass

    End If

    APS_FocusHwnd = newFocusHwnd

    APS_FocusOriginalWndProc = _
        APS_InstallSubclass( _
            APS_FocusHwnd)

    If APS_FocusOriginalWndProc <> 0 Then

        APS_FocusSubclassInstalled = True

        APS_Log _
            "WHEEL", _
            "Focus subclass installed | hwnd=" & _
            CStr(APS_FocusHwnd) & _
            " | class=" & _
            APS_GetWindowClassName(APS_FocusHwnd)

    Else

        APS_FocusHwnd = 0

        APS_LastSubclassError = _
            "Could not subclass focused child"

        APS_Log _
            "WHEEL", _
            APS_LastSubclassError

    End If

End Sub


Private Sub APS_RemoveFocusWheelSubclass()

    Dim oldFocusHwnd As LongPtr

    On Error Resume Next

    oldFocusHwnd = APS_FocusHwnd

    If APS_FocusSubclassInstalled Then

        If APS_FocusHwnd <> 0 And _
           APS_FocusOriginalWndProc <> 0 Then

            If APS_IsWindow(APS_FocusHwnd) <> 0 Then

                APS_SetWindowLongPtr _
                    APS_FocusHwnd, _
                    APS_GWLP_WNDPROC, _
                    APS_FocusOriginalWndProc

            End If

        End If

    End If

    APS_FocusSubclassInstalled = False
    APS_FocusHwnd = 0
    APS_FocusOriginalWndProc = 0

    If oldFocusHwnd <> 0 Then

        APS_Log _
            "WHEEL", _
            "Focus subclass removed"

    End If

    On Error GoTo 0

End Sub


Private Function APS_InstallSubclass( _
    ByVal hWnd As LongPtr) As LongPtr

    Dim previousProc As LongPtr
    Dim lastError As Long

    APS_InstallSubclass = 0

    If hWnd = 0 Then Exit Function

    If APS_IsWindow(hWnd) = 0 Then

        APS_LastSubclassError = _
            "APS_InstallSubclass invalid HWND=" & _
            CStr(hWnd)

        Exit Function

    End If

    APS_SetLastError 0

    previousProc = _
        APS_SetWindowLongPtr( _
            hWnd, _
            APS_GWLP_WNDPROC, _
            AddressOf APS_WheelWndProc)

    If previousProc = 0 Then

        lastError = APS_GetLastError

        APS_LastSubclassError = _
            "SetWindowLongPtr(" & _
            CStr(hWnd) & _
            ") returned 0; LastError=" & _
            CStr(lastError)

        Exit Function

    End If

    APS_InstallSubclass = previousProc

End Function


Public Sub APS_DisableWheelSubclass()

    Dim oldFormHwnd As LongPtr

    On Error Resume Next

    APS_WheelShuttingDown = True

    APS_MouseOverList = False
    APS_WheelBusy = False
    APS_WheelRemainder = 0

    APS_RemoveFocusWheelSubclass

    oldFormHwnd = APS_FormHwnd

    If APS_FormSubclassInstalled Then

        If APS_FormHwnd <> 0 And _
           APS_FormOriginalWndProc <> 0 Then

            If APS_IsWindow(APS_FormHwnd) <> 0 Then

                APS_SetWindowLongPtr _
                    APS_FormHwnd, _
                    APS_GWLP_WNDPROC, _
                    APS_FormOriginalWndProc

            End If

        End If

    End If

    APS_FormSubclassInstalled = False
    APS_FormHwnd = 0
    APS_FormOriginalWndProc = 0

    Set APS_WheelTarget = Nothing

    If oldFormHwnd <> 0 Then

        APS_Log _
            "WHEEL", _
            "Form subclass removed"

    End If

    APS_WheelShuttingDown = False

    On Error GoTo 0

End Sub


Public Function APS_WheelWndProc( _
    ByVal hWnd As LongPtr, _
    ByVal uMsg As Long, _
    ByVal wParam As LongPtr, _
    ByVal lParam As LongPtr) As LongPtr

    Dim wheelDelta As Long
    Dim oldProc As LongPtr

    On Error GoTo CallbackError

    oldProc = _
        APS_GetOriginalProcForHwnd( _
            hWnd)

    If APS_WheelShuttingDown Then
        GoTo ForwardMessage
    End If

    If uMsg = APS_WM_MOUSEWHEEL Then

        If APS_MouseOverList And _
           Not APS_WheelBusy Then

            If Not APS_WheelTarget Is Nothing Then

                wheelDelta = APS_GetWheelDelta(wParam)

                If wheelDelta <> 0 Then

                    APS_WheelBusy = True
                    APS_WheelEventCount = APS_WheelEventCount + 1
                    APS_LastWheelDelta = wheelDelta

                    APS_ScrollWheelTarget wheelDelta

                    APS_WheelBusy = False
                    APS_WheelWndProc = 0

                    Exit Function

                End If

            End If

        End If

    End If

ForwardMessage:

    APS_WheelBusy = False

    If oldProc <> 0 Then

        APS_WheelWndProc = _
            APS_CallWindowProc( _
                oldProc, _
                hWnd, _
                uMsg, _
                wParam, _
                lParam)

    Else

        APS_WheelWndProc = 0

    End If

    Exit Function

CallbackError:

    APS_LastSubclassError = _
        "WndProc Err=" & _
        CStr(Err.Number) & _
        " | " & _
        Err.Description

    APS_WheelBusy = False

    Resume ForwardMessage

End Function


Private Function APS_GetOriginalProcForHwnd( _
    ByVal hWnd As LongPtr) As LongPtr

    If APS_FocusSubclassInstalled Then

        If hWnd = APS_FocusHwnd Then

            APS_GetOriginalProcForHwnd = _
                APS_FocusOriginalWndProc

            Exit Function

        End If

    End If

    If APS_FormSubclassInstalled Then

        If hWnd = APS_FormHwnd Then

            APS_GetOriginalProcForHwnd = _
                APS_FormOriginalWndProc

            Exit Function

        End If

    End If

End Function


Private Function APS_GetWheelDelta( _
    ByVal wParam As LongPtr) As Long

#If Win64 Then

    Dim rawValue As LongLong
    Dim highWord As LongLong

    rawValue = wParam
    highWord = (rawValue \ 65536) Mod 65536

    If highWord < 0 Then
        highWord = highWord + 65536
    End If

    If highWord >= 32768 Then
        highWord = highWord - 65536
    End If

    APS_GetWheelDelta = CLng(highWord)

#Else

    Dim highWord32 As Long

    highWord32 = (wParam \ 65536) And &HFFFF&

    If highWord32 >= 32768 Then
        highWord32 = highWord32 - 65536
    End If

    APS_GetWheelDelta = highWord32

#End If

End Function


Private Sub APS_ScrollWheelTarget( _
    ByVal wheelDelta As Long)

    Dim wheelNotches As Long
    Dim oldTop As Long
    Dim newTop As Long
    Dim listCount As Long

    On Error GoTo Finished

    If APS_WheelTarget Is Nothing Then Exit Sub

    listCount = APS_WheelTarget.ListCount

    If listCount <= 1 Then Exit Sub

    APS_WheelRemainder = APS_WheelRemainder + wheelDelta

    If Abs(APS_WheelRemainder) < APS_WHEEL_DELTA Then Exit Sub

    wheelNotches = _
        Fix( _
            CDbl(APS_WheelRemainder) / _
            CDbl(APS_WHEEL_DELTA))

    APS_WheelRemainder = _
        APS_WheelRemainder - _
        (wheelNotches * APS_WHEEL_DELTA)

    oldTop = APS_WheelTarget.TopIndex

    If oldTop < 0 Then oldTop = 0

    newTop = _
        oldTop - _
        (wheelNotches * APS_SCROLL_LINES)

    If newTop < 0 Then newTop = 0

    If newTop > listCount - 1 Then
        newTop = listCount - 1
    End If

    If newTop <> oldTop Then
        APS_WheelTarget.TopIndex = newTop
    End If

Finished:

End Sub


Private Function APS_GetWindowClassName( _
    ByVal hWnd As LongPtr) As String

    Dim buffer As String
    Dim length As Long

    If hWnd = 0 Then Exit Function
    If APS_IsWindow(hWnd) = 0 Then Exit Function

    buffer = String$(256, vbNullChar)

    length = _
        APS_GetClassName( _
            hWnd, _
            buffer, _
            255)

    If length > 0 Then
        APS_GetWindowClassName = Left$(buffer, length)
    End If

End Function


'===========================================================
' TEXT HELPERS
'===========================================================

Private Function APS_NormalizeText( _
    ByVal text As String) As String

    Dim separators As Variant
    Dim separator As Variant

    text = LCase$(Trim$(text))

    text = _
        Replace$( _
            text, _
            ".sldprt", _
            "")

    text = _
        Replace$( _
            text, _
            ".sldasm", _
            "")

    separators = _
        Array( _
            "-", _
            "_", _
            ".", _
            "/", _
            "\", _
            "^", _
            "@", _
            "(", _
            ")", _
            "[", _
            "]", _
            "{", _
            "}", _
            ",", _
            ";", _
            ":", _
            vbTab)

    For Each separator In separators

        text = _
            Replace$( _
                text, _
                CStr(separator), _
                " ")

    Next separator

    APS_NormalizeText = _
        APS_CollapseSpaces(text)

End Function


Private Function APS_CollapseSpaces( _
    ByVal text As String) As String

    Dim i As Long
    Dim ch As String

    Dim result As String
    Dim previousWasSpace As Boolean

    For i = 1 To Len(text)

        ch = Mid$(text, i, 1)

        If ch = " " Then

            If Not previousWasSpace Then

                result = result & " "
                previousWasSpace = True

            End If

        Else

            result = result & ch
            previousWasSpace = False

        End If

    Next i

    APS_CollapseSpaces = Trim$(result)

End Function


Private Function APS_CompactText( _
    ByVal text As String) As String

    APS_CompactText = _
        Replace$( _
            APS_NormalizeText(text), _
            " ", _
            "")

End Function


Private Function APS_SafeGetPath( _
    ByVal swComp As SldWorks.Component2) As String

    On Error Resume Next

    APS_SafeGetPath = _
        swComp.GetPathName

    On Error GoTo 0

End Function


Private Function APS_SafeGetComponentName( _
    ByVal swComp As SldWorks.Component2) As String

    On Error Resume Next

    APS_SafeGetComponentName = _
        swComp.Name2

    On Error GoTo 0

End Function


Private Function APS_GetFileBaseName( _
    ByVal fullPath As String) As String

    Dim fileName As String

    Dim slash1 As Long
    Dim slash2 As Long
    Dim slashPosition As Long

    Dim dotPosition As Long

    If Len(fullPath) = 0 Then Exit Function

    slash1 = InStrRev(fullPath, "\")
    slash2 = InStrRev(fullPath, "/")

    If slash1 > slash2 Then
        slashPosition = slash1
    Else
        slashPosition = slash2
    End If

    If slashPosition > 0 Then

        fileName = _
            Mid$( _
                fullPath, _
                slashPosition + 1)

    Else

        fileName = fullPath

    End If

    dotPosition = InStrRev(fileName, ".")

    If dotPosition > 1 Then

        fileName = _
            Left$( _
                fileName, _
                dotPosition - 1)

    End If

    APS_GetFileBaseName = fileName

End Function


Private Function APS_GetLeafComponentName( _
    ByVal fullName As String) As String

    Dim position As Long

    position = InStrRev(fullName, "/")

    If position > 0 Then

        APS_GetLeafComponentName = _
            Mid$( _
                fullName, _
                position + 1)

    Else

        APS_GetLeafComponentName = fullName

    End If

End Function


Private Function APS_RemoveInstanceSuffix( _
    ByVal componentName As String) As String

    Dim dashPosition As Long
    Dim suffix As String

    dashPosition = InStrRev(componentName, "-")

    If dashPosition <= 0 Then

        APS_RemoveInstanceSuffix = componentName
        Exit Function

    End If

    suffix = _
        Mid$( _
            componentName, _
            dashPosition + 1)

    If IsNumeric(suffix) Then

        APS_RemoveInstanceSuffix = _
            Left$( _
                componentName, _
                dashPosition - 1)

    Else

        APS_RemoveInstanceSuffix = componentName

    End If

End Function
Procedure index · 71 declarations
File checksum

SHA-256: 5cf7d0b9cd6cccc2b528db5e7dfbb30ee5242166df5f4db2e1e359f7f651b790