VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "clsLanguage"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Option Compare Text

' A entry in a database
Private Type Entry
    Name As String
    Type As String
    Value As Variant
End Type

' The different databases currently in use
Private Type Database
    Name As String
    Entries() As Entry
    EntryCount As Long
    Process As Boolean
End Type

' Information regarding the database
Public DatabaseName As String
Public DatabaseVersion As String

' The array containing all the databases
Private aDatabase() As Database
Private aCount As Long

Public Sub SaveXML(sFile As String)

    ' Reference msxml4.dll for this
    Dim Document As New DOMDocument, Root As IXMLDOMElement, Database As IXMLDOMElement
    Dim Tell As Long, Entry As Long, Temp As Long, oEntry As IXMLDOMElement, Item As IXMLDOMElement
    
    ' Firstly, create the root-container
    Set Root = Document.createElement("language")

    ' Add it to the document and set its properties
    Document.appendChild Root
    Root.setAttribute "version", DatabaseVersion
    Root.setAttribute "name", DatabaseName

    ' Add all databases to the document
    For Tell = 0 To aCount - 1
    
        ' Create a new tag for the database
        Set Database = Document.createElement("database")
        
        ' Add it to the root node
        Root.appendChild Document.createTextNode(vbCrLf & Space(3))
        Root.appendChild Database
        
        ' Open the database
        With aDatabase(Tell)
        
            ' Set its properties
            Database.setAttribute "name", .Name
            Database.setAttribute "process", IIf(.Process, "true", "false")
            
            ' Add all entries
            For Entry = 0 To .EntryCount - 1
            
                ' Create a new entry
                Set oEntry = Document.createElement("entry")
                
                ' Add it to the database
                Database.appendChild Document.createTextNode(vbCrLf & Space(6))
                Database.appendChild oEntry
                
                ' Set its properties
                oEntry.setAttribute "name", .Entries(Entry).Name
                oEntry.setAttribute "type", .Entries(Entry).Type
                oEntry.setAttribute "array", "false"
                
                ' See if we have an array or not
                If IsArray(.Entries(Entry).Value) Then
                    
                    ' Create the bondary-element
                    Set Item = Document.createElement("bound")
                    
                    ' Add the beginning space
                    oEntry.appendChild Document.createTextNode(vbCrLf & Space(9))
                    
                    ' Add it to the entry and set its properties
                    oEntry.appendChild Item
                    Item.setAttribute "min", LBound(.Entries(Entry).Value)
                    Item.setAttribute "max", UBound(.Entries(Entry).Value)
                    
                    ' Add the array as several item-elements
                    For Temp = LBound(.Entries(Entry).Value) To UBound(.Entries(Entry).Value)
                    
                        ' Create a new entry,
                        Set Item = Document.createElement("item")
                        
                        '  ... and add it to the right parent
                        oEntry.appendChild Document.createTextNode(vbCrLf & Space(9))
                        oEntry.appendChild Item

                        ' Set its content
                        Item.setAttribute "id", Temp
                        Item.Text = .Entries(Entry).Value(Temp)
                    
                        ' In this case we have an array
                        oEntry.setAttribute "array", "true"
                    
                    Next
                    
                    ' Add the finall ending
                    oEntry.appendChild Document.createTextNode(vbCrLf & Space(6))
                    
                Else
                    oEntry.Text = CStr(.Entries(Entry).Value)
                End If
            
            Next
        
        End With

        ' Add a ending tag
        Database.appendChild Document.createTextNode(vbCrLf & Space(3))

    Next
    
    ' Add ending lines
    Root.appendChild Document.createTextNode(vbCrLf)
    
    ' Save the document
    Document.save sFile

End Sub

Public Sub LoadXML(sFile As String)

    Dim Document As New DOMDocument, Root As IXMLDOMElement, Bound As IXMLDOMElement
    Dim Database As IXMLDOMElement, Entry As IXMLDOMElement, Item As IXMLDOMElement
    Dim Index As Long, Temp As Long

    ' See if the given file is valid
    If Dir(sFile) = "" Then
        Exit Sub
    End If

    ' Firstly, clear all databases
    ClearDatabases
    
    ' Load the file
    Document.Load sFile
    
    ' Retrieve the root-element
    Set Root = Document.getElementsByTagName("language")(0)
    
    ' Load its properties
    DatabaseName = Root.getAttribute("name")
    DatabaseVersion = Root.getAttribute("version")
    
    ' Then go through all its children
    For Each Database In Root.childNodes
    
        ' See if this is a database
        If Database.nodeName = "database" Then
    
            ' Add the database
            Index = AppendDatabase(Database.getAttribute("name"), Database.getAttribute("process"))
    
            ' Add all the entries
            For Each Entry In Database.childNodes
            
                ' See if this is a entry
                If Entry.nodeName = "entry" Then
                
                    ' Add the entry
                    Temp = AppendEntry(Index, Entry.getAttribute("name"), , Entry.getAttribute("type"))
                    
                    ' Open the database
                    With aDatabase(Index)
                    
                        ' Add the value depending on whether or not it is an array
                        Select Case Entry.getAttribute("array")
                        Case "false"
                            
                            ' Just set the value
                            .Entries(Temp).Value = Entry.Text
                        
                        Case "true"
                    
                            ' Retrieve the boundary-item
                            Set Bound = Entry.getElementsByTagName("bound")(0)
                    
                            ' Make an array
                            ReDim .Entries(Temp).Value(Bound.getAttribute("min") To Bound.getAttribute("max"))
                    
                            ' Set its element
                            For Each Item In Entry.childNodes
                            
                                ' See if this is a item-element
                                If Item.nodeName = "item" Then
                                    
                                    ' Set the array
                                    .Entries(Temp).Value(Item.getAttribute("id")) = Item.Text
                                
                                End If
                            
                            Next
                    
                        End Select
                    
                    ' Close the database
                    End With
                
                End If
            
            Next
    
        End If
    
    Next

End Sub

' Manually append a database
Public Function AppendDatabase(sName As String, bProcess As Boolean) As Long

    ' Firstly, reallocate the array
    ReDim Preserve aDatabase(aCount)
    
    ' Set the different properties
    aDatabase(aCount).Name = sName
    aDatabase(aCount).Process = bProcess
    
    ' Return the index of the created database
    AppendDatabase = aCount
    
    ' Increase the count
    aCount = aCount + 1

End Function

' Manually append an entry
Public Function AppendEntry(ByVal Index As Long, sName As String, vValue As Variant, Optional sType As String = "Property") As Long

    ' Use the current database
    With aDatabase(Index)

        ' Firstly, reallocate the array
        ReDim Preserve .Entries(.EntryCount)
        
        ' Set the different properties
        .Entries(.EntryCount).Name = sName
        .Entries(.EntryCount).Value = vValue
        .Entries(.EntryCount).Type = sType
        
        ' Return the index of the created database
        AppendEntry = .EntryCount
        
        ' Increase the count
        .EntryCount = .EntryCount + 1
    
    End With

End Function

Public Sub ClearDatabases()

    ' Clear the array
    Erase aDatabase
    
    ' Clear the count
    aCount = 0

End Sub

Public Sub ClearEntries(ByVal Index As Long)
    
    ' Only continue if the index is valid
    If IsDatabase(Index) Then
    
        ' Clear both the array and the count
        Erase aDatabase(Index).Entries
        aDatabase(Index).EntryCount = 0

    End If

End Sub

Public Function IsDatabase(ByVal Index As Long) As Boolean

    ' Return whether or not this is a database
    IsDatabase = CBool(Index >= 0 And Index < aCount)

End Function

Public Function IsEntry(ByVal DataIndex As Long, ByVal EntryIndex As Long) As Boolean

    ' Return whether or not this is a database
    If IsDatabase(DataIndex) Then
        With aDatabase(DataIndex)
            IsEntry = CBool(.EntryCount >= 0 And Index < .EntryCount)
        End With
    End If

End Function

Public Function FindDatabase(Expression As String, Process As Boolean) As Long

    Dim Tell As Long
    
    ' Go through all the databases, ...
    For Tell = 0 To aCount - 1
    
        ' ... checking if any one of them corresponds to the string
        If (Expression = aDatabase(Tell).Name) And (aDatabase(Tell).Process = Process) Then
        
            ' Return the index
            FindDatabase = Tell
            
            ' We're done
            Exit Function
        
        End If
    
    Next
 
    ' No database found
    FindDatabase = -1
 
End Function

Public Function FindEntry(ByVal Index As Long, sName As String, vValue As Variant, sType As String)

    Dim Tell As Long
    
    ' See if the index is valid
    If IsDatabase(Index) Then
    
        ' Open the database
        With aDatabase(Index)
        
            ' Go through all entries in the database
            For Tell = 0 To .EntryCount - 1
                
                ' See if it corresponds to some of the search values
                If sName = .Entries(Tell).Name Or vValue = .Entries(Tell).Value _
                 Or sType = .Entries(Tell).Type Then
                
                    ' Return the index of the entry
                    FindEntry = Tell
                    
                    ' We're done
                    Exit Function
                
                End If
        
            Next
        
        End With
    
    End If

    ' Nothing was found
    FindEntry = -1

End Function

' Add the content of the form
Public Sub AppendForm(Form As Form, Optional ControlsExpression As String)

    Dim aControls, oControl As Object, sControl As String
    Dim lngFind As Long, Index As Long, vData As Variant

    ' Search for this form
    lngFind = FindDatabase(Form.Name, True)

    ' Then see if the form actually exists
    If lngFind >= 0 Then

        ' Clear the entries
        ClearEntries lngFind
        
        ' Use this database
        Index = lngFind
        
    Else
    
        ' No, create a new database
        Index = AppendDatabase(Form.Name, True)
    
    End If

    ' Save the caption of the form
    AppendEntry Index, "Caption", Form.Caption
    
    ' Secondly, get all the controls that we'll have to process
    aControls = Split(ControlsExpression, ";")
    
    ' Then go through all the controls
    For Each oControl In Form.Controls
    
        ' Get the name of the control
        sControl = TypeName(oControl)
        
        ' See if the control is valid
        If ControlsExpression = "*" Or InArray(aControls, sControl) >= 0 Then
        
            ' Clear variables
            vData = ""
        
            ' Read from the different controls
            Select Case sControl
            Case "ListView": vData = ToArray(oControl.ColumnHeaders, , , 1)
            Case "TabStrip": vData = ToArray(oControl.Tabs)
            Case "vbalListViewCtl1": vData = ToArray(oControl.Columns)
            Case "SSTab": vData = ToArray(oControl, "Tabs", "TabCaption")
            Case "ListBox", "ComboBox": vData = ToArray(oControl, "ListCount", "List")
            Case "CommandButton": vData = oControl.Caption
            Case Else: vData = oControl ' Just use the default property
            End Select
            
            ' Add the entry to the database
            AppendEntry Index, oControl.Name, vData, sControl
        
        End If
    
    Next

End Sub

' Convert all the data within a class to an ordinary array
Private Function ToArray(refObject As Object, Optional sCount As String = "Count", Optional sRead As String = "Item", Optional Start As Long) As Variant

    Dim Tell As Long, tempArray()
    
    ' Allocate array
    ReDim tempArray(Start To CallByName(refObject, sCount, VbGet) - 1)
    
    ' Go through all elements, ...
    For Tell = Start To UBound(tempArray)

        ' Set each element
        tempArray(Tell) = CallByName(refObject, sRead, VbGet, Tell).Text

    Next

    ' Return the array
    ToArray = tempArray
    
End Function

Private Sub WriteArray(refObject As Object, Data As Variant, sReset As String, sInitialize As String, sAdd As String, bSpecial As Boolean)

    Dim Tell As Long
    
    ' Make sure we're dealing with an array
    If Not IsArray(Data) Then
        Exit Sub
    End If
    
    ' Reset the control in question
    If sReset <> "" Then
        CallByName refObject, sReset, VbMethod
    End If
    
    ' Intialize/allocate (if neeeded) the control
    If sInitialize <> "" Then
        CallByName refObject, sInitialize, VbLet, (UBound(Data) - LBound(Data)) + 1
    End If

    ' Add all the elements
    For Tell = LBound(Data) To UBound(Data)
    
        ' Use a method/property depending on the initialize-function and bSpecial
        If Not bSpecial Then
            If sInitialize <> "" Then
                CallByName refObject, sAdd, VbLet, Tell, CStr(Data(Tell))
            Else
                CallByName refObject, sAdd, VbMethod, CStr(Data(Tell))
            End If
        Else
            ' In this case, the text paramenter is the third
            CallByName refObject, sAdd, VbMethod, , , CStr(Data(Tell))
        End If
    
    Next

End Sub
' Call when you need to update a form
Public Sub ProcessForm(Form As Form)

    On Error Resume Next
    Dim Index As Long, Tell As Long, vData, oControl As Object
    
    ' Find the form to use
    Index = FindDatabase(Form.Name, True)
    
    ' See if the index is valid
    If IsDatabase(Index) Then
    
        ' Open this database
        With aDatabase(Index)
    
            ' Go through all the entries in the database
            For Tell = 0 To .EntryCount - 1
            
                ' Attempt at getting the control
                Set oControl = Nothing
                Set oControl = CallByName(Form, .Entries(Tell).Name, VbGet)
            
                ' Continue if the control exists
                If Not (oControl Is Nothing) Then
                
                    ' Retrieve the data
                    vData = .Entries(Tell).Value
                    
                    ' Write the data differently depending on the type
                    Select Case .Entries(Tell).Type
                    Case "ListView": WriteArray oControl.ColumnHeaders, vData, "Clear", "", "Add", True
                    Case "TabStrip": WriteArray oControl.Tabs, vData, "Clear", "", "Add", True
                    Case "SSTab": WriteArray oControl, vData, "", "Tabs", "TabCaption", False
                    Case "vbalListViewCtl1": WriteArray oControl.Columns, vData, "Clear", "", "Add", True
                    Case "ListBox", "ComboBox": WriteArray oControl, vData, "Clear", "", "AddItem", False
                    Case "CommandButton": oControl.Caption = vData
                    Case Else: oControl = vData ' Just use the default property
                    End Select
                
                End If
                
            Next
    
        End With

    End If

End Sub

' Used when you need to update all forms in the project
Public Sub UpdateAll(aForms As Collection)
    
    Dim Form As Form
    
    ' Go through all the forms in the collection
    For Each Form In aForms
        
        ' Update it
        ProcessForm Form
        
    Next

End Sub

Private Function InArray(refArray, vEntry As Variant) As Long

    Dim Tell As Long
    
    ' Go through an array, ...
    For Tell = LBound(refArray) To UBound(refArray)
    
        ' Checking whether or not it contain a given value
        If refArray(Tell) = vEntry Then
        
            ' Return the index
            InArray = Tell
            
            ' Nothing more to find
            Exit Function
        
        End If
        
    Next

    ' Didn't find anything
    InArray = LBound(refArray) - 1

End Function

Private Sub Class_Initialize()

    ' Use a default version
    DatabaseVersion = "1.0"

End Sub
