[
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Ensure that source files use CRLF for newlines, in case they are downloaded\n# in a compressed archive directly from GitHub. (Otherwise class modules may\n# not be imported correctly. See issue #150 for more details.)\n###############################################################################\n# Most source files use this extension\n*.bas text eol=crlf\n# Class modules\n*.cls text eol=crlf\n# Some object definitions\n*.xml text eol=crlf\n# SQL output\n*.sql text eol=crlf\n# Forms 2.0 form definitions (rarely used)\n*.frm text eol=crlf\n# Common source file\n*.json text eol=crlf\n\n###############################################################################\n# Clarify that the source language is VBA (Auto-detection not always accurate)\n# https://github.com/github/linguist/blob/master/docs/overrides.md\n###############################################################################\n*.bas linguist-language=VBA\n*.cls linguist-language=VBA\n*.twin linguist-language=VBA\n\n# Git files\n*.gitattributes text\n*.gitattributes linguist-language=gitattributes\n\n# Ignore files (like .npmignore or .gitignore)\n*.*ignore       text\n*.*ignore       export-ignore\n"
  },
  {
    "path": ".gitattributes.default",
    "content": "# gitattributes template for Microsoft Access database source files\n# Source: https://github.com/joyfullservice/msaccess-vcs-integration\n#\n\n###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Ensure that source files use CRLF for newlines, in case they are downloaded\n# in a compressed archive directly from GitHub. (Otherwise class modules may\n# not be imported correctly. See issue #150 for more details.)\n###############################################################################\n# Most source files use this extension\n*.bas text eol=crlf\n# Class modules\n*.cls text eol=crlf\n# Some object definitions\n*.xml text eol=crlf\n# SQL output\n*.sql text eol=crlf\n# Forms 2.0 form definitions (rarely used)\n*.frm text eol=crlf\n# Common source file\n*.json text eol=crlf\n\n###############################################################################\n# Clarify that the source language is VBA (Auto-detection not always accurate)\n# https://github.com/github/linguist/blob/master/docs/overrides.md\n###############################################################################\n*.bas linguist-language=VBA\n*.cls linguist-language=VBA\n*.twin linguist-language=VBA\n\n# Git files\n*.gitattributes text\n*.gitattributes linguist-language=gitattributes\n\n# Ignore files (like .npmignore or .gitignore)\n*.*ignore       text\n*.*ignore       export-ignore\n"
  },
  {
    "path": ".github/workflows/update-wiki.yml",
    "content": "name: Update Wiki\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      # Limit to changes to the wiki folder\n      - 'Wiki/**'\n  workflow_dispatch:\n\njobs:\n  update-wiki:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v3\n\n      - name: Set up git user\n        run: |\n          git config --global user.name \"github-actions\"\n          git config --global user.email \"github-actions@joyfullservice.com\"\n\n      - name: Clone the wiki repo\n        run: |\n          git clone \"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.wiki.git\" wiki-tmp\n          mkdir -p wiki\n          cp -r wiki-tmp/.git wiki/\n    \n      - name: Sync wiki folder\n        run: |\n          rsync -av --delete --exclude='.git' Wiki/ wiki/\n\n      - name: Commit and push changes\n        run: |\n          cd wiki\n          if git status --porcelain | grep .; then\n            git add .\n            git commit -m \"Update wiki from main/Wiki\"\n            git push\n          else\n            echo \"No changes to commit\"\n          fi\n"
  },
  {
    "path": ".gitignore",
    "content": "# Version Control Add-in Binaries\n# (This should be built from source and not committed to version control)\n*.mdb\n*.accda\n*.accdb\n*.zip\n\n# Database lock files\n*.laccdb\n\n# Comment out the following line if you wish to include the log files in git.\n*.log\n\n# The local VCS index file is paired with the database and should not\n# be comitted to version control.\nvcs-index.json\n\n# Ignore any dotenv files (used for external database connections)\n*.env\n\n# TwinBasic ribbon project packages\nRibbon/Source/Packages\n"
  },
  {
    "path": ".gitignore.default",
    "content": "# gitattributes template for Microsoft Access database source files\n# Website: https://github.com/joyfullservice/msaccess-vcs-addin\n#\n\n# Ignore Microsoft Access database binary files (Build these from source)\n*.accda\n*.accdb\n*.mdb\n\n# Ignore database lock files\n*.laccdb\n*.ldb\n\n# The local VCS index file is paired with the binary database file \n# and should not be comitted to version control.\nvcs-index.json\n\n# Ignore any dotenv files (used for external database connections)\n*.env\n\n# Ignore log files generated by the VCS Add-in\n# Comment out the following line if you wish to include log files in git.\n*.log\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "Change log\n==========\nOngoing Development\n-------------------\njoyfullservice:\n\nAs this project continues to move forward, many changes and updates are being made but not specifically documented in this change log. For details on updates since 2015, please review the commit messages in this repository.\n\n(If you use this project and would find more detailed change logs to be helpful, please let me know, and I will take that into consideration.)\n\nVersion 1.1.0 - 15 May 2015\n----------------------------\njoyfullservice:\n* Performed some significant refactoring to use the system as a reference library rather than a collection of modules that need to be added to each project.\n* Added support for exporting VBE objects directly, allowing drag-and-drop within projects in the IDE.\n* Standardized module names (special prefix no longer needed)\n* Source folder name changed to be specific to the current database (avoids unexpected results when working with two databases in the same folder.)\n* Source folders only created when they will contain content\n* General code cleanup and organization\n* Improved debug output in debug mode\n* Converted module constants to input parameters for debug mode and tables to save data\n* Updated sample database\n\nVersion 1.0.0 - 11 Mar 2015\n----------------------------\njwbrookes:\n* Added support for Table Data Macros\n* Added support for Linked Tables (supports relative paths for linked files)\n* Added support for Print Variables in Reports (Page size and orientation) \n* Added support for Relation for all types of table\n* LoadVCS warnings removed when no object delete is required\n* Removed elements from Report export that change constantly but don't affect import\n* Fixed query import bug (complex queries being rearranged on import)\n* Fixed missing constraints in table export\n\nprmills:\n* Added support for bit fields in table Import/Export\n* Added support for References without GUIDs\n* Export all table data with `INCLUDE_TABLES = \"*\"`\n\nVersion 0.12.0 - 28 Jan 2015\n----------------------------\njwbrookes:\n* Refactored AppCodeImportExport into several modules\n* Added VCS_Loader, a module to import multiple vba modules into a database \n\n\nVersion 0.11.1 - 14 Jan 2015\n---------------------------\njwbrookes:\n* Fixed bug in ExportTableDef function\n* Removed redundant DeleteFile function (had been left commented out in the module)\n\n\nVersion 0.11 - 01 May 2014\n-------------------------\nmatonb:\n* Removed DeleteFile function and replaced calls to Kill with FileSystemObject.DeleteFile\n\n\nVersion 0.10 - 09 Mar 2014\n-------------------------\nmatonb:\n* Added DoEvents in loops to avoid \"Unresponsive\" state.\n\n\nVersion 0.9 - 15 Feb 2014\n-------------------------\nmatonb:\n*  Aggressive Sanitise, moved BaseInfo from \"Block\" regex to Line level.\n*  Changed line level skipping to include lines with deeper indendation the follow.\n  This catches split lines mostly found in BaseInfo exports.\n\n\nVersion 0.8 - 14 Feb 2014\n-------------------------\nmatonb:\n*  Aggressive Sanitise now excludes \"BaseInfo\" lines.\n  These lines were seen to be randomly switching between being empty,\n  not present or containing SQL on an arbitary basis.\n\n\nVersion 0.7 - 06 Jul 2013\n-------------------------\nmatonb:\n*  Replaced TempFile function.\n*  Temporary file names now generated via external MS libraries.\n*  Functions using TempFile updated to only call TempFile function once.\n   *  Temporary file path and name stored in tmepFileName variable.\n   *  Temporary files deleted when done.\n*  Changed db declaration in ImportProject to DAO.database.\n\n\nVersion 0.6 - 06 Jul 2013\n-------------------------\n\nmatonb:\n\n*  AppcodeImportExport excluded from ExportAllSource\n*  Added ImportProject sub-routine,  \n   Deletes all forms, macros, modules and queries before calling ImportAllSource.  \n   By clearing out the existing objects, you know that your database only contains  \n   code from your version control database.  \n   Excludes *AppCodeImportExport*\n\nVersion 0.5 - 29 May 2013\n--------------------------\n\nmatonb:\n\n*  All \"exclusion\" patterns are now matched by regex.\n*  Added StripPublishOption constant.  \n   If set to _True_ the following lines are also excluded from the export files\n  * dbByte \"PublishToWeb\" =\"1\"\n  * PublishOption =1\n*  Added DeleteFile(FileName) function  \n   The function tries to delete _FileName_ three (3) times before giving up.  \n   A delay of 100ms is introduced between delete attempts should the first fail.\n\nVersion 0.4 - 19 Apr 2013\n--------------------------\n\nmatonb:\n\n*  Added dbLongBinary \"DOL\" to aggressive sanitize, these statements were\n   appearing in queries and being flagged by git as modified in files that\n   hadn't been touched by developers.\n\nVersion 0.3.2 - 8 Apr 2013\n--------------------------\n\nmatonb:\n* 0.3.1 Patched - Serious Problem:  SanitizeTextFiles If logic removed all\n        lines containing \"Begin\".\n* 0.3.2 Replaced if block for skipping code sections in SanitizeTextFiles with\n        regular expression.\n\nVersion 0.3 - 6 Apr 2013\n------------------------\n\nbkidwell:\n* Sanitize query exports.\n* Fixed SERIOUS TYPO in UCS2-to-UTF-8 conversion (wrong threshold for 2 byte versus 3 byte symbol in output stream).\n* AggressiveSanitize default True.\n\nmatonb:\n* Added AggressiveSanitize constant, it's a number to allow for different levels in the future. ~~Default False.~~\n* Added Skipping for GUID & Namemap in aggressive sanitize mode.\n* ~~If AggressiveSanitize is on, also sanitize query exports.~~\n* Append Number of objects imported/exported to information lines in immediate window.\n* Updated readme (removed references to terminal window).\n* Close all open forms and reports when importing and exporting because you can't import an open form or report.\n\nVersion 0.2 - 4 Apr 2013\n------------------------\n\nmatonb:\n* Added dbLongBinary \"DOL\" to SkipList in SanitizeTextFiles.\n* Added Source directory check to ImportAllSource, pops up a message box if missing.\n* Only create source directories if there is something to export.\n\nbkidwell:\n* Removed external executable for converting UCS-2-little-endian to and from UTF-8; replaced with VB6 methods.\n* Added demo database to the repository.\n* Removed the need for a special \"export_[name]\" query to export and import a lookup table.\n* Added check to determine if Queries, Forms, etc. are exported from THIS database (depending on which version of Access created it) uses UCS-2-little-endian, or a legacy 8-bit Windows character set. Skip converting to/from UTF-8 if not using UCS-2, because the point of the conversion was to avoid writing 0x00 bytes in the text files and confuse diff/merge tools.\n\nVersion 0.1 - 22 Oct 2012\n-------------------------\n\nInitial release\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Found a Problem? Have an Idea?\n=============\nThe easiest way to contribute is to create a detailed [Issue](https://github.com/joyfullservice/msaccess-vcs-integration/issues). Be sure to include details about the version of OS, Access, and VCS add-in. If you can, provide a [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) of the problem.\n\nAlso be sure to check out the [Project Wiki](https://github.com/joyfullservice/msaccess-vcs-integration/wiki) which contains detailed documentation and other helpful tips for using this add-in.\n\n\nDevelopment Version\n---------\nIf you want the very latest updates since the last published release, you will need to build it from source. Here is how to go from GitHub to git, to Access:\n\n* Make sure you have a fairly recent version of the add-in installed. If not:\n  * Download the latest release.\n  * Install the add-in. (Just open *Version Control.accda*.)\n* Clone this repository.\n* Pull your clone down to your local machine.\n* Choose a branch in git. (Typically `dev`)\n* Use the add-in to *Build From Source*, selecting the cloned `Version Control.accda.src` folder.\n* Run the newly compiled *Version Control.accda* file to install the development version.\n\nMaking your first Pull Request (PR)\n---------\nA *Pull Request* is how you can propose that your code changes be included in the main project. (This project is the work of many people who have donated their efforts to make it better for everyone.) If you followed the steps to get to the Development Version then you can follow these steps to go back from Access to git, back up to GitHub:\n* *Optional: For larger changes, you should consider making a branch that describes the changes you are proposing.*\n* Open the development copy of *Version Control.accda* from the cloned GitHub project.\n* Perform testing on your development version.\n* Make updates to the database project. (This is where the magic happens)\n* When you are ready to make a commit run the **Deploy** procedure by typing `Deploy` into the VBA immediate window and press **Enter**. This will:\n   * Increment the version number.\n   * Export the project to source.\n   * Install the version you have open. \n* Close Access.\n* Open an Access project and ensure that the version installed matches the new version you just deployed.\n* Perform testing to confirm that your new version works as expected. \n* Make a git **commit*** and briefly describe your changes in the commit notes. (You can add more verbose details in your pull request.) \n  * _*When creating the commit, please select only the files that reflect the actual changes you are proposing. It is usually not necessary to include auto-generated files that don't include substantive or intended changes._\n* **Push** your branch up to your cloned repository.\n* Make a **pull request** to the upstream project! Be sure to clearly describe what you did and why in the pull request. This will allow reviewers to better understand why your PR should be merged.\n* Pull requests should target the `dev` branch, where most active development takes place. `Master` branch PRs should be mainly limited to Wiki changes. Critical bug fixes can be cherry-picked over to the `master` branch if needed.\n* *Tip: If you have many different types of changes to propose, please use different pull requests for each of them. That will be easier to review and implement them individually.*\n\nThank you again for your support for the Microsoft Access development community!!\n"
  },
  {
    "path": "Hook/README.txt",
    "content": "The source code for the hook library is licensed under LGPL-2.1, and can be found in the following repository:\n\nhttps://github.com/bclothier/AccessAppHook"
  },
  {
    "path": "LICENSE.txt",
    "content": "Copyright © 2012 Brendan Kidwell et al\n\nUse of msaccess-vcs-integration and documentation are subject to the following\nBSD-style license:\n\nPermission to use, copy, modify, and/or distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright notice\nand this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\nOF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\nTORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "Version Control Add-in (msaccess-vcs-addin)\n======================\n*for Microsoft Access Database Development*\n----------\nSupports Microsoft Access 2010, 2013, 2016, 2019, and 365\n\nAbout\n-----\n\nEasily export your Microsoft Access Database objects for use with a version control system like **GitHub** or **GitLab**. (Allows you to track code changes over time, and even collaborate with other developers on the same project.)\n\nThis project functions as a **Microsoft Access add-in**, allowing you to use a custom ribbon toolbar to export the objects and source code from the currently open Microsoft Access Database.\n\n![Export-All](img/gui-demo.gif)\n\nDevelopment Focus\n-----------------\nThis project was originally developed to manage the in-house development of numerous Microsoft Access database applications and integrations, some of which are very complex with hundreds of components. The development focus of this particular add-in is primarily in the following areas:\n* **Intuitive user interface** for managing code exports and related options, instead of having to set all these options in code or remember commands to run in the immediate window.\n* **Ribbon Toolbar** with 64-bit support! Thanks to some fantastic work by Wayne Phillips on the [twinBASIC](https://twinbasic.com/) project, we have a practical *and easy* way to implement a user-friendly ribbon through a light-weight COM Add-in wrapper that passes commands back to the Access add-in.\n* **Optimal performance**, even with very complex databases. This is largely achieved through the indexing of database components to export only items that have changed since the last export. Most databases can be exported in a few seconds or less.\n* **Extensive support** for different types of database components beyond the standard database objects. See [Supported Objects](https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Supported-Objects) for more details on what can be exported from a database.\n* **Build From Source** - Using this add-in you can actually build a database entirely from exported source files. This allows collaborative development where changes can be managed at the source code level. See this [this link](https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Documentation) for additional details.\n* **Code quality** - I am doing my best to continually refine and organize the code into efficient and well commented logical flows. This is a work in progress, but I feel it is critical to the long-term success of the project.\n* **ADP Project** support. While this is an outdated technology, there are some of us that still support and maintain complex ADP projects. This tool has been extended to export objects from ADP projects, including server-side SQL object details.\n\nGetting Started\n---------\n Simply download the add-in from the [**Releases**](https://github.com/joyfullservice/msaccess-vcs-addin/releases) page, and run the file. It will install or update the add-in for you. Additional notes on installation and use can be found on the [project wiki](https://github.com/joyfullservice/msaccess-vcs-addin/wiki).\n\n[Quick Start](https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Quick-Start) is a simple overview to help you test out this add-in on your project in less than five minutes.\n\nContributing\n------------\nInterested in adding a feature or fixing a bug? [Issues](https://github.com/joyfullservice/msaccess-vcs-addin/issues) and [pull requests](https://github.com/joyfullservice/msaccess-vcs-addin/pulls) are welcome for this project. [This page](/CONTRIBUTING.md) describes some guidelines and the types of contributions would be most helpful.\n\nDevelopment Roadmap\n-------------------\nIn addition to fixing bugs and adding features here and there, here are some of the long-term goals I want to implement in the future:\n\n* Release Version 4 (Early 2024). This version includes a number of substantial updates including an integrated ribbon toolbar interface, support for merging changes into an existing database (instead of having to build entirely from source) and numerous other enhancements.\n\n* Add support for translations in the user interface. After version 4 rolls out, I am hoping to finishing out some functionality to translate the user interface for other languages. While most International developers are at least somewhat familiar with English, I feel like this would make the tool even more comfortable for everyday use.\n\n* Finish an automated testing process where a complex sample database can be exported, reconstructed, exported again, and compared with the original export to ensure that the build process is fully constructing the database from the exported source files.\n\n* Build out an automated deployment workflow using a GitLab runner that is triggered on a commit to automatically build, test, and deploy a database project. Discussion on this idea can be found [here](https://github.com/joyfullservice/msaccess-vcs-addin/issues/51).\n\nProject History\n----------------\nThis project was originally forked from [timabell/msaccess-vcs-integration](https://github.com/timabell/msaccess-vcs-integration) in 2015, but has been extensively rewritten over the years. In 2023 the project was detached from the upstream fork and converted to a stand-alone project.\n"
  },
  {
    "path": "Ribbon/MSAccessVCS_Ribbon.code-workspace",
    "content": "{\n    \"folders\": [\n        {\n            \"name\": \"BUILD CONFIGURATIONS\",\n            \"uri\": \"twinbasic-builds:/___BUILDS/\"\n        },\n        {\n            \"name\": \"PROJECT: MSAccessVCSLib\",\n            \"uri\": \"twinbasic:/MSAccessVCSLib/\"\n        }\n    ],\n    \"settings\": {\n        \"window.title\": \"${dirty}MSAccessVCSLib${separator}twinBASIC\",\n        \"files.associations\": {\n            \"*.module\": \"php\",\n            \"*.mht\": \"html\",\n            \"*.pwmacro\": \"powershell\",\n            \"/___BUILDS/*\": \"twinbasicBuildConfig\",\n            \"/___BUILDS/win64\": \"twinbasicBuildConfigACTIVE\",\n            \"/*/Settings\": \"twinbasicProjectConfig\"\n        },\n        \"files.defaultLanguage\": \"twinbasic\",\n        \"editor.trimAutoWhitespace\": false,\n        \"editor.wordBasedSuggestions\": false,\n        \"editor.quickSuggestionsDelay\": 350,\n        \"editor.semanticTokenColorCustomizations\": {\n            \"[Monokai Dimmed]\": {\n                \"enabled\": true,\n                \"rules\": {\n                    \"basicClass\": {\n                        \"foreground\": \"#e4c685\"\n                    },\n                    \"basicLibrary\": {\n                        \"foreground\": \"#bb6464\"\n                    },\n                    \"basicParameterByVal\": {\n                        \"foreground\": \"#9b79b3\"\n                    },\n                    \"basicParameterByRef\": {\n                        \"foreground\": \"#9b79b3\"\n                    },\n                    \"basicUDT\": {\n                        \"foreground\": \"#a5630d\"\n                    },\n                    \"basicModule\": {\n                        \"foreground\": \"#a8a887\"\n                    },\n                    \"basicBuiltInDataType\": {\n                        \"foreground\": \"#b1551f\"\n                    },\n                    \"basicVariable\": {\n                        \"foreground\": \"#8b8b52\"\n                    },\n                    \"basicField\": {\n                        \"foreground\": \"#f59e1b\"\n                    },\n                    \"basicEnum\": {\n                        \"foreground\": \"#738dc5\"\n                    },\n                    \"basicEnumMember\": {\n                        \"foreground\": \"#a1adc7\"\n                    },\n                    \"basicKeyword\": {\n                        \"foreground\": \"#6c8eda\"\n                    },\n                    \"basicLiteralString\": {\n                        \"foreground\": \"#aeca89\",\n                        \"fontStyle\": \"italic\"\n                    },\n                    \"basicOperator\": {\n                        \"foreground\": \"#80a1a5\"\n                    },\n                    \"basicGenericDataType\": {\n                        \"foreground\": \"#eeda83\"\n                    },\n                    \"basicGenericValue\": {\n                        \"foreground\": \"#86ee83\"\n                    },\n                    \"basicReturnValue\": {\n                        \"foreground\": \"#ee8391\"\n                    },\n                    \"basicMe\": {\n                        \"foreground\": \"#a13838\"\n                    },\n                    \"basicComment\": {\n                        \"foreground\": \"#448a63\"\n                    },\n                    \"basicNamedArgument\": {\n                        \"foreground\": \"#74384c\"\n                    },\n                    \"basicMultiLineSeperator\": {\n                        \"foreground\": \"#74384c\"\n                    },\n                    \"basicLateBoundFunction\": {\n                        \"foreground\": \"#e7ac5f\"\n                    },\n                    \"basicLineLabel\": {\n                        \"foreground\": \"#ccc6be\",\n                        \"underline\": true\n                    },\n                    \"basicLineNumber\": {\n                        \"foreground\": \"#ccc6be\",\n                        \"underline\": true\n                    },\n                    \"basicLiteralNumeric\": {\n                        \"foreground\": \"#aeca89\"\n                    },\n                    \"basicLiteralBoolean\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicLiteralNull\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicLiteralNothing\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicLiteralEmpty\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicLiteralDate\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicConditionalCompilationDirective\": {\n                        \"foreground\": \"#ad8c98\"\n                    },\n                    \"basicConditionalCompilationExcludedCode\": {\n                        \"foreground\": \"#989599\",\n                        \"italic\": true\n                    },\n                    \"basicVariableUndeclared\": {\n                        \"foreground\": \"#b9929c\"\n                    },\n                    \"basicFunction\": {\n                        \"foreground\": \"#cf9a5d\"\n                    },\n                    \"basicDeclareFunction\": {\n                        \"foreground\": \"#bb956a\"\n                    },\n                    \"basicSub\": {\n                        \"foreground\": \"#cf9a5d\"\n                    },\n                    \"basicDeclareSub\": {\n                        \"foreground\": \"#bb956a\"\n                    },\n                    \"basicPropertyGet\": {\n                        \"foreground\": \"#864f0f\"\n                    },\n                    \"basicPropertyLet\": {\n                        \"foreground\": \"#864f0f\"\n                    },\n                    \"basicPropertySet\": {\n                        \"foreground\": \"#864f0f\"\n                    },\n                    \"basicGlobalVariablePrivate\": {\n                        \"foreground\": \"#f38096\"\n                    },\n                    \"basicGlobalVariablePublic\": {\n                        \"foreground\": \"#d34056\"\n                    },\n                    \"basicAttribute\": {\n                        \"foreground\": \"#5c5c53\",\n                        \"italic\": false\n                    },\n                    \"basicLineContinuationCharacter\": {\n                        \"foreground\": \"#808080\"\n                    }\n                }\n            },\n            \"[Default Dark+]\": {\n                \"enabled\": true,\n                \"rules\": {\n                    \"basicClass\": {\n                        \"foreground\": \"#e4c685\"\n                    },\n                    \"basicLibrary\": {\n                        \"foreground\": \"#bb6464\"\n                    },\n                    \"basicParameterByVal\": {\n                        \"foreground\": \"#9b79b3\"\n                    },\n                    \"basicParameterByRef\": {\n                        \"foreground\": \"#9b79b3\"\n                    },\n                    \"basicUDT\": {\n                        \"foreground\": \"#a5630d\"\n                    },\n                    \"basicModule\": {\n                        \"foreground\": \"#a8a887\"\n                    },\n                    \"basicBuiltInDataType\": {\n                        \"foreground\": \"#b1551f\"\n                    },\n                    \"basicVariable\": {\n                        \"foreground\": \"#8b8b52\"\n                    },\n                    \"basicField\": {\n                        \"foreground\": \"#f59e1b\"\n                    },\n                    \"basicEnum\": {\n                        \"foreground\": \"#738dc5\"\n                    },\n                    \"basicEnumMember\": {\n                        \"foreground\": \"#a1adc7\"\n                    },\n                    \"basicKeyword\": {\n                        \"foreground\": \"#6c8eda\"\n                    },\n                    \"basicLiteralString\": {\n                        \"foreground\": \"#aeca89\",\n                        \"fontStyle\": \"italic\"\n                    },\n                    \"basicOperator\": {\n                        \"foreground\": \"#80a1a5\"\n                    },\n                    \"basicGenericDataType\": {\n                        \"foreground\": \"#eeda83\"\n                    },\n                    \"basicGenericValue\": {\n                        \"foreground\": \"#86ee83\"\n                    },\n                    \"basicReturnValue\": {\n                        \"foreground\": \"#ee8391\"\n                    },\n                    \"basicMe\": {\n                        \"foreground\": \"#a13838\"\n                    },\n                    \"basicComment\": {\n                        \"foreground\": \"#448a63\"\n                    },\n                    \"basicNamedArgument\": {\n                        \"foreground\": \"#74384c\"\n                    },\n                    \"basicMultiLineSeperator\": {\n                        \"foreground\": \"#74384c\"\n                    },\n                    \"basicLateBoundFunction\": {\n                        \"foreground\": \"#e7ac5f\"\n                    },\n                    \"basicLineLabel\": {\n                        \"foreground\": \"#ccc6be\",\n                        \"underline\": true\n                    },\n                    \"basicLineNumber\": {\n                        \"foreground\": \"#ccc6be\",\n                        \"underline\": true\n                    },\n                    \"basicLiteralNumeric\": {\n                        \"foreground\": \"#aeca89\"\n                    },\n                    \"basicLiteralBoolean\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicLiteralNull\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicLiteralNothing\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicLiteralEmpty\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicLiteralDate\": {\n                        \"foreground\": \"#c495d3\"\n                    },\n                    \"basicConditionalCompilationDirective\": {\n                        \"foreground\": \"#ad8c98\"\n                    },\n                    \"basicConditionalCompilationExcludedCode\": {\n                        \"foreground\": \"#989599\",\n                        \"italic\": true\n                    },\n                    \"basicVariableUndeclared\": {\n                        \"foreground\": \"#b9929c\"\n                    },\n                    \"basicFunction\": {\n                        \"foreground\": \"#cf9a5d\"\n                    },\n                    \"basicDeclareFunction\": {\n                        \"foreground\": \"#bb956a\"\n                    },\n                    \"basicSub\": {\n                        \"foreground\": \"#cf9a5d\"\n                    },\n                    \"basicDeclareSub\": {\n                        \"foreground\": \"#bb956a\"\n                    },\n                    \"basicPropertyGet\": {\n                        \"foreground\": \"#864f0f\"\n                    },\n                    \"basicPropertyLet\": {\n                        \"foreground\": \"#864f0f\"\n                    },\n                    \"basicPropertySet\": {\n                        \"foreground\": \"#864f0f\"\n                    },\n                    \"basicGlobalVariablePrivate\": {\n                        \"foreground\": \"#f38096\"\n                    },\n                    \"basicGlobalVariablePublic\": {\n                        \"foreground\": \"#d34056\"\n                    },\n                    \"basicAttribute\": {\n                        \"foreground\": \"#5c5c53\",\n                        \"italic\": false\n                    },\n                    \"basicLineContinuationCharacter\": {\n                        \"foreground\": \"#808080\"\n                    }\n                }\n            },\n            \"[Default Light+]\": {\n                \"enabled\": true,\n                \"rules\": {\n                    \"basicClass\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicLibrary\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicParameterByVal\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicParameterByRef\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicUDT\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicModule\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicBuiltInDataType\": {\n                        \"foreground\": \"#315dbb\"\n                    },\n                    \"basicVariable\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicField\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicEnum\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicEnumMember\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicKeyword\": {\n                        \"foreground\": \"#315dbb\"\n                    },\n                    \"basicLiteralString\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicOperator\": {\n                        \"foreground\": \"#315dbb\"\n                    },\n                    \"basicGenericDataType\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicGenericValue\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicReturnValue\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicMe\": {\n                        \"foreground\": \"#315dbb\"\n                    },\n                    \"basicComment\": {\n                        \"foreground\": \"#26774a\"\n                    },\n                    \"basicNamedArgument\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicMultiLineSeperator\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicLateBoundFunction\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicLineLabel\": {\n                        \"foreground\": \"#000000\",\n                        \"italic\": true\n                    },\n                    \"basicLineNumber\": {\n                        \"foreground\": \"#000000\",\n                        \"italic\": true\n                    },\n                    \"basicLiteralNumeric\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicLiteralBoolean\": {\n                        \"foreground\": \"#315dbb\"\n                    },\n                    \"basicLiteralNull\": {\n                        \"foreground\": \"#315dbb\"\n                    },\n                    \"basicLiteralNothing\": {\n                        \"foreground\": \"#315dbb\"\n                    },\n                    \"basicLiteralEmpty\": {\n                        \"foreground\": \"#315dbb\"\n                    },\n                    \"basicLiteralDate\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicConditionalCompilationDirective\": {\n                        \"foreground\": \"#315dbb\"\n                    },\n                    \"basicConditionalCompilationExcludedCode\": {\n                        \"foreground\": \"#d2cdd3\"\n                    },\n                    \"basicVariableUndeclared\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicFunction\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicDeclareFunction\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicSub\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicDeclareSub\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicPropertyGet\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicPropertyLet\": {\n                        \"foreground\": \"#000000\"\n                    },\n                    \"basicPropertySet\": {\n                        \"foreground\": \"#000000\"\n                    }\n                }\n            }\n        },\n        \n        \n        \"workbench.colorCustomizations\": {\n            \"separators.classes.borderColor\": \"#ffffff54\",\n            \"separators.constructors.borderColor\": \"#ffffff54\",\n            \"separators.enums.borderColor\": \"#ffffff54\",\n            \"separators.functions.borderColor\": \"#ffffff54\",\n            \"separators.interfaces.borderColor\": \"#ffffff54\",\n            \"separators.methods.borderColor\": \"#ffffff54\",\n            \"separators.namespaces.borderColor\": \"#ffffff54\",\n        },\n        \"twinbasic.buildConfigurationData\": {\n            \"buildConfigurations\": [\n                {\n                    \"configuration.name\": \"win32\",\n                    \"configuration.inherits\": \"Defaults\",\n                    \"compiler.target\": \"win32\",\n                    \"compiler.autoRestart\": true,\n                    \"project.warnings\": {\n                        \"ignored\": [],\n                        \"errors\": []\n                    }\n                },\n                {\n                    \"configuration.name\": \"win64\",\n                    \"configuration.inherits\": \"Defaults\",\n                    \"compiler.target\": \"win64\",\n                    \"compiler.autoRestart\": true\n                }\n            ]\n        },\n        \"twinbasic.projectPaths\": [\n            \"\\\\helloworld.twinproj\"\n        ],\n        \"workbench.tree.indent\": 20,\n        \"workbench.iconTheme\": \"twinbasic\",\n        \"debug.openDebug\": \"neverOpen\",\n        \"twinbasic.hasCheckedOpenDebugSetting\": true,\n        \"twinbasic.hasCheckedCompactFolders\": true,\n        \"twinbasic.hasCheckedOpenDebugSetting2\": true,\n        \"debug.console.historySuggestions\": false,\n        \"twinbasic.hasSetDebugConsoleHistorySuggestionsOff\": true,\n        \"editor.formatOnType\": true,\n        \"twinbasic.hasCheckedFormatOnType\": true,\n        \"debug.saveBeforeStart\": \"none\",\n        \"twinbasic.hasCheckedSaveBeforeStart\": true\n    },\n    \"launch\": {\n        \"version\": \"0.2.0\",\n        \"configurations\": [\n            {\n                \"type\": \"twindebug\",\n                \"request\": \"launch\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "Ribbon/Ribbon.xml",
    "content": "﻿<!-- Created with IDBE Ribbon Creator (Version: 1.1041) -->\r\n<!--            http://www.RibbonCreator.com            -->\r\n<!-- Additional modifications and formatting added      -->\r\n\r\n<customUI \r\n  xmlns=\"http://schemas.microsoft.com/office/2006/01/customui\" \r\n  onLoad=\"OnRibbonLoad\" \r\n  >\r\n  <ribbon startFromScratch=\"false\">\r\n    <tabs>\r\n      <tab \r\n\t\tid=\"tabVersionControl\"\r\n\t\tgetVisible =\"GetVisible\"\r\n\t\tgetLabel=\"GetLabel\"\r\n\t  >\r\n        <group\r\n\t\t  id=\"grpActions\"\r\n\t\t  getVisible =\"GetVisible\"\r\n\t\t  getLabel=\"GetLabel\"\r\n\t\t>\r\n          <button \r\n\t\t\tid=\"btnShow\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"AccessFormWizard\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <separator \r\n\t\t\tid=\"sep1\"\r\n\t\t\tgetVisible=\"GetVisible\" \r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnExport\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"SourceControlCheckIn\" \r\n\t\t\tonAction=\"OnActionButton\" \r\n\t\t\tgetVisible=\"GetVisible\" \r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button \r\n\t\t\tid=\"btnExportVBA\" \r\n\t\t\tsize=\"large\" \r\n\t\t\timageMso=\"GroupMacro\" \r\n\t\t\tonAction=\"OnActionButton\" \r\n\t\t\tgetVisible=\"GetVisible\" \r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnExportSelected\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"SelectionPane\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <separator\r\n\t\t\tid=\"sep2\"\r\n\t\t\tgetVisible=\"GetVisible\" />\r\n          <button\r\n\t\t\tid=\"btnBuild\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"FileCompactAndRepairDatabase\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnMergeBuild\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"CellsInsertDialog\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnBuildAs\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"FileSaveAs\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnLoadSelected\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"AlignDialog\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n        </group>\r\n        <group\r\n\t\t  id=\"grpOptions\"\r\n\t\t  getVisible =\"GetVisible\"\r\n\t\t  getLabel=\"GetLabel\"\r\n\t\t>\r\n          <button\r\n\t\t\tid=\"btnShowOptions\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"AdvancedFileProperties\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n        </group>\r\n        <group\r\n\t\t  id=\"grpTools\"\r\n\t\t  getVisible =\"GetVisible\"\r\n\t\t  getLabel=\"GetLabel\"\r\n\t\t>\r\n\t\t  <button\r\n\t\t\tid=\"btnOpenRepository\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"FileServerTransferDatabase\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnOpenSourceFolder\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"BlogOpenExisting\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnOpenExportLog\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"ExportTextFile\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnOpenBuildLog\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"ImportTextFile\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <menu\r\n\t\t\tid=\"mnuAdvancedTools\"\r\n\t\t\tsize=\"large\" itemSize=\"large\"\r\n\t\t\timageMso=\"GroupDesign\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t\t>\r\n\t\t\t  <button\r\n\t\t\t\tid=\"btnGoToLinkAdvancedTools\"\r\n\t\t\t\timageMso=\"TentativeAcceptInvitation\"\r\n\t\t\t\tonAction=\"OnActionButton\"\r\n\t\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t\t  />\r\n\t\t\t  <button\r\n\t\t\t\tid=\"btnLocalizeLibraryReferences\"\r\n\t\t\t\timageMso=\"MacroShowAllActions\"\r\n\t\t\t\tonAction=\"OnActionButton\"\r\n\t\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t\t  />\r\n\t\t\t  <button\r\n\t\t\t\tid=\"btnRepairColors\"\r\n\t\t\t\timageMso=\"SmartArtChangeColorsGallery\"\r\n\t\t\t\tonAction=\"OnActionButton\"\r\n\t\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t\t  />\r\n\t\t\t  <button\r\n\t\t\t\tid=\"btnSplitFiles\"\r\n\t\t\t\timageMso=\"AdpDiagramArrangeTables\"\r\n\t\t\t\tonAction=\"OnActionButton\"\r\n\t\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t\t  />\r\n\t\t\t  <button\r\n\t\t\t\tid=\"btnActivateHook\"\r\n\t\t\t\timageMso=\"Call\"\r\n\t\t\t\tonAction=\"OnActionButton\"\r\n\t\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t\t  />\r\n\t\t\t  <button\r\n\t\t\t\tid=\"btnReloadRibbon\"\r\n\t\t\t\timageMso=\"DataRefreshAll\"\r\n\t\t\t\tonAction=\"OnActionButton\"\r\n\t\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t\t  />\r\n          </menu>\r\n          <!--End menu (mnuAdvancedTools) NOT delete this remark-->\r\n        </group>\r\n        <group\r\n\t\t  id=\"grpGitHub\"\r\n\t\t  getVisible =\"GetVisible\"\r\n\t\t  getLabel=\"GetLabel\"\r\n\t\t>\r\n          <button\r\n\t\t\tid=\"btnGoToLinkHome\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"BlogHomePage\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnGoToLinkDocumentation\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"ViewMasterDocumentViewClassic\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnGoToLinkSupport\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"FileCreateDocumentWorkspace\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n          <button\r\n\t\t\tid=\"btnGoToLinkDownload\"\r\n\t\t\tsize=\"large\"\r\n\t\t\timageMso=\"GetExternalDataFromWeb\"\r\n\t\t\tonAction=\"OnActionButton\"\r\n\t\t\tgetVisible=\"GetVisible\"\r\n\t\t\tgetEnabled=\"GetEnabled\"\r\n\t\t\tgetLabel=\"GetLabel\"\r\n\t\t\tgetDescription=\"GetDescription\"\r\n\t\t\tgetSupertip=\"GetSupertip\"\r\n\t\t  />\r\n        </group>\r\n      </tab>\r\n    </tabs>\r\n  </ribbon>\r\n</customUI>\r\n"
  },
  {
    "path": "Ribbon/Source/Resources/MANIFEST/#1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\r\n   <assemblyIdentity\r\n      type=\"win32\"\r\n      processorArchitecture=\"*\"\r\n      name=\"MSAccessVCSLib\"\r\n      version=\"1.0.0.1\"\r\n   />\r\n   <description>Microsoft Access COM add-in to add Fluent UI ribbon support to Access add-in project</description>\r\n   <dependency>\r\n      <dependentAssembly>\r\n         <assemblyIdentity\r\n            type=\"win32\"\r\n            processorArchitecture=\"*\"\r\n            name=\"Microsoft.Windows.Common-Controls\"\r\n            version=\"6.0.0.0\"\r\n            publicKeyToken=\"6595b64144ccf1df\"\r\n            language=\"*\"\r\n         />\r\n      </dependentAssembly>\r\n   </dependency>\r\n</assembly>"
  },
  {
    "path": "Ribbon/Source/Settings",
    "content": "{\n\t\"configuration.inherits\": \"Defaults\",\n\t\"project.appTitle\": \"Microsoft Access Version Control Ribbon Add-in\",\n\t\"project.buildPath\": \"${SourcePath}\\\\Build\\\\${ProjectName}_${Architecture}.${FileExtension}\",\n\t\"project.buildStackReserveSize\": 4096,\n\t\"project.buildType\": \"ActiveX DLL\",\n\t\"project.description\": \"Microsoft Access Version Control Ribbon Add-in\",\n\t\"project.dllRegisterAfterBuild\": false,\n\t\"project.exportAfterSave\": true,\n\t\"project.exportPath\": \"${SourcePath}\\\\Source\",\n\t\"project.exportPathIsV2\": true,\n\t\"project.id\": \"{ddde82dd-9e54-46ba-8167-1cf40ec2b1ea}\",\n\t\"project.licence\": \"MIT\",\n\t\"project.name\": \"MSAccessVCSLib\",\n\t\"project.optionExplicit\": true,\n\t\"project.references\": [\n\t\t{\n\t\t\t\"id\": \"{00020430-0000-0000-C000-000000000046}\",\n\t\t\t\"lcid\": 0,\n\t\t\t\"name\": \"OLE Automation\",\n\t\t\t\"path32\": \"C:\\\\Windows\\\\SysWOW64\\\\stdole2.tlb\",\n\t\t\t\"path64\": \"C:\\\\Windows\\\\System32\\\\stdole2.tlb\",\n\t\t\t\"symbolId\": \"stdole\",\n\t\t\t\"versionMajor\": 2,\n\t\t\t\"versionMinor\": 0\n\t\t},\n\t\t{\n\t\t\t\"id\": \"{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}\",\n\t\t\t\"lcid\": 0,\n\t\t\t\"name\": \"Microsoft Add-In Designer\",\n\t\t\t\"path32\": \"C:\\\\Program Files (x86)\\\\Common Files\\\\Designer\\\\MSADDNDR.DLL\",\n\t\t\t\"path64\": \"\",\n\t\t\t\"symbolId\": \"AddInDesignerObjects\",\n\t\t\t\"versionMajor\": 1,\n\t\t\t\"versionMinor\": 0\n\t\t},\n\t\t{\n\t\t\t\"id\": \"{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}\",\n\t\t\t\"lcid\": 0,\n\t\t\t\"name\": \"Microsoft Office 14.0 Object Library\",\n\t\t\t\"path32\": \"C:\\\\Program Files (x86)\\\\Common Files\\\\Microsoft Shared\\\\OFFICE14\\\\MSO.DLL\",\n\t\t\t\"path64\": \"\",\n\t\t\t\"symbolId\": \"Office\",\n\t\t\t\"versionMajor\": 2,\n\t\t\t\"versionMinor\": 5\n\t\t},\n\t\t{\n\t\t\t\"id\": \"{420B2830-E718-11CF-893D-00A0C9054228}\",\n\t\t\t\"lcid\": 0,\n\t\t\t\"name\": \"Microsoft Scripting Runtime\",\n\t\t\t\"path32\": \"C:\\\\Windows\\\\SysWOW64\\\\scrrun.dll\",\n\t\t\t\"path64\": \"C:\\\\Windows\\\\System32\\\\scrrun.dll\",\n\t\t\t\"symbolId\": \"Scripting\",\n\t\t\t\"versionMajor\": 1,\n\t\t\t\"versionMinor\": 0\n\t\t},\n\t\t{\n\t\t\t\"id\": \"{B691E011-1797-432E-907A-4D8C69339129}\",\n\t\t\t\"lcid\": 0,\n\t\t\t\"name\": \"Microsoft ActiveX Data Objects 6.1 Library\",\n\t\t\t\"path32\": \"C:\\\\Program Files (x86)\\\\Common Files\\\\System\\\\ado\\\\msado15.dll\",\n\t\t\t\"path64\": \"C:\\\\Program Files\\\\Common Files\\\\System\\\\ado\\\\msado15.dll\",\n\t\t\t\"symbolId\": \"ADODB\",\n\t\t\t\"versionMajor\": 6,\n\t\t\t\"versionMinor\": 1\n\t\t},\n\t\t{\n\t\t\t\"id\": \"{F50B82D0-DCAB-43FE-9631-11959D4A4728}\",\n\t\t\t\"isCompilerPackage\": true,\n\t\t\t\"licence\": \"MIT\",\n\t\t\t\"name\": \"[COMPILER PACKAGE] twinBASIC - VB Compatibility Package (Forms)\",\n\t\t\t\"path32\": \"\",\n\t\t\t\"path64\": \"\",\n\t\t\t\"publisher\": \"TWINBASIC-COMPILER\",\n\t\t\t\"symbolId\": \"VB\",\n\t\t\t\"versionBuild\": 0,\n\t\t\t\"versionMajor\": 0,\n\t\t\t\"versionMinor\": 0,\n\t\t\t\"versionRevision\": 31\n\t\t}\n\t],\n\t\"project.settingsVersion\": 1,\n\t\"project.useProjectIdForTypeLibraryId\": true,\n\t\"project.versionBuild\": 1,\n\t\"project.versionMajor\": 1,\n\t\"project.versionMinor\": 0,\n\t\"project.versionRevision\": 0,\n\t\"project.warnings\": {\n\t\t\"errors\": [],\n\t\t\"hints\": [],\n\t\t\"ignored\": [],\n\t\t\"info\": [],\n\t\t\"warnings\": []\n\t},\n\t\"runtime.useUnicodeStandardLibrary\": true\n}"
  },
  {
    "path": "Ribbon/Source/Sources/AddInRibbon.twin",
    "content": "\nClass AddInRibbon\n\n\tImplements IDTExtensibility2\n\n\t/*\n\t\tPLEASE NOTE: make sure you create a DLL of the correct bitness to match your version of VBA\n\t\t(e.g. if you're using the 64-bit version of VBA, make sure you change the 'Active Build' in the twinBASIC panel to 'win64')\n\t*/\n\t\n\t[WithDispatchForwarding]\n\tImplements IRibbonExtensibility\n\t\n\t/* \n\t\tThe [WithDispatchForwarding] attribute seen above is needed so that late-bound calls\n\t\ton the IRibbonExtensibility interface get routed to our default interface.\n\t\tWithout it, events will not fire.\n\t*/\n    \n    ' This corresponds to the name of the Access Add-in Project\n    Const PROJECT_NAME As String = \"MSAccessVCS\"\n \n\t' Cached copy of the ribbon XML as loaded from the Ribbon.xml file.\n\tPrivate strRibbonXML As String\n\t\n\t' A reference to the Microsoft Access application\n\tPrivate applicationObject As Object\n\t\n\t' A reference to the ribbon object\n\tPrivate activeRibbon As IRibbonUI\n    \n    ' Dictionary of menu Labels, Descriptions and Supertips loaded from Ribbon.json\n    Private ribbonStrings As Dictionary\n\n\t\n    /* \n\t\tFirstly we implement the IDTExtensibility2 interface members\n\t\tWe don't do anything particularly interesting here, except for taking a reference\n\t\tto the Application object provided to us in the OnConnection event\n\t*/\n\tPublic Sub OnConnection(ByVal Application As Object, _\n\t\t\t\t\t ByVal ConnectMode As ext_ConnectMode, _\n\t\t\t\t\t ByVal AddInInst As Object, _\n\t\t\t\t\t ByRef custom As Variant()) Implements IDTExtensibility2.OnConnection\n\t\t\n\t\t'MsgBox \"MyCOMAddin.OnConnection\"\n\t\tSet applicationObject = Application\n\tEnd Sub\n\t\n\t\n\tPublic Sub OnDisconnection(ByVal RemoveMode As ext_DisconnectMode, _\n\t\t\t\t\t\tByRef custom As Variant()) Implements IDTExtensibility2.OnDisconnection\n\t\t\n\t\t'MsgBox \"MyCOMAddin.OnDisconnection\"\n\t\tSet applicationObject = Nothing\n\tEnd Sub\n\t\n\t\n\tPublic Sub OnAddInsUpdate(ByRef custom As Variant()) Implements IDTExtensibility2.OnAddInsUpdate\n\t\t\n\t\t'MsgBox \"MyCOMAddin.OnAddInsUpdate\"\n\tEnd Sub\n\t\n\t\n\tPublic Sub OnStartupComplete(ByRef custom As Variant()) Implements IDTExtensibility2.OnStartupComplete\n\t\t\n\t\t'MsgBox \"MyCOMAddin.OnStartupComplete\"\n\tEnd Sub\n\t\n\t\n\tPublic Sub OnBeginShutdown(ByRef custom As Variant()) Implements IDTExtensibility2.OnBeginShutdown\n\t\tSet activeRibbon = Nothing\n\t\t'MsgBox \"MyCOMAddin.OnBeginShutdown\"\n\tEnd Sub\n\t\n\t\n\t/* \n\t\tNext we implement IRibbonExtensibility.GetCustomUI which lets us provide\n\t\ta ribbon XML string to the host\n\t*/\n\tPrivate Function GetCustomUI(ByVal RibbonID As String) As String Implements IRibbonExtensibility.GetCustomUI\n\t\t\n\t\tDim strPath As String\n        Dim strContent As String\n        Dim dStrings As Dictionary\n        \n        On Error GoTo ErrHandler\n        \n        ' Load translation strings\n        strPath = App.Path & \"\\Ribbon.json\"\n\t\tIf Me.ribbonStrings Is Nothing Then\n        \tIf FSO.FileExists(strPath) Then\n                strContent = ReadFile(strPath)\n                If Len(strContent) Then\n                    Set dStrings = ParseJson(strContent)\n                    If Not dStrings Is Nothing Then Set Me.ribbonStrings = dStrings\n                End If\n            End If\n        End If\n        \n\t\t' Load XML to cached variable if needed\n        strPath = App.Path & \"\\Ribbon.xml\"\n        If Me.strRibbonXML = vbNullString Then\n\t\t\tIf FSO.FileExists(strPath) Then Me.strRibbonXML = ReadFile(strPath)\n        End If\n\t\t\n\t\tReturn Me.strRibbonXML\n\t\tExit Function\n     \n\tErrHandler:\n\t\tMsgBox \"Error: \" & Err.Number & vbCrLf & Err.Description, vbExclamation\n\t\tResume Next\n     \n\tEnd Function\n\n    \n    /*\n\t\tRead content from text file\n    */\n    Private Function ReadFile(strPath As String) As String\n\t\n        Dim strContent As String\n        \n        ' If the file exists, load content as string\n        On Error Resume Next\n        If FSO.FileExists(strPath) Then\n            With New ADODB.Stream\n                .Charset = \"utf-8\"\n                .Open\n                .LoadFromFile strPath\n                strContent = .ReadText\n                .Close\n            End With\n        End If\n        \n        Return strContent\n    End Function\n    \n    \n\t/*\n\t\tEnsure that the add-in project is loaded in VBE\n\t*/\n\tPrivate Function VerifyAccessAddinProject() As Boolean\n\n\t\tDim strAddInLib As String\n\t\tDim proj As Object      \t' VBProject\n\t\tDim addInProject As Object\t' VBProject\n\t\tDim strName As String\n\t\tDim strTempDbPath As String = App.Path & \"Temp.accdb\"\n\t\t\n\t\t' Get relative path to Access add-in library (Should be in same folder as this dll)\n\t\tstrAddInLib = App.Path & \"\\Version Control\"\n\n        ' If NO file is open, and NO add-ins are loaded, we may be unable to load\n        ' the MSAccessVCS add-in. To resolve this, create a new blank\n        ' database project, load the addin, then close the temporary database project.\n        If applicationObject.VBE.ActiveVBProject Is Nothing Then\n            ' Create a new database project.\n            If FSO.FileExists(strTempDbPath) Then FSO.DeleteFile(strTempDbPath)\n            applicationObject.NewCurrentDatabase strTempDbPath\n        End If\n        \n        ' At this point we should have a database file open, but just in case...\n        If applicationObject.VBE.ActiveVBProject Is Nothing Then\n            MsgBox \"Please open a database file before using this add-in\", vbInformation\n        Else\n            ' Attempt to call the Preload routine\n            On Error Resume Next\n            With applicationObject\n                .DoCmd.Hourglass True\n                .Run strAddInLib & \".Preload\"\n                .DoCmd.Hourglass False\n            End With\n            If Err Then\n                MsgBox \"Failed to Load Add-In\" & vbCrLf & vbCrLf & _\n                    Err.Number & \": \" & Err.Description, vbExclamation\n                Err.Clear()\n            End If\n            On Error GoTo 0\n        End If\n\t\t\n\t\t' Clean up any temporary database\n\t\tIf applicationObject.CurrentProject.FullName = strTempDbPath Then applicationObject.CloseCurrentDatabase\n\t\tIf FSO.FileExists(strTempDbPath) Then FSO.DeleteFile(strTempDbPath)\n\n        ' Technically, we are just returning true if ANY project is loaded.\n        ' (We should be able to call the add-in directly as long as a project is loaded.)\n        Return Not (applicationObject.VBE.ActiveVBProject Is Nothing)\n        \n\tEnd Function\n\t\n\t\n\t/*\n\t\tWrapper for the file system object\n\t*/\n\tPrivate Function FSO() As FileSystemObject\n\t\tStatic objFSO As FileSystemObject\n\t\tIf objFSO Is Nothing Then Set objFSO = New FileSystemObject\n\t\tReturn objFSO\n\tEnd Function\n\n\t\n\t/*\n\t\tPrimary ribbon callback function. Relay control ID back to add-in for execution.\n\t*/\n\tPublic Sub OnActionButton(control As IRibbonControl)\n\t\tIf VerifyAccessAddinProject Then\n\t\t\t' Pass the control ID to the handler function\n\t\t\tapplicationObject.Run App.Path & \"\\Version Control.HandleRibbonCommand\", control.Id\n\t\tEnd If\n\tEnd Sub\n\t\n    /*\n\t\tFunctions to dynamically load labels and descriptions\n    */\n    Public Function GetLabel(control As IRibbonControl) As String\n        Return GetString(control, \"Label\")\n    End Function\n    \n    Public Function GetDescription(control As IRibbonControl) As String\n        Return GetString(control, \"Description\")\n    End Function\n    \n    Public Function GetSupertip(control As IRibbonControl) As String\n        Return GetString(control, \"Supertip\")\n    End Function\n\t\n    ' Wrapper to look up a string value from the dictionary loaded from `Ribbon.json`\n    Private Function GetString(control As IRibbonControl, strKey As String) As String\n        Dim strValue As String\n        If Not Me.ribbonStrings Is Nothing Then\n\t\t\tIf Me.ribbonStrings.Exists(control.Id) Then\n\t\t\t\tstrValue = Me.ribbonStrings(control.Id)(strKey)\n\t\t\tEnd If\n        End If\n        Return strValue\n    End Function\n    \n\t/*\n\t\tStub functions for default ribbon callbacks. (Not currently used)\n\t*/\n\tPublic Function GetVisible(control As IRibbonControl) As Boolean\n\t\t' Always show all buttons for now.\n\t\tReturn True\n\tEnd Function\n\t\n\tPublic Function GetEnabled(control As IRibbonControl) As Boolean\n\t\t' Always enable all buttons for now.\n\t\tReturn True\n\tEnd Function\n\t\n\t\n\tPublic Sub OnRibbonLoad(ribbon As IRibbonUI)\n\t\t' Save reference to ribbon object\n\t\tSet activeRibbon = ribbon\n\tEnd Sub\nEnd Class\n"
  },
  {
    "path": "Ribbon/Source/Sources/DllRegistration.twin",
    "content": "Module DllRegistration\n    \n    Private Const AddinProjectName As String = VBA.Compilation.CurrentProjectName\n    Private Const AddinClassName As String = \"AddInRibbon\"\n    Private Const AddinQualifiedClassName As String = AddinProjectName & \".\" & AddinClassName\n    Private Const AddinFriendlyName As String = \"Ribbon integration for MSAccessVCS add-in\"\n    Private Const AddinDescription As String = \"Microsoft Access COM add-in to add Fluent UI ribbon support to Access add-in project\"\n    Private Const RootRegistryFolder_ACCESS As String = \"HKCU\\SOFTWARE\\Microsoft\\Office\\Access\\Addins\\\" & AddinQualifiedClassName & \"\\\"\n\n    Public Function DllRegisterServer() As Boolean\n    \n        On Error GoTo RegError\n        \n        ' Integrated DLL registration for Access COM Add-in list\n        With CreateObject(\"wscript.shell\")\n            .RegWrite RootRegistryFolder_ACCESS & \"FriendlyName\", AddinFriendlyName, \"REG_SZ\"\n            .RegWrite RootRegistryFolder_ACCESS & \"Description\", AddinDescription, \"REG_SZ\"\n            .RegWrite RootRegistryFolder_ACCESS & \"LoadBehavior\", 3, \"REG_DWORD\"\n        End With\n    \n        Return True\n        \n    RegError:\n        MsgBox \"DllRegisterServer -- An error occured trying to write to the system registry:\" & vbCrLf & _\n                Err.Description & \" (\" & Hex(Err.Number) & \")\"\n                \n        Return False\n    End Function\n \n    Public Function DllUnregisterServer() As Boolean\n        \n        On Error GoTo RegError\n        \n        With CreateObject(\"wscript.shell\")\n            .RegDelete RootRegistryFolder_ACCESS & \"FriendlyName\"\n            .RegDelete RootRegistryFolder_ACCESS & \"Description\"\n            .RegDelete RootRegistryFolder_ACCESS & \"LoadBehavior\"\n            .RegDelete RootRegistryFolder_ACCESS\n        End With\n    \n        Return True\n        \n    RegError:\n        MsgBox \"DllUnregisterServer -- An error occured trying to delete from the system registry:\" & vbCrLf & _\n                Err.Description & \" (\" & Hex(Err.Number) & \")\"\n                \n        Return False\n    End Function\nEnd Module"
  },
  {
    "path": "Ribbon/Source/Sources/JsonConverter.twin",
    "content": "\nModule JsonConverter\n\n    ''\n    ' VBA-JSON v2.3.1\n    ' (c) Tim Hall - https://github.com/VBA-tools/VBA-JSON\n    '\n    ' JSON Converter for VBA\n    '\n    ' Errors:\n    ' 10001 - JSON parse error\n    '\n    ' @class JsonConverter\n    ' @author tim.hall.engr@gmail.com\n    ' @license MIT (http://www.opensource.org/licenses/mit-license.php)\n    '' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '\n    '\n    ' Based originally on vba-json (with extensive changes)\n    ' BSD license included below\n    '\n    ' JSONLib, http://code.google.com/p/vba-json/\n    '\n    ' Copyright (c) 2013, Ryo Yokoyama\n    ' All rights reserved.\n    '\n    ' Redistribution and use in source and binary forms, with or without\n    ' modification, are permitted provided that the following conditions are met:\n    '     * Redistributions of source code must retain the above copyright\n    '       notice, this list of conditions and the following disclaimer.\n    '     * Redistributions in binary form must reproduce the above copyright\n    '       notice, this list of conditions and the following disclaimer in the\n    '       documentation and/or other materials provided with the distribution.\n    '     * Neither the name of the <organization> nor the\n    '       names of its contributors may be used to endorse or promote products\n    '       derived from this software without specific prior written permission.\n    '\n    ' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n    ' ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n    ' WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n    ' DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\n    ' DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n    ' (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n    ' LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n    ' ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n    ' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n    ' SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '\n    Option Compare Database\n    Option Explicit\n    Option Private Module\n\n    ' === VBA-UTC Headers\n    #If Mac Then\n\n    #If VBA7 Then\n\n    ' 64-bit Mac (2016)\n    Private Declare PtrSafe Function utc_popen Lib \"/usr/lib/libc.dylib\" Alias \"popen\" ( _\n        ByVal utc_Command As String, ByVal utc_Mode As String) As LongPtr\n    Private Declare PtrSafe Function utc_pclose Lib \"/usr/lib/libc.dylib\" Alias \"pclose\" ( _\n        ByVal utc_File As LongPtr) As LongPtr\n    Private Declare PtrSafe Function utc_fread Lib \"/usr/lib/libc.dylib\" Alias \"fread\" ( _\n        ByVal utc_Buffer As String, ByVal utc_Size As LongPtr, ByVal utc_Number As LongPtr, ByVal utc_File As LongPtr) As LongPtr\n    Private Declare PtrSafe Function utc_feof Lib \"/usr/lib/libc.dylib\" Alias \"feof\" ( _\n        ByVal utc_File As LongPtr) As LongPtr\n\n    #Else\n\n    ' 32-bit Mac\n    Private Declare Function utc_popen Lib \"libc.dylib\" Alias \"popen\" ( _\n        ByVal utc_Command As String, ByVal utc_Mode As String) As Long\n    Private Declare Function utc_pclose Lib \"libc.dylib\" Alias \"pclose\" ( _\n        ByVal utc_File As Long) As Long\n    Private Declare Function utc_fread Lib \"libc.dylib\" Alias \"fread\" ( _\n        ByVal utc_Buffer As String, ByVal utc_Size As Long, ByVal utc_Number As Long, ByVal utc_File As Long) As Long\n    Private Declare Function utc_feof Lib \"libc.dylib\" Alias \"feof\" ( _\n        ByVal utc_File As Long) As Long\n\n    #End If\n\n    #ElseIf VBA7 Then\n\n    ' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724421.aspx\n    ' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724949.aspx\n    ' http://msdn.microsoft.com/en-us/library/windows/desktop/ms725485.aspx\n    Private Declare PtrSafe Function utc_GetTimeZoneInformation Lib \"kernel32\" Alias \"GetTimeZoneInformation\" ( _\n        utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) As Long\n    Private Declare PtrSafe Function utc_SystemTimeToTzSpecificLocalTime Lib \"kernel32\" Alias \"SystemTimeToTzSpecificLocalTime\" ( _\n        utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) As Long\n    Private Declare PtrSafe Function utc_TzSpecificLocalTimeToSystemTime Lib \"kernel32\" Alias \"TzSpecificLocalTimeToSystemTime\" ( _\n        utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) As Long\n\n    #Else\n\n    Private Declare Function utc_GetTimeZoneInformation Lib \"kernel32\" Alias \"GetTimeZoneInformation\" ( _\n        utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) As Long\n    Private Declare Function utc_SystemTimeToTzSpecificLocalTime Lib \"kernel32\" Alias \"SystemTimeToTzSpecificLocalTime\" ( _\n        utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) As Long\n    Private Declare Function utc_TzSpecificLocalTimeToSystemTime Lib \"kernel32\" Alias \"TzSpecificLocalTimeToSystemTime\" ( _\n        utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) As Long\n\n    #End If\n\n    #If Mac Then\n\n    #If VBA7 Then\n    Private Type utc_ShellResult\n        utc_Output As String\n        utc_ExitCode As LongPtr\n    End Type\n\n    #Else\n\n    Private Type utc_ShellResult\n        utc_Output As String\n        utc_ExitCode As Long\n    End Type\n\n    #End If\n\n    #Else\n\n    Private Type utc_SYSTEMTIME\n        utc_wYear As Integer\n        utc_wMonth As Integer\n        utc_wDayOfWeek As Integer\n        utc_wDay As Integer\n        utc_wHour As Integer\n        utc_wMinute As Integer\n        utc_wSecond As Integer\n        utc_wMilliseconds As Integer\n    End Type\n\n    Private Type utc_TIME_ZONE_INFORMATION\n        utc_Bias As Long\n        utc_StandardName(0 To 31) As Integer\n        utc_StandardDate As utc_SYSTEMTIME\n        utc_StandardBias As Long\n        utc_DaylightName(0 To 31) As Integer\n        utc_DaylightDate As utc_SYSTEMTIME\n        utc_DaylightBias As Long\n    End Type\n\n    #End If\n    ' === End VBA-UTC\n\n    Private Type json_Options\n        ' VBA only stores 15 significant digits, so any numbers larger than that are truncated\n        ' This can lead to issues when BIGINT's are used (e.g. for Ids or Credit Cards), as they will be invalid above 15 digits\n        ' See: http://support.microsoft.com/kb/269370\n        '\n        ' By default, VBA-JSON will use String for numbers longer than 15 characters that contain only digits\n        ' to override set `JsonConverter.JsonOptions.UseDoubleForLargeNumbers = True`\n        UseDoubleForLargeNumbers As Boolean\n\n        ' The JSON standard requires object keys to be quoted (\" or '), use this option to allow unquoted keys\n        AllowUnquotedKeys As Boolean\n\n        ' The solidus (/) is not required to be escaped, use this option to escape them as \\/ in ConvertToJson\n        EscapeSolidus As Boolean\n\n        ' Before version 2.3.1 dates were converted to UTC in ConvertToJson method, but not when json was parsed.\n        ' Convert datetime values to UTC/ISO8601 (true, slower) or dont change local <-> global times (false, faster)\n        ConvertDateToIso As Boolean\n\n        ' Allow Unicode characters in JSON text. Set to True to use native Unicode or false for escaped values.\n        AllowUnicodeChars As Boolean\n    End Type\n    Public JsonOptions As json_Options\n\n    ' ============================================= '\n    ' Public Methods\n    ' ============================================= '\n\n    ''\n    ' Convert JSON string to object (Dictionary/Collection)\n    '\n    ' @method ParseJson\n    ' @param {String} json_String\n    ' @return {Object} (Dictionary or Collection)\n    ' @throws 10001 - JSON parse error\n    ''\n    Public Function ParseJson(ByVal JsonString As String) As Object\n        Dim json_Index As Long\n        json_Index = 1\n\n        ' Remove vbCr, vbLf, and vbTab from json_String\n        JsonString = VBA.Replace(VBA.Replace(VBA.Replace(JsonString, VBA.vbCr, vbNullString), VBA.vbLf, vbNullString), VBA.vbTab, vbNullString)\n\n        json_SkipSpaces JsonString, json_Index\n        Select Case VBA.Mid$(JsonString, json_Index, 1)\n        Case \"{\"\n            Set ParseJson = json_ParseObject(JsonString, json_Index)\n        Case \"[\"\n            Set ParseJson = json_ParseArray(JsonString, json_Index)\n        Case Else\n            ' Error: Invalid JSON string\n            Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(JsonString, json_Index, \"Expecting '{' or '['\")\n        End Select\n\n    End Function\n\n    ''\n    ' Convert object (Dictionary/Collection/Array) to JSON\n    '\n    ' @method ConvertToJson\n    ' @param {Variant} JsonValue (Dictionary, Collection, or Array)\n    ' @param {Integer|String} Whitespace \"Pretty\" print json with given number of spaces per indentation (Integer) or given string\n    ' @return {String}\n    ''\n    Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitespace As Variant, Optional ByVal json_CurrentIndentation As Long = 0) As String\n        Dim json_Buffer As String\n        Dim json_BufferPosition As Long\n        Dim json_BufferLength As Long\n        Dim json_Index As Long\n        Dim json_LBound As Long\n        Dim json_UBound As Long\n        Dim json_IsFirstItem As Boolean\n        Dim json_Index2D As Long\n        Dim json_LBound2D As Long\n        Dim json_UBound2D As Long\n        Dim json_IsFirstItem2D As Boolean\n        Dim json_Key As Variant\n        Dim json_Value As Variant\n        Dim json_DateStr As String\n        Dim json_Converted As String\n        Dim json_SkipItem As Boolean\n        Dim json_PrettyPrint As Boolean\n        Dim json_Indentation As String\n        Dim json_InnerIndentation As String\n\n        json_LBound = -1\n        json_UBound = -1\n        json_IsFirstItem = True\n        json_LBound2D = -1\n        json_UBound2D = -1\n        json_IsFirstItem2D = True\n        json_PrettyPrint = Not IsMissing(Whitespace)\n\n        Select Case VBA.VarType(JsonValue)\n        Case VBA.vbNull\n            ConvertToJson = \"null\"\n\n        Case VBA.vbDate\n            ' Date\n            json_DateStr = VBA.CStr(JsonValue)\n            ConvertToJson = \"\"\"\" & json_DateStr & \"\"\"\"\n\n        Case VBA.vbString\n            ' String (or large number encoded as string)\n            If Not JsonOptions.UseDoubleForLargeNumbers And json_StringIsLargeNumber(JsonValue) Then\n                ConvertToJson = JsonValue\n            Else\n                ConvertToJson = \"\"\"\" & json_Encode(JsonValue) & \"\"\"\"\n            End If\n        Case VBA.vbBoolean\n            If JsonValue Then\n                ConvertToJson = \"true\"\n            Else\n                ConvertToJson = \"false\"\n            End If\n        Case VBA.vbArray To VBA.vbArray + VBA.vbByte\n            If json_PrettyPrint Then\n                If VBA.VarType(Whitespace) = VBA.vbString Then\n                    json_Indentation = VBA.String$(json_CurrentIndentation + 1, Whitespace)\n                    json_InnerIndentation = VBA.String$(json_CurrentIndentation + 2, Whitespace)\n                Else\n                    json_Indentation = VBA.Space$((json_CurrentIndentation + 1) * Whitespace)\n                    json_InnerIndentation = VBA.Space$((json_CurrentIndentation + 2) * Whitespace)\n                End If\n            End If\n\n            ' Array\n            json_BufferAppend json_Buffer, \"[\", json_BufferPosition, json_BufferLength\n\n            On Error Resume Next\n\n            json_LBound = LBound(JsonValue, 1)\n            json_UBound = UBound(JsonValue, 1)\n            json_LBound2D = LBound(JsonValue, 2)\n            json_UBound2D = UBound(JsonValue, 2)\n\n            If json_LBound >= 0 And json_UBound >= 0 Then\n                For json_Index = json_LBound To json_UBound\n                    If json_IsFirstItem Then\n                        json_IsFirstItem = False\n                    Else\n                        ' Append comma to previous line\n                        json_BufferAppend json_Buffer, \",\", json_BufferPosition, json_BufferLength\n                    End If\n\n                    If json_LBound2D >= 0 And json_UBound2D >= 0 Then\n                        ' 2D Array\n                        If json_PrettyPrint Then\n                            json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\n                        End If\n                        json_BufferAppend json_Buffer, json_Indentation & \"[\", json_BufferPosition, json_BufferLength\n\n                        For json_Index2D = json_LBound2D To json_UBound2D\n                            If json_IsFirstItem2D Then\n                                json_IsFirstItem2D = False\n                            Else\n                                json_BufferAppend json_Buffer, \",\", json_BufferPosition, json_BufferLength\n                            End If\n\n                            json_Converted = ConvertToJson(JsonValue(json_Index, json_Index2D), Whitespace, json_CurrentIndentation + 2)\n\n                            ' For Arrays/Collections, undefined (Empty/Nothing) is treated as null\n                            If json_Converted = vbNullString Then\n                                ' (nest to only check if converted = \"\")\n                                If json_IsUndefined(JsonValue(json_Index, json_Index2D)) Then\n                                    json_Converted = \"null\"\n                                End If\n                            End If\n\n                            If json_PrettyPrint Then\n                                json_Converted = vbNewLine & json_InnerIndentation & json_Converted\n                            End If\n\n                            json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength\n                        Next json_Index2D\n\n                        If json_PrettyPrint Then\n                            json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\n                        End If\n\n                        json_BufferAppend json_Buffer, json_Indentation & \"]\", json_BufferPosition, json_BufferLength\n                        json_IsFirstItem2D = True\n                    Else\n                        ' 1D Array\n                        json_Converted = ConvertToJson(JsonValue(json_Index), Whitespace, json_CurrentIndentation + 1)\n\n                        ' For Arrays/Collections, undefined (Empty/Nothing) is treated as null\n                        If json_Converted = vbNullString Then\n                            ' (nest to only check if converted = \"\")\n                            If json_IsUndefined(JsonValue(json_Index)) Then\n                                json_Converted = \"null\"\n                            End If\n                        End If\n\n                        If json_PrettyPrint Then\n                            json_Converted = vbNewLine & json_Indentation & json_Converted\n                        End If\n\n                        json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength\n                    End If\n                Next json_Index\n            End If\n\n            On Error GoTo 0\n\n            If json_PrettyPrint Then\n                json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\n\n                If VBA.VarType(Whitespace) = VBA.vbString Then\n                    json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)\n                Else\n                    json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)\n                End If\n            End If\n\n            json_BufferAppend json_Buffer, json_Indentation & \"]\", json_BufferPosition, json_BufferLength\n\n            ConvertToJson = json_BufferToString(json_Buffer, json_BufferPosition)\n\n        ' Dictionary or Collection\n        Case VBA.vbObject\n            If json_PrettyPrint Then\n                If VBA.VarType(Whitespace) = VBA.vbString Then\n                    json_Indentation = VBA.String$(json_CurrentIndentation + 1, Whitespace)\n                Else\n                    json_Indentation = VBA.Space$((json_CurrentIndentation + 1) * Whitespace)\n                End If\n            End If\n\n            ' Dictionary\n            If VBA.TypeName(JsonValue) = \"Dictionary\" Then\n                json_BufferAppend json_Buffer, \"{\", json_BufferPosition, json_BufferLength\n                For Each json_Key In JsonValue.Keys\n                    ' For Objects, undefined (Empty/Nothing) is not added to object\n                    json_Converted = ConvertToJson(JsonValue(json_Key), Whitespace, json_CurrentIndentation + 1)\n                    If json_Converted = vbNullString Then\n                        json_SkipItem = json_IsUndefined(JsonValue(json_Key))\n                    Else\n                        json_SkipItem = False\n                    End If\n\n                    If Not json_SkipItem Then\n                        If json_IsFirstItem Then\n                            json_IsFirstItem = False\n                        Else\n                            json_BufferAppend json_Buffer, \",\", json_BufferPosition, json_BufferLength\n                        End If\n\n                        If json_PrettyPrint Then\n                            json_Converted = vbNewLine & json_Indentation & \"\"\"\" & json_Encode(json_Key) & \"\"\": \" & json_Converted\n                        Else\n                            json_Converted = \"\"\"\" & json_Encode(json_Key) & \"\"\":\" & json_Converted\n                        End If\n\n                        json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength\n                    End If\n                Next json_Key\n\n                If json_PrettyPrint Then\n                    json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\n\n                    If VBA.VarType(Whitespace) = VBA.vbString Then\n                        json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)\n                    Else\n                        json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)\n                    End If\n                End If\n\n                json_BufferAppend json_Buffer, json_Indentation & \"}\", json_BufferPosition, json_BufferLength\n\n            ' Collection\n            ElseIf VBA.TypeName(JsonValue) = \"Collection\" Then\n                json_BufferAppend json_Buffer, \"[\", json_BufferPosition, json_BufferLength\n                For Each json_Value In JsonValue\n                    If json_IsFirstItem Then\n                        json_IsFirstItem = False\n                    Else\n                        json_BufferAppend json_Buffer, \",\", json_BufferPosition, json_BufferLength\n                    End If\n\n                    json_Converted = ConvertToJson(json_Value, Whitespace, json_CurrentIndentation + 1)\n\n                    ' For Arrays/Collections, undefined (Empty/Nothing) is treated as null\n                    If json_Converted = vbNullString Then\n                        ' (nest to only check if converted = \"\")\n                        If json_IsUndefined(json_Value) Then\n                            json_Converted = \"null\"\n                        End If\n                    End If\n\n                    If json_PrettyPrint Then\n                        json_Converted = vbNewLine & json_Indentation & json_Converted\n                    End If\n\n                    json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength\n                Next json_Value\n\n                If json_PrettyPrint Then\n                    json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\n\n                    If VBA.VarType(Whitespace) = VBA.vbString Then\n                        json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)\n                    Else\n                        json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)\n                    End If\n                End If\n\n                json_BufferAppend json_Buffer, json_Indentation & \"]\", json_BufferPosition, json_BufferLength\n            End If\n\n            ConvertToJson = json_BufferToString(json_Buffer, json_BufferPosition)\n        Case VBA.vbInteger, VBA.vbLong, VBA.vbSingle, VBA.vbDouble, VBA.vbCurrency, VBA.vbDecimal\n            ' Number (use decimals for numbers)\n            ConvertToJson = VBA.Replace(JsonValue, \",\", \".\")\n        Case Else\n            ' vbEmpty, vbError, vbDataObject, vbByte, vbUserDefinedType\n            ' Use VBA's built-in to-string\n            On Error Resume Next\n            ConvertToJson = JsonValue\n            On Error GoTo 0\n        End Select\n\n    End Function\n\n    ' ============================================= '\n    ' Private Functions\n    ' ============================================= '\n\n    Private Function json_ParseObject(json_String As String, ByRef json_Index As Long) As Dictionary\n        Dim json_Key As String\n        Dim json_NextChar As String\n\n        Set json_ParseObject = New Dictionary\n        json_SkipSpaces json_String, json_Index\n        If VBA.Mid$(json_String, json_Index, 1) <> \"{\" Then\n            Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting '{'\")\n        Else\n            json_Index = json_Index + 1\n\n            Do\n                json_SkipSpaces json_String, json_Index\n                If VBA.Mid$(json_String, json_Index, 1) = \"}\" Then\n                    json_Index = json_Index + 1\n                    Exit Function\n                ElseIf VBA.Mid$(json_String, json_Index, 1) = \",\" Then\n                    json_Index = json_Index + 1\n                    json_SkipSpaces json_String, json_Index\n                End If\n\n                json_Key = json_ParseKey(json_String, json_Index)\n                json_NextChar = json_Peek(json_String, json_Index)\n                If json_NextChar = \"[\" Or json_NextChar = \"{\" Then\n                    Set json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)\n                Else\n                    json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)\n                End If\n            Loop\n        End If\n    End Function\n\n    Private Function json_ParseArray(json_String As String, ByRef json_Index As Long) As Collection\n        Set json_ParseArray = New Collection\n\n        json_SkipSpaces json_String, json_Index\n        If VBA.Mid$(json_String, json_Index, 1) <> \"[\" Then\n            Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting '['\")\n        Else\n            json_Index = json_Index + 1\n\n            Do\n                json_SkipSpaces json_String, json_Index\n                If VBA.Mid$(json_String, json_Index, 1) = \"]\" Then\n                    json_Index = json_Index + 1\n                    Exit Function\n                ElseIf VBA.Mid$(json_String, json_Index, 1) = \",\" Then\n                    json_Index = json_Index + 1\n                    json_SkipSpaces json_String, json_Index\n                End If\n\n                json_ParseArray.Add json_ParseValue(json_String, json_Index)\n            Loop\n        End If\n    End Function\n\n    Private Function json_ParseValue(json_String As String, ByRef json_Index As Long) As Variant\n        json_SkipSpaces json_String, json_Index\n        Select Case VBA.Mid$(json_String, json_Index, 1)\n        Case \"{\"\n            Set json_ParseValue = json_ParseObject(json_String, json_Index)\n        Case \"[\"\n            Set json_ParseValue = json_ParseArray(json_String, json_Index)\n        Case \"\"\"\", \"'\"\n            json_ParseValue = json_ParseString(json_String, json_Index)\n        Case Else\n            If VBA.Mid$(json_String, json_Index, 4) = \"true\" Then\n                json_ParseValue = True\n                json_Index = json_Index + 4\n            ElseIf VBA.Mid$(json_String, json_Index, 5) = \"false\" Then\n                json_ParseValue = False\n                json_Index = json_Index + 5\n            ElseIf VBA.Mid$(json_String, json_Index, 4) = \"null\" Then\n                json_ParseValue = Null\n                json_Index = json_Index + 4\n            ElseIf VBA.InStr(\"+-0123456789\", VBA.Mid$(json_String, json_Index, 1)) Then\n                json_ParseValue = json_ParseNumber(json_String, json_Index)\n            Else\n                Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting 'STRING', 'NUMBER', null, true, false, '{', or '['\")\n            End If\n        End Select\n    End Function\n\n    Private Function json_ParseString(ByRef json_String As String _\n                                    , ByRef json_Index As Long) As Variant\n\n        Dim json_Quote As String\n        Dim json_Char As String\n        Dim json_Code As String\n        Dim json_Buffer As String\n        Dim json_BufferPosition As Long\n        Dim json_BufferLength As Long\n\n        json_SkipSpaces json_String, json_Index\n\n        ' Store opening quote to look for matching closing quote\n        json_Quote = VBA.Mid$(json_String, json_Index, 1)\n        json_Index = json_Index + 1\n\n        Do While json_Index > 0 And json_Index <= Len(json_String)\n            json_Char = VBA.Mid$(json_String, json_Index, 1)\n\n            Select Case json_Char\n            Case \"\\\"\n                ' Escaped string, \\\\, or \\/\n                json_Index = json_Index + 1\n                json_Char = VBA.Mid$(json_String, json_Index, 1)\n\n                Select Case json_Char\n                Case \"\"\"\", \"\\\", \"/\", \"'\"\n                    json_BufferAppend json_Buffer, json_Char, json_BufferPosition, json_BufferLength\n                    json_Index = json_Index + 1\n                Case \"b\"\n                    json_BufferAppend json_Buffer, vbBack, json_BufferPosition, json_BufferLength\n                    json_Index = json_Index + 1\n                Case \"f\"\n                    json_BufferAppend json_Buffer, vbFormFeed, json_BufferPosition, json_BufferLength\n                    json_Index = json_Index + 1\n                Case \"n\"\n                    'json_BufferAppend json_Buffer, vbCrLf, json_BufferPosition, json_BufferLength\n                    ' This should be treated as a line feed, not CrLf. - Adam 7/24/2023\n                    json_BufferAppend json_Buffer, vbLf, json_BufferPosition, json_BufferLength\n                    json_Index = json_Index + 1\n                Case \"r\"\n                    json_BufferAppend json_Buffer, vbCr, json_BufferPosition, json_BufferLength\n                    json_Index = json_Index + 1\n                Case \"t\"\n                    json_BufferAppend json_Buffer, vbTab, json_BufferPosition, json_BufferLength\n                    json_Index = json_Index + 1\n                Case \"u\"\n                    ' Unicode character escape (e.g. \\u00a9 = Copyright)\n                    json_Index = json_Index + 1\n                    json_Code = VBA.Mid$(json_String, json_Index, 4)\n                    json_BufferAppend json_Buffer, VBA.ChrW$(VBA.Val(\"&h\" + json_Code)), json_BufferPosition, json_BufferLength\n                    json_Index = json_Index + 4\n                End Select\n            Case json_Quote\n                json_ParseString = json_BufferToString(json_Buffer, json_BufferPosition)\n                json_Index = json_Index + 1\n                Exit Function\n            Case Else\n                json_BufferAppend json_Buffer, json_Char, json_BufferPosition, json_BufferLength\n                json_Index = json_Index + 1\n            End Select\n        Loop\n    End Function\n\n    Private Function json_ParseNumber(json_String As String, ByRef json_Index As Long) As Variant\n        Dim json_Char As String\n        Dim json_Value As String\n        Dim json_IsLargeNumber As Boolean\n\n        json_SkipSpaces json_String, json_Index\n\n        Do While json_Index > 0 And json_Index <= Len(json_String)\n            json_Char = VBA.Mid$(json_String, json_Index, 1)\n\n            If VBA.InStr(\"+-0123456789.eE\", json_Char) Then\n                ' Unlikely to have massive number, so use simple append rather than buffer here\n                json_Value = json_Value & json_Char\n                json_Index = json_Index + 1\n            Else\n                ' Excel only stores 15 significant digits, so any numbers larger than that are truncated\n                ' This can lead to issues when BIGINT's are used (e.g. for Ids or Credit Cards), as they will be invalid above 15 digits\n                ' See: http://support.microsoft.com/kb/269370\n                '\n                ' Fix: Parse -> String, Convert -> String longer than 15/16 characters containing only numbers and decimal points -> Number\n                ' (decimal doesn't factor into significant digit count, so if present check for 15 digits + decimal = 16)\n                json_IsLargeNumber = IIf(InStr(json_Value, \".\"), Len(json_Value) >= 17, Len(json_Value) >= 16)\n                If Not JsonOptions.UseDoubleForLargeNumbers And json_IsLargeNumber Then\n                    json_ParseNumber = json_Value\n                Else\n                    ' VBA.Val does not use regional settings, so guard for comma is not needed\n                    json_ParseNumber = VBA.Val(json_Value)\n                End If\n                Exit Function\n            End If\n        Loop\n    End Function\n\n    Private Function json_ParseKey(json_String As String, ByRef json_Index As Long) As String\n        ' Parse key with single or double quotes\n        If VBA.Mid$(json_String, json_Index, 1) = \"\"\"\" Or VBA.Mid$(json_String, json_Index, 1) = \"'\" Then\n            json_ParseKey = json_ParseString(json_String, json_Index)\n        ElseIf JsonOptions.AllowUnquotedKeys Then\n            Dim json_Char As String\n            Do While json_Index > 0 And json_Index <= Len(json_String)\n                json_Char = VBA.Mid$(json_String, json_Index, 1)\n                If (json_Char <> \" \") And (json_Char <> \":\") Then\n                    json_ParseKey = json_ParseKey & json_Char\n                    json_Index = json_Index + 1\n                Else\n                    Exit Do\n                End If\n            Loop\n        Else\n            Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting '\"\"' or '''\")\n        End If\n\n        ' Check for colon and skip if present or throw if not present\n        json_SkipSpaces json_String, json_Index\n        If VBA.Mid$(json_String, json_Index, 1) <> \":\" Then\n            Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting ':'\")\n        Else\n            json_Index = json_Index + 1\n        End If\n    End Function\n\n    Private Function json_IsUndefined(ByVal json_Value As Variant) As Boolean\n        ' Empty / Nothing -> undefined\n        Select Case VBA.VarType(json_Value)\n        Case VBA.vbEmpty\n            json_IsUndefined = True\n        Case VBA.vbObject\n            Select Case VBA.TypeName(json_Value)\n            Case \"Empty\", \"Nothing\"\n                json_IsUndefined = True\n            End Select\n        End Select\n    End Function\n\n    Private Function json_Encode(ByVal json_Text As Variant) As String\n        ' Reference: http://www.ietf.org/rfc/rfc4627.txt\n        ' Escape: \", \\, /, backspace, form feed, line feed, carriage return, tab\n        Dim json_Index As Long\n        Dim json_Char As String\n        Dim json_AscCode As Long\n        Dim json_Buffer As String\n        Dim json_BufferPosition As Long\n        Dim json_BufferLength As Long\n\n        For json_Index = 1 To VBA.Len(json_Text)\n            json_Char = VBA.Mid$(json_Text, json_Index, 1)\n            json_AscCode = VBA.AscW(json_Char)\n\n            ' When AscW returns a negative number, it returns the twos complement form of that number.\n            ' To convert the twos complement notation into normal binary notation, add 0xFFF to the return result.\n            ' https://support.microsoft.com/en-us/kb/272138\n            If json_AscCode < 0 Then\n                json_AscCode = json_AscCode + 65536\n            End If\n\n            ' From spec, \", \\, and control characters must be escaped (solidus is optional)\n\n            Select Case json_AscCode\n            Case 34\n                ' \" -> 34 -> \\\"\n                json_Char = \"\\\"\"\"\n            Case 92\n                ' \\ -> 92 -> \\\\\n                json_Char = \"\\\\\"\n            Case 47\n                ' / -> 47 -> \\/ (optional)\n                If JsonOptions.EscapeSolidus Then\n                    json_Char = \"\\/\"\n                End If\n            Case 8\n                ' backspace -> 8 -> \\b\n                json_Char = \"\\b\"\n            Case 12\n                ' form feed -> 12 -> \\f\n                json_Char = \"\\f\"\n            Case 10\n                ' line feed -> 10 -> \\n\n                json_Char = \"\\n\"\n            Case 13\n                ' carriage return -> 13 -> \\r\n                json_Char = \"\\r\"\n            Case 9\n                ' tab -> 9 -> \\t\n                json_Char = \"\\t\"\n            Case 0 To 31\n                ' Non-ascii characters -> convert to 4-digit hex\n                json_Char = \"\\u\" & VBA.Right$(\"0000\" & VBA.Hex$(json_AscCode), 4)\n            Case 127 To 65535\n                ' Unicode character range\n                If Not JsonOptions.AllowUnicodeChars Then\n                    json_Char = \"\\u\" & VBA.Right$(\"0000\" & VBA.Hex$(json_AscCode), 4)\n                End If\n            End Select\n\n            json_BufferAppend json_Buffer, json_Char, json_BufferPosition, json_BufferLength\n        Next json_Index\n\n        json_Encode = json_BufferToString(json_Buffer, json_BufferPosition)\n    End Function\n\n    Private Function json_Peek(json_String As String, ByVal json_Index As Long, Optional json_NumberOfCharacters As Long = 1) As String\n        ' \"Peek\" at the next number of characters without incrementing json_Index (ByVal instead of ByRef)\n        json_SkipSpaces json_String, json_Index\n        json_Peek = VBA.Mid$(json_String, json_Index, json_NumberOfCharacters)\n    End Function\n\n    Private Sub json_SkipSpaces(json_String As String, ByRef json_Index As Long)\n        ' Increment index to skip over spaces\n        Do While json_Index > 0 And json_Index <= VBA.Len(json_String) And VBA.Mid$(json_String, json_Index, 1) = \" \"\n            json_Index = json_Index + 1\n        Loop\n    End Sub\n\n    Private Function json_StringIsLargeNumber(json_String As Variant) As Boolean\n        ' Check if the given string is considered a \"large number\"\n        ' (See json_ParseNumber)\n\n        Dim json_Length As Long\n        Dim json_CharIndex As Long\n        json_Length = VBA.Len(json_String)\n\n        ' Length with be at least 16 characters and assume will be less than 100 characters\n        If json_Length >= 16 And json_Length <= 100 Then\n            Dim json_CharCode As String\n\n            json_StringIsLargeNumber = True\n\n            For json_CharIndex = 1 To json_Length\n                json_CharCode = VBA.Asc(VBA.Mid$(json_String, json_CharIndex, 1))\n                Select Case json_CharCode\n                ' Look for .|0-9|E|e\n                Case 46, 48 To 57, 69, 101\n                    ' Continue through characters\n                Case Else\n                    json_StringIsLargeNumber = False\n                    Exit Function\n                End Select\n            Next json_CharIndex\n        End If\n    End Function\n\n    Private Function json_ParseErrorMessage(json_String As String, ByRef json_Index As Long, errorMessage As String) As Variant\n        ' Provide detailed parse error message, including details of where and what occurred\n        '\n        ' Example:\n        ' Error parsing JSON:\n        ' {\"abcde\":True}\n        '          ^\n        ' Expecting 'STRING', 'NUMBER', null, true, false, '{', or '['\n\n        Dim json_StartIndex As Long\n        Dim json_StopIndex As Long\n\n        ' Include 10 characters before and after error (if possible)\n        json_StartIndex = json_Index - 10\n        json_StopIndex = json_Index + 10\n        If json_StartIndex <= 0 Then\n            json_StartIndex = 1\n        End If\n        If json_StopIndex > VBA.Len(json_String) Then\n            json_StopIndex = VBA.Len(json_String)\n        End If\n\n        json_ParseErrorMessage = \"Error parsing JSON:\" & VBA.vbNewLine & _\n                                 VBA.Mid$(json_String, json_StartIndex, json_StopIndex - json_StartIndex + 1) & VBA.vbNewLine & _\n                                 VBA.Space$(json_Index - json_StartIndex) & \"^\" & VBA.vbNewLine & _\n                                 errorMessage\n    End Function\n\n    Private Sub json_BufferAppend(ByRef json_Buffer As String, _\n                                  ByRef json_Append As Variant, _\n                                  ByRef json_BufferPosition As Long, _\n                                  ByRef json_BufferLength As Long)\n        ' VBA can be slow to append strings due to allocating a new string for each append\n        ' Instead of using the traditional append, allocate a large empty string and then copy string at append position\n        '\n        ' Example:\n        ' Buffer: \"abc  \"\n        ' Append: \"def\"\n        ' Buffer Position: 3\n        ' Buffer Length: 5\n        '\n        ' Buffer position + Append length > Buffer length -> Append chunk of blank space to buffer\n        ' Buffer: \"abc       \"\n        ' Buffer Length: 10\n        '\n        ' Put \"def\" into buffer at position 3 (0-based)\n        ' Buffer: \"abcdef    \"\n        '\n        ' Approach based on cStringBuilder from vbAccelerator\n        ' http://www.vbaccelerator.com/home/VB/Code/Techniques/RunTime_Debug_Tracing/VB6_Tracer_Utility_zip_cStringBuilder_cls.asp\n        '\n        ' and clsStringAppend from Philip Swannell\n        ' https://github.com/VBA-tools/VBA-JSON/pull/82\n\n        Dim json_AppendLength As Long\n        Dim json_LengthPlusPosition As Long\n\n        json_AppendLength = VBA.Len(json_Append)\n        json_LengthPlusPosition = json_AppendLength + json_BufferPosition\n\n        If json_LengthPlusPosition > json_BufferLength Then\n            ' Appending would overflow buffer, add chunk\n            ' (double buffer length or append length, whichever is bigger)\n            Dim json_AddedLength As Long\n            json_AddedLength = IIf(json_AppendLength > json_BufferLength, json_AppendLength, json_BufferLength)\n\n            json_Buffer = json_Buffer & VBA.Space$(json_AddedLength)\n            json_BufferLength = json_BufferLength + json_AddedLength\n        End If\n\n        ' Note: Namespacing with VBA.Mid$ doesn't work properly here, throwing compile error:\n        ' Function call on left-hand side of assignment must return Variant or Object\n        Mid$(json_Buffer, json_BufferPosition + 1, json_AppendLength) = CStr(json_Append)\n        json_BufferPosition = json_BufferPosition + json_AppendLength\n    End Sub\n\n    Private Function json_BufferToString(ByRef json_Buffer As String, ByVal json_BufferPosition As Long) As String\n        If json_BufferPosition > 0 Then\n            json_BufferToString = VBA.Left$(json_Buffer, json_BufferPosition)\n        End If\n    End Function\n    \nEnd Module"
  },
  {
    "path": "Testing/Linked.csv",
    "content": "ID,Color\n1,Red\n2,Orange\n3,Yellow\n4,Green\n5,Blue"
  },
  {
    "path": "Testing/Testing.accdb.src/dbs-properties.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbProperty\",\r\n    \"Description\": \"Database Properties (DAO)\"\r\n  },\r\n  \"Items\": {\r\n    \"AccessVersion\": {\r\n      \"Value\": \"09.50\",\r\n      \"Type\": 10\r\n    },\r\n    \"AllowBuiltInToolbars\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowDatasheetSchema\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowFullMenus\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowShortcutMenus\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowSpecialKeys\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowToolbarChanges\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"ANSI Query Mode\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"AppIcon\": {\r\n      \"Value\": \"App Icon.ico\",\r\n      \"Type\": 10\r\n    },\r\n    \"AppTitle\": {\r\n      \"Value\": \"Version Control Testing\",\r\n      \"Type\": 10\r\n    },\r\n    \"Auto Compact\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"Build\": {\r\n      \"Value\": 720,\r\n      \"Type\": 4\r\n    },\r\n    \"CheckTruncatedNumFields\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Clear Cache on Close\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"CollatingOrder\": {\r\n      \"Value\": 1033,\r\n      \"Type\": 3\r\n    },\r\n    \"Connect\": {\r\n      \"Value\": \"\",\r\n      \"Type\": 12\r\n    },\r\n    \"DAOProperty\": {\r\n      \"Value\": \"DAO\",\r\n      \"Type\": 10\r\n    },\r\n    \"Date stored as Text\": {\r\n      \"Value\": \"11/17/2023 2:15:08 PM\",\r\n      \"Type\": 10\r\n    },\r\n    \"DesignMasterID\": {\r\n      \"Value\": \"\",\r\n      \"Type\": 15\r\n    },\r\n    \"DesignWithData\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"HasOfflineLists\": {\r\n      \"Value\": 70,\r\n      \"Type\": 3\r\n    },\r\n    \"ISO8601 Date as Text\": {\r\n      \"Value\": \"2023-11-17T17:51:00.000Z\",\r\n      \"Type\": 10\r\n    },\r\n    \"Name\": {\r\n      \"Value\": \"rel:Testing.accdb\",\r\n      \"Type\": 12\r\n    },\r\n    \"NavPane Category\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"NavPane Closed\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"NavPane Sort By\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"NavPane View By\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"NavPane Width\": {\r\n      \"Value\": 215,\r\n      \"Type\": 4\r\n    },\r\n    \"Never Cache\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"Picture Property Storage Format\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"ProjVer\": {\r\n      \"Value\": 119,\r\n      \"Type\": 3\r\n    },\r\n    \"QueryTimeout\": {\r\n      \"Value\": 60,\r\n      \"Type\": 3\r\n    },\r\n    \"RecordsAffected\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"ReplicaID\": {\r\n      \"Value\": \"\",\r\n      \"Type\": 15\r\n    },\r\n    \"SavedDateValue\": {\r\n      \"Value\": \"2023-11-17T19:35:41.000Z\",\r\n      \"Type\": 8\r\n    },\r\n    \"Show Navigation Pane Search Bar\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Show Values in Indexed\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Show Values in Non-Indexed\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Show Values in Remote\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"Show Values Limit\": {\r\n      \"Value\": 1000,\r\n      \"Type\": 4\r\n    },\r\n    \"ShowDocumentTabs\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"StartUpShowDBWindow\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"StartUpShowStatusBar\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"Theme Resource Name\": {\r\n      \"Value\": \"Angles\",\r\n      \"Type\": 10\r\n    },\r\n    \"Themed Form Controls\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Transactions\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"Updatable\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"Use Microsoft Access 2007 compatible cache\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"UseAppIconForFrmRpt\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"UseMDIMode\": {\r\n      \"Value\": 0,\r\n      \"Type\": 2\r\n    },\r\n    \"Version\": {\r\n      \"Value\": \"14.0\",\r\n      \"Type\": 12\r\n    },\r\n    \"WebDesignMode\": {\r\n      \"Value\": 0,\r\n      \"Type\": 2\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/documents.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbDocument\",\r\n    \"Description\": \"Database Documents Properties (DAO)\"\r\n  },\r\n  \"Items\": {\r\n    \"Databases\": {\r\n      \"SummaryInfo\": {\r\n        \"Author\": \"Adam Waller\",\r\n        \"Hyperlink base\": \"https://github.com/joyfullservice/msaccess-vcs-integration/\",\r\n        \"Subject\": \"Test functionality of Version Control System\",\r\n        \"Title\": \"VCS Testing\"\r\n      }\r\n    },\r\n    \"Modules\": {\r\n      \"basUtility\": {\r\n        \"Description\": \"My special description on the code module.\"\r\n      }\r\n    },\r\n    \"Tables\": {\r\n      \"tblHidden\": {\r\n        \"Description\": \"This table should be hidden.\"\r\n      },\r\n      \"tblLinkedCSV\": {\r\n        \"Description\": \"This is simply a test CSV file that were are linking to.\"\r\n      },\r\n      \"tblSaveXML\": {\r\n        \"Description\": \"Saved description in XML table.\"\r\n      }\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/forms/Form1.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =8884\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =1\r\n    Right =25575\r\n    Bottom =12555\r\n    RecSrcDt = Begin\r\n        0xef22ea99ebc8e540\r\n    End\r\n    RecordSource =\"SELECT tblAttachment.ImageObject FROM tblAttachment; \"\r\n    DatasheetFontName =\"Franklin Gothic Book\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin BoundObjectFrame\r\n            AddColon = NotDefault\r\n            SizeMode =3\r\n            SpecialEffect =2\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =7560\r\n            Name =\"Detail\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin BoundObjectFrame\r\n                    OverlapFlags =85\r\n                    Left =2880\r\n                    Top =2100\r\n                    Width =855\r\n                    Height =855\r\n                    BorderColor =10921638\r\n                    Name =\"ImageObject\"\r\n                    ControlSource =\"ImageObject\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =2880\r\n                    LayoutCachedTop =2100\r\n                    LayoutCachedWidth =3735\r\n                    LayoutCachedHeight =2955\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextFontFamily =0\r\n                            Left =1080\r\n                            Top =2100\r\n                            Width =1260\r\n                            Height =315\r\n                            BorderColor =8355711\r\n                            ForeColor =8355711\r\n                            Name =\"Label0\"\r\n                            Caption =\"ImageObject\"\r\n                            FontName =\"Franklin Gothic Book\"\r\n                            GridlineColor =10921638\r\n                            LayoutCachedLeft =1080\r\n                            LayoutCachedTop =2100\r\n                            LayoutCachedWidth =2340\r\n                            LayoutCachedHeight =2415\r\n                        End\r\n                    End\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/forms/frmColors.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    RecordSelectors = NotDefault\r\n    NavigationButtons = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =14400\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =50\r\n    Right =19650\r\n    Bottom =12585\r\n    RecSrcDt = Begin\r\n        0x8b5c5de351a8e540\r\n    End\r\n    DatasheetFontName =\"Franklin Gothic Book\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Rectangle\r\n            SpecialEffect =3\r\n            BackStyle =0\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Line\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Image\r\n            BackStyle =0\r\n            OldBorderStyle =0\r\n            BorderLineStyle =0\r\n            SizeMode =3\r\n            PictureAlignment =2\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CommandButton\r\n            FontSize =11\r\n            FontWeight =400\r\n            FontName =\"Calibri\"\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =1\r\n            Gradient =12\r\n            BackThemeColorIndex =4\r\n            BackTint =60.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =4\r\n            BorderTint =60.0\r\n            ThemeFontIndex =1\r\n            HoverThemeColorIndex =4\r\n            HoverTint =40.0\r\n            PressedThemeColorIndex =4\r\n            PressedShade =75.0\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin UnboundObjectFrame\r\n            SpecialEffect =2\r\n            OldBorderStyle =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =2\r\n            ForeShade =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin FormHeader\r\n            Height =780\r\n            Name =\"FormHeader\"\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =2\r\n            BackTint =20.0\r\n        End\r\n        Begin Section\r\n            Height =7050\r\n            Name =\"Detail\"\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =2880\r\n                    Top =1620\r\n                    Width =840\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label0\"\r\n                    Caption =\"Type\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =2880\r\n                    LayoutCachedTop =1620\r\n                    LayoutCachedWidth =3720\r\n                    LayoutCachedHeight =1980\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Line\r\n                    OverlapFlags =85\r\n                    Left =480\r\n                    Top =2100\r\n                    Width =12540\r\n                    Name =\"Line1\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =480\r\n                    LayoutCachedTop =2100\r\n                    LayoutCachedWidth =13020\r\n                    LayoutCachedHeight =2100\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =4320\r\n                    Top =1620\r\n                    Width =1260\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label3\"\r\n                    Caption =\"Theme Color\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =4320\r\n                    LayoutCachedTop =1620\r\n                    LayoutCachedWidth =5580\r\n                    LayoutCachedHeight =1980\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =93\r\n                    TextFontFamily =0\r\n                    Left =7200\r\n                    Top =1620\r\n                    Width =1440\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label4\"\r\n                    Caption =\"Pallet Color\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =7200\r\n                    LayoutCachedTop =1620\r\n                    LayoutCachedWidth =8640\r\n                    LayoutCachedHeight =1980\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =93\r\n                    TextFontFamily =0\r\n                    Left =10080\r\n                    Top =1620\r\n                    Width =1305\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label5\"\r\n                    Caption =\"Default Color\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =10080\r\n                    LayoutCachedTop =1620\r\n                    LayoutCachedWidth =11385\r\n                    LayoutCachedHeight =1980\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =2880\r\n                    Top =2280\r\n                    Width =870\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label6\"\r\n                    Caption =\"Text Box\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =2880\r\n                    LayoutCachedTop =2280\r\n                    LayoutCachedWidth =3750\r\n                    LayoutCachedHeight =2640\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =2880\r\n                    Top =2820\r\n                    Width =870\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label7\"\r\n                    Caption =\"Label\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =2880\r\n                    LayoutCachedTop =2820\r\n                    LayoutCachedWidth =3750\r\n                    LayoutCachedHeight =3180\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =2880\r\n                    Top =3300\r\n                    Width =870\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label8\"\r\n                    Caption =\"Button\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =2880\r\n                    LayoutCachedTop =3300\r\n                    LayoutCachedWidth =3750\r\n                    LayoutCachedHeight =3660\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =2880\r\n                    Top =3780\r\n                    Width =870\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label9\"\r\n                    Caption =\"Box\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =2880\r\n                    LayoutCachedTop =3780\r\n                    LayoutCachedWidth =3750\r\n                    LayoutCachedHeight =4140\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =600\r\n                    Top =2280\r\n                    Width =1635\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label12\"\r\n                    Caption =\"Orange, Accent 2\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =600\r\n                    LayoutCachedTop =2280\r\n                    LayoutCachedWidth =2235\r\n                    LayoutCachedHeight =2640\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin TextBox\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =4320\r\n                    Top =2280\r\n                    Width =1200\r\n                    Height =315\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"Text13\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =4320\r\n                    LayoutCachedTop =2280\r\n                    LayoutCachedWidth =5520\r\n                    LayoutCachedHeight =2595\r\n                    BackThemeColorIndex =5\r\n                End\r\n                Begin TextBox\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =7200\r\n                    Top =2280\r\n                    Width =1200\r\n                    Height =315\r\n                    TabIndex =1\r\n                    BackColor =5676533\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"Text15\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =7200\r\n                    LayoutCachedTop =2280\r\n                    LayoutCachedWidth =8400\r\n                    LayoutCachedHeight =2595\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin TextBox\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =8640\r\n                    Top =2280\r\n                    Width =1200\r\n                    Height =315\r\n                    TabIndex =2\r\n                    BackColor =3439082\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"Text16\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =8640\r\n                    LayoutCachedTop =2280\r\n                    LayoutCachedWidth =9840\r\n                    LayoutCachedHeight =2595\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin TextBox\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =11520\r\n                    Top =2280\r\n                    Width =1200\r\n                    Height =315\r\n                    TabIndex =3\r\n                    BackColor =-2147483598\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"Text18\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =11520\r\n                    LayoutCachedTop =2280\r\n                    LayoutCachedWidth =12720\r\n                    LayoutCachedHeight =2595\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =11520\r\n                    Top =1620\r\n                    Width =1305\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label19\"\r\n                    Caption =\"System Color\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =11520\r\n                    LayoutCachedTop =1620\r\n                    LayoutCachedWidth =12825\r\n                    LayoutCachedHeight =1980\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =87\r\n                    TextFontFamily =0\r\n                    Left =8640\r\n                    Top =1620\r\n                    Width =1440\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label20\"\r\n                    Caption =\"Custom Color\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =8640\r\n                    LayoutCachedTop =1620\r\n                    LayoutCachedWidth =10080\r\n                    LayoutCachedHeight =1980\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =4320\r\n                    Top =2760\r\n                    Width =1200\r\n                    Height =300\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label21\"\r\n                    Caption =\" \"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =4320\r\n                    LayoutCachedTop =2760\r\n                    LayoutCachedWidth =5520\r\n                    LayoutCachedHeight =3060\r\n                    BackThemeColorIndex =5\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =10080\r\n                    Top =2760\r\n                    Width =1200\r\n                    Height =300\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label22\"\r\n                    Caption =\" \"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =10080\r\n                    LayoutCachedTop =2760\r\n                    LayoutCachedWidth =11280\r\n                    LayoutCachedHeight =3060\r\n                End\r\n                Begin Label\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =7200\r\n                    Top =2760\r\n                    Width =1200\r\n                    Height =300\r\n                    BackColor =5676533\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label23\"\r\n                    Caption =\" \"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =7200\r\n                    LayoutCachedTop =2760\r\n                    LayoutCachedWidth =8400\r\n                    LayoutCachedHeight =3060\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =8640\r\n                    Top =2760\r\n                    Width =1200\r\n                    Height =300\r\n                    BackColor =3439082\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label24\"\r\n                    Caption =\" \"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =8640\r\n                    LayoutCachedTop =2760\r\n                    LayoutCachedWidth =9840\r\n                    LayoutCachedHeight =3060\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =11520\r\n                    Top =2760\r\n                    Width =1200\r\n                    Height =300\r\n                    BackColor =-2147483598\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label25\"\r\n                    Caption =\" \"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =11520\r\n                    LayoutCachedTop =2760\r\n                    LayoutCachedWidth =12720\r\n                    LayoutCachedHeight =3060\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin TextBox\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =10080\r\n                    Top =2280\r\n                    Width =1260\r\n                    Height =315\r\n                    TabIndex =4\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"Text28\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =10080\r\n                    LayoutCachedTop =2280\r\n                    LayoutCachedWidth =11340\r\n                    LayoutCachedHeight =2595\r\n                End\r\n                Begin TextBox\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =5760\r\n                    Top =2280\r\n                    Width =1200\r\n                    Height =315\r\n                    TabIndex =5\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"Text30\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =5760\r\n                    LayoutCachedTop =2280\r\n                    LayoutCachedWidth =6960\r\n                    LayoutCachedHeight =2595\r\n                    BackThemeColorIndex =5\r\n                    BackTint =60.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =5760\r\n                    Top =1620\r\n                    Width =1260\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label31\"\r\n                    Caption =\"Shade\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =5760\r\n                    LayoutCachedTop =1620\r\n                    LayoutCachedWidth =7020\r\n                    LayoutCachedHeight =1980\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =5760\r\n                    Top =2760\r\n                    Width =1200\r\n                    Height =300\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label32\"\r\n                    Caption =\" \"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =5760\r\n                    LayoutCachedTop =2760\r\n                    LayoutCachedWidth =6960\r\n                    LayoutCachedHeight =3060\r\n                    BackThemeColorIndex =5\r\n                    BackTint =60.0\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =4320\r\n                    Top =3240\r\n                    Width =1200\r\n                    TabIndex =6\r\n                    ForeColor =4210752\r\n                    Name =\"Command33\"\r\n                    Caption =\"Command33\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =4320\r\n                    LayoutCachedTop =3240\r\n                    LayoutCachedWidth =5520\r\n                    LayoutCachedHeight =3600\r\n                    Gradient =0\r\n                    BackThemeColorIndex =5\r\n                    BackTint =100.0\r\n                    BorderColor =11710639\r\n                    HoverColor =13355721\r\n                    PressedColor =6249563\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =5760\r\n                    Top =3240\r\n                    Width =1200\r\n                    TabIndex =7\r\n                    ForeColor =4210752\r\n                    Name =\"Command34\"\r\n                    Caption =\"Command33\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =5760\r\n                    LayoutCachedTop =3240\r\n                    LayoutCachedWidth =6960\r\n                    LayoutCachedHeight =3600\r\n                    Gradient =0\r\n                    BackThemeColorIndex =5\r\n                    BorderColor =11710639\r\n                    HoverColor =13355721\r\n                    PressedColor =6249563\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =7200\r\n                    Top =3240\r\n                    Width =1200\r\n                    TabIndex =8\r\n                    ForeColor =4210752\r\n                    Name =\"Command35\"\r\n                    Caption =\"Command33\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =7200\r\n                    LayoutCachedTop =3240\r\n                    LayoutCachedWidth =8400\r\n                    LayoutCachedHeight =3600\r\n                    Gradient =0\r\n                    BackColor =5676533\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =11710639\r\n                    HoverColor =13355721\r\n                    PressedColor =6249563\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =8640\r\n                    Top =3240\r\n                    Width =1200\r\n                    TabIndex =9\r\n                    ForeColor =4210752\r\n                    Name =\"Command36\"\r\n                    Caption =\"Command33\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =8640\r\n                    LayoutCachedTop =3240\r\n                    LayoutCachedWidth =9840\r\n                    LayoutCachedHeight =3600\r\n                    Gradient =0\r\n                    BackColor =3439082\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =11710639\r\n                    HoverColor =13355721\r\n                    PressedColor =6249563\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =10080\r\n                    Top =3240\r\n                    Width =1200\r\n                    TabIndex =10\r\n                    ForeColor =4210752\r\n                    Name =\"Command37\"\r\n                    Caption =\"Command33\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =10080\r\n                    LayoutCachedTop =3240\r\n                    LayoutCachedWidth =11280\r\n                    LayoutCachedHeight =3600\r\n                    BackColor =11710639\r\n                    BorderColor =11710639\r\n                    HoverColor =13355721\r\n                    PressedColor =6249563\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =11520\r\n                    Top =3240\r\n                    Width =1200\r\n                    TabIndex =11\r\n                    ForeColor =4210752\r\n                    Name =\"Command38\"\r\n                    Caption =\"Command33\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =11520\r\n                    LayoutCachedTop =3240\r\n                    LayoutCachedWidth =12720\r\n                    LayoutCachedHeight =3600\r\n                    Gradient =0\r\n                    BackColor =-2147483598\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =11710639\r\n                    HoverColor =13355721\r\n                    PressedColor =6249563\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                End\r\n                Begin Rectangle\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    Left =4320\r\n                    Top =3780\r\n                    Width =1200\r\n                    Height =360\r\n                    BorderColor =10921638\r\n                    Name =\"Box41\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =4320\r\n                    LayoutCachedTop =3780\r\n                    LayoutCachedWidth =5520\r\n                    LayoutCachedHeight =4140\r\n                    BackThemeColorIndex =5\r\n                End\r\n                Begin Rectangle\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    Left =5760\r\n                    Top =3780\r\n                    Width =1200\r\n                    Height =360\r\n                    BorderColor =10921638\r\n                    Name =\"Box42\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =5760\r\n                    LayoutCachedTop =3780\r\n                    LayoutCachedWidth =6960\r\n                    LayoutCachedHeight =4140\r\n                    BackThemeColorIndex =5\r\n                    BackTint =60.0\r\n                End\r\n                Begin Rectangle\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    Left =7200\r\n                    Top =3780\r\n                    Width =1200\r\n                    Height =360\r\n                    BackColor =5676533\r\n                    BorderColor =10921638\r\n                    Name =\"Box43\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =7200\r\n                    LayoutCachedTop =3780\r\n                    LayoutCachedWidth =8400\r\n                    LayoutCachedHeight =4140\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Rectangle\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    Left =8640\r\n                    Top =3780\r\n                    Width =1200\r\n                    Height =360\r\n                    BackColor =3439082\r\n                    BorderColor =10921638\r\n                    Name =\"Box44\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =8640\r\n                    LayoutCachedTop =3780\r\n                    LayoutCachedWidth =9840\r\n                    LayoutCachedHeight =4140\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Rectangle\r\n                    OverlapFlags =85\r\n                    Left =10080\r\n                    Top =3780\r\n                    Width =1200\r\n                    Height =360\r\n                    BorderColor =10921638\r\n                    Name =\"Box45\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =10080\r\n                    LayoutCachedTop =3780\r\n                    LayoutCachedWidth =11280\r\n                    LayoutCachedHeight =4140\r\n                End\r\n                Begin Rectangle\r\n                    BackStyle =1\r\n                    OverlapFlags =85\r\n                    Left =11520\r\n                    Top =3780\r\n                    Width =1200\r\n                    Height =360\r\n                    BackColor =-2147483598\r\n                    BorderColor =10921638\r\n                    Name =\"Box46\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =11520\r\n                    LayoutCachedTop =3780\r\n                    LayoutCachedWidth =12720\r\n                    LayoutCachedHeight =4140\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =600\r\n                    Top =4860\r\n                    Width =1860\r\n                    Height =360\r\n                    BorderColor =8355711\r\n                    Name =\"Label48\"\r\n                    Caption =\"Screenshot Image\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =600\r\n                    LayoutCachedTop =4860\r\n                    LayoutCachedWidth =2460\r\n                    LayoutCachedHeight =5220\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Image\r\n                    PictureType =2\r\n                    Left =2760\r\n                    Top =4620\r\n                    Width =10245\r\n                    Height =2190\r\n                    BorderColor =10921638\r\n                    Name =\"Image49\"\r\n                    Picture =\"Colors\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =2760\r\n                    LayoutCachedTop =4620\r\n                    LayoutCachedWidth =13005\r\n                    LayoutCachedHeight =6810\r\n                    TabIndex =12\r\n                End\r\n            End\r\n        End\r\n        Begin FormFooter\r\n            Height =660\r\n            Name =\"FormFooter\"\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmColors.cls\"\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/forms/frmColors.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/forms/frmExtendedChars.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =8884\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =2\r\n    Right =25320\r\n    Bottom =14385\r\n    RecSrcDt = Begin\r\n        0xbd57868e6cf5e540\r\n    End\r\n    DatasheetFontName =\"Franklin Gothic Book\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =7560\r\n            Name =\"Detail\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =1080\r\n                    Top =1140\r\n                    Width =3420\r\n                    Height =600\r\n                    FontSize =18\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label0\"\r\n                    Caption =\"Extëndëd Cháráctërs\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =1080\r\n                    LayoutCachedTop =1140\r\n                    LayoutCachedWidth =4500\r\n                    LayoutCachedHeight =1740\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =1080\r\n                    Top =2220\r\n                    Width =3420\r\n                    Height =600\r\n                    FontSize =18\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label1\"\r\n                    Caption =\"Unicode 👍 🚀 ☺\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =1080\r\n                    LayoutCachedTop =2220\r\n                    LayoutCachedWidth =4500\r\n                    LayoutCachedHeight =2820\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmExtendedChars.cls\"\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/forms/frmExtendedChars.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'——————————————————————————————————————————————————————————————————————————————————————————¬\r\n' This module is used to prove that all ASCII characters survive the export\\import cycle.  |\r\n'——————————————————————————————————————————————————————————————————————————————————————————+\r\n\r\n' ASCII Table Using Windows-1252 codepage\r\n'  Table generated using https://ozh.github.io/ascii-tables/\r\n'\r\n' +=====+=====+==========+=======+\r\n' | Dec | Hex |  Binary  | Char  |\r\n' +=====+=====+==========+=======+\r\n' |   0 | 00  | 00000000 | NUL   |\r\n' +-----+-----+----------+-------+\r\n' |   1 | 01  | 00000001 | SOH   |\r\n' +-----+-----+----------+-------+\r\n' |   2 | 02  | 00000010 | STX   |\r\n' +-----+-----+----------+-------+\r\n' |   3 | 03  | 00000011 | ETX   |\r\n' +-----+-----+----------+-------+\r\n' |   4 | 04  | 00000100 | EOT   |\r\n' +-----+-----+----------+-------+\r\n' |   5 | 05  | 00000101 | ENQ   |\r\n' +-----+-----+----------+-------+\r\n' |   6 | 06  | 00000110 | ACK   |\r\n' +-----+-----+----------+-------+\r\n' |   7 | 07  | 00000111 | BEL   |\r\n' +-----+-----+----------+-------+\r\n' |   8 | 08  | 00001000 | BS    |\r\n' +-----+-----+----------+-------+\r\n' |   9 | 09  | 00001001 | HT    |\r\n' +-----+-----+----------+-------+\r\n' |  10 | 0A  | 00001010 | LF    |\r\n' +-----+-----+----------+-------+\r\n' |  11 | 0B  | 00001011 | VT    |\r\n' +-----+-----+----------+-------+\r\n' |  12 | 0C  | 00001100 | FF    |\r\n' +-----+-----+----------+-------+\r\n' |  13 | 0D  | 00001101 | CR    |\r\n' +-----+-----+----------+-------+\r\n' |  14 | 0E  | 00001110 | SO    |\r\n' +-----+-----+----------+-------+\r\n' |  15 | 0F  | 00001111 | SI    |\r\n' +-----+-----+----------+-------+\r\n' |  16 | 10  | 00010000 | DLE   |\r\n' +-----+-----+----------+-------+\r\n' |  17 | 11  | 00010001 | DC1   |\r\n' +-----+-----+----------+-------+\r\n' |  18 | 12  | 00010010 | DC2   |\r\n' +-----+-----+----------+-------+\r\n' |  19 | 13  | 00010011 | DC3   |\r\n' +-----+-----+----------+-------+\r\n' |  20 | 14  | 00010100 | DC4   |\r\n' +-----+-----+----------+-------+\r\n' |  21 | 15  | 00010101 | NAK   |\r\n' +-----+-----+----------+-------+\r\n' |  22 | 16  | 00010110 | SYN   |\r\n' +-----+-----+----------+-------+\r\n' |  23 | 17  | 00010111 | ETB   |\r\n' +-----+-----+----------+-------+\r\n' |  24 | 18  | 00011000 | CAN   |\r\n' +-----+-----+----------+-------+\r\n' |  25 | 19  | 00011001 | EM    |\r\n' +-----+-----+----------+-------+\r\n' |  26 | 1A  | 00011010 | SUB   |\r\n' +-----+-----+----------+-------+\r\n' |  27 | 1B  | 00011011 | ESC   |\r\n' +-----+-----+----------+-------+\r\n' |  28 | 1C  | 00011100 | FS    |\r\n' +-----+-----+----------+-------+\r\n' |  29 | 1D  | 00011101 | GS    |\r\n' +-----+-----+----------+-------+\r\n' |  30 | 1E  | 00011110 | RS    |\r\n' +-----+-----+----------+-------+\r\n' |  31 | 1F  | 00011111 | US    |\r\n' +-----+-----+----------+-------+\r\n' |  32 | 20  | 00100000 | space |\r\n' +-----+-----+----------+-------+\r\n' |  33 | 21  | 00100001 | !     |\r\n' +-----+-----+----------+-------+\r\n' |  34 | 22  | 00100010 | \"     |\r\n' +-----+-----+----------+-------+\r\n' |  35 | 23  | 00100011 | #     |\r\n' +-----+-----+----------+-------+\r\n' |  36 | 24  | 00100100 | $     |\r\n' +-----+-----+----------+-------+\r\n' |  37 | 25  | 00100101 | %     |\r\n' +-----+-----+----------+-------+\r\n' |  38 | 26  | 00100110 | &     |\r\n' +-----+-----+----------+-------+\r\n' |  39 | 27  | 00100111 | '     |\r\n' +-----+-----+----------+-------+\r\n' |  40 | 28  | 00101000 | (     |\r\n' +-----+-----+----------+-------+\r\n' |  41 | 29  | 00101001 | )     |\r\n' +-----+-----+----------+-------+\r\n' |  42 | 2A  | 00101010 | *     |\r\n' +-----+-----+----------+-------+\r\n' |  43 | 2B  | 00101011 | +     |\r\n' +-----+-----+----------+-------+\r\n' |  44 | 2C  | 00101100 | ,     |\r\n' +-----+-----+----------+-------+\r\n' |  45 | 2D  | 00101101 | -     |\r\n' +-----+-----+----------+-------+\r\n' |  46 | 2E  | 00101110 | .     |\r\n' +-----+-----+----------+-------+\r\n' |  47 | 2F  | 00101111 | /     |\r\n' +-----+-----+----------+-------+\r\n' |  48 | 30  | 00110000 | 0     |\r\n' +-----+-----+----------+-------+\r\n' |  49 | 31  | 00110001 | 1     |\r\n' +-----+-----+----------+-------+\r\n' |  50 | 32  | 00110010 | 2     |\r\n' +-----+-----+----------+-------+\r\n' |  51 | 33  | 00110011 | 3     |\r\n' +-----+-----+----------+-------+\r\n' |  52 | 34  | 00110100 | 4     |\r\n' +-----+-----+----------+-------+\r\n' |  53 | 35  | 00110101 | 5     |\r\n' +-----+-----+----------+-------+\r\n' |  54 | 36  | 00110110 | 6     |\r\n' +-----+-----+----------+-------+\r\n' |  55 | 37  | 00110111 | 7     |\r\n' +-----+-----+----------+-------+\r\n' |  56 | 38  | 00111000 | 8     |\r\n' +-----+-----+----------+-------+\r\n' |  57 | 39  | 00111001 | 9     |\r\n' +-----+-----+----------+-------+\r\n' |  58 | 3A  | 00111010 | :     |\r\n' +-----+-----+----------+-------+\r\n' |  59 | 3B  | 00111011 | ;     |\r\n' +-----+-----+----------+-------+\r\n' |  60 | 3C  | 00111100 | <     |\r\n' +-----+-----+----------+-------+\r\n' |  61 | 3D  | 00111101 | =     |\r\n' +-----+-----+----------+-------+\r\n' |  62 | 3E  | 00111110 | >     |\r\n' +-----+-----+----------+-------+\r\n' |  63 | 3F  | 00111111 | ?     |\r\n' +-----+-----+----------+-------+\r\n' |  64 | 40  | 01000000 | @     |\r\n' +-----+-----+----------+-------+\r\n' |  65 | 41  | 01000001 | A     |\r\n' +-----+-----+----------+-------+\r\n' |  66 | 42  | 01000010 | B     |\r\n' +-----+-----+----------+-------+\r\n' |  67 | 43  | 01000011 | C     |\r\n' +-----+-----+----------+-------+\r\n' |  68 | 44  | 01000100 | D     |\r\n' +-----+-----+----------+-------+\r\n' |  69 | 45  | 01000101 | E     |\r\n' +-----+-----+----------+-------+\r\n' |  70 | 46  | 01000110 | F     |\r\n' +-----+-----+----------+-------+\r\n' |  71 | 47  | 01000111 | G     |\r\n' +-----+-----+----------+-------+\r\n' |  72 | 48  | 01001000 | H     |\r\n' +-----+-----+----------+-------+\r\n' |  73 | 49  | 01001001 | I     |\r\n' +-----+-----+----------+-------+\r\n' |  74 | 4A  | 01001010 | J     |\r\n' +-----+-----+----------+-------+\r\n' |  75 | 4B  | 01001011 | K     |\r\n' +-----+-----+----------+-------+\r\n' |  76 | 4C  | 01001100 | L     |\r\n' +-----+-----+----------+-------+\r\n' |  77 | 4D  | 01001101 | M     |\r\n' +-----+-----+----------+-------+\r\n' |  78 | 4E  | 01001110 | N     |\r\n' +-----+-----+----------+-------+\r\n' |  79 | 4F  | 01001111 | O     |\r\n' +-----+-----+----------+-------+\r\n' |  80 | 50  | 01010000 | P     |\r\n' +-----+-----+----------+-------+\r\n' |  81 | 51  | 01010001 | Q     |\r\n' +-----+-----+----------+-------+\r\n' |  82 | 52  | 01010010 | R     |\r\n' +-----+-----+----------+-------+\r\n' |  83 | 53  | 01010011 | S     |\r\n' +-----+-----+----------+-------+\r\n' |  84 | 54  | 01010100 | T     |\r\n' +-----+-----+----------+-------+\r\n' |  85 | 55  | 01010101 | U     |\r\n' +-----+-----+----------+-------+\r\n' |  86 | 56  | 01010110 | V     |\r\n' +-----+-----+----------+-------+\r\n' |  87 | 57  | 01010111 | W     |\r\n' +-----+-----+----------+-------+\r\n' |  88 | 58  | 01011000 | X     |\r\n' +-----+-----+----------+-------+\r\n' |  89 | 59  | 01011001 | Y     |\r\n' +-----+-----+----------+-------+\r\n' |  90 | 5A  | 01011010 | Z     |\r\n' +-----+-----+----------+-------+\r\n' |  91 | 5B  | 01011011 | [     |\r\n' +-----+-----+----------+-------+\r\n' |  92 | 5C  | 01011100 | \\     |\r\n' +-----+-----+----------+-------+\r\n' |  93 | 5D  | 01011101 | ]     |\r\n' +-----+-----+----------+-------+\r\n' |  94 | 5E  | 01011110 | ^     |\r\n' +-----+-----+----------+-------+\r\n' |  95 | 5F  | 01011111 | _     |\r\n' +-----+-----+----------+-------+\r\n' |  96 | 60  | 01100000 | `     |\r\n' +-----+-----+----------+-------+\r\n' |  97 | 61  | 01100001 | a     |\r\n' +-----+-----+----------+-------+\r\n' |  98 | 62  | 01100010 | b     |\r\n' +-----+-----+----------+-------+\r\n' |  99 | 63  | 01100011 | c     |\r\n' +-----+-----+----------+-------+\r\n' | 100 | 64  | 01100100 | d     |\r\n' +-----+-----+----------+-------+\r\n' | 101 | 65  | 01100101 | e     |\r\n' +-----+-----+----------+-------+\r\n' | 102 | 66  | 01100110 | f     |\r\n' +-----+-----+----------+-------+\r\n' | 103 | 67  | 01100111 | g     |\r\n' +-----+-----+----------+-------+\r\n' | 104 | 68  | 01101000 | h     |\r\n' +-----+-----+----------+-------+\r\n' | 105 | 69  | 01101001 | i     |\r\n' +-----+-----+----------+-------+\r\n' | 106 | 6A  | 01101010 | j     |\r\n' +-----+-----+----------+-------+\r\n' | 107 | 6B  | 01101011 | k     |\r\n' +-----+-----+----------+-------+\r\n' | 108 | 6C  | 01101100 | l     |\r\n' +-----+-----+----------+-------+\r\n' | 109 | 6D  | 01101101 | m     |\r\n' +-----+-----+----------+-------+\r\n' | 110 | 6E  | 01101110 | n     |\r\n' +-----+-----+----------+-------+\r\n' | 111 | 6F  | 01101111 | o     |\r\n' +-----+-----+----------+-------+\r\n' | 112 | 70  | 01110000 | p     |\r\n' +-----+-----+----------+-------+\r\n' | 113 | 71  | 01110001 | q     |\r\n' +-----+-----+----------+-------+\r\n' | 114 | 72  | 01110010 | r     |\r\n' +-----+-----+----------+-------+\r\n' | 115 | 73  | 01110011 | s     |\r\n' +-----+-----+----------+-------+\r\n' | 116 | 74  | 01110100 | t     |\r\n' +-----+-----+----------+-------+\r\n' | 117 | 75  | 01110101 | u     |\r\n' +-----+-----+----------+-------+\r\n' | 118 | 76  | 01110110 | v     |\r\n' +-----+-----+----------+-------+\r\n' | 119 | 77  | 01110111 | w     |\r\n' +-----+-----+----------+-------+\r\n' | 120 | 78  | 01111000 | x     |\r\n' +-----+-----+----------+-------+\r\n' | 121 | 79  | 01111001 | y     |\r\n' +-----+-----+----------+-------+\r\n' | 122 | 7A  | 01111010 | z     |\r\n' +-----+-----+----------+-------+\r\n' | 123 | 7B  | 01111011 | {     |\r\n' +-----+-----+----------+-------+\r\n' | 124 | 7C  | 01111100 | |     |\r\n' +-----+-----+----------+-------+\r\n' | 125 | 7D  | 01111101 | }     |\r\n' +-----+-----+----------+-------+\r\n' | 126 | 7E  | 01111110 | ~     |\r\n' +-----+-----+----------+-------+\r\n' | 127 | 7F  | 01111111 | DEL   |\r\n' +-----+-----+----------+-------+\r\n' | 128 | 80  | 10000000 | €     |\r\n' +-----+-----+----------+-------+\r\n' | 129 | 81  | 10000001 |       |\r\n' +-----+-----+----------+-------+\r\n' | 130 | 82  | 10000010 | ‚     |\r\n' +-----+-----+----------+-------+\r\n' | 131 | 83  | 10000011 | ƒ     |\r\n' +-----+-----+----------+-------+\r\n' | 132 | 84  | 10000100 | „     |\r\n' +-----+-----+----------+-------+\r\n' | 133 | 85  | 10000101 | …     |\r\n' +-----+-----+----------+-------+\r\n' | 134 | 86  | 10000110 | †     |\r\n' +-----+-----+----------+-------+\r\n' | 135 | 87  | 10000111 | ‡     |\r\n' +-----+-----+----------+-------+\r\n' | 136 | 88  | 10001000 | ˆ     |\r\n' +-----+-----+----------+-------+\r\n' | 137 | 89  | 10001001 | ‰     |\r\n' +-----+-----+----------+-------+\r\n' | 138 | 8A  | 10001010 | Š     |\r\n' +-----+-----+----------+-------+\r\n' | 139 | 8B  | 10001011 | ‹     |\r\n' +-----+-----+----------+-------+\r\n' | 140 | 8C  | 10001100 | Œ     |\r\n' +-----+-----+----------+-------+\r\n' | 141 | 8D  | 10001101 |       |\r\n' +-----+-----+----------+-------+\r\n' | 142 | 8E  | 10001110 | Ž     |\r\n' +-----+-----+----------+-------+\r\n' | 143 | 8F  | 10001111 |       |\r\n' +-----+-----+----------+-------+\r\n' | 144 | 90  | 10010000 |       |\r\n' +-----+-----+----------+-------+\r\n' | 145 | 91  | 10010001 | ‘     |\r\n' +-----+-----+----------+-------+\r\n' | 146 | 92  | 10010010 | ’     |\r\n' +-----+-----+----------+-------+\r\n' | 147 | 93  | 10010011 | “     |\r\n' +-----+-----+----------+-------+\r\n' | 148 | 94  | 10010100 | ”     |\r\n' +-----+-----+----------+-------+\r\n' | 149 | 95  | 10010101 | •     |\r\n' +-----+-----+----------+-------+\r\n' | 150 | 96  | 10010110 | –     |\r\n' +-----+-----+----------+-------+\r\n' | 151 | 97  | 10010111 | —     |\r\n' +-----+-----+----------+-------+\r\n' | 152 | 98  | 10011000 | ˜      |\r\n' +-----+-----+----------+-------+\r\n' | 153 | 99  | 10011001 | ™     |\r\n' +-----+-----+----------+-------+\r\n' | 154 | 9A  | 10011010 | š     |\r\n' +-----+-----+----------+-------+\r\n' | 155 | 9B  | 10011011 | ›     |\r\n' +-----+-----+----------+-------+\r\n' | 156 | 9C  | 10011100 | œ     |\r\n' +-----+-----+----------+-------+\r\n' | 157 | 9D  | 10011101 |       |\r\n' +-----+-----+----------+-------+\r\n' | 158 | 9E  | 10011110 | ž     |\r\n' +-----+-----+----------+-------+\r\n' | 159 | 9F  | 10011111 | Ÿ     |\r\n' +-----+-----+----------+-------+\r\n' | 160 | A0  | 10100000 |       |\r\n' +-----+-----+----------+-------+\r\n' | 161 | A1  | 10100001 | ¡     |\r\n' +-----+-----+----------+-------+\r\n' | 162 | A2  | 10100010 | ¢     |\r\n' +-----+-----+----------+-------+\r\n' | 163 | A3  | 10100011 | £     |\r\n' +-----+-----+----------+-------+\r\n' | 164 | A4  | 10100100 | ¤     |\r\n' +-----+-----+----------+-------+\r\n' | 165 | A5  | 10100101 | ¥     |\r\n' +-----+-----+----------+-------+\r\n' | 166 | A6  | 10100110 | ¦     |\r\n' +-----+-----+----------+-------+\r\n' | 167 | A7  | 10100111 | §     |\r\n' +-----+-----+----------+-------+\r\n' | 168 | A8  | 10101000 | ¨     |\r\n' +-----+-----+----------+-------+\r\n' | 169 | A9  | 10101001 | ©     |\r\n' +-----+-----+----------+-------+\r\n' | 170 | AA  | 10101010 | ª     |\r\n' +-----+-----+----------+-------+\r\n' | 171 | AB  | 10101011 | «     |\r\n' +-----+-----+----------+-------+\r\n' | 172 | AC  | 10101100 | ¬     |\r\n' +-----+-----+----------+-------+\r\n' | 173 | AD  | 10101101 | ­     |\r\n' +-----+-----+----------+-------+\r\n' | 174 | AE  | 10101110 | ®     |\r\n' +-----+-----+----------+-------+\r\n' | 175 | AF  | 10101111 | ¯     |\r\n' +-----+-----+----------+-------+\r\n' | 176 | B0  | 10110000 | °     |\r\n' +-----+-----+----------+-------+\r\n' | 177 | B1  | 10110001 | ±     |\r\n' +-----+-----+----------+-------+\r\n' | 178 | B2  | 10110010 | ²     |\r\n' +-----+-----+----------+-------+\r\n' | 179 | B3  | 10110011 | ³     |\r\n' +-----+-----+----------+-------+\r\n' | 180 | B4  | 10110100 | ´     |\r\n' +-----+-----+----------+-------+\r\n' | 181 | B5  | 10110101 | µ     |\r\n' +-----+-----+----------+-------+\r\n' | 182 | B6  | 10110110 | ¶     |\r\n' +-----+-----+----------+-------+\r\n' | 183 | B7  | 10110111 | ·     |\r\n' +-----+-----+----------+-------+\r\n' | 184 | B8  | 10111000 | ¸     |\r\n' +-----+-----+----------+-------+\r\n' | 185 | B9  | 10111001 | ¹     |\r\n' +-----+-----+----------+-------+\r\n' | 186 | BA  | 10111010 | º     |\r\n' +-----+-----+----------+-------+\r\n' | 187 | BB  | 10111011 | »     |\r\n' +-----+-----+----------+-------+\r\n' | 188 | BC  | 10111100 | ¼     |\r\n' +-----+-----+----------+-------+\r\n' | 189 | BD  | 10111101 | ½     |\r\n' +-----+-----+----------+-------+\r\n' | 190 | BE  | 10111110 | ¾     |\r\n' +-----+-----+----------+-------+\r\n' | 191 | BF  | 10111111 | ¿     |\r\n' +-----+-----+----------+-------+\r\n' | 192 | C0  | 11000000 | À     |\r\n' +-----+-----+----------+-------+\r\n' | 193 | C1  | 11000001 | Á     |\r\n' +-----+-----+----------+-------+\r\n' | 194 | C2  | 11000010 | Â     |\r\n' +-----+-----+----------+-------+\r\n' | 195 | C3  | 11000011 | Ã     |\r\n' +-----+-----+----------+-------+\r\n' | 196 | C4  | 11000100 | Ä     |\r\n' +-----+-----+----------+-------+\r\n' | 197 | C5  | 11000101 | Å     |\r\n' +-----+-----+----------+-------+\r\n' | 198 | C6  | 11000110 | Æ     |\r\n' +-----+-----+----------+-------+\r\n' | 199 | C7  | 11000111 | Ç     |\r\n' +-----+-----+----------+-------+\r\n' | 200 | C8  | 11001000 | È     |\r\n' +-----+-----+----------+-------+\r\n' | 201 | C9  | 11001001 | É     |\r\n' +-----+-----+----------+-------+\r\n' | 202 | CA  | 11001010 | Ê     |\r\n' +-----+-----+----------+-------+\r\n' | 203 | CB  | 11001011 | Ë     |\r\n' +-----+-----+----------+-------+\r\n' | 204 | CC  | 11001100 | Ì     |\r\n' +-----+-----+----------+-------+\r\n' | 205 | CD  | 11001101 | Í     |\r\n' +-----+-----+----------+-------+\r\n' | 206 | CE  | 11001110 | Î     |\r\n' +-----+-----+----------+-------+\r\n' | 207 | CF  | 11001111 | Ï     |\r\n' +-----+-----+----------+-------+\r\n' | 208 | D0  | 11010000 | Ð     |\r\n' +-----+-----+----------+-------+\r\n' | 209 | D1  | 11010001 | Ñ     |\r\n' +-----+-----+----------+-------+\r\n' | 210 | D2  | 11010010 | Ò     |\r\n' +-----+-----+----------+-------+\r\n' | 211 | D3  | 11010011 | Ó     |\r\n' +-----+-----+----------+-------+\r\n' | 212 | D4  | 11010100 | Ô     |\r\n' +-----+-----+----------+-------+\r\n' | 213 | D5  | 11010101 | Õ     |\r\n' +-----+-----+----------+-------+\r\n' | 214 | D6  | 11010110 | Ö     |\r\n' +-----+-----+----------+-------+\r\n' | 215 | D7  | 11010111 | ×     |\r\n' +-----+-----+----------+-------+\r\n' | 216 | D8  | 11011000 | Ø     |\r\n' +-----+-----+----------+-------+\r\n' | 217 | D9  | 11011001 | Ù     |\r\n' +-----+-----+----------+-------+\r\n' | 218 | DA  | 11011010 | Ú     |\r\n' +-----+-----+----------+-------+\r\n' | 219 | DB  | 11011011 | Û     |\r\n' +-----+-----+----------+-------+\r\n' | 220 | DC  | 11011100 | Ü     |\r\n' +-----+-----+----------+-------+\r\n' | 221 | DD  | 11011101 | Ý     |\r\n' +-----+-----+----------+-------+\r\n' | 222 | DE  | 11011110 | Þ     |\r\n' +-----+-----+----------+-------+\r\n' | 223 | DF  | 11011111 | ß     |\r\n' +-----+-----+----------+-------+\r\n' | 224 | E0  | 11100000 | à     |\r\n' +-----+-----+----------+-------+\r\n' | 225 | E1  | 11100001 | á     |\r\n' +-----+-----+----------+-------+\r\n' | 226 | E2  | 11100010 | â     |\r\n' +-----+-----+----------+-------+\r\n' | 227 | E3  | 11100011 | ã     |\r\n' +-----+-----+----------+-------+\r\n' | 228 | E4  | 11100100 | ä     |\r\n' +-----+-----+----------+-------+\r\n' | 229 | E5  | 11100101 | å     |\r\n' +-----+-----+----------+-------+\r\n' | 230 | E6  | 11100110 | æ     |\r\n' +-----+-----+----------+-------+\r\n' | 231 | E7  | 11100111 | ç     |\r\n' +-----+-----+----------+-------+\r\n' | 232 | E8  | 11101000 | è     |\r\n' +-----+-----+----------+-------+\r\n' | 233 | E9  | 11101001 | é     |\r\n' +-----+-----+----------+-------+\r\n' | 234 | EA  | 11101010 | ê     |\r\n' +-----+-----+----------+-------+\r\n' | 235 | EB  | 11101011 | ë     |\r\n' +-----+-----+----------+-------+\r\n' | 236 | EC  | 11101100 | ì     |\r\n' +-----+-----+----------+-------+\r\n' | 237 | ED  | 11101101 | í     |\r\n' +-----+-----+----------+-------+\r\n' | 238 | EE  | 11101110 | î     |\r\n' +-----+-----+----------+-------+\r\n' | 239 | EF  | 11101111 | ï     |\r\n' +-----+-----+----------+-------+\r\n' | 240 | F0  | 11110000 | ð     |\r\n' +-----+-----+----------+-------+\r\n' | 241 | F1  | 11110001 | ñ     |\r\n' +-----+-----+----------+-------+\r\n' | 242 | F2  | 11110010 | ò     |\r\n' +-----+-----+----------+-------+\r\n' | 243 | F3  | 11110011 | ó     |\r\n' +-----+-----+----------+-------+\r\n' | 244 | F4  | 11110100 | ô     |\r\n' +-----+-----+----------+-------+\r\n' | 245 | F5  | 11110101 | õ     |\r\n' +-----+-----+----------+-------+\r\n' | 246 | F6  | 11110110 | ö     |\r\n' +-----+-----+----------+-------+\r\n' | 247 | F7  | 11110111 | ÷     |\r\n' +-----+-----+----------+-------+\r\n' | 248 | F8  | 11111000 | ø     |\r\n' +-----+-----+----------+-------+\r\n' | 249 | F9  | 11111001 | ù     |\r\n' +-----+-----+----------+-------+\r\n' | 250 | FA  | 11111010 | ú     |\r\n' +-----+-----+----------+-------+\r\n' | 251 | FB  | 11111011 | û     |\r\n' +-----+-----+----------+-------+\r\n' | 252 | FC  | 11111100 | ü     |\r\n' +-----+-----+----------+-------+\r\n' | 253 | FD  | 11111101 | ý     |\r\n' +-----+-----+----------+-------+\r\n' | 254 | FE  | 11111110 | þ     |\r\n' +-----+-----+----------+-------+\r\n' | 255 | FF  | 11111111 | ÿ     |\r\n' +-----+-----+----------+-------+\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/forms/frmMain.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    RecordSelectors = NotDefault\r\n    NavigationButtons = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    TabularFamily =18\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =9360\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =13\r\n    Right =25320\r\n    Bottom =12465\r\n    RecSrcDt = Begin\r\n        0x18691eff0b76e540\r\n    End\r\n    Caption =\"Main Form\"\r\n    DatasheetFontName =\"Palatino Linotype\"\r\n    OnLoad =\"[Event Procedure]\"\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    ThemeName =\"Executive\"\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Image\r\n            BackStyle =0\r\n            OldBorderStyle =0\r\n            BorderLineStyle =0\r\n            SizeMode =3\r\n            PictureAlignment =2\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CommandButton\r\n            FontSize =11\r\n            FontWeight =400\r\n            FontName =\"Calibri\"\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =1\r\n            Gradient =12\r\n            BackThemeColorIndex =4\r\n            BackTint =60.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =4\r\n            BorderTint =60.0\r\n            ThemeFontIndex =1\r\n            HoverThemeColorIndex =4\r\n            HoverTint =40.0\r\n            PressedThemeColorIndex =4\r\n            PressedShade =75.0\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n        End\r\n        Begin ListBox\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AllowValueListEdits =1\r\n            InheritValueList =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin UnboundObjectFrame\r\n            SpecialEffect =2\r\n            OldBorderStyle =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =2\r\n            ForeShade =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CustomControl\r\n            OldBorderStyle =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =7620\r\n            Name =\"Detail\"\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =18\r\n                    Left =5400\r\n                    Top =2940\r\n                    Width =3120\r\n                    Height =720\r\n                    ForeColor =4210752\r\n                    Name =\"cmdRunTests\"\r\n                    Caption =\"  Verify Database Objects\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    FontName =\"Palatino Linotype\"\r\n                    GridlineColor =10921638\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x0000000000000000b0a090ff604830ff604830ff604830ff604830ff604830ff ,\r\n                        0x604830ff604830ff604830ff604830ff604830ff000000000000000000000000 ,\r\n                        0x0000000000000000b0a090fffff8f0ffe0e0e0ffe0d8d0ffe0d8d0ffe0d0c0ff ,\r\n                        0xe0c8c0ffd0c0b0ffd0c0b0ffd0b8a0ff604830ff000000000000000000000000 ,\r\n                        0x0000000000000000b0a090ffffffffffd0b8b0ffd0b8a0ffd0b0a0ffb0a090ff ,\r\n                        0x604830ff604830ff604830ff604830ff604830ff604830ff604830ff604830ff ,\r\n                        0x604830ff604830ffb0a090fffffffffffffffffffff8f0fffff0f0ffb0a090ff ,\r\n                        0xfff8f0ffe0e0e0ffe0d8d0ffe0d8d0ffe0d0c0ffe0c8c0ffd0c0b0ffd0c0b0ff ,\r\n                        0xd0b8a0ff604830ffc0a8a0ffffffffffe0c8c0ffe0c8c0ffd0c0b0ffb0a090ff ,\r\n                        0xffffffffd0b8b0ffd0b8a0ffd0b0a0ffffe8e0ffc09080ffc09080ffc09080ff ,\r\n                        0xd0b8b0ff604830ffc0b0a0ffffffffffb0a090ff604830ff604830ffb0a090ff ,\r\n                        0xfffffffffffffffffff8f0fffff0f0fffff0e0ffffe8e0ffffe0d0ffffd8d0ff ,\r\n                        0xd0c0b0ff604830ffd0b0a0ffffffffffb0a090fffff8f0ffe0e0e0ffc0a8a0ff ,\r\n                        0xffffffffe0c8c0ffe0c8c0ffd0c0b0fffff8f0ffc0a890ffc0a890ffc0a090ff ,\r\n                        0xe0d0c0ff604830ffd0b8a0ffffffffffb0a090ffffffffffd0b8b0ffc0b0a0ff ,\r\n                        0xfffffffffffffffffffffffffffffffffffffffffff8f0fffff0f0ffffe8e0ff ,\r\n                        0xe0d8d0ff604830fff0a890fff0a880ffb0a090ffffffffffffffffffd0b0a0ff ,\r\n                        0xffffffffe0c8c0ffe0c8c0ffe0c8c0ffffffffffd0b0a0ffd0b0a0ffd0a8a0ff ,\r\n                        0xe0e0e0ff604830fff0a890ffffc0a0ffc0a8a0ffffffffffe0c8c0ffd0b8a0ff ,\r\n                        0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8ffff ,\r\n                        0xfff8f0ff604830fff0a890fff0a880ffc0b0a0fffffffffffffffffff0a890ff ,\r\n                        0xf0a880fff0a080fff0a070ffe09870ffe09060ffe08860ffe08050ffe07840ff ,\r\n                        0xe07840ffd06030ff0000000000000000d0b0a0ffffffffffe0c8c0fff0a890ff ,\r\n                        0xffc0a0ffffc0a0ffffb890ffffb890ffffb090ffffa880fff0a070fff0a070ff ,\r\n                        0xf09870ffd06830ff0000000000000000d0b8a0fffffffffffffffffff0a890ff ,\r\n                        0xf0a880fff0a080fff0a080ffe09870ffe09060ffe08860ffe08850ffe08050ff ,\r\n                        0xe07840ffe07840ff0000000000000000f0a890fff0a880fff0a080fff0a070ff ,\r\n                        0xe09870ffe09060ffe08860ffe08050ffe07840ffe07840ffd06030ff00000000 ,\r\n                        0x00000000000000000000000000000000f0a890ffffc0a0ffffc0a0ffffb890ff ,\r\n                        0xffb890ffffb090ffffa880fff0a070fff0a070fff09870ffd06830ff00000000 ,\r\n                        0x00000000000000000000000000000000f0a890fff0a880fff0a080fff0a080ff ,\r\n                        0xe09870ffe09060ffe08860ffe08850ffe08050ffe07840ffe07840ff00000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n\r\n                    LayoutCachedLeft =5400\r\n                    LayoutCachedTop =2940\r\n                    LayoutCachedWidth =8520\r\n                    LayoutCachedHeight =3660\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =13807008\r\n                    BorderColor =13807008\r\n                    HoverColor =14796991\r\n                    PressedColor =9262658\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                    Overlaps =1\r\n                End\r\n                Begin ListBox\r\n                    RowSourceTypeInt =1\r\n                    OverlapFlags =85\r\n                    TextFontFamily =18\r\n                    IMESentenceMode =3\r\n                    ColumnCount =2\r\n                    Left =600\r\n                    Top =1140\r\n                    Width =4455\r\n                    Height =5745\r\n                    TabIndex =1\r\n                    ForeColor =4210752\r\n                    BorderColor =10921638\r\n                    Name =\"lstResults\"\r\n                    RowSourceType =\"Value List\"\r\n                    RowSource =\"✔;Access Table exists;✔;tblInternal has data;✔;Linked Table exists;✔;tblLinkedCS\"\r\n                        \"V has data;✔;Saved Table Data (TDF);✔;Saved Table Data (XML);✔;Table SQL;✔;Linke\"\r\n                        \"d Table JSON;✔;Linked Table structure;✔;Table Relationship;✔;Table Data Macro Ex\"\r\n                        \"ists;✔;Query exists;✔;Form exists;✔;Report exists;✔;Application Icon is set;✔;Cu\"\r\n                        \"stom Database (DAO) property;✔;Custom Project Property\"\r\n                    ColumnWidths =\"479;3975\"\r\n                    FontName =\"Palatino Linotype\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =600\r\n                    LayoutCachedTop =1140\r\n                    LayoutCachedWidth =5055\r\n                    LayoutCachedHeight =6885\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextFontFamily =18\r\n                            Left =780\r\n                            Top =720\r\n                            Width =2880\r\n                            Height =320\r\n                            BorderColor =8355711\r\n                            ForeColor =8355711\r\n                            Name =\"Col1_Label\"\r\n                            Caption =\"Test Results\"\r\n                            FontName =\"Palatino Linotype\"\r\n                            GridlineColor =10921638\r\n                            LayoutCachedLeft =780\r\n                            LayoutCachedTop =720\r\n                            LayoutCachedWidth =3660\r\n                            LayoutCachedHeight =1040\r\n                        End\r\n                    End\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =18\r\n                    Left =5400\r\n                    Top =1140\r\n                    Width =3720\r\n                    Height =1635\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label4\"\r\n                    Caption =\"Click the button below to verify the objects in the current database. This helps\"\r\n                        \" confirm that everything was correctly reconstructed from source.\"\r\n                    FontName =\"Palatino Linotype\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =5400\r\n                    LayoutCachedTop =1140\r\n                    LayoutCachedWidth =9120\r\n                    LayoutCachedHeight =2775\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =18\r\n                    Left =5400\r\n                    Top =3900\r\n                    Width =3120\r\n                    Height =720\r\n                    TabIndex =2\r\n                    ForeColor =4210752\r\n                    Name =\"cmdEditTests\"\r\n                    Caption =\"  Edit Tests...\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    FontName =\"Palatino Linotype\"\r\n                    GridlineColor =10921638\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x000000000000000000000000000000000000000000000000bb461e4800000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x000000003088305000000000000000000000000080281040803010ffd0502040 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x20302040005000ff40a040400000000080281040e05020ff803010ff803010ff ,\r\n                        0xd050204000000000000000000000000000000000000000000000000020302040 ,\r\n                        0x208030ff005000ff005000ff30983040e05020ffe06840ffe09880ffd07860ff ,\r\n                        0x803010ff00000000000000000038f040001890ff0018803000000000208030ff ,\r\n                        0x309850ff60c080ff208030ff005000ffe07050fff0a890ffffc8b0fff0b8a0ff ,\r\n                        0xc07050ff000000000030f0400038ffff001890ff001890ff0018803030a850ff ,\r\n                        0x80d890ffb0e8c0ff90e0a0ff208030fff0907020f08870ffffc0b0fff09070ff ,\r\n                        0xf0907040000000000030f0ff2058ffff7098ffff0028d0ff001890ff40c07040 ,\r\n                        0x30a850ffa0e8b0ff30a850ff60986040706860c0b0786030f09070fff0907070 ,\r\n                        0x00000000000000001050ffff90a8ffffa0c0ffff7098ffff0020b0ff00000000 ,\r\n                        0x40c0604030a850ff50a87040706860f070686020000000000018801000000000 ,\r\n                        0x00000000000000005080ff405078ffffa0b0ffff5078ffff6070c04000000000 ,\r\n                        0x0000000070686020706860e070686020706860e02048d060001890ff00188020 ,\r\n                        0x0000000000000000000000005080ff405080ffff6070b050706860e070686020 ,\r\n                        0x70686020706860e070686010000000002048c0600038ffff001890ff001890ff ,\r\n                        0x00188020000000000000000000000000000000000000000070686030508090a0 ,\r\n                        0x006080ff2060707000000000000000000030f0ff2058ffff7098ffff0028d0ff ,\r\n                        0x001890ff000000000000000000000000000000000000000000a8f04000a0e0ff ,\r\n                        0x006890ff006890ff00608070000000001050ffff90a8ffffa0c0ffff7098ffff ,\r\n                        0x0020b0ff000000000000000000000000000000000000000000a0e0ff10b0e0ff ,\r\n                        0x80e0ffff0090c0ff006890ff000000005080ff405078ffffa0b0ffff5078ffff ,\r\n                        0x5078ff40000000000000000000000000000000000000000000a0e0ff90e8ffff ,\r\n                        0xb0f0ffff90e0ffff00a0e0ff00000000000000005080ff405080ffff5080ff40 ,\r\n                        0x00000000000000000000000000000000000000000000000040c8ff4000a0e0ff ,\r\n                        0xa0e8ffff00a0e0ff50d0ff400000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000060d0ff70 ,\r\n                        0x00a0e0ff50d0ff40000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n\r\n                    LayoutCachedLeft =5400\r\n                    LayoutCachedTop =3900\r\n                    LayoutCachedWidth =8520\r\n                    LayoutCachedHeight =4620\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =13807008\r\n                    BorderColor =13807008\r\n                    HoverColor =14796991\r\n                    PressedColor =9262658\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                    Overlaps =1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =93\r\n                    TextFontFamily =18\r\n                    Left =5400\r\n                    Top =4860\r\n                    Width =3060\r\n                    Height =1635\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"lblResults\"\r\n                    Caption =\"17 tests passed\\015\\0120 tests failed\"\r\n                    FontName =\"Palatino Linotype\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =5400\r\n                    LayoutCachedTop =4860\r\n                    LayoutCachedWidth =8460\r\n                    LayoutCachedHeight =6495\r\n                End\r\n                Begin Image\r\n                    PictureType =2\r\n                    Left =7860\r\n                    Top =4860\r\n                    Width =600\r\n                    Height =720\r\n                    BorderColor =10921638\r\n                    Name =\"imgResult\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =7860\r\n                    LayoutCachedTop =4860\r\n                    LayoutCachedWidth =8460\r\n                    LayoutCachedHeight =5580\r\n                    TabIndex =3\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmMain.cls\"\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/forms/frmMain.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' Keep track of total results\r\nPrivate m_Totals(True To False) As Integer\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdRunTests_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/1/2020\r\n' Purpose   : Trying to keep things simple here... Verify that the object exists in the\r\n'           : correct format. (Adjust as needed)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub cmdRunTests_Click()\r\n\r\n    Dim strTest As String\r\n    Dim intTest As Integer\r\n    Dim dbs As DAO.Database\r\n    Dim rsc As SharedResource\r\n\r\n    Set dbs = CurrentDb\r\n\r\n    ' Clear list and totals\r\n    lstResults.RowSource = \"\"\r\n    m_Totals(True) = 0\r\n    m_Totals(False) = 0\r\n    imgResult.Picture = vbNullString\r\n\r\n    ' Ignore any errors.\r\n    ' NOTE: don't include the test result on a line that may throw an error.\r\n    On Error Resume Next\r\n\r\n    ' Update linked tables/CSV to use the current directory\r\n    dbs.TableDefs(\"tblLinkedAccess\").Connect = \";DATABASE=\" & Application.CurrentProject.Path & \"\\Testing.accdb\"\r\n    dbs.TableDefs(\"tblLinkedAccess\").RefreshLink\r\n    dbs.TableDefs(\"tblLinkedCSV\").Connect = \"Text;DSN=Linked Link Specification;FMT=Delimited;HDR=NO;IMEX=2;CharacterSet=437;ACCDB=YES;DATABASE=\" & Application.CurrentProject.Path\r\n    dbs.TableDefs(\"tblLinkedCSV\").RefreshLink\r\n\r\n    '========================\r\n    '   BEGIN TESTS\r\n    '========================\r\n\r\n    ' Tables\r\n    strTest = dbs.TableDefs(\"tblInternal\").Name\r\n    ShowResult \"Access Table exists\", (strTest = \"tblInternal\")\r\n\r\n    intTest = 0\r\n    intTest = DCount(\"*\", \"tblInternal\")\r\n    ShowResult \"tblInternal has data\", (intTest > 0)\r\n\r\n    strTest = dbs.TableDefs(\"tblLinkedCSV\").Name\r\n    ShowResult \"Linked Table exists\", (strTest = \"tblLinkedCSV\")\r\n\r\n    intTest = 0\r\n    intTest = DCount(\"*\", \"tblLinkedCSV\")\r\n    ShowResult \"tblLinkedCSV has data\", (intTest > 0)\r\n\r\n    ShowResult \"Saved Table Data (TDF)\", FSO.FileExists(ExportFolder & \"tables\\tblInternal.txt\")\r\n\r\n    ShowResult \"Saved Table Data (XML)\", FSO.FileExists(ExportFolder & \"tables\\tblSaveXML.xml\")\r\n\r\n    ShowResult \"Table SQL\", FSO.FileExists(ExportFolder & \"tbldefs\\tblInternal.sql\")\r\n\r\n    ShowResult \"Linked Table JSON\", FSO.FileExists(ExportFolder & \"tbldefs\\tblLinkedCSV.json\")\r\n\r\n    intTest = 0\r\n    intTest = dbs.Relations(\"tblInternaltblSaveXML\").Fields.Count\r\n    ShowResult \"Table Relationship\", (intTest = 1)\r\n\r\n    intTest = 0\r\n    intTest = DCount(\"*\", \"MSysObjects\", \"Not IsNull(LvExtra) and Type = 1 and [Name] = 'tblSaveXML'\")\r\n    ShowResult \"Table Data Macro Exists\", (intTest > 0)\r\n\r\n\r\n    ' Queries\r\n    strTest = dbs.QueryDefs(\"qryNavigationPaneGroups\").Name\r\n    ShowResult \"Query exists\", (strTest = \"qryNavigationPaneGroups\")\r\n\r\n    strTest = DLookup(\"DatabaseFile\", \"qryVBAFunction\")\r\n    ShowResult \"VBA Function in Query\", (strTest = CurrentDb.Name)\r\n\r\n    ' Forms\r\n    strTest = CurrentProject.AllForms(\"frmMain\").Name\r\n    ShowResult \"Form exists\", (strTest = \"frmMain\")\r\n\r\n\r\n    ' Reports\r\n    strTest = CurrentProject.AllReports(\"rptNavigationPaneGroups\").Name\r\n    ShowResult \"Report exists\", (strTest = \"rptNavigationPaneGroups\")\r\n    ShowResult \"Landscape Orientation\", (Report_rptNonDefaultPaperSize.Printer.Orientation = acPRORLandscape)\r\n    ShowResult \"A4 Paper Size\", (Report_rptNonDefaultPaperSize.Printer.PaperSize = acPRPSA4)\r\n\r\n\r\n    ' Macros\r\n    strTest = CurrentProject.AllMacros(\"AutoExec\").Name\r\n    ShowResult \"Macro exists\", (strTest = \"AutoExec\")\r\n\r\n\r\n    ' Modules\r\n    strTest = CurrentProject.AllModules(\"basUtility\").Name\r\n    ShowResult \"Standard Module exists\", (strTest = \"basUtility\")\r\n    strTest = GetVBProjectForCurrentDB.VBComponents(\"basExtendedChars\").CodeModule.Lines(6, 1)\r\n    ShowResult \"Extended ASCII text in VBA\", (Mid$(strTest, 10, 1) = Chr(151))\r\n\r\n    strTest = CurrentProject.AllModules(\"clsPerson\").Name\r\n    ShowResult \"Class Module exists\", (strTest = \"clsPerson\")\r\n\r\n    ' Access the code module first to correctly load the instancing property\r\n    strTest = GetVBProjectForCurrentDB.VBComponents(\"clsPublic\").CodeModule.CountOfLines\r\n    strTest = GetVBProjectForCurrentDB.VBComponents(\"clsPublic\").Properties(\"Instancing\")\r\n    ShowResult \"Class with Public Creatable Instancing\", (strTest = \"5\")\r\n\r\n\r\n    ' Database properties\r\n    strTest = \"\"\r\n    strTest = dbs.Properties(\"AppIcon\")\r\n    ShowResult \"Application Icon is set\", (Len(strTest) > 5)\r\n\r\n    strTest = dbs.Properties(\"DAOProperty\").Value\r\n    ShowResult \"Custom Database (DAO) property\", (strTest = \"DAO\")\r\n\r\n    strTest = CurrentProject.Properties(\"ProjectProperty\").Value\r\n    ShowResult \"Custom Project Property\", (strTest = \"TestValue\")\r\n\r\n    strTest = dbs.Containers(\"Databases\").Documents(\"SummaryInfo\").Properties(\"Title\")\r\n    ShowResult \"Database Summary Property (Title)\", (strTest = \"VCS Testing\")\r\n\r\n    strTest = dbs.Containers(\"Tables\").Documents(\"tblSaveXML\").Properties(\"Description\")\r\n    ShowResult \"Navigation pane object description\", (strTest = \"Saved description in XML table.\")\r\n\r\n    strTest = dbs.Containers(\"Modules\").Documents(\"basUtility\").Properties(\"Description\")\r\n    ShowResult \"Module description\", (strTest = \"My special description on the code module.\")\r\n\r\n    ShowResult \"Saved shared images\", (CurrentProject.Resources.Count > 2)\r\n\r\n    ShowResult \"Saved import/export specs (XML)\", (CurrentProject.ImportExportSpecifications.Count > 0)\r\n\r\n    strTest = CurrentProject.ImportExportSpecifications(0).Name\r\n    ShowResult \"Name of saved specification\", (strTest = \"Export-MSysIMEXColumns\")\r\n\r\n    strTest = Nz(DLookup(\"SpecName\", \"MSysIMEXSpecs\", \"SpecName=\"\"Test 2\"\"\"))\r\n    ShowResult \"Saved IMEX spec (Table based)\", (strTest = \"Test 2\")\r\n\r\n    strTest = Nz(DLookup(\"Name\", \"MSysNavPaneGroups\", \"Name=\"\"My Modules\"\"\"))\r\n    ShowResult \"Custom navigation pane group\", (strTest = \"My Modules\")\r\n\r\n    ' VBE Project\r\n    With GetVBProjectForCurrentDB\r\n\r\n        ShowResult \"VBE project name\", (.Name = \"VCS Testing\")\r\n        ShowResult \"VBE project description\", (.Description = \"For automated testing of Version Control\")\r\n        ShowResult \"Help context id\", (.HelpContextId = 123456)\r\n\r\n        strTest = .References(\"Scripting\").Name\r\n        ShowResult \"GUID reference (scripting)\", (strTest = \"Scripting\")\r\n\r\n        strTest = .References(\"MSForms\").Name\r\n        ShowResult \"MS Forms 2.0 reference\", (strTest = \"MSForms\")\r\n\r\n    End With\r\n\r\n    ' Theme\r\n    strTest = CurrentDb.Properties(\"Theme Resource Name\")\r\n    ShowResult \"Active theme = Angles\", (strTest = \"Angles\")\r\n\r\n    strTest = vbNullString\r\n    For Each rsc In CurrentProject.Resources\r\n        If rsc.Type = acResourceTheme Then\r\n            strTest = rsc.Name\r\n            If strTest = \"Angles\" Then Exit For\r\n        End If\r\n    Next rsc\r\n    ShowResult \"Theme resource exists\", (strTest = \"Angles\")\r\n\r\n    ' CommandBar Menus\r\n    strTest = CommandBars(\"Demo Popup\").Name\r\n    ShowResult \"Popup Menu\", (strTest = \"Demo Popup\")\r\n\r\n    strTest = CommandBars(\"Demo Popup\").Controls(1).Caption\r\n    ShowResult \"Menu Control\", (strTest = \"Custom Button\")\r\n\r\n    strTest = CommandBars(\"Special \\\\..// Popup\").Name\r\n    ShowResult \"Special Char Menu\", (strTest = \"Special \\\\..// Popup\")\r\n\r\n    strTest = CommandBars(\"Special \\\\..// Popup\").Controls(1).Caption\r\n    ShowResult \"Special Char Caption\", (strTest = \"Special \\\\..// Button\")\r\n\r\n\r\n    ' Other\r\n    ShowResult \"VCS Options file exists\", FSO.FileExists(ExportFolder & \"vcs-options.json\")\r\n\r\n\r\n    '========================\r\n    '   END TESTS\r\n    '========================\r\n\r\n\r\n    ' Display result icon\r\n    If m_Totals(False) = 0 Then\r\n        imgResult.Picture = \"button_ok\"\r\n    Else\r\n        imgResult.Picture = \"button_error\"\r\n    End If\r\n\r\n    If Err Then Err.Clear\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ShowResult\r\n' Author    : Adam Waller\r\n' Date      : 5/1/2020\r\n' Purpose   : Add the result to the list.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ShowResult(strText As String, blnPassed As Boolean)\r\n    Dim strIcon As String\r\n    If blnPassed Then\r\n        strIcon = ChrW(10004)   ' Check\r\n    Else\r\n        strIcon = ChrW(10060)   ' X\r\n    End If\r\n    m_Totals(blnPassed) = m_Totals(blnPassed) + 1\r\n    With lstResults\r\n        .SetFocus\r\n        .AddItem strIcon & \";\" & strText\r\n        .ListIndex = .ListCount - 1\r\n    End With\r\n    ' Display incrementing totals\r\n    lblResults.Caption = _\r\n        m_Totals(True) & \" tests passed\" & vbCrLf & _\r\n        m_Totals(False) & \" tests failed\"\r\n    DoEvents\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdEditTests_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/1/2020\r\n' Purpose   : Go to the code where you can edit the tests.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdEditTests_Click()\r\n    Dim intLine As Integer\r\n    VBE.MainWindow.Visible = True\r\n    With VBE.VBProjects(\"VCS Testing\").VBComponents(\"Form_frmMain\")\r\n        .Activate\r\n        With .CodeModule\r\n            intLine = 27 + .ProcStartLine(\"cmdRunTests_Click\", vbext_pk_Proc)\r\n            .CodePane.SetSelection intLine, 1, intLine, 1\r\n            .CodePane.Show\r\n        End With\r\n    End With\r\n    AppActivate VBE.MainWindow.Caption\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportFolder\r\n' Author    : Adam Waller\r\n' Date      : 5/7/2020\r\n' Purpose   : Return base export folder for testing for source files.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ExportFolder() As String\r\n    ExportFolder = CurrentProject.FullName & \".src\\\"\r\nEnd Function\r\n\r\n\r\nPrivate Sub Form_Load()\r\n    imgResult.Picture = vbNullString\r\nEnd Sub\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/forms/frmTestMenu.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    PopUp = NotDefault\r\n    RecordSelectors = NotDefault\r\n    NavigationButtons = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    BorderStyle =1\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =9900\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =7\r\n    Left =31980\r\n    Top =2040\r\n    Right =-23746\r\n    Bottom =9585\r\n    RecSrcDt = Begin\r\n        0x4bb011b6cb1fe640\r\n    End\r\n    DatasheetFontName =\"Franklin Gothic Book\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CommandButton\r\n            FontSize =11\r\n            FontWeight =400\r\n            FontName =\"Calibri\"\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =1\r\n            Gradient =12\r\n            BackThemeColorIndex =4\r\n            BackTint =60.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =4\r\n            BorderTint =60.0\r\n            ThemeFontIndex =1\r\n            HoverThemeColorIndex =4\r\n            HoverTint =40.0\r\n            PressedThemeColorIndex =4\r\n            PressedShade =75.0\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n        End\r\n        Begin Tab\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =3\r\n            BackThemeColorIndex =1\r\n            BackShade =85.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =2\r\n            BorderTint =60.0\r\n            HoverThemeColorIndex =1\r\n            PressedThemeColorIndex =1\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n        End\r\n        Begin Page\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =7560\r\n            Name =\"Detail\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =720\r\n                    Top =840\r\n                    Width =2580\r\n                    Height =645\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label1\"\r\n                    Caption =\"Click this button to show \\015\\012the custom Popup Menu.\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =840\r\n                    LayoutCachedWidth =3300\r\n                    LayoutCachedHeight =1485\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =720\r\n                    Top =1620\r\n                    Width =2340\r\n                    Height =480\r\n                    ForeColor =4210752\r\n                    Name =\"cmdShowMenu\"\r\n                    Caption =\" Show Menu...\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000c0a890ff604830ff604830ff604830ff604830ff604830ff ,\r\n                        0x604830ff604830ff604830ff604830ff604830ff604830ff604830ff604830ff ,\r\n                        0x604830ff604830ffc0a890fffff8ffffe0d8d0ffe0d0d0ffe0d0c0ffd0c8c0ff ,\r\n                        0xd0c8c0ffd0c8b0ffd0c8b0ff805840ffd0d0d0ffd0c8c0ffc0c0b0ffc0b8b0ff ,\r\n                        0xc0b0a0ff604830ffc0a890fffff8ffffd0b8a0ffd0b0a0ffd0b0a0ffd0a890ff ,\r\n                        0xc0a890fff0d0c0ffd0c8b0ff906040ffe0d8e0ffc0b8b0ff403020ffb0a090ff ,\r\n                        0xc0b8b0ff604830ffc0a8a0fffff8fffffff8fffffff8fffffff8f0fffff0e0ff ,\r\n                        0xffe8e0ffffe8d0ffd0c8b0ff906040ffe0e0e0ff908880ff806860ff503830ff ,\r\n                        0xc0c0b0ff604830ffc0a8a0fffff8ffffd0c0b0ffd0b8b0ffd0b8a0ffd0b0a0ff ,\r\n                        0xd0b0a0fff0d8d0ffd0c8b0ff906040fff0e8f0ffe0e0e0ffe0d8e0ffd0d0d0ff ,\r\n                        0xc0c8c0ff604830ffc0a8a0fffff8fffffff8fffffff8fffffff8fffffff8f0ff ,\r\n                        0xfff0e0ffffe8e0ffd0c8c0ff906040ff906040ff906040ff906040ff906040ff ,\r\n                        0x906040ff604830ffc0a8a0fffff8ffffd0c0b0ffd0c0b0ffd0c0b0ffd0b8b0ff ,\r\n                        0xd0b8b0fff0d8d0ffd0c8c0ff906040ffd0d0d0ffd0c8c0ffc0c0b0ffc0b8b0ff ,\r\n                        0xc0b0a0ff604830ffc0b0a0fffff8fffffff8fffffff8fffffff8fffffff8ffff ,\r\n                        0xfff8f0fffff8f0ffe0d0c0ff906040ffe0d8e0ff604840ff403020ff302820ff ,\r\n                        0xc0b8b0ff604830ffc0b0a0fffff8ffffd0c0b0ffd0c0b0ffd0c0b0ffd0c0b0ff ,\r\n                        0xd0c0b0ffffe8d0ffe0d0d0ff906040ffe0e0e0ffc0c0c0ff806860ffb0a8a0ff ,\r\n                        0xc0c0b0ff604830ffc0b0a0fffff8fffffff8fffffff8fffffff8fffffff8ffff ,\r\n                        0xfff8fffffff8fffffff8ffff906040fff0e8f0ffe0e0e0ffe0d8e0ffd0d0d0ff ,\r\n                        0xc0c8c0ff604830ffc0b0a0ffc0b0a0ffc0b0a0ffc0b0a0ffc0b0a0ffc0a8a0ff ,\r\n                        0xc0a8a0ffb0a090ffb0a090ff906040ff906040ff906040ff906040ff906040ff ,\r\n                        0x906040ff604830ffc0b0a0ffffffffffe0e0e0ffe0e0e0ffe0d8d0ffe0d0d0ff ,\r\n                        0xd0c8c0ffd0c0c0ffd0b8b0ff906040ff805840ff705040ff705040ff604830ff ,\r\n                        0x604830ff604830ffc0b0a0fffffffffffffffffffffffffffff8f0fffff0f0ff ,\r\n                        0xf0e8e0fff0e0d0ffd0c0b0ff906040ff906040ff907860ffe0d8e0ff806860ff ,\r\n                        0x604830ff604830ffd0b0a0fffffffffffffffffffffffffffff8fffffff8f0ff ,\r\n                        0xf0f0e0fff0e0e0fff0d8d0ff906040ff906040fff0e8f0fff0e8e0ffe0e0e0ff ,\r\n                        0x705040ff604830ffd0b0a0ffd0b0a0ffc0b0a0ffc0b0a0ffc0b0a0ffc0b0a0ff ,\r\n                        0xc0b0a0ffc0b0a0ffc0a8a0ff906040ff805840ff805840ff805840ff705040ff ,\r\n                        0x705040ff604830ff\r\n                    End\r\n\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =1620\r\n                    LayoutCachedWidth =3060\r\n                    LayoutCachedHeight =2100\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =11710639\r\n                    BorderColor =11710639\r\n                    HoverColor =13355721\r\n                    PressedColor =6249563\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =5640\r\n                    Top =1620\r\n                    Width =2340\r\n                    Height =480\r\n                    TabIndex =1\r\n                    ForeColor =4210752\r\n                    Name =\"cmdSpecial\"\r\n                    Caption =\" Show Menu...\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000c0a890ff604830ff604830ff604830ff604830ff604830ff ,\r\n                        0x604830ff604830ff604830ff604830ff604830ff604830ff604830ff604830ff ,\r\n                        0x604830ff604830ffc0a890fffff8ffffe0d8d0ffe0d0d0ffe0d0c0ffd0c8c0ff ,\r\n                        0xd0c8c0ffd0c8b0ffd0c8b0ff805840ffd0d0d0ffd0c8c0ffc0c0b0ffc0b8b0ff ,\r\n                        0xc0b0a0ff604830ffc0a890fffff8ffffd0b8a0ffd0b0a0ffd0b0a0ffd0a890ff ,\r\n                        0xc0a890fff0d0c0ffd0c8b0ff906040ffe0d8e0ffc0b8b0ff403020ffb0a090ff ,\r\n                        0xc0b8b0ff604830ffc0a8a0fffff8fffffff8fffffff8fffffff8f0fffff0e0ff ,\r\n                        0xffe8e0ffffe8d0ffd0c8b0ff906040ffe0e0e0ff908880ff806860ff503830ff ,\r\n                        0xc0c0b0ff604830ffc0a8a0fffff8ffffd0c0b0ffd0b8b0ffd0b8a0ffd0b0a0ff ,\r\n                        0xd0b0a0fff0d8d0ffd0c8b0ff906040fff0e8f0ffe0e0e0ffe0d8e0ffd0d0d0ff ,\r\n                        0xc0c8c0ff604830ffc0a8a0fffff8fffffff8fffffff8fffffff8fffffff8f0ff ,\r\n                        0xfff0e0ffffe8e0ffd0c8c0ff906040ff906040ff906040ff906040ff906040ff ,\r\n                        0x906040ff604830ffc0a8a0fffff8ffffd0c0b0ffd0c0b0ffd0c0b0ffd0b8b0ff ,\r\n                        0xd0b8b0fff0d8d0ffd0c8c0ff906040ffd0d0d0ffd0c8c0ffc0c0b0ffc0b8b0ff ,\r\n                        0xc0b0a0ff604830ffc0b0a0fffff8fffffff8fffffff8fffffff8fffffff8ffff ,\r\n                        0xfff8f0fffff8f0ffe0d0c0ff906040ffe0d8e0ff604840ff403020ff302820ff ,\r\n                        0xc0b8b0ff604830ffc0b0a0fffff8ffffd0c0b0ffd0c0b0ffd0c0b0ffd0c0b0ff ,\r\n                        0xd0c0b0ffffe8d0ffe0d0d0ff906040ffe0e0e0ffc0c0c0ff806860ffb0a8a0ff ,\r\n                        0xc0c0b0ff604830ffc0b0a0fffff8fffffff8fffffff8fffffff8fffffff8ffff ,\r\n                        0xfff8fffffff8fffffff8ffff906040fff0e8f0ffe0e0e0ffe0d8e0ffd0d0d0ff ,\r\n                        0xc0c8c0ff604830ffc0b0a0ffc0b0a0ffc0b0a0ffc0b0a0ffc0b0a0ffc0a8a0ff ,\r\n                        0xc0a8a0ffb0a090ffb0a090ff906040ff906040ff906040ff906040ff906040ff ,\r\n                        0x906040ff604830ffc0b0a0ffffffffffe0e0e0ffe0e0e0ffe0d8d0ffe0d0d0ff ,\r\n                        0xd0c8c0ffd0c0c0ffd0b8b0ff906040ff805840ff705040ff705040ff604830ff ,\r\n                        0x604830ff604830ffc0b0a0fffffffffffffffffffffffffffff8f0fffff0f0ff ,\r\n                        0xf0e8e0fff0e0d0ffd0c0b0ff906040ff906040ff907860ffe0d8e0ff806860ff ,\r\n                        0x604830ff604830ffd0b0a0fffffffffffffffffffffffffffff8fffffff8f0ff ,\r\n                        0xf0f0e0fff0e0e0fff0d8d0ff906040ff906040fff0e8f0fff0e8e0ffe0e0e0ff ,\r\n                        0x705040ff604830ffd0b0a0ffd0b0a0ffc0b0a0ffc0b0a0ffc0b0a0ffc0b0a0ff ,\r\n                        0xc0b0a0ffc0b0a0ffc0a8a0ff906040ff805840ff805840ff805840ff705040ff ,\r\n                        0x705040ff604830ff\r\n                    End\r\n\r\n                    LayoutCachedLeft =5640\r\n                    LayoutCachedTop =1620\r\n                    LayoutCachedWidth =7980\r\n                    LayoutCachedHeight =2100\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =11710639\r\n                    BorderColor =11710639\r\n                    HoverColor =13355721\r\n                    PressedColor =6249563\r\n                    HoverForeColor =4210752\r\n                    PressedForeColor =4210752\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    TextFontFamily =0\r\n                    Left =5640\r\n                    Top =840\r\n                    Width =2580\r\n                    Height =645\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label6\"\r\n                    Caption =\"This menu has a name with special characters.\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =5640\r\n                    LayoutCachedTop =840\r\n                    LayoutCachedWidth =8220\r\n                    LayoutCachedHeight =1485\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmTestMenu.cls\"\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/forms/frmTestMenu.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\nPrivate Sub cmdShowMenu_Click()\r\n    Menu.PopupBelow cmdShowMenu, \"Demo Popup\"\r\n    'PrintPos\r\nEnd Sub\r\n\r\nPrivate Sub cmdSpecial_Click()\r\n    Menu.PopupBelow cmdSpecial, \"Special \\\\..// Popup\"\r\nEnd Sub\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/hidden-attributes.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbHiddenAttribute\",\r\n    \"Description\": \"Database objects hidden attribute\"\r\n  },\r\n  \"Items\": {\r\n    \"Modules\": [\r\n      \"clsPerson\"\r\n    ],\r\n    \"Tables\": [\r\n      \"tblHidden\"\r\n    ]\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/images/1370126936_button_ok.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbSharedImage\",\r\n    \"Description\": \"Shared Image Gallery Item\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"1370126936_button_ok\",\r\n    \"FileName\": \"1370126936_button_ok.png\",\r\n    \"Extension\": \"png\",\r\n    \"ContentHash\": \"01a9f08\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/images/Colors.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbSharedImage\",\r\n    \"Description\": \"Shared Image Gallery Item\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"Colors\",\r\n    \"FileName\": \"Colors.jpg\",\r\n    \"Extension\": \"jpg\",\r\n    \"ContentHash\": \"a9d4adf\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/images/button_error.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbSharedImage\",\r\n    \"Description\": \"Shared Image Gallery Item\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"button_error\",\r\n    \"FileName\": \"1429903314_error.png\",\r\n    \"Extension\": \"png\",\r\n    \"ContentHash\": \"fc7eb17\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/images/button_ok.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbSharedImage\",\r\n    \"Description\": \"Shared Image Gallery Item\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"button_ok\",\r\n    \"FileName\": \"1_1370126936_button_ok.png\",\r\n    \"Extension\": \"png\",\r\n    \"ContentHash\": \"01a9f08\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/imexspecs/Linked Link Specification.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbImexSpec\",\r\n    \"Description\": \"Import/Export Specification from MSysIMEXSpecs\"\r\n  },\r\n  \"Items\": {\r\n    \"DateDelim\": \"/\",\r\n    \"DateFourDigitYear\": true,\r\n    \"DateLeadingZeros\": false,\r\n    \"DateOrder\": 2,\r\n    \"DecimalPoint\": \".\",\r\n    \"FieldSeparator\": \",\",\r\n    \"FileType\": 437,\r\n    \"SpecName\": \"Linked Link Specification\",\r\n    \"SpecType\": 1,\r\n    \"StartRow\": 1,\r\n    \"TextDelim\": \"\",\r\n    \"TimeDelim\": \":\",\r\n    \"Columns\": {\r\n      \"Color\": {\r\n        \"Attributes\": 0,\r\n        \"DataType\": 10,\r\n        \"IndexType\": 0,\r\n        \"SkipColumn\": false,\r\n        \"Start\": 4,\r\n        \"Width\": 7\r\n      },\r\n      \"ID\": {\r\n        \"Attributes\": 0,\r\n        \"DataType\": 4,\r\n        \"IndexType\": 0,\r\n        \"SkipColumn\": false,\r\n        \"Start\": 1,\r\n        \"Width\": 3\r\n      }\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/imexspecs/Test 2.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbImexSpec\",\r\n    \"Description\": \"Import/Export Specification from MSysIMEXSpecs\"\r\n  },\r\n  \"Items\": {\r\n    \"DateDelim\": \"/\",\r\n    \"DateFourDigitYear\": true,\r\n    \"DateLeadingZeros\": false,\r\n    \"DateOrder\": 2,\r\n    \"DecimalPoint\": \".\",\r\n    \"FieldSeparator\": \",\",\r\n    \"FileType\": 437,\r\n    \"SpecName\": \"Test 2\",\r\n    \"SpecType\": 1,\r\n    \"StartRow\": 1,\r\n    \"TextDelim\": \"\",\r\n    \"TimeDelim\": \":\",\r\n    \"Columns\": {\r\n      \"Color\": {\r\n        \"Attributes\": 0,\r\n        \"DataType\": 10,\r\n        \"IndexType\": 0,\r\n        \"SkipColumn\": false,\r\n        \"Start\": 4,\r\n        \"Width\": 7\r\n      },\r\n      \"ID\": {\r\n        \"Attributes\": 0,\r\n        \"DataType\": 4,\r\n        \"IndexType\": 0,\r\n        \"SkipColumn\": false,\r\n        \"Start\": 1,\r\n        \"Width\": 3\r\n      }\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/macros/AutoExec.bas",
    "content": "﻿Version =196611\r\nColumnsShown =0\r\nBegin\r\n    Action =\"OpenForm\"\r\n    Argument =\"frmMain\"\r\n    Argument =\"0\"\r\n    Argument =\"\"\r\n    Argument =\"\"\r\n    Argument =\"-1\"\r\n    Argument =\"0\"\r\nEnd\r\nBegin\r\n    Action =\"RunCode\"\r\n    Argument =\"RunTests()\"\r\nEnd\r\nBegin\r\n    Comment =\"_AXL:<?xml version=\\\"1.0\\\" encoding=\\\"UTF-16\\\" standalone=\\\"no\\\"?>\\015\\012<UserI\"\r\n        \"nterfaceMacro MinimumClientDesignVersion=\\\"14.0.0000.0000\\\" xmlns=\\\"http://schem\"\r\n        \"as.microsoft.com/office/accessservices/2009/11/application\\\" xmlns:a=\\\"http://sc\"\r\n        \"hemas.microsoft.com/office/acc\"\r\nEnd\r\nBegin\r\n    Comment =\"_AXL:essservices/2009/11/forms\\\"><Statements><Action Name=\\\"OpenForm\\\"><Argument\"\r\n        \" Name=\\\"FormName\\\">frmMain</Argument></Action><Action Name=\\\"RunCode\\\"><Argument\"\r\n        \" Name=\\\"FunctionName\\\">RunTests()</Argument></Action></Statements></UserInterfac\"\r\n        \"eMacro>\"\r\nEnd\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/menus/Demo Popup.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbCommandBar\",\r\n    \"Description\": \"CommandBar\"\r\n  },\r\n  \"Items\": {\r\n    \"AdaptiveMenu\": false,\r\n    \"Context\": \"\",\r\n    \"Enabled\": true,\r\n    \"Height\": 26,\r\n    \"Left\": 2334,\r\n    \"NameLocal\": \"Demo Popup\",\r\n    \"Position\": 5,\r\n    \"Protection\": 22,\r\n    \"RowIndex\": -1,\r\n    \"Top\": 277,\r\n    \"Type\": 2,\r\n    \"Visible\": false,\r\n    \"Width\": 154,\r\n    \"Controls\": [\r\n      {\r\n        \"BeginGroup\": false,\r\n        \"BuiltIn\": false,\r\n        \"BuiltInFace\": false,\r\n        \"Caption\": \"Custom Button\",\r\n        \"DescriptionText\": \"\",\r\n        \"Enabled\": true,\r\n        \"FaceId\": 0,\r\n        \"Height\": 22,\r\n        \"HelpContextId\": 0,\r\n        \"HelpFile\": \"\",\r\n        \"HyperlinkType\": 0,\r\n        \"Index\": 1,\r\n        \"IsPriorityDropped\": false,\r\n        \"OLEUsage\": 1,\r\n        \"OnAction\": \"=MenuHandler(\\\"Demo Popup.Custom Button\\\")\",\r\n        \"Parameter\": \"\",\r\n        \"Priority\": 3,\r\n        \"ShortcutText\": \"\",\r\n        \"State\": 0,\r\n        \"Style\": 0,\r\n        \"Tag\": \"\",\r\n        \"TooltipText\": \"Custom Button\",\r\n        \"Type\": 1,\r\n        \"Visible\": true,\r\n        \"Width\": 152,\r\n        \"ImagePath\": \"rel:Demo Popup_Images\\\\Custom Button\"\r\n      }\r\n    ]\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/menus/Special %5C%5C..%2F%2F Popup.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbCommandBar\",\r\n    \"Description\": \"CommandBar\"\r\n  },\r\n  \"Items\": {\r\n    \"AdaptiveMenu\": false,\r\n    \"Context\": \"\",\r\n    \"Enabled\": true,\r\n    \"Height\": 26,\r\n    \"Left\": 2686,\r\n    \"NameLocal\": \"Special \\\\\\\\..// Popup\",\r\n    \"Position\": 5,\r\n    \"Protection\": 22,\r\n    \"RowIndex\": -1,\r\n    \"Top\": 277,\r\n    \"Type\": 2,\r\n    \"Visible\": false,\r\n    \"Width\": 178,\r\n    \"Controls\": [\r\n      {\r\n        \"BeginGroup\": false,\r\n        \"BuiltIn\": false,\r\n        \"BuiltInFace\": false,\r\n        \"Caption\": \"Special \\\\\\\\..// Button\",\r\n        \"DescriptionText\": \"\",\r\n        \"Enabled\": true,\r\n        \"FaceId\": 0,\r\n        \"Height\": 22,\r\n        \"HelpContextId\": 0,\r\n        \"HelpFile\": \"\",\r\n        \"HyperlinkType\": 0,\r\n        \"Index\": 1,\r\n        \"IsPriorityDropped\": false,\r\n        \"OLEUsage\": 1,\r\n        \"OnAction\": \"=MenuHandler(\\\"Special \\\\\\\\..// Popup.Special \\\\\\\\..// Button\\\")\",\r\n        \"Parameter\": \"\",\r\n        \"Priority\": 3,\r\n        \"ShortcutText\": \"\",\r\n        \"State\": 0,\r\n        \"Style\": 0,\r\n        \"Tag\": \"\",\r\n        \"TooltipText\": \"Special \\\\\\\\..// Button\",\r\n        \"Type\": 1,\r\n        \"Visible\": true,\r\n        \"Width\": 176,\r\n        \"ImagePath\": \"rel:Special %5C%5C..%2F%2F Popup_Images\\\\Special %5C%5C..%2F%2F Button\"\r\n      }\r\n    ]\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/modules/Module1.bas",
    "content": "﻿Attribute VB_Name = \"Module1\"\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\nPrivate Type str_PRTMIP\r\n    strRGB As String * 28\r\nEnd Type\r\n\r\n\r\nPrivate Type tDevNamesBuffer\r\n    strBuffer As String * 255\r\nEnd Type\r\n\r\nPrivate Type type_PRTMIP\r\n    xLeftMargin As Long\r\n    yTopMargin As Long\r\n    xRightMargin As Long\r\n    yBotMargin As Long\r\n    fDataOnly As Long\r\n    xWidth As Long\r\n    yHeight As Long\r\n    fDefaultSize As Long\r\n    cxColumns As Long\r\n    yColumnSpacing As Long\r\n    xRowSpacing As Long\r\n    rItemLayout As Long\r\n    fFastPrint As Long\r\n    fDatasheet As Long\r\nEnd Type\r\n\r\n\r\nPrivate Type DEVNAMES\r\n  wDriverOffset As Integer\r\n  wDeviceOffset As Integer\r\n  wOutputOffset As Integer\r\n  wDefault As Integer\r\n  extra(1 To 255) As Byte\r\nEnd Type\r\n\r\n'noncompilingcodeissue ' Showing that we can build even with VBA errors\r\n\r\nPublic Sub PrtMipCols(ByVal strName As String)\r\n\r\n    Dim PrtMipString As str_PRTMIP\r\n    Dim PM As type_PRTMIP\r\n    Dim rpt As Report\r\n    Const PM_HORIZONTALCOLS = 1953\r\n    Const PM_VERTICALCOLS = 1954\r\n\r\n    ' Open the report.\r\n    DoCmd.OpenReport strName, acDesign\r\n    Set rpt = Reports(strName)\r\n    PrtMipString.strRGB = rpt.PrtMip\r\n    LSet PM = PrtMipString\r\n\r\n    ' Create two columns.\r\n    PM.cxColumns = 2\r\n\r\n    ' Set 0.25 inch between rows.\r\n    PM.xRowSpacing = 0.25 * 1440\r\n\r\n    ' Set 0.5 inch between columns.\r\n    PM.yColumnSpacing = 0.5 * 1440\r\n    PM.rItemLayout = PM_HORIZONTALCOLS\r\n\r\n    ' Update property.\r\n    LSet PrtMipString = PM\r\n    rpt.PrtMip = PrtMipString.strRGB\r\n\r\n    Set rpt = Nothing\r\n\r\nEnd Sub\r\n\r\n\r\nPublic Sub TestPrinterMIP()\r\n\r\n    Dim rpt As Report\r\n    Dim tDevNames As DEVNAMES\r\n    Dim tBuffer As tDevNamesBuffer\r\n    Dim strData As String\r\n    Dim lngNull As Long\r\n    Dim lngStart As Long\r\n\r\n    Set rpt = Report_rptDefaultPrinter\r\n    Set rpt = Report_rptNavigationPaneGroups\r\n\r\n    tBuffer.strBuffer = rpt.PrtDevNames\r\n    LSet tDevNames = tBuffer\r\n\r\n    ' Bytes in structure before the data string starts\r\n\r\n    'debug.Print mid$(strdata,tDevNames.wDeviceOffset- lngstart,instr\r\n\r\n    strData = StrConv(tDevNames.extra, vbUnicode)\r\n\r\n    Debug.Print GetNullTermStringByOffset(strData, 7, tDevNames.wDriverOffset)\r\n    Debug.Print GetNullTermStringByOffset(strData, 7, tDevNames.wDeviceOffset)\r\n    Debug.Print GetNullTermStringByOffset(strData, 7, tDevNames.wOutputOffset)\r\n\r\n\r\n    Stop\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetNullTermStringByOffset\r\n' Author    : Adam Waller\r\n' Date      : 5/18/2020\r\n' Purpose   : Returns the value of a null-terminated string by offset.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetNullTermStringByOffset(strData As String, lngHeaderLen As Long, intOffset As Integer) As String\r\n\r\n    Dim lngNull As Long\r\n    Dim lngStart As Long\r\n\r\n    lngStart = intOffset - lngHeaderLen\r\n    lngNull = InStr(lngStart, strData, vbNullChar)\r\n\r\n    ' Return the string if we found a null terminator\r\n    If lngNull > 0 Then GetNullTermStringByOffset = Mid$(strData, lngStart, lngNull - lngStart)\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/modules/basExtendedChars.bas",
    "content": "﻿Attribute VB_Name = \"basExtendedChars\"\r\nOption Compare Database\r\nOption Explicit\r\n\r\n'——————————————————————————————————————————————————————————————————————————————————————————¬\r\n' This module is used to prove that all ASCII characters survive the export\\import cycle.  |\r\n'——————————————————————————————————————————————————————————————————————————————————————————+\r\n\r\n' ASCII Table Using Windows-1252 codepage\r\n'  Table generated using https://ozh.github.io/ascii-tables/\r\n'\r\n' +=====+=====+==========+=======+\r\n' | Dec | Hex |  Binary  | Char  |\r\n' +=====+=====+==========+=======+\r\n' |   0 | 00  | 00000000 | NUL   |\r\n' +-----+-----+----------+-------+\r\n' |   1 | 01  | 00000001 | SOH   |\r\n' +-----+-----+----------+-------+\r\n' |   2 | 02  | 00000010 | STX   |\r\n' +-----+-----+----------+-------+\r\n' |   3 | 03  | 00000011 | ETX   |\r\n' +-----+-----+----------+-------+\r\n' |   4 | 04  | 00000100 | EOT   |\r\n' +-----+-----+----------+-------+\r\n' |   5 | 05  | 00000101 | ENQ   |\r\n' +-----+-----+----------+-------+\r\n' |   6 | 06  | 00000110 | ACK   |\r\n' +-----+-----+----------+-------+\r\n' |   7 | 07  | 00000111 | BEL   |\r\n' +-----+-----+----------+-------+\r\n' |   8 | 08  | 00001000 | BS    |\r\n' +-----+-----+----------+-------+\r\n' |   9 | 09  | 00001001 | HT    |\r\n' +-----+-----+----------+-------+\r\n' |  10 | 0A  | 00001010 | LF    |\r\n' +-----+-----+----------+-------+\r\n' |  11 | 0B  | 00001011 | VT    |\r\n' +-----+-----+----------+-------+\r\n' |  12 | 0C  | 00001100 | FF    |\r\n' +-----+-----+----------+-------+\r\n' |  13 | 0D  | 00001101 | CR    |\r\n' +-----+-----+----------+-------+\r\n' |  14 | 0E  | 00001110 | SO    |\r\n' +-----+-----+----------+-------+\r\n' |  15 | 0F  | 00001111 | SI    |\r\n' +-----+-----+----------+-------+\r\n' |  16 | 10  | 00010000 | DLE   |\r\n' +-----+-----+----------+-------+\r\n' |  17 | 11  | 00010001 | DC1   |\r\n' +-----+-----+----------+-------+\r\n' |  18 | 12  | 00010010 | DC2   |\r\n' +-----+-----+----------+-------+\r\n' |  19 | 13  | 00010011 | DC3   |\r\n' +-----+-----+----------+-------+\r\n' |  20 | 14  | 00010100 | DC4   |\r\n' +-----+-----+----------+-------+\r\n' |  21 | 15  | 00010101 | NAK   |\r\n' +-----+-----+----------+-------+\r\n' |  22 | 16  | 00010110 | SYN   |\r\n' +-----+-----+----------+-------+\r\n' |  23 | 17  | 00010111 | ETB   |\r\n' +-----+-----+----------+-------+\r\n' |  24 | 18  | 00011000 | CAN   |\r\n' +-----+-----+----------+-------+\r\n' |  25 | 19  | 00011001 | EM    |\r\n' +-----+-----+----------+-------+\r\n' |  26 | 1A  | 00011010 | SUB   |\r\n' +-----+-----+----------+-------+\r\n' |  27 | 1B  | 00011011 | ESC   |\r\n' +-----+-----+----------+-------+\r\n' |  28 | 1C  | 00011100 | FS    |\r\n' +-----+-----+----------+-------+\r\n' |  29 | 1D  | 00011101 | GS    |\r\n' +-----+-----+----------+-------+\r\n' |  30 | 1E  | 00011110 | RS    |\r\n' +-----+-----+----------+-------+\r\n' |  31 | 1F  | 00011111 | US    |\r\n' +-----+-----+----------+-------+\r\n' |  32 | 20  | 00100000 | space |\r\n' +-----+-----+----------+-------+\r\n' |  33 | 21  | 00100001 | !     |\r\n' +-----+-----+----------+-------+\r\n' |  34 | 22  | 00100010 | \"     |\r\n' +-----+-----+----------+-------+\r\n' |  35 | 23  | 00100011 | #     |\r\n' +-----+-----+----------+-------+\r\n' |  36 | 24  | 00100100 | $     |\r\n' +-----+-----+----------+-------+\r\n' |  37 | 25  | 00100101 | %     |\r\n' +-----+-----+----------+-------+\r\n' |  38 | 26  | 00100110 | &     |\r\n' +-----+-----+----------+-------+\r\n' |  39 | 27  | 00100111 | '     |\r\n' +-----+-----+----------+-------+\r\n' |  40 | 28  | 00101000 | (     |\r\n' +-----+-----+----------+-------+\r\n' |  41 | 29  | 00101001 | )     |\r\n' +-----+-----+----------+-------+\r\n' |  42 | 2A  | 00101010 | *     |\r\n' +-----+-----+----------+-------+\r\n' |  43 | 2B  | 00101011 | +     |\r\n' +-----+-----+----------+-------+\r\n' |  44 | 2C  | 00101100 | ,     |\r\n' +-----+-----+----------+-------+\r\n' |  45 | 2D  | 00101101 | -     |\r\n' +-----+-----+----------+-------+\r\n' |  46 | 2E  | 00101110 | .     |\r\n' +-----+-----+----------+-------+\r\n' |  47 | 2F  | 00101111 | /     |\r\n' +-----+-----+----------+-------+\r\n' |  48 | 30  | 00110000 | 0     |\r\n' +-----+-----+----------+-------+\r\n' |  49 | 31  | 00110001 | 1     |\r\n' +-----+-----+----------+-------+\r\n' |  50 | 32  | 00110010 | 2     |\r\n' +-----+-----+----------+-------+\r\n' |  51 | 33  | 00110011 | 3     |\r\n' +-----+-----+----------+-------+\r\n' |  52 | 34  | 00110100 | 4     |\r\n' +-----+-----+----------+-------+\r\n' |  53 | 35  | 00110101 | 5     |\r\n' +-----+-----+----------+-------+\r\n' |  54 | 36  | 00110110 | 6     |\r\n' +-----+-----+----------+-------+\r\n' |  55 | 37  | 00110111 | 7     |\r\n' +-----+-----+----------+-------+\r\n' |  56 | 38  | 00111000 | 8     |\r\n' +-----+-----+----------+-------+\r\n' |  57 | 39  | 00111001 | 9     |\r\n' +-----+-----+----------+-------+\r\n' |  58 | 3A  | 00111010 | :     |\r\n' +-----+-----+----------+-------+\r\n' |  59 | 3B  | 00111011 | ;     |\r\n' +-----+-----+----------+-------+\r\n' |  60 | 3C  | 00111100 | <     |\r\n' +-----+-----+----------+-------+\r\n' |  61 | 3D  | 00111101 | =     |\r\n' +-----+-----+----------+-------+\r\n' |  62 | 3E  | 00111110 | >     |\r\n' +-----+-----+----------+-------+\r\n' |  63 | 3F  | 00111111 | ?     |\r\n' +-----+-----+----------+-------+\r\n' |  64 | 40  | 01000000 | @     |\r\n' +-----+-----+----------+-------+\r\n' |  65 | 41  | 01000001 | A     |\r\n' +-----+-----+----------+-------+\r\n' |  66 | 42  | 01000010 | B     |\r\n' +-----+-----+----------+-------+\r\n' |  67 | 43  | 01000011 | C     |\r\n' +-----+-----+----------+-------+\r\n' |  68 | 44  | 01000100 | D     |\r\n' +-----+-----+----------+-------+\r\n' |  69 | 45  | 01000101 | E     |\r\n' +-----+-----+----------+-------+\r\n' |  70 | 46  | 01000110 | F     |\r\n' +-----+-----+----------+-------+\r\n' |  71 | 47  | 01000111 | G     |\r\n' +-----+-----+----------+-------+\r\n' |  72 | 48  | 01001000 | H     |\r\n' +-----+-----+----------+-------+\r\n' |  73 | 49  | 01001001 | I     |\r\n' +-----+-----+----------+-------+\r\n' |  74 | 4A  | 01001010 | J     |\r\n' +-----+-----+----------+-------+\r\n' |  75 | 4B  | 01001011 | K     |\r\n' +-----+-----+----------+-------+\r\n' |  76 | 4C  | 01001100 | L     |\r\n' +-----+-----+----------+-------+\r\n' |  77 | 4D  | 01001101 | M     |\r\n' +-----+-----+----------+-------+\r\n' |  78 | 4E  | 01001110 | N     |\r\n' +-----+-----+----------+-------+\r\n' |  79 | 4F  | 01001111 | O     |\r\n' +-----+-----+----------+-------+\r\n' |  80 | 50  | 01010000 | P     |\r\n' +-----+-----+----------+-------+\r\n' |  81 | 51  | 01010001 | Q     |\r\n' +-----+-----+----------+-------+\r\n' |  82 | 52  | 01010010 | R     |\r\n' +-----+-----+----------+-------+\r\n' |  83 | 53  | 01010011 | S     |\r\n' +-----+-----+----------+-------+\r\n' |  84 | 54  | 01010100 | T     |\r\n' +-----+-----+----------+-------+\r\n' |  85 | 55  | 01010101 | U     |\r\n' +-----+-----+----------+-------+\r\n' |  86 | 56  | 01010110 | V     |\r\n' +-----+-----+----------+-------+\r\n' |  87 | 57  | 01010111 | W     |\r\n' +-----+-----+----------+-------+\r\n' |  88 | 58  | 01011000 | X     |\r\n' +-----+-----+----------+-------+\r\n' |  89 | 59  | 01011001 | Y     |\r\n' +-----+-----+----------+-------+\r\n' |  90 | 5A  | 01011010 | Z     |\r\n' +-----+-----+----------+-------+\r\n' |  91 | 5B  | 01011011 | [     |\r\n' +-----+-----+----------+-------+\r\n' |  92 | 5C  | 01011100 | \\     |\r\n' +-----+-----+----------+-------+\r\n' |  93 | 5D  | 01011101 | ]     |\r\n' +-----+-----+----------+-------+\r\n' |  94 | 5E  | 01011110 | ^     |\r\n' +-----+-----+----------+-------+\r\n' |  95 | 5F  | 01011111 | _     |\r\n' +-----+-----+----------+-------+\r\n' |  96 | 60  | 01100000 | `     |\r\n' +-----+-----+----------+-------+\r\n' |  97 | 61  | 01100001 | a     |\r\n' +-----+-----+----------+-------+\r\n' |  98 | 62  | 01100010 | b     |\r\n' +-----+-----+----------+-------+\r\n' |  99 | 63  | 01100011 | c     |\r\n' +-----+-----+----------+-------+\r\n' | 100 | 64  | 01100100 | d     |\r\n' +-----+-----+----------+-------+\r\n' | 101 | 65  | 01100101 | e     |\r\n' +-----+-----+----------+-------+\r\n' | 102 | 66  | 01100110 | f     |\r\n' +-----+-----+----------+-------+\r\n' | 103 | 67  | 01100111 | g     |\r\n' +-----+-----+----------+-------+\r\n' | 104 | 68  | 01101000 | h     |\r\n' +-----+-----+----------+-------+\r\n' | 105 | 69  | 01101001 | i     |\r\n' +-----+-----+----------+-------+\r\n' | 106 | 6A  | 01101010 | j     |\r\n' +-----+-----+----------+-------+\r\n' | 107 | 6B  | 01101011 | k     |\r\n' +-----+-----+----------+-------+\r\n' | 108 | 6C  | 01101100 | l     |\r\n' +-----+-----+----------+-------+\r\n' | 109 | 6D  | 01101101 | m     |\r\n' +-----+-----+----------+-------+\r\n' | 110 | 6E  | 01101110 | n     |\r\n' +-----+-----+----------+-------+\r\n' | 111 | 6F  | 01101111 | o     |\r\n' +-----+-----+----------+-------+\r\n' | 112 | 70  | 01110000 | p     |\r\n' +-----+-----+----------+-------+\r\n' | 113 | 71  | 01110001 | q     |\r\n' +-----+-----+----------+-------+\r\n' | 114 | 72  | 01110010 | r     |\r\n' +-----+-----+----------+-------+\r\n' | 115 | 73  | 01110011 | s     |\r\n' +-----+-----+----------+-------+\r\n' | 116 | 74  | 01110100 | t     |\r\n' +-----+-----+----------+-------+\r\n' | 117 | 75  | 01110101 | u     |\r\n' +-----+-----+----------+-------+\r\n' | 118 | 76  | 01110110 | v     |\r\n' +-----+-----+----------+-------+\r\n' | 119 | 77  | 01110111 | w     |\r\n' +-----+-----+----------+-------+\r\n' | 120 | 78  | 01111000 | x     |\r\n' +-----+-----+----------+-------+\r\n' | 121 | 79  | 01111001 | y     |\r\n' +-----+-----+----------+-------+\r\n' | 122 | 7A  | 01111010 | z     |\r\n' +-----+-----+----------+-------+\r\n' | 123 | 7B  | 01111011 | {     |\r\n' +-----+-----+----------+-------+\r\n' | 124 | 7C  | 01111100 | |     |\r\n' +-----+-----+----------+-------+\r\n' | 125 | 7D  | 01111101 | }     |\r\n' +-----+-----+----------+-------+\r\n' | 126 | 7E  | 01111110 | ~     |\r\n' +-----+-----+----------+-------+\r\n' | 127 | 7F  | 01111111 | DEL   |\r\n' +-----+-----+----------+-------+\r\n' | 128 | 80  | 10000000 | €     |\r\n' +-----+-----+----------+-------+\r\n' | 129 | 81  | 10000001 |       |\r\n' +-----+-----+----------+-------+\r\n' | 130 | 82  | 10000010 | ‚     |\r\n' +-----+-----+----------+-------+\r\n' | 131 | 83  | 10000011 | ƒ     |\r\n' +-----+-----+----------+-------+\r\n' | 132 | 84  | 10000100 | „     |\r\n' +-----+-----+----------+-------+\r\n' | 133 | 85  | 10000101 | …     |\r\n' +-----+-----+----------+-------+\r\n' | 134 | 86  | 10000110 | †     |\r\n' +-----+-----+----------+-------+\r\n' | 135 | 87  | 10000111 | ‡     |\r\n' +-----+-----+----------+-------+\r\n' | 136 | 88  | 10001000 | ˆ     |\r\n' +-----+-----+----------+-------+\r\n' | 137 | 89  | 10001001 | ‰     |\r\n' +-----+-----+----------+-------+\r\n' | 138 | 8A  | 10001010 | Š     |\r\n' +-----+-----+----------+-------+\r\n' | 139 | 8B  | 10001011 | ‹     |\r\n' +-----+-----+----------+-------+\r\n' | 140 | 8C  | 10001100 | Œ     |\r\n' +-----+-----+----------+-------+\r\n' | 141 | 8D  | 10001101 |       |\r\n' +-----+-----+----------+-------+\r\n' | 142 | 8E  | 10001110 | Ž     |\r\n' +-----+-----+----------+-------+\r\n' | 143 | 8F  | 10001111 |       |\r\n' +-----+-----+----------+-------+\r\n' | 144 | 90  | 10010000 |       |\r\n' +-----+-----+----------+-------+\r\n' | 145 | 91  | 10010001 | ‘     |\r\n' +-----+-----+----------+-------+\r\n' | 146 | 92  | 10010010 | ’     |\r\n' +-----+-----+----------+-------+\r\n' | 147 | 93  | 10010011 | “     |\r\n' +-----+-----+----------+-------+\r\n' | 148 | 94  | 10010100 | ”     |\r\n' +-----+-----+----------+-------+\r\n' | 149 | 95  | 10010101 | •     |\r\n' +-----+-----+----------+-------+\r\n' | 150 | 96  | 10010110 | –     |\r\n' +-----+-----+----------+-------+\r\n' | 151 | 97  | 10010111 | —     |\r\n' +-----+-----+----------+-------+\r\n' | 152 | 98  | 10011000 | ˜      |\r\n' +-----+-----+----------+-------+\r\n' | 153 | 99  | 10011001 | ™     |\r\n' +-----+-----+----------+-------+\r\n' | 154 | 9A  | 10011010 | š     |\r\n' +-----+-----+----------+-------+\r\n' | 155 | 9B  | 10011011 | ›     |\r\n' +-----+-----+----------+-------+\r\n' | 156 | 9C  | 10011100 | œ     |\r\n' +-----+-----+----------+-------+\r\n' | 157 | 9D  | 10011101 |       |\r\n' +-----+-----+----------+-------+\r\n' | 158 | 9E  | 10011110 | ž     |\r\n' +-----+-----+----------+-------+\r\n' | 159 | 9F  | 10011111 | Ÿ     |\r\n' +-----+-----+----------+-------+\r\n' | 160 | A0  | 10100000 |       |\r\n' +-----+-----+----------+-------+\r\n' | 161 | A1  | 10100001 | ¡     |\r\n' +-----+-----+----------+-------+\r\n' | 162 | A2  | 10100010 | ¢     |\r\n' +-----+-----+----------+-------+\r\n' | 163 | A3  | 10100011 | £     |\r\n' +-----+-----+----------+-------+\r\n' | 164 | A4  | 10100100 | ¤     |\r\n' +-----+-----+----------+-------+\r\n' | 165 | A5  | 10100101 | ¥     |\r\n' +-----+-----+----------+-------+\r\n' | 166 | A6  | 10100110 | ¦     |\r\n' +-----+-----+----------+-------+\r\n' | 167 | A7  | 10100111 | §     |\r\n' +-----+-----+----------+-------+\r\n' | 168 | A8  | 10101000 | ¨     |\r\n' +-----+-----+----------+-------+\r\n' | 169 | A9  | 10101001 | ©     |\r\n' +-----+-----+----------+-------+\r\n' | 170 | AA  | 10101010 | ª     |\r\n' +-----+-----+----------+-------+\r\n' | 171 | AB  | 10101011 | «     |\r\n' +-----+-----+----------+-------+\r\n' | 172 | AC  | 10101100 | ¬     |\r\n' +-----+-----+----------+-------+\r\n' | 173 | AD  | 10101101 | ­     |\r\n' +-----+-----+----------+-------+\r\n' | 174 | AE  | 10101110 | ®     |\r\n' +-----+-----+----------+-------+\r\n' | 175 | AF  | 10101111 | ¯     |\r\n' +-----+-----+----------+-------+\r\n' | 176 | B0  | 10110000 | °     |\r\n' +-----+-----+----------+-------+\r\n' | 177 | B1  | 10110001 | ±     |\r\n' +-----+-----+----------+-------+\r\n' | 178 | B2  | 10110010 | ²     |\r\n' +-----+-----+----------+-------+\r\n' | 179 | B3  | 10110011 | ³     |\r\n' +-----+-----+----------+-------+\r\n' | 180 | B4  | 10110100 | ´     |\r\n' +-----+-----+----------+-------+\r\n' | 181 | B5  | 10110101 | µ     |\r\n' +-----+-----+----------+-------+\r\n' | 182 | B6  | 10110110 | ¶     |\r\n' +-----+-----+----------+-------+\r\n' | 183 | B7  | 10110111 | ·     |\r\n' +-----+-----+----------+-------+\r\n' | 184 | B8  | 10111000 | ¸     |\r\n' +-----+-----+----------+-------+\r\n' | 185 | B9  | 10111001 | ¹     |\r\n' +-----+-----+----------+-------+\r\n' | 186 | BA  | 10111010 | º     |\r\n' +-----+-----+----------+-------+\r\n' | 187 | BB  | 10111011 | »     |\r\n' +-----+-----+----------+-------+\r\n' | 188 | BC  | 10111100 | ¼     |\r\n' +-----+-----+----------+-------+\r\n' | 189 | BD  | 10111101 | ½     |\r\n' +-----+-----+----------+-------+\r\n' | 190 | BE  | 10111110 | ¾     |\r\n' +-----+-----+----------+-------+\r\n' | 191 | BF  | 10111111 | ¿     |\r\n' +-----+-----+----------+-------+\r\n' | 192 | C0  | 11000000 | À     |\r\n' +-----+-----+----------+-------+\r\n' | 193 | C1  | 11000001 | Á     |\r\n' +-----+-----+----------+-------+\r\n' | 194 | C2  | 11000010 | Â     |\r\n' +-----+-----+----------+-------+\r\n' | 195 | C3  | 11000011 | Ã     |\r\n' +-----+-----+----------+-------+\r\n' | 196 | C4  | 11000100 | Ä     |\r\n' +-----+-----+----------+-------+\r\n' | 197 | C5  | 11000101 | Å     |\r\n' +-----+-----+----------+-------+\r\n' | 198 | C6  | 11000110 | Æ     |\r\n' +-----+-----+----------+-------+\r\n' | 199 | C7  | 11000111 | Ç     |\r\n' +-----+-----+----------+-------+\r\n' | 200 | C8  | 11001000 | È     |\r\n' +-----+-----+----------+-------+\r\n' | 201 | C9  | 11001001 | É     |\r\n' +-----+-----+----------+-------+\r\n' | 202 | CA  | 11001010 | Ê     |\r\n' +-----+-----+----------+-------+\r\n' | 203 | CB  | 11001011 | Ë     |\r\n' +-----+-----+----------+-------+\r\n' | 204 | CC  | 11001100 | Ì     |\r\n' +-----+-----+----------+-------+\r\n' | 205 | CD  | 11001101 | Í     |\r\n' +-----+-----+----------+-------+\r\n' | 206 | CE  | 11001110 | Î     |\r\n' +-----+-----+----------+-------+\r\n' | 207 | CF  | 11001111 | Ï     |\r\n' +-----+-----+----------+-------+\r\n' | 208 | D0  | 11010000 | Ð     |\r\n' +-----+-----+----------+-------+\r\n' | 209 | D1  | 11010001 | Ñ     |\r\n' +-----+-----+----------+-------+\r\n' | 210 | D2  | 11010010 | Ò     |\r\n' +-----+-----+----------+-------+\r\n' | 211 | D3  | 11010011 | Ó     |\r\n' +-----+-----+----------+-------+\r\n' | 212 | D4  | 11010100 | Ô     |\r\n' +-----+-----+----------+-------+\r\n' | 213 | D5  | 11010101 | Õ     |\r\n' +-----+-----+----------+-------+\r\n' | 214 | D6  | 11010110 | Ö     |\r\n' +-----+-----+----------+-------+\r\n' | 215 | D7  | 11010111 | ×     |\r\n' +-----+-----+----------+-------+\r\n' | 216 | D8  | 11011000 | Ø     |\r\n' +-----+-----+----------+-------+\r\n' | 217 | D9  | 11011001 | Ù     |\r\n' +-----+-----+----------+-------+\r\n' | 218 | DA  | 11011010 | Ú     |\r\n' +-----+-----+----------+-------+\r\n' | 219 | DB  | 11011011 | Û     |\r\n' +-----+-----+----------+-------+\r\n' | 220 | DC  | 11011100 | Ü     |\r\n' +-----+-----+----------+-------+\r\n' | 221 | DD  | 11011101 | Ý     |\r\n' +-----+-----+----------+-------+\r\n' | 222 | DE  | 11011110 | Þ     |\r\n' +-----+-----+----------+-------+\r\n' | 223 | DF  | 11011111 | ß     |\r\n' +-----+-----+----------+-------+\r\n' | 224 | E0  | 11100000 | à     |\r\n' +-----+-----+----------+-------+\r\n' | 225 | E1  | 11100001 | á     |\r\n' +-----+-----+----------+-------+\r\n' | 226 | E2  | 11100010 | â     |\r\n' +-----+-----+----------+-------+\r\n' | 227 | E3  | 11100011 | ã     |\r\n' +-----+-----+----------+-------+\r\n' | 228 | E4  | 11100100 | ä     |\r\n' +-----+-----+----------+-------+\r\n' | 229 | E5  | 11100101 | å     |\r\n' +-----+-----+----------+-------+\r\n' | 230 | E6  | 11100110 | æ     |\r\n' +-----+-----+----------+-------+\r\n' | 231 | E7  | 11100111 | ç     |\r\n' +-----+-----+----------+-------+\r\n' | 232 | E8  | 11101000 | è     |\r\n' +-----+-----+----------+-------+\r\n' | 233 | E9  | 11101001 | é     |\r\n' +-----+-----+----------+-------+\r\n' | 234 | EA  | 11101010 | ê     |\r\n' +-----+-----+----------+-------+\r\n' | 235 | EB  | 11101011 | ë     |\r\n' +-----+-----+----------+-------+\r\n' | 236 | EC  | 11101100 | ì     |\r\n' +-----+-----+----------+-------+\r\n' | 237 | ED  | 11101101 | í     |\r\n' +-----+-----+----------+-------+\r\n' | 238 | EE  | 11101110 | î     |\r\n' +-----+-----+----------+-------+\r\n' | 239 | EF  | 11101111 | ï     |\r\n' +-----+-----+----------+-------+\r\n' | 240 | F0  | 11110000 | ð     |\r\n' +-----+-----+----------+-------+\r\n' | 241 | F1  | 11110001 | ñ     |\r\n' +-----+-----+----------+-------+\r\n' | 242 | F2  | 11110010 | ò     |\r\n' +-----+-----+----------+-------+\r\n' | 243 | F3  | 11110011 | ó     |\r\n' +-----+-----+----------+-------+\r\n' | 244 | F4  | 11110100 | ô     |\r\n' +-----+-----+----------+-------+\r\n' | 245 | F5  | 11110101 | õ     |\r\n' +-----+-----+----------+-------+\r\n' | 246 | F6  | 11110110 | ö     |\r\n' +-----+-----+----------+-------+\r\n' | 247 | F7  | 11110111 | ÷     |\r\n' +-----+-----+----------+-------+\r\n' | 248 | F8  | 11111000 | ø     |\r\n' +-----+-----+----------+-------+\r\n' | 249 | F9  | 11111001 | ù     |\r\n' +-----+-----+----------+-------+\r\n' | 250 | FA  | 11111010 | ú     |\r\n' +-----+-----+----------+-------+\r\n' | 251 | FB  | 11111011 | û     |\r\n' +-----+-----+----------+-------+\r\n' | 252 | FC  | 11111100 | ü     |\r\n' +-----+-----+----------+-------+\r\n' | 253 | FD  | 11111101 | ý     |\r\n' +-----+-----+----------+-------+\r\n' | 254 | FE  | 11111110 | þ     |\r\n' +-----+-----+----------+-------+\r\n' | 255 | FF  | 11111111 | ÿ     |\r\n' +-----+-----+----------+-------+\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/modules/basUtility.bas",
    "content": "﻿Attribute VB_Name = \"basUtility\"\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' Keep a persistent reference to file system object after initializing version control.\r\n' This way we don't have to recreate this object dozens of times while using VCS.\r\nPrivate m_FSO As Scripting.FileSystemObject\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FSO\r\n' Author    : Adam Waller\r\n' Date      : 1/18/2019\r\n' Purpose   : Wrapper for file system object. A property allows us to clear the object\r\n'           : reference when we have completed an export or import operation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get FSO() As Scripting.FileSystemObject\r\n    If m_FSO Is Nothing Then Set m_FSO = New Scripting.FileSystemObject\r\n    Set FSO = m_FSO\r\nEnd Property\r\nPublic Property Set FSO(ByVal RHS As Scripting.FileSystemObject)\r\n    Set m_FSO = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RunAfterBuild\r\n' Author    : Adam Waller\r\n' Date      : 5/1/2020\r\n' Purpose   : Compile and run testing after build.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub RunAfterBuild()\r\n\r\n    ' Compile and save VBA code. Should prompt for any errors here.\r\n    DoCmd.RunCommand acCmdCompileAndSaveAllModules\r\n\r\n    ' Run startup macro to execute tests.\r\n    DoCmd.RunMacro \"AutoExec\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TestRegEx\r\n' Author    : Adam Waller\r\n' Date      : 5/1/2020\r\n' Purpose   : This tests VBA code that uses early binding with non-built-in\r\n'           : VBE References. (Add references before importing code)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub TestRegEx()\r\n    Dim regEx As VBScript_RegExp_55.RegExp\r\nEnd Sub\r\n\r\n\r\nPublic Sub TestProperties()\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim prp As AccessObjectProperty\r\n    Dim proj As CurrentProject\r\n\r\n    Set dbs = CurrentDb\r\n    Set proj = CurrentProject\r\n\r\n    For Each prp In proj.AllModules(0).Properties\r\n    'For Each prp In dbs.TableDefs(\"tblLinkedCSV\").Properties\r\n        Debug.Print prp.Name & \": \" & prp.Value\r\n    Next prp\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RunTests\r\n' Author    : Adam Waller\r\n' Date      : 5/13/2020\r\n' Purpose   : Run the tests (Fired from autoexec macro)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function RunTests()\r\n    Form_frmMain.cmdRunTests_Click\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetVBProjectForCurrentDB\r\n' Author    : Adam Waller\r\n' Date      : 7/25/2017\r\n' Purpose   : Get the actual VBE project for the current top-level database.\r\n'           : (This is harder than you would think!)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetVBProjectForCurrentDB() As VBProject\r\n\r\n    Dim objProj As Object\r\n    Dim strPath As String\r\n\r\n    strPath = CurrentProject.FullName\r\n    If VBE.ActiveVBProject.FileName = strPath Then\r\n        ' Use currently active project\r\n        Set GetVBProjectForCurrentDB = VBE.ActiveVBProject\r\n    Else\r\n        ' Search for project with matching filename.\r\n        For Each objProj In VBE.VBProjects\r\n            If objProj.FileName = strPath Then\r\n                Set GetVBProjectForCurrentDB = objProj\r\n                Exit For\r\n            End If\r\n        Next objProj\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetClassInstancing\r\n' Author    : Adam Waller\r\n' Date      : 11/25/2023\r\n' Purpose   : Set the clsPublic class to use Public Creatable instancing.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetClassInstancing()\r\n    Const PublicCreatable = 5\r\n    GetVBProjectForCurrentDB.VBComponents(\"clsPublic\").Properties(\"Instancing\") = PublicCreatable\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Menu\r\n' Author    : Adam Waller\r\n' Date      : 1/16/2024\r\n' Purpose   : Wrapper for persistent class instance\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Menu() As clsPopupMenu\r\n    Static cMenu As clsPopupMenu\r\n    If cMenu Is Nothing Then Set cMenu = New clsPopupMenu\r\n    Set Menu = cMenu\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MenuHandler\r\n' Author    : Adam Waller\r\n' Date      : 1/16/2024\r\n' Purpose   : Global handler for managing command bar events. The single parameter\r\n'           : is the name of the command bar, followed by the caption of the item.\r\n'           : I.e. \"MyCommandBar.Print Preview\"\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function MenuHandler(strSource As String)\r\n\r\n    Select Case strSource\r\n        Case \"CRM Navigation General.Contact\": MsgBox \"Contact\"\r\n        Case Else\r\n            Debug.Print \"Source not defined in MenuHandler: Case \"\"\" & strSource & \"\"\"\"\r\n    End Select\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : NZ2\r\n' Author    : Adam Waller\r\n' Date      : 2/18/2021\r\n' Purpose   : Extend the NZ function to also include 0 or empty string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function NZ2(varValue, Optional varIfNull) As Variant\r\n    Select Case varValue\r\n        Case vbNullString, 0\r\n            If IsMissing(varIfNull) Then\r\n                NZ2 = vbNullString\r\n            Else\r\n                NZ2 = varIfNull\r\n            End If\r\n        Case Else\r\n            If IsNull(varValue) Then\r\n                NZ2 = varIfNull\r\n            Else\r\n                NZ2 = varValue\r\n            End If\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MultiReplace\r\n' Author    : Adam Waller\r\n' Date      : 1/18/2019\r\n' Purpose   : Does a string replacement of multiple items in one call.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function MultiReplace(ByVal strText As String, ParamArray varPairs()) As String\r\n    Dim intPair As Integer\r\n    For intPair = 0 To UBound(varPairs) Step 2\r\n        strText = Replace(strText, varPairs(intPair), varPairs(intPair + 1))\r\n    Next intPair\r\n    MultiReplace = strText\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDatabaseFileName\r\n' Author    : Adam Waller\r\n' Date      : 2/8/2025\r\n' Purpose   : Function for use in queries\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetDatabaseFileName() As String\r\n    GetDatabaseFileName = CurrentDb.Name\r\nEnd Function\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/modules/clsPerson.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsPerson\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPublic FirstName As String\r\nPublic LastName As String\r\n\r\n\r\nPublic Sub Greet()\r\n    MsgBox \"Hello, \" & Me.FirstName & \"!\"\r\nEnd Sub\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/modules/clsPopupMenu.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsPopupMenu\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' API calls for getting screen cordinates in relation to controls\r\nPrivate Declare PtrSafe Function GetFocus Lib \"user32\" () As LongPtr\r\nPrivate Declare Function GetDeviceCaps Lib \"Gdi32\" (ByVal hDC As LongPtr, ByVal nIndex As Long) As Long\r\nPrivate Declare Function GetDC Lib \"user32\" (ByVal hWnd As LongPtr) As Long\r\nPrivate Declare Function ReleaseDC Lib \"user32\" (ByVal hWnd As LongPtr, ByVal hDC As LongPtr) As Long\r\nPrivate Declare Function GetCursorPos Lib \"user32\" (lpPoint As POINTAPI) As Long\r\nPrivate Declare PtrSafe Function GetWindowRect Lib \"user32\" (ByVal hWnd As LongPtr, ByRef lpRect As RECT) As Long\r\nPrivate Declare PtrSafe Function GetSystemMetrics Lib \"user32\" (ByVal nIndex As Long) As Long\r\n\r\nPrivate Const LOGPIXELSX = 88\r\nPrivate Const LOGPIXELSY = 90\r\n\r\nPrivate Type RECT\r\n  Left As Long\r\n  Top As Long\r\n  Right As Long\r\n  Bottom As Long\r\nEnd Type\r\n\r\nPrivate Type POINTAPI\r\n    x As Long\r\n    y As Long\r\nEnd Type\r\n\r\n'https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics\r\nPrivate Enum SystemMetricsConstants\r\n    SM_CYCAPTION = 4\r\n    SM_CXBORDER = 5\r\n    SM_CYBORDER = 6\r\n    SM_CXDLGFRAME = 7\r\n    SM_CYDLGFRAME = 8\r\n    SM_CXFRAME = 32\r\n    SM_CYFRAME = 33\r\n    SM_CXSIZEFRAME = SM_CXFRAME\r\n    SM_CYSIZEFRAME = SM_CYFRAME\r\nEnd Enum\r\n\r\n' Valid control types for popup menu controls\r\nPublic Enum eControlType\r\n    ectControlButton = msoControlButton\r\n    ectControlEdit = msoControlEdit\r\n    ectControlDropdown = msoControlDropdown\r\n    ectControlComboBox = msoControlComboBox\r\n    ectControlPopup = msoControlPopup\r\nEnd Enum\r\n\r\n' A reference to the menu bar we are working with\r\nPrivate m_barMenu As CommandBar\r\nPrivate dFaceIds As Dictionary\r\nPrivate lngTotal As Long\r\nPrivate lngNames As Long\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SysMetric\r\n' Author    : Adapted from work by Paul J. Champion in 2004\r\n' Date      : 1/22/2024\r\n' Purpose   : Return a system metric in pixels or twips.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SysMetric(SystemMetricRequired As SystemMetricsConstants, Optional ConvertToTwips As Boolean = True) As Variant\r\n    If ConvertToTwips Then\r\n        Select Case SystemMetricRequired\r\n            'Case SM_CYSCREEN, SM_CYHSCROLL, SM_CYCAPTION, _\r\n                    SM_CYBORDER, SM_CXDLGFRAME, SM_CYDLGFRAME, _\r\n                    SM_CYVTHUMB, SM_CYICON, SM_CYCURSOR, _\r\n                    SM_CYMENU, SM_CYFULLSCREEN, SM_CYKANJIWINDOW, _\r\n                    SM_CYVSCROLL, SM_CYMIN, SM_CYSIZE, _\r\n                    SM_CYFRAME, SM_CYMINTRACK, SM_CYDOUBLECLK, _\r\n                    SM_CYICONSPACING, SM_CYSIZEFRAME, SM_CYFIXEDFRAME\r\n            ' The ones we actually use\r\n            Case SM_CYCAPTION, SM_CYBORDER, SM_CXDLGFRAME, SM_CYDLGFRAME, _\r\n                    SM_CYFRAME, SM_CYSIZEFRAME\r\n                SysMetric = GetTwipsFromPixels(GetSystemMetrics(SystemMetricRequired), False)\r\n            Case Else: SysMetric = GetTwipsFromPixels(GetSystemMetrics(SystemMetricRequired), True)\r\n        End Select\r\n    Else\r\n        SysMetric = GetSystemMetrics(SystemMetricRequired)\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetPixelsFromTwips\r\n' Author    : Adam Waller\r\n' Date      : 1/19/2024\r\n' Purpose   : Return pixels (i.e. cursor position, menu position) from twips.\r\n'           : Caches the ratio value after first call.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetPixelsFromTwips(lngTwips As Long, blnX As Boolean) As Long\r\n\r\n    Static lngPixels(True To False) As Long\r\n    Dim hDC As Long\r\n\r\n    If lngPixels(blnX) = 0 Then\r\n        hDC = GetDC(Application.hWndAccessApp)\r\n        lngPixels(blnX) = 1440 / GetDeviceCaps(hDC, IIf(blnX, LOGPIXELSX, LOGPIXELSY))\r\n        ReleaseDC Application.hWndAccessApp, hDC\r\n    End If\r\n    GetPixelsFromTwips = lngTwips / lngPixels(blnX)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTwipsFromPixels\r\n' Author    : Adam Waller\r\n' Date      : 1/22/2024\r\n' Purpose   : Return a twips size from a pixel size.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetTwipsFromPixels(lngPixels As Long, blnX As Boolean) As Long\r\n\r\n    Static lngTwips(True To False) As Long\r\n    Dim hDC As Long\r\n\r\n    If lngTwips(blnX) = 0 Then\r\n        hDC = GetDC(Application.hWndAccessApp)\r\n        lngTwips(blnX) = 1440 / GetDeviceCaps(hDC, IIf(blnX, LOGPIXELSX, LOGPIXELSY))\r\n        ReleaseDC Application.hWndAccessApp, hDC\r\n    End If\r\n    GetTwipsFromPixels = lngPixels * lngTwips(blnX)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PopupBelow\r\n' Author    : Adam Waller\r\n' Date      : 1/19/2024\r\n' Purpose   : Displays a popup menu below a specified control. (From bottom left corner)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub PopupBelow(ctl As Access.Control, strMenu As String)\r\n\r\n    Dim objParent As Object\r\n    Dim lngFocus As Long\r\n    Dim lngOffsetX As Long\r\n    Dim lngOffsetY As Long\r\n    Dim hwdMdi As LongPtr\r\n    Dim recMdi As RECT\r\n\r\n    ' Work our way back up to the parent form, calculating the offset as we go.\r\n    Set objParent = ctl\r\n    Do\r\n        lngOffsetX = lngOffsetX + objParent.Left\r\n        lngOffsetY = lngOffsetY + objParent.Top\r\n        Set objParent = objParent.Parent\r\n        If objParent Is Screen.ActiveForm Then\r\n            ' We have a reference to the parent form\r\n            Exit Do\r\n        End If\r\n    Loop\r\n\r\n    ' Add height of control to set popup just below\r\n    lngOffsetY = lngOffsetY + ctl.Height\r\n\r\n    ' Add any title bar height for popup forms\r\n    If objParent.PopUp Then\r\n        Select Case objParent.BorderStyle\r\n            Case 0      ' None\r\n            Case 1, 3   ' Thin, Dialog\r\n                lngOffsetX = lngOffsetX + SysMetric(SM_CXDLGFRAME)\r\n                lngOffsetY = lngOffsetY + SysMetric(SM_CYCAPTION) + SysMetric(SM_CYDLGFRAME)\r\n            Case 2      ' Sizeable\r\n                lngOffsetX = lngOffsetX + SysMetric(SM_CXSIZEFRAME) + SysMetric(SM_CXBORDER)\r\n                lngOffsetY = lngOffsetY + SysMetric(SM_CYCAPTION) + SysMetric(SM_CYSIZEFRAME) + SysMetric(SM_CYBORDER)\r\n        End Select\r\n    Else\r\n        ' Subtract border size from x,y to reflect the actual edge metric\r\n        lngOffsetX = lngOffsetX - SysMetric(SM_CXBORDER) * 2\r\n        lngOffsetY = lngOffsetY - SysMetric(SM_CYBORDER) * 2\r\n    End If\r\n\r\n    ' Get the top left coordinates of the Access application multi document interface area,\r\n    ' Or the open form, in the case of a popup form.\r\n    hwdMdi = GetFocus\r\n    GetWindowRect hwdMdi, recMdi\r\n\r\n    ' Show the popup at the specified location (Left aligned)\r\n    CommandBars(strMenu).ShowPopup _\r\n        GetPixelsFromTwips(lngOffsetX, True) + recMdi.Left + CommandBars(strMenu).Width, _\r\n        GetPixelsFromTwips(lngOffsetY, False) + recMdi.Top\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddCustomControl\r\n' Author    : Adam Waller\r\n' Date      : 1/16/2024\r\n' Purpose   : Add a custom control to the popup menu\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddCustomControl(intType As eControlType, strCaption As String, Optional intFaceID As Integer, _\r\n    Optional strMsoImageId As String, Optional barMenu As CommandBar) As CommandBarControl\r\n\r\n    ' Make sure we have a menu bar to work with\r\n    If Not BarDefined(barMenu) Then Exit Function\r\n\r\n    ' Add control\r\n    Set AddCustomControl = m_barMenu.Controls.Add(intType, , , , False)\r\n    With AddCustomControl\r\n        .Caption = strCaption\r\n        If intFaceID <> 0 Then .FaceId = intFaceID\r\n        If Len(strMsoImageId) Then .Picture = CommandBars.GetImageMso(strMsoImageId, 16, 16)\r\n        CheckControlActions m_barMenu.Controls, m_barMenu.Name\r\n    End With\r\n\r\n    ' Popup menu to show new control\r\n    m_barMenu.ShowPopup\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddExistingControl\r\n' Author    : Adam Waller\r\n' Date      : 1/22/2024\r\n' Purpose   : Add an existing (built-in) control to the menu.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddExistingControl(lngId As Long, Optional barMenu As CommandBar)\r\n\r\n    'Dim ctlExisting As Object\r\n\r\n    ' Make sure we have a menu bar to work with\r\n    If Not BarDefined(barMenu) Then Exit Function\r\n\r\n    ' Add control\r\n    Set AddExistingControl = m_barMenu.Controls.Add(, lngId, , , False)\r\n'    With AddExistingControl\r\n'        .Caption = strCaption\r\n'        If intFaceID <> 0 Then .FaceId = intFaceID\r\n'        If Len(strMsoImageId) Then .Picture = CommandBars.GetImageMso(strMsoImageId, 16, 16)\r\n'        CheckControlActions m_barMenu.Controls, m_barMenu.Name\r\n'    End With\r\n\r\n    ' Popup menu to show new control\r\n    m_barMenu.ShowPopup\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FindID\r\n' Author    : Dale Fye\r\n' Date      : 2014-02-08\r\n' Purpose   : Find control IDs with matching captions. (Existing items)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub FindID(CaptionContains As String)\r\n    Dim cbr As CommandBar\r\n    Dim ctrl As CommandBarControl\r\n\r\n    For Each cbr In Application.CommandBars\r\n        For Each ctrl In cbr.Controls\r\n\r\n            'Because many of the controls contain keyboard shortcuts, they\r\n            'have ampersands embedded within the caption that must be\r\n            'removed before doing the comparison to the search string.\r\n            If InStr(Replace(ctrl.Caption, \"&\", \"\"), CaptionContains) > 0 Then\r\n                Debug.Print cbr.Name, ctrl.Caption, ctrl.Id\r\n            End If\r\n        Next\r\n    Next\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LastItem\r\n' Author    : Adam Waller\r\n' Date      : 1/22/2024\r\n' Purpose   : Get the last control added to the command bar.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function LastItem(Optional barMenu As CommandBar) As CommandBarControl\r\n\r\n    ' Make sure we have a menu bar to work with\r\n    If Not BarDefined(barMenu) Then Exit Function\r\n\r\n    ' Return last control\r\n    Set LastItem = m_barMenu.Controls(m_barMenu.Controls.Count)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyOnAction\r\n' Author    : Adam Waller\r\n' Date      : 1/16/2024\r\n' Purpose   : Update the command bar actions to reflect the command bar name, and the\r\n'           : caption of the control. (Without the ampersand control characters)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub VerifyOnAction(Optional cbrMenu As CommandBar)\r\n\r\n    Dim strTemp As String\r\n    Dim cbr As CommandBar\r\n    Dim ctl As CommandBarControl\r\n\r\n    ' Make sure we have a menu bar to work with\r\n    If Not BarDefined(cbrMenu) Then Exit Sub\r\n\r\n    ' Loop through custom command bars\r\n    For Each cbr In CommandBars\r\n        If Not cbr.BuiltIn Then\r\n            ' Recursively process controls\r\n            CheckControlActions m_barMenu.Controls, cbr.Name\r\n        End If\r\n    Next cbr\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckControlActions\r\n' Author    : Adam Waller\r\n' Date      : 1/16/2024\r\n' Purpose   : Recursive function to check command bar control actions\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub CheckControlActions(ctls As CommandBarControls, strPath As String)\r\n\r\n    Dim strName As String\r\n    Dim strSource As String\r\n    Dim ctl As CommandBarControl\r\n    Dim strTemp As String\r\n    Dim strOnAction As String\r\n\r\n    ' Loop through controls\r\n    For Each ctl In ctls\r\n        If TypeOf ctl Is CommandBarPopup Then\r\n            ' Recursively call this function on the nested controls.\r\n            CheckControlActions ctl.Controls, strPath & \".\" & ctl.Name\r\n        Else\r\n            ' Use caption or index to make name\r\n            strName = NZ2(ctl.Caption, ctl.Index)\r\n\r\n            ' Build source path\r\n            strSource = strPath & \".\" & strName\r\n\r\n            ' Add a Unicode placeholder for double ampersand, and remove\r\n            ' control ampersand characters to make captions more readable.\r\n            strTemp = ChrW(55357) & ChrW(56999)\r\n            strSource = MultiReplace(strSource, _\r\n                \"&&\", strTemp, _\r\n                \"&\", vbNullString, _\r\n                strTemp, \"&\", _\r\n                \"\"\"\", vbNullString)\r\n\r\n            ' Build out the desired OnAction value\r\n            strOnAction = \"=MenuHandler(\"\"\" & strSource & \"\"\")\"\r\n            If ctl.OnAction <> strOnAction Then ctl.OnAction = strOnAction\r\n        End If\r\n    Next ctl\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddMenu\r\n' Author    : Adam Waller\r\n' Date      : 1/16/2024\r\n' Purpose   : Add a new command bar (popup menu)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddMenu(strName As String) As CommandBar\r\n    Set m_barMenu = CommandBars.Add(strName, msoBarPopup)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ListCustomMenus\r\n' Author    : Adam Waller\r\n' Date      : 1/16/2024\r\n' Purpose   : List out any custom command bars in the current database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ListCustomMenus()\r\n    Dim bar As CommandBar\r\n    Debug.Print \"Custom Command Bars:\"\r\n    Debug.Print \"==========================\"\r\n    For Each bar In CommandBars\r\n        If Not bar.BuiltIn Then Debug.Print \"CommandBars(\"\"\" & bar.Name & \"\"\")\"\r\n    Next bar\r\n    Debug.Print \"==========================\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetPictureFromMsoImage\r\n' Author    : Adam Waller\r\n' Date      : 1/17/2024\r\n' Purpose   : Apply a MSO image to a command bar control (popup menu item)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetPictureFromMsoImage(ctl As CommandBarControl, strImageCaseSensitive As String)\r\n    ctl.Picture = CommandBars.GetImageMso(strImageCaseSensitive, 16, 16)\r\n    ctl.Parent.ShowPopup\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BarDefined\r\n' Author    : Adam Waller\r\n' Date      : 1/16/2024\r\n' Purpose   : Returns true if a command bar object has been specified, either in this\r\n'           : call, or a previous one.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function BarDefined(cbrMenu As CommandBar) As Boolean\r\n    If Not cbrMenu Is Nothing Then Set m_barMenu = cbrMenu\r\n    If m_barMenu Is Nothing Then\r\n        MsgBox \"You must specify a menu bar object on first call\", vbExclamation\r\n    Else\r\n        BarDefined = True\r\n    End If\r\nEnd Function\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/modules/clsPublic.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsPublic\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = True\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsPublic\r\n' Author    : Adam Waller\r\n' Date      : 11/25/2023\r\n' Purpose   : This class is set to public creatable instancing.\r\n'---------------------------------------------------------------------------------------\r\n\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPublic MyProperty As String\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/nav-pane-groups.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbNavPaneGroup\",\r\n    \"Description\": \"Navigation Pane Custom Groups\"\r\n  },\r\n  \"Items\": {\r\n    \"Categories\": [\r\n      {\r\n        \"Name\": \"My Category\",\r\n        \"Flags\": 0,\r\n        \"Position\": 2,\r\n        \"Groups\": [\r\n          {\r\n            \"Name\": \"Macros & Reports\",\r\n            \"Flags\": 0,\r\n            \"Position\": 1,\r\n            \"Objects\": [\r\n              {\r\n                \"Name\": \"AutoExec\",\r\n                \"Type\": -32766,\r\n                \"Flags\": 0,\r\n                \"Icon\": 0,\r\n                \"Position\": 0\r\n              },\r\n              {\r\n                \"Name\": \"rptDefaultPrinter\",\r\n                \"Type\": -32764,\r\n                \"Flags\": 0,\r\n                \"Icon\": 0,\r\n                \"Position\": 1\r\n              },\r\n              {\r\n                \"Name\": \"rptNavigationPaneGroups\",\r\n                \"Type\": -32764,\r\n                \"Flags\": 0,\r\n                \"Icon\": 0,\r\n                \"Position\": 2\r\n              }\r\n            ]\r\n          },\r\n          {\r\n            \"Name\": \"My Modules\",\r\n            \"Flags\": 0,\r\n            \"Position\": 2,\r\n            \"Objects\": [\r\n              {\r\n                \"Name\": \"Module1\",\r\n                \"Type\": -32761,\r\n                \"Flags\": 0,\r\n                \"Icon\": 0,\r\n                \"Position\": 1\r\n              }\r\n            ]\r\n          },\r\n          {\r\n            \"Name\": \"Unassigned Objects\",\r\n            \"Flags\": 4,\r\n            \"Position\": 3,\r\n            \"Objects\": [\r\n            ]\r\n          }\r\n        ]\r\n      }\r\n    ]\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/proj-properties.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbProjProperty\",\r\n    \"Description\": \"Project Properties (Access)\"\r\n  },\r\n  \"Items\": {\r\n    \"ProjectProperty\": \"TestValue\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/project.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbProject\",\r\n    \"Description\": \"Project\"\r\n  },\r\n  \"Items\": {\r\n    \"FileFormat\": 12,\r\n    \"RemovePersonalInformation\": false\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/queries/qryFormControl.bas",
    "content": "﻿Operation =1\r\nOption =0\r\nBegin InputTables\r\nEnd\r\nBegin OutputColumns\r\n    Alias =\"FormControl\"\r\n    Expression =\"[Forms]![frmColors]![Text18]\"\r\n    Alias =\"TestExpression\"\r\n    Expression =\"IIf([Forms]![frmVCSInstall]![chkUseRibbon],Eval(\\\"True\\\"),False)\"\r\nEnd\r\ndbBoolean \"ReturnsRecords\" =\"-1\"\r\ndbInteger \"ODBCTimeout\" =\"60\"\r\ndbBoolean \"OrderByOn\" =\"0\"\r\ndbByte \"Orientation\" =\"0\"\r\ndbByte \"DefaultView\" =\"2\"\r\ndbBoolean \"FilterOnLoad\" =\"0\"\r\ndbBoolean \"OrderByOnLoad\" =\"-1\"\r\ndbByte \"RecordsetType\" =\"0\"\r\ndbBoolean \"TotalsRow\" =\"0\"\r\nBegin\r\n    Begin\r\n        dbText \"Name\" =\"FormControl\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n        dbInteger \"ColumnWidth\" =\"1590\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"TestExpression\"\r\n        dbInteger \"ColumnWidth\" =\"1815\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\nEnd\r\nBegin\r\n    State =0\r\n    Left =0\r\n    Top =0\r\n    Right =1368\r\n    Bottom =856\r\n    Left =-1\r\n    Top =-1\r\n    Right =1352\r\n    Bottom =577\r\n    Left =0\r\n    Top =0\r\n    ColumnsShown =539\r\nEnd\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/queries/qryFormControl.sql",
    "content": "﻿SELECT\r\n  [Forms]![frmColors]![Text18] AS FormControl,\r\n  IIf(\r\n    [Forms]![frmVCSInstall]![chkUseRibbon],\r\n    Eval(\"True\"),\r\n    False\r\n  ) AS TestExpression;\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/queries/qryNavigationPaneGroups.bas",
    "content": "﻿Operation =1\r\nOption =0\r\nWhere =\"(((MSysNavPaneGroups.Name) Is Not Null) AND ((MSysNavPaneGroups.GroupCategoryID)\"\r\n    \"=3))\"\r\nBegin InputTables\r\n    Name =\"MSysNavPaneGroups\"\r\n    Name =\"MSysNavPaneGroupToObjects\"\r\n    Name =\"MSysObjects\"\r\nEnd\r\nBegin OutputColumns\r\n    Alias =\"GroupName\"\r\n    Expression =\"MSysNavPaneGroups.Name\"\r\n    Alias =\"GroupFlags\"\r\n    Expression =\"MSysNavPaneGroups.Flags\"\r\n    Alias =\"GroupPosition\"\r\n    Expression =\"MSysNavPaneGroups.Position\"\r\n    Alias =\"ObjectType\"\r\n    Expression =\"MSysObjects.Type\"\r\n    Alias =\"ObjectName\"\r\n    Expression =\"MSysObjects.Name\"\r\n    Alias =\"ObjectFlags\"\r\n    Expression =\"MSysNavPaneGroupToObjects.Flags\"\r\n    Alias =\"ObjectIcon\"\r\n    Expression =\"MSysNavPaneGroupToObjects.Icon\"\r\n    Alias =\"ObjectPosition\"\r\n    Expression =\"MSysNavPaneGroupToObjects.Position\"\r\nEnd\r\nBegin Joins\r\n    LeftTable =\"MSysNavPaneGroupToObjects\"\r\n    RightTable =\"MSysObjects\"\r\n    Expression =\"MSysNavPaneGroupToObjects.ObjectID = MSysObjects.Id\"\r\n    Flag =2\r\n    LeftTable =\"MSysNavPaneGroups\"\r\n    RightTable =\"MSysNavPaneGroupToObjects\"\r\n    Expression =\"MSysNavPaneGroups.Id = MSysNavPaneGroupToObjects.GroupID\"\r\n    Flag =2\r\nEnd\r\nBegin OrderBy\r\n    Expression =\"MSysNavPaneGroups.Name\"\r\n    Flag =0\r\n    Expression =\"MSysObjects.Type\"\r\n    Flag =0\r\n    Expression =\"MSysObjects.Name\"\r\n    Flag =0\r\nEnd\r\ndbBoolean \"ReturnsRecords\" =\"-1\"\r\ndbInteger \"ODBCTimeout\" =\"60\"\r\ndbBoolean \"OrderByOn\" =\"0\"\r\ndbByte \"Orientation\" =\"0\"\r\ndbByte \"DefaultView\" =\"2\"\r\ndbBoolean \"FilterOnLoad\" =\"0\"\r\ndbBoolean \"OrderByOnLoad\" =\"-1\"\r\ndbByte \"RecordsetType\" =\"0\"\r\ndbBoolean \"TotalsRow\" =\"0\"\r\nBegin\r\n    Begin\r\n        dbText \"Name\" =\"GroupName\"\r\n        dbInteger \"ColumnWidth\" =\"1545\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectName\"\r\n        dbInteger \"ColumnWidth\" =\"1665\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectType\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"GroupFlags\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"GroupPosition\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectFlags\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectIcon\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectPosition\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\nEnd\r\nBegin\r\n    State =0\r\n    Left =0\r\n    Top =0\r\n    Right =1065\r\n    Bottom =800\r\n    Left =-1\r\n    Top =-1\r\n    Right =727\r\n    Bottom =504\r\n    Left =0\r\n    Top =0\r\n    ColumnsShown =539\r\n    Begin\r\n        Left =48\r\n        Top =12\r\n        Right =210\r\n        Bottom =197\r\n        Top =0\r\n        Name =\"MSysNavPaneGroups\"\r\n        Name =\"\"\r\n    End\r\n    Begin\r\n        Left =251\r\n        Top =15\r\n        Right =450\r\n        Bottom =212\r\n        Top =0\r\n        Name =\"MSysNavPaneGroupToObjects\"\r\n        Name =\"\"\r\n    End\r\n    Begin\r\n        Left =487\r\n        Top =14\r\n        Right =666\r\n        Bottom =373\r\n        Top =0\r\n        Name =\"MSysObjects\"\r\n        Name =\"\"\r\n    End\r\nEnd\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/queries/qryNavigationPaneGroups.sql",
    "content": "﻿SELECT\r\n  MSysNavPaneGroups.Name AS GroupName,\r\n  MSysNavPaneGroups.Flags AS GroupFlags,\r\n  MSysNavPaneGroups.Position AS GroupPosition,\r\n  MSysObjects.Type AS ObjectType,\r\n  MSysObjects.Name AS ObjectName,\r\n  MSysNavPaneGroupToObjects.Flags AS ObjectFlags,\r\n  MSysNavPaneGroupToObjects.Icon AS ObjectIcon,\r\n  MSysNavPaneGroupToObjects.Position AS ObjectPosition\r\nFROM\r\n  MSysNavPaneGroups\r\n  LEFT JOIN (\r\n    MSysNavPaneGroupToObjects\r\n    LEFT JOIN MSysObjects ON MSysNavPaneGroupToObjects.ObjectID = MSysObjects.Id\r\n  ) ON MSysNavPaneGroups.Id = MSysNavPaneGroupToObjects.GroupID\r\nWHERE\r\n  (\r\n    (\r\n      (MSysNavPaneGroups.Name) Is Not Null\r\n    )\r\n    AND (\r\n      (\r\n        MSysNavPaneGroups.GroupCategoryID\r\n      )= 3\r\n    )\r\n  )\r\nORDER BY\r\n  MSysNavPaneGroups.Name,\r\n  MSysObjects.Type,\r\n  MSysObjects.Name;\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/queries/qryTestSqlFormat.bas",
    "content": "﻿dbMemo \"SQL\" =\"SELECT MSysQueries.Attribute, MSysQueries.Flag\\015\\012FROM MSysQueries\\015\\012WH\"\r\n    \"ERE (((MSysQueries.Flag) In (SELECT\\015\\012           Flag\\015\\012         from\\015\"\r\n    \"\\012           [MSysQueries]\\015\\012         where\\015\\012           flag = 0\\015\"\r\n    \"\\012         )));\\015\\012\"\r\ndbMemo \"Connect\" =\"\"\r\ndbBoolean \"ReturnsRecords\" =\"-1\"\r\ndbInteger \"ODBCTimeout\" =\"60\"\r\ndbBoolean \"OrderByOn\" =\"0\"\r\ndbByte \"Orientation\" =\"0\"\r\ndbByte \"DefaultView\" =\"2\"\r\ndbBoolean \"FilterOnLoad\" =\"0\"\r\ndbBoolean \"OrderByOnLoad\" =\"-1\"\r\nBegin\r\nEnd\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/queries/qryTestSqlFormat.sql",
    "content": "﻿SELECT\r\n  MSysQueries.Attribute,\r\n  MSysQueries.Flag\r\nFROM\r\n  MSysQueries\r\nWHERE\r\n  (\r\n    (\r\n      (MSysQueries.Flag) In (\r\n        SELECT\r\n          Flag\r\n        from\r\n          [MSysQueries]\r\n        where\r\n          flag = 0\r\n      )\r\n    )\r\n  );\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/queries/qryVBAFunction.bas",
    "content": "﻿Operation =1\r\nOption =0\r\nBegin InputTables\r\n    Name =\"tblInternal\"\r\nEnd\r\nBegin OutputColumns\r\n    Alias =\"DatabaseFile\"\r\n    Expression =\"GetDatabaseFileName()\"\r\nEnd\r\ndbBoolean \"ReturnsRecords\" =\"-1\"\r\ndbInteger \"ODBCTimeout\" =\"60\"\r\ndbBoolean \"OrderByOn\" =\"0\"\r\ndbByte \"Orientation\" =\"0\"\r\ndbByte \"DefaultView\" =\"2\"\r\ndbBoolean \"FilterOnLoad\" =\"0\"\r\ndbBoolean \"OrderByOnLoad\" =\"-1\"\r\ndbByte \"RecordsetType\" =\"0\"\r\ndbBoolean \"TotalsRow\" =\"0\"\r\nBegin\r\n    Begin\r\n        dbText \"Name\" =\"DatabaseFile\"\r\n        dbInteger \"ColumnWidth\" =\"8700\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\nEnd\r\nBegin\r\n    State =0\r\n    Left =0\r\n    Top =0\r\n    Right =1305\r\n    Bottom =848\r\n    Left =-1\r\n    Top =-1\r\n    Right =1289\r\n    Bottom =586\r\n    Left =0\r\n    Top =0\r\n    ColumnsShown =539\r\n    Begin\r\n        Left =105\r\n        Top =99\r\n        Right =277\r\n        Bottom =318\r\n        Top =0\r\n        Name =\"tblInternal\"\r\n        Name =\"\"\r\n    End\r\nEnd\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/queries/qryVBAFunction.sql",
    "content": "﻿SELECT\r\n  GetDatabaseFileName() AS DatabaseFile\r\nFROM\r\n  tblInternal;\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/relations/tblInternaltblSaveXML.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbRelation\",\r\n    \"Description\": \"Database relationship\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"tblInternaltblSaveXML\",\r\n    \"Attributes\": 2,\r\n    \"Table\": \"tblInternal\",\r\n    \"ForeignTable\": \"tblSaveXML\",\r\n    \"Fields\": [\r\n      {\r\n        \"Name\": \"ObjectType\",\r\n        \"ForeignName\": \"ObjectType\"\r\n      }\r\n    ]\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/reports/rptDefaultPrinter.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Report\r\n    LayoutForPrint = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    TabularFamily =0\r\n    DateGrouping =1\r\n    GrpKeepTogether =1\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =8884\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =1\r\n    RecSrcDt = Begin\r\n        0xe05ff061b477e540\r\n    End\r\n    DatasheetFontName =\"Franklin Gothic Book\"\r\n    FilterOnLoad =0\r\n    FitToPage =1\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin PageHeader\r\n            Height =360\r\n            Name =\"PageHeaderSection\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n        End\r\n        Begin Section\r\n            KeepTogether = NotDefault\r\n            Height =7560\r\n            Name =\"Detail\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin Label\r\n                    TextFontFamily =0\r\n                    Left =1260\r\n                    Top =240\r\n                    Width =4800\r\n                    Height =420\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label0\"\r\n                    Caption =\"This report does not have any saved print settings.\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =1260\r\n                    LayoutCachedTop =240\r\n                    LayoutCachedWidth =6060\r\n                    LayoutCachedHeight =660\r\n                End\r\n            End\r\n        End\r\n        Begin PageFooter\r\n            Height =360\r\n            Name =\"PageFooterSection\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"rptDefaultPrinter.cls\"\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/reports/rptDefaultPrinter.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/reports/rptNavigationPaneGroups.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Report\r\n    LayoutForPrint = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    TabularFamily =0\r\n    DateGrouping =1\r\n    GrpKeepTogether =1\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =11700\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =7\r\n    RecSrcDt = Begin\r\n        0x2df649898e77e540\r\n    End\r\n    RecordSource =\"qryNavigationPaneGroups\"\r\n    Caption =\"qryNavigationPaneGroups\"\r\n    DatasheetFontName =\"Franklin Gothic Book\"\r\n    FilterOnLoad =0\r\n    FitToPage =1\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            ShowDatePicker =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin BreakLevel\r\n            GroupHeader = NotDefault\r\n            ControlSource =\"ObjectName\"\r\n        End\r\n        Begin BreakLevel\r\n            ControlSource =\"GroupName\"\r\n        End\r\n        Begin FormHeader\r\n            KeepTogether = NotDefault\r\n            Height =960\r\n            Name =\"ReportHeader\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =2\r\n            BackTint =20.0\r\n            Begin\r\n                Begin Label\r\n                    TextFontFamily =0\r\n                    Left =60\r\n                    Top =60\r\n                    Width =4350\r\n                    Height =540\r\n                    FontSize =20\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label4\"\r\n                    Caption =\"qryNavigationPaneGroups\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =60\r\n                    LayoutCachedTop =60\r\n                    LayoutCachedWidth =4410\r\n                    LayoutCachedHeight =600\r\n                End\r\n            End\r\n        End\r\n        Begin PageHeader\r\n            Height =435\r\n            Name =\"PageHeaderSection\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin Label\r\n                    TextAlign =1\r\n                    TextFontFamily =0\r\n                    Left =360\r\n                    Top =60\r\n                    Width =3660\r\n                    Height =315\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"ObjectName_Label\"\r\n                    Caption =\"ObjectName\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    Tag =\"DetachedLabel\"\r\n                    GridlineStyleBottom =1\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =360\r\n                    LayoutCachedTop =60\r\n                    LayoutCachedWidth =4020\r\n                    LayoutCachedHeight =375\r\n                End\r\n                Begin Label\r\n                    TextAlign =1\r\n                    TextFontFamily =0\r\n                    Left =4380\r\n                    Top =60\r\n                    Width =7260\r\n                    Height =315\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"GroupName_Label\"\r\n                    Caption =\"GroupName\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    Tag =\"DetachedLabel\"\r\n                    GridlineStyleBottom =1\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =4380\r\n                    LayoutCachedTop =60\r\n                    LayoutCachedWidth =11640\r\n                    LayoutCachedHeight =375\r\n                End\r\n            End\r\n        End\r\n        Begin BreakHeader\r\n            KeepTogether = NotDefault\r\n            Height =390\r\n            Name =\"GroupHeader0\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin TextBox\r\n                    DecimalPlaces =0\r\n                    OldBorderStyle =0\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =360\r\n                    Width =3660\r\n                    Height =330\r\n                    ColumnWidth =1665\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"ObjectName\"\r\n                    ControlSource =\"ObjectName\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =360\r\n                    LayoutCachedWidth =4020\r\n                    LayoutCachedHeight =330\r\n                End\r\n            End\r\n        End\r\n        Begin Section\r\n            KeepTogether = NotDefault\r\n            Height =390\r\n            Name =\"Detail\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin TextBox\r\n                    OldBorderStyle =0\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =4380\r\n                    Width =7260\r\n                    Height =330\r\n                    ColumnWidth =1545\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"GroupName\"\r\n                    ControlSource =\"GroupName\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =4380\r\n                    LayoutCachedWidth =11640\r\n                    LayoutCachedHeight =330\r\n                End\r\n            End\r\n        End\r\n        Begin PageFooter\r\n            Height =570\r\n            Name =\"PageFooterSection\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin TextBox\r\n                    OldBorderStyle =0\r\n                    TextAlign =1\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =60\r\n                    Top =240\r\n                    Width =5040\r\n                    Height =330\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"Text5\"\r\n                    ControlSource =\"=Now()\"\r\n                    Format =\"Long Date\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =60\r\n                    LayoutCachedTop =240\r\n                    LayoutCachedWidth =5100\r\n                    LayoutCachedHeight =570\r\n                End\r\n                Begin TextBox\r\n                    OldBorderStyle =0\r\n                    TextAlign =3\r\n                    TextFontFamily =0\r\n                    IMESentenceMode =3\r\n                    Left =6600\r\n                    Top =240\r\n                    Width =5040\r\n                    Height =330\r\n                    TabIndex =1\r\n                    BorderColor =10921638\r\n                    ForeColor =4210752\r\n                    Name =\"Text6\"\r\n                    ControlSource =\"=\\\"Page \\\" & [Page] & \\\" of \\\" & [Pages]\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n\r\n                    LayoutCachedLeft =6600\r\n                    LayoutCachedTop =240\r\n                    LayoutCachedWidth =11640\r\n                    LayoutCachedHeight =570\r\n                End\r\n            End\r\n        End\r\n        Begin FormFooter\r\n            KeepTogether = NotDefault\r\n            Height =0\r\n            Name =\"ReportFooter\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"rptNavigationPaneGroups.cls\"\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/reports/rptNavigationPaneGroups.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/reports/rptNavigationPaneGroups.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbReport\",\r\n    \"Description\": \"rptNavigationPaneGroups Print Settings\"\r\n  },\r\n  \"Items\": {\r\n    \"Printer\": {\r\n      \"Orientation\": \"Landscape\",\r\n      \"PaperSize\": \"Letter\"\r\n    },\r\n    \"Margins\": {\r\n      \"LeftMargin\": 0.25,\r\n      \"TopMargin\": 0.25,\r\n      \"RightMargin\": 0.25,\r\n      \"BotMargin\": 0.25,\r\n      \"DataOnly\": false,\r\n      \"Width\": 8.125,\r\n      \"Height\": 0.2708,\r\n      \"DefaultSize\": true,\r\n      \"Columns\": 1,\r\n      \"ColumnSpacing\": 0.25,\r\n      \"RowSpacing\": 0,\r\n      \"ItemLayout\": \"Horizontal Columns\",\r\n      \"FastPrint\": 1,\r\n      \"Datasheet\": 1\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/reports/rptNonDefaultPaperSize.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Report\r\n    LayoutForPrint = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DateGrouping =1\r\n    GrpKeepTogether =1\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =8884\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =1\r\n    RecSrcDt = Begin\r\n        0xe05ff061b477e540\r\n    End\r\n    DatasheetFontName =\"Franklin Gothic Book\"\r\n    FilterOnLoad =0\r\n    FitToPage =1\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin PageHeader\r\n            Height =360\r\n            Name =\"PageHeaderSection\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n        End\r\n        Begin Section\r\n            KeepTogether = NotDefault\r\n            Height =7560\r\n            Name =\"Detail\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin Label\r\n                    TextFontFamily =0\r\n                    Left =1260\r\n                    Top =240\r\n                    Width =4800\r\n                    Height =420\r\n                    BorderColor =8355711\r\n                    ForeColor =8355711\r\n                    Name =\"Label0\"\r\n                    Caption =\"This report uses A4 landscape paper size\"\r\n                    FontName =\"Franklin Gothic Book\"\r\n                    GridlineColor =10921638\r\n                    LayoutCachedLeft =1260\r\n                    LayoutCachedTop =240\r\n                    LayoutCachedWidth =6060\r\n                    LayoutCachedHeight =660\r\n                End\r\n            End\r\n        End\r\n        Begin PageFooter\r\n            Height =360\r\n            Name =\"PageFooterSection\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"rptNonDefaultPaperSize.cls\"\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/reports/rptNonDefaultPaperSize.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/reports/rptNonDefaultPaperSize.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbReport\",\r\n    \"Description\": \"rptNonDefaultPaperSize Print Settings\"\r\n  },\r\n  \"Items\": {\r\n    \"Printer\": {\r\n      \"Orientation\": \"Landscape\",\r\n      \"PaperSize\": \"A4\"\r\n    },\r\n    \"Margins\": {\r\n      \"LeftMargin\": 0.25,\r\n      \"TopMargin\": 0.25,\r\n      \"RightMargin\": 0.25,\r\n      \"BotMargin\": 0.25,\r\n      \"DataOnly\": false,\r\n      \"Width\": 6.1694,\r\n      \"Height\": 5.25,\r\n      \"DefaultSize\": true,\r\n      \"Columns\": 1,\r\n      \"ColumnSpacing\": 0.25,\r\n      \"RowSpacing\": 0,\r\n      \"ItemLayout\": \"Horizontal Columns\",\r\n      \"FastPrint\": 1,\r\n      \"Datasheet\": 1\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/savedspecs/Export-MSysIMEXColumns.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbSavedSpec\",\r\n    \"Description\": \"Saved Import/Export Specification\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"Export-MSysIMEXColumns\",\r\n    \"Description\": \"Test table export.\",\r\n    \"XML\": \"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\" ?>\\r\\n<ImportExportSpecification Path = \\\"C:\\\\Users\\\\Adam Waller\\\\Documents\\\\MSysIMEXColumns.txt\\\" xmlns=\\\"urn:www.microsoft.com/office/access/imexspec\\\">\\r\\n\\t<ExportTextPresentation  AccessObject=\\\"MSysIMEXColumns\\\" ObjectType=\\\"Table\\\" AutoStart=\\\"true\\\" Encoding=\\\"WindowsDefault\\\" />\\r\\n</ImportExportSpecification>\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tables/tblInternal.txt",
    "content": "﻿ID\tObjectType\tNotes\tIndex&Test\tMyAttachment\n1\tTable\t\t\tUNSUPPORTED DATA TYPE\n2\tForm\t\t\tUNSUPPORTED DATA TYPE\n3\tQuery\t\t\tUNSUPPORTED DATA TYPE\n4\tReport\t\t\tUNSUPPORTED DATA TYPE\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tables/tblLinkedAccess.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<dataroot xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <tblLinkedAccess>\r\n    <ID>1</ID>\r\n    <ObjectType>Table</ObjectType>\r\n  </tblLinkedAccess>\r\n  <tblLinkedAccess>\r\n    <ID>2</ID>\r\n    <ObjectType>Form</ObjectType>\r\n  </tblLinkedAccess>\r\n  <tblLinkedAccess>\r\n    <ID>3</ID>\r\n    <ObjectType>Query</ObjectType>\r\n  </tblLinkedAccess>\r\n  <tblLinkedAccess>\r\n    <ID>4</ID>\r\n    <ObjectType>Report</ObjectType>\r\n  </tblLinkedAccess>\r\n</dataroot>\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tables/tblSaveXML.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<dataroot xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n  <tblSaveXML>\r\n    <ID>1</ID>\r\n    <ObjectType>Table</ObjectType>\r\n  </tblSaveXML>\r\n  <tblSaveXML>\r\n    <ID>2</ID>\r\n    <ObjectType>Form</ObjectType>\r\n    <Notes>Test</Notes>\r\n    <UpdateDate>2020-05-07T17:03:13</UpdateDate>\r\n  </tblSaveXML>\r\n  <tblSaveXML>\r\n    <ID>3</ID>\r\n    <ObjectType>Query</ObjectType>\r\n    <Notes>Test Req'd</Notes>\r\n    <UpdateDate>2020-05-07T17:03:19</UpdateDate>\r\n    <NotReq_x0027_d>Test data</NotReq_x0027_d>\r\n    <Please_x0022_don_x0027_t_x0022_use>S'more \"tests\"</Please_x0022_don_x0027_t_x0022_use>\r\n  </tblSaveXML>\r\n  <tblSaveXML>\r\n    <ID>4</ID>\r\n    <ObjectType>Report</ObjectType>\r\n    <UpdateDate>2020-05-07T17:03:20</UpdateDate>\r\n  </tblSaveXML>\r\n</dataroot>\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/ImageFile.sql",
    "content": "﻿CREATE TABLE [ImageFile] (\r\n  [FileData] VARCHAR (255),\r\n  [FileName] VARCHAR (255),\r\n  [FileType] VARCHAR (255)\r\n)\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/ImageFile.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"ImageFile\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"ImageFile\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"FileData\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"FileName\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"FileType\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/USysApplicationLog.sql",
    "content": "﻿CREATE TABLE [USysApplicationLog] (\r\n  [ID] AUTOINCREMENT CONSTRAINT [PrimaryKey] PRIMARY KEY UNIQUE NOT NULL,\r\n  [SourceObject] VARCHAR (255),\r\n  [Data Macro Instance ID] VARCHAR (255),\r\n  [Error Number] LONG,\r\n  [Category] VARCHAR (255),\r\n  [Object Type] VARCHAR (255),\r\n  [Description] LONGTEXT,\r\n  [Context] VARCHAR (255),\r\n  [Created] DATETIME\r\n)\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/USysApplicationLog.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"USysApplicationLog\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"USysApplicationLog\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"ID \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"ID\" minOccurs=\"1\" od:jetType=\"autonumber\" od:sqlSType=\"int\" od:autoUnique=\"yes\" od:nonNullable=\"yes\" type=\"xsd:int\"></xsd:element>\r\n        <xsd:element name=\"SourceObject\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Data_x0020_Macro_x0020_Instance_x0020_ID\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Error_x0020_Number\" minOccurs=\"0\" od:jetType=\"longinteger\" od:sqlSType=\"int\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"Category\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Object_x0020_Type\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Description\" minOccurs=\"0\" od:jetType=\"memo\" od:sqlSType=\"ntext\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Context\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Created\" minOccurs=\"0\" od:jetType=\"datetime\" od:sqlSType=\"datetime\" type=\"xsd:dateTime\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblAttachment.sql",
    "content": "﻿CREATE TABLE [tblAttachment] (\r\n  [ID] AUTOINCREMENT CONSTRAINT [PrimaryKey] PRIMARY KEY UNIQUE NOT NULL,\r\n  [ImageFile] VARCHAR,\r\n  [ImageObject] LONGBINARY\r\n)\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblAttachment.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblAttachment\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblAttachment\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"ID\" index-key=\"ID \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"ID \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOn\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DefaultView\" type=\"2\" value=\"2\"></od:tableProperty>\r\n        <od:tableProperty name=\"DisplayViewsOnSharePointSite\" type=\"2\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"TotalsRow\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"FilterOnLoad\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOnLoad\" type=\"1\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"HideNewField\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ThemeFontIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackThemeColorIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackShade\" type=\"6\" value=\"95\"></od:tableProperty>\r\n        <od:tableProperty name=\"ReadOnlyWhenDisconnected\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetGridlinesThemeColorIndex\" type=\"4\" value=\"3\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetForeThemeColorIndex\" type=\"4\" value=\"0\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"ID\" minOccurs=\"1\" od:jetType=\"autonumber\" od:sqlSType=\"int\" od:autoUnique=\"yes\" od:nonNullable=\"yes\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"ImageFile\" minOccurs=\"0\" od:jetType=\"complex\" od:jetComplexType=\"MSysComplexType_Attachment\" maxOccurs=\"unbounded\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"1215\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Description\" type=\"10\" value=\"Embedded image file\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"126\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:complexType>\r\n            <xsd:sequence>\r\n              <xsd:element name=\"FileData\" minOccurs=\"0\" od:jetType=\"oleobject\" od:sqlSType=\"image\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:base64Binary\">\r\n                    <xsd:maxLength value=\"1476395008\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n              <xsd:element name=\"FileFlags\" minOccurs=\"0\" od:jetType=\"longinteger\" od:sqlSType=\"int\" type=\"xsd:int\"></xsd:element>\r\n              <xsd:element name=\"FileName\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:string\">\r\n                    <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n              <xsd:element name=\"FileTimeStamp\" minOccurs=\"0\" od:jetType=\"datetime\" od:sqlSType=\"datetime\" type=\"xsd:dateTime\"></xsd:element>\r\n              <xsd:element name=\"FileType\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:string\">\r\n                    <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n              <xsd:element name=\"FileURL\" minOccurs=\"0\" od:jetType=\"memo\" od:sqlSType=\"ntext\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:string\">\r\n                    <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n            </xsd:sequence>\r\n          </xsd:complexType>\r\n        </xsd:element>\r\n        <xsd:element name=\"ImageObject\" minOccurs=\"0\" od:jetType=\"oleobject\" od:sqlSType=\"image\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"1815\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:base64Binary\">\r\n              <xsd:maxLength value=\"1476395008\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblColors.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbTableDef\",\r\n    \"Description\": \"Linked Table\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"tblColors\",\r\n    \"Connect\": \";DATABASE=rel:Test2.accdb\",\r\n    \"SourceTableName\": \"tblColors\",\r\n    \"Attributes\": 1073741824,\r\n    \"PrimaryKey\": \"[ID]\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblHidden.sql",
    "content": "﻿CREATE TABLE [tblHidden] (\r\n  [ID] AUTOINCREMENT CONSTRAINT [PrimaryKey] PRIMARY KEY UNIQUE NOT NULL\r\n)\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblHidden.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblHidden\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblHidden\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"ID\" index-key=\"ID \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"ID \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"Description\" type=\"10\" value=\"This table should be hidden.\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"ID\" minOccurs=\"1\" od:jetType=\"autonumber\" od:sqlSType=\"int\" od:autoUnique=\"yes\" od:nonNullable=\"yes\" type=\"xsd:int\"></xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblInternal.sql",
    "content": "﻿CREATE TABLE [tblInternal] (\r\n  [ID] AUTOINCREMENT,\r\n  [ObjectType] VARCHAR (255),\r\n  [Notes] VARCHAR (255),\r\n  [Index&Test] VARCHAR (255),\r\n  [MyAttachment] VARCHAR,\r\n   CONSTRAINT [PrimaryKey] PRIMARY KEY ([ID], [ObjectType])\r\n)\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblInternal.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblInternal\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblInternal\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"ID\" index-key=\"ID \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"Index&amp;Test\" index-key=\"Index_x0026_Test \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"ID ObjectType \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc asc\"></od:index>\r\n        <od:index index-name=\"tblInternalObjectType\" index-key=\"ObjectType \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOn\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DefaultView\" type=\"2\" value=\"2\"></od:tableProperty>\r\n        <od:tableProperty name=\"DisplayViewsOnSharePointSite\" type=\"2\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"TotalsRow\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"FilterOnLoad\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOnLoad\" type=\"1\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"HideNewField\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ThemeFontIndex\" type=\"4\" value=\"-1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackThemeColorIndex\" type=\"4\" value=\"-1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ReadOnlyWhenDisconnected\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetGridlinesThemeColorIndex\" type=\"4\" value=\"-1\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetForeThemeColorIndex\" type=\"4\" value=\"-1\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"ID\" minOccurs=\"1\" od:jetType=\"autonumber\" od:sqlSType=\"int\" od:autoUnique=\"yes\" od:nonNullable=\"yes\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"ObjectType\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Notes\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Index_x0026_Test\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"MyAttachment\" minOccurs=\"0\" od:jetType=\"complex\" od:jetComplexType=\"MSysComplexType_Attachment\" maxOccurs=\"unbounded\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"126\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:complexType>\r\n            <xsd:sequence>\r\n              <xsd:element name=\"FileData\" minOccurs=\"0\" od:jetType=\"oleobject\" od:sqlSType=\"image\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:base64Binary\">\r\n                    <xsd:maxLength value=\"1476395008\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n              <xsd:element name=\"FileFlags\" minOccurs=\"0\" od:jetType=\"longinteger\" od:sqlSType=\"int\" type=\"xsd:int\"></xsd:element>\r\n              <xsd:element name=\"FileName\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:string\">\r\n                    <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n              <xsd:element name=\"FileTimeStamp\" minOccurs=\"0\" od:jetType=\"datetime\" od:sqlSType=\"datetime\" type=\"xsd:dateTime\"></xsd:element>\r\n              <xsd:element name=\"FileType\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:string\">\r\n                    <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n              <xsd:element name=\"FileURL\" minOccurs=\"0\" od:jetType=\"memo\" od:sqlSType=\"ntext\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:string\">\r\n                    <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n            </xsd:sequence>\r\n          </xsd:complexType>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblLinkedAccess.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbTableDef\",\r\n    \"Description\": \"Linked Table\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"tblLinkedAccess\",\r\n    \"Connect\": \";DATABASE=rel:Testing.accdb\",\r\n    \"SourceTableName\": \"tblInternal\",\r\n    \"Attributes\": 1073741824,\r\n    \"PrimaryKey\": \"[ID], [ObjectType]\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblLinkedCSV.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbTableDef\",\r\n    \"Description\": \"Linked Table\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"tblLinkedCSV\",\r\n    \"Connect\": \"Text;DSN=Linked Link Specification;FMT=Delimited;HDR=NO;IMEX=2;CharacterSet=437;ACCDB=YES;DATABASE=rel:\",\r\n    \"SourceTableName\": \"Linked.csv\",\r\n    \"Attributes\": 1073741824\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblLinkedExcel.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbTableDef\",\r\n    \"Description\": \"Linked Table\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"tblLinkedExcel\",\r\n    \"Connect\": \"Excel 12.0 Xml;HDR=YES;IMEX=2;ACCDB=YES;DATABASE=rel:Workbook.xlsx\",\r\n    \"SourceTableName\": \"Sheet1$\",\r\n    \"Attributes\": 1073741824\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblSaveXML.sql",
    "content": "﻿CREATE TABLE [tblSaveXML] (\r\n  [ID] AUTOINCREMENT,\r\n  [ObjectType] VARCHAR (255),\r\n  [Notes] VARCHAR (255),\r\n  [AddDate] DATETIME,\r\n  [UpdateDate] DATETIME,\r\n  [NotReq''d] VARCHAR (255),\r\n  [Please\"\"don''t\"\"use] VARCHAR (255),\r\n   CONSTRAINT [PrimaryKey] PRIMARY KEY ([ID], [ObjectType])\r\n)\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tbldefs/tblSaveXML.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblSaveXML\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblSaveXML\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"ID\" index-key=\"ID \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"ID ObjectType \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc asc\"></od:index>\r\n        <od:index index-name=\"tblSaveXMLObjectType\" index-key=\"ObjectType \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"Description\" type=\"10\" value=\"Saved description in XML table.\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOn\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DefaultView\" type=\"2\" value=\"2\"></od:tableProperty>\r\n        <od:tableProperty name=\"DisplayViewsOnSharePointSite\" type=\"2\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"TotalsRow\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"FilterOnLoad\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOnLoad\" type=\"1\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"HideNewField\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ThemeFontIndex\" type=\"4\" value=\"-1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackThemeColorIndex\" type=\"4\" value=\"-1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ReadOnlyWhenDisconnected\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetGridlinesThemeColorIndex\" type=\"4\" value=\"-1\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetForeThemeColorIndex\" type=\"4\" value=\"-1\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"ID\" minOccurs=\"1\" od:jetType=\"autonumber\" od:sqlSType=\"int\" od:autoUnique=\"yes\" od:nonNullable=\"yes\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"ObjectType\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Notes\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"AddDate\" minOccurs=\"0\" od:jetType=\"datetime\" od:sqlSType=\"datetime\" type=\"xsd:dateTime\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ShowDatePicker\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"UpdateDate\" minOccurs=\"0\" od:jetType=\"datetime\" od:sqlSType=\"datetime\" type=\"xsd:dateTime\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ShowDatePicker\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"NotReq_x0027_d\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Please_x0022_don_x0027_t_x0022_use\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/tdmacros/tblSaveXML.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<DataMacros xmlns=\"http://schemas.microsoft.com/office/accessservices/2009/11/application\">\r\n  <DataMacro Event=\"AfterUpdate\">\r\n    <Statements>\r\n      <Action Name=\"SetLocalVar\">\r\n        <Argument Name=\"Name\">UpdatedID</Argument>\r\n        <Argument Name=\"Value\">[ID]</Argument>\r\n      </Action>\r\n      <ForEachRecord>\r\n        <Data>\r\n          <Reference>tblSaveXML</Reference>\r\n          <WhereCondition>[ID]=[UpdatedID]</WhereCondition>\r\n        </Data>\r\n        <Statements>\r\n          <EditRecord>\r\n            <Data></Data>\r\n            <Statements>\r\n              <Action Name=\"SetField\">\r\n                <Argument Name=\"Field\">UpdateDate</Argument>\r\n                <Argument Name=\"Value\">Now()</Argument>\r\n              </Action>\r\n            </Statements>\r\n          </EditRecord>\r\n        </Statements>\r\n      </ForEachRecord>\r\n    </Statements>\r\n  </DataMacro>\r\n</DataMacros>\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/vbe-project.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbVbeProject\",\r\n    \"Description\": \"VBE Project\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"VCS Testing\",\r\n    \"Description\": \"For automated testing of Version Control\",\r\n    \"FileName\": \"Testing.accdb\",\r\n    \"HelpFile\": \"\",\r\n    \"HelpContextId\": 123456,\r\n    \"ConditionalCompilationArguments\": \"\",\r\n    \"Mode\": 0,\r\n    \"Protection\": 0,\r\n    \"Type\": 100\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/vbe-references.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbVbeReference\",\r\n    \"Description\": \"VBE References\"\r\n  },\r\n  \"Items\": {\r\n    \"stdole\": {\r\n      \"GUID\": \"{00020430-0000-0000-C000-000000000046}\",\r\n      \"Version\": \"2.0\"\r\n    },\r\n    \"DAO\": {\r\n      \"GUID\": \"{4AC9E1DA-5BAD-4AC7-86E3-24F4CDCECA28}\",\r\n      \"Version\": \"12.0\"\r\n    },\r\n    \"VBIDE\": {\r\n      \"GUID\": \"{0002E157-0000-0000-C000-000000000046}\",\r\n      \"Version\": \"5.3\"\r\n    },\r\n    \"VBScript_RegExp_55\": {\r\n      \"GUID\": \"{3F4DACA7-160D-11D2-A8E9-00104B365C9F}\",\r\n      \"Version\": \"5.5\"\r\n    },\r\n    \"Scripting\": {\r\n      \"GUID\": \"{420B2830-E718-11CF-893D-00A0C9054228}\",\r\n      \"Version\": \"1.0\"\r\n    },\r\n    \"MSForms\": {\r\n      \"GUID\": \"{0D452EE1-E08F-101A-852E-02608C4D0BB4}\",\r\n      \"Version\": \"2.0\"\r\n    },\r\n    \"Office\": {\r\n      \"GUID\": \"{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}\",\r\n      \"Version\": \"2.8\"\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/vbeforms/frmForm20.frm",
    "content": "VERSION 5.00\r\nBegin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} frmForm20 \r\n   Caption         =   \"MS Forms 2.0 Example\"\r\n   ClientHeight    =   5415\r\n   ClientLeft      =   45\r\n   ClientTop       =   390\r\n   ClientWidth     =   7590\r\n   OleObjectBlob   =   \"frmForm20.frx\":0000\r\n   StartUpPosition =   1  'CenterOwner\r\nEnd\r\nAttribute VB_Name = \"frmForm20\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\n\r\n\r\n\r\nOption Explicit\r\n\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/vbeforms/frmForm20.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbVbeForm\",\r\n    \"Description\": \"Serialized VBE Form\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"frmForm20\",\r\n    \"Designer\": {\r\n      \"Controls\": [\r\n        {\r\n          \"Class\": \"Label\",\r\n          \"Name\": \"Label1\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 18,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 30,\r\n          \"TabIndex\": 0,\r\n          \"TabStop\": false,\r\n          \"Tag\": \"\",\r\n          \"Top\": 12,\r\n          \"Visible\": true,\r\n          \"Width\": 162,\r\n          \"Accelerator\": \"\",\r\n          \"AutoSize\": false,\r\n          \"BackColor\": -2147483633,\r\n          \"BackStyle\": 1,\r\n          \"BorderColor\": -2147483642,\r\n          \"BorderStyle\": 0,\r\n          \"Caption\": \"This is a Forms 2.0 User form\",\r\n          \"Enabled\": true,\r\n          \"Font\": {\r\n            \"Bold\": false,\r\n            \"Charset\": 0,\r\n            \"Italic\": false,\r\n            \"Name\": \"Tahoma\",\r\n            \"Size\": 12,\r\n            \"Strikethrough\": false,\r\n            \"Underline\": false,\r\n            \"Weight\": 400\r\n          },\r\n          \"ForeColor\": -2147483630,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Picture\": \"\",\r\n          \"PicturePosition\": 7,\r\n          \"SpecialEffect\": 0,\r\n          \"TextAlign\": 1,\r\n          \"WordWrap\": true\r\n        },\r\n        {\r\n          \"Class\": \"Label\",\r\n          \"Name\": \"Label2\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 66,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 30,\r\n          \"TabIndex\": 1,\r\n          \"TabStop\": false,\r\n          \"Tag\": \"\",\r\n          \"Top\": 42,\r\n          \"Visible\": true,\r\n          \"Width\": 162,\r\n          \"Accelerator\": \"\",\r\n          \"AutoSize\": false,\r\n          \"BackColor\": -2147483633,\r\n          \"BackStyle\": 1,\r\n          \"BorderColor\": -2147483642,\r\n          \"BorderStyle\": 0,\r\n          \"Caption\": \"This demonstrates the ability of Version Control System to successfully export and recreate a MS Forms 2.0 form in an Access Database.\",\r\n          \"Enabled\": true,\r\n          \"Font\": {\r\n            \"Bold\": false,\r\n            \"Charset\": 0,\r\n            \"Italic\": false,\r\n            \"Name\": \"Tahoma\",\r\n            \"Size\": 9.75,\r\n            \"Strikethrough\": false,\r\n            \"Underline\": false,\r\n            \"Weight\": 400\r\n          },\r\n          \"ForeColor\": -2147483630,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Picture\": \"\",\r\n          \"PicturePosition\": 7,\r\n          \"SpecialEffect\": 0,\r\n          \"TextAlign\": 1,\r\n          \"WordWrap\": true\r\n        },\r\n        {\r\n          \"Class\": \"TextBox\",\r\n          \"Name\": \"TextBox1\",\r\n          \"ControlSource\": \"\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 18,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 24,\r\n          \"TabIndex\": 2,\r\n          \"TabStop\": true,\r\n          \"Tag\": \"\",\r\n          \"Top\": 120,\r\n          \"Visible\": true,\r\n          \"Width\": 96,\r\n          \"AutoSize\": false,\r\n          \"AutoTab\": false,\r\n          \"AutoWordSelect\": true,\r\n          \"BackColor\": -2147483643,\r\n          \"BackStyle\": 1,\r\n          \"BorderColor\": -2147483642,\r\n          \"BorderStyle\": 0,\r\n          \"DragBehavior\": 0,\r\n          \"Enabled\": true,\r\n          \"EnterFieldBehavior\": 0,\r\n          \"EnterKeyBehavior\": false,\r\n          \"Font\": {\r\n            \"Bold\": false,\r\n            \"Charset\": 0,\r\n            \"Italic\": false,\r\n            \"Name\": \"Tahoma\",\r\n            \"Size\": 8.25,\r\n            \"Strikethrough\": false,\r\n            \"Underline\": false,\r\n            \"Weight\": 400\r\n          },\r\n          \"ForeColor\": -2147483640,\r\n          \"HideSelection\": true,\r\n          \"IMEMode\": 0,\r\n          \"IntegralHeight\": true,\r\n          \"Locked\": false,\r\n          \"MaxLength\": 0,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"MultiLine\": false,\r\n          \"PasswordChar\": \"\",\r\n          \"ScrollBars\": 0,\r\n          \"SelectionMargin\": true,\r\n          \"SpecialEffect\": 2,\r\n          \"TabKeyBehavior\": false,\r\n          \"Text\": \"\",\r\n          \"TextAlign\": 1,\r\n          \"Value\": \"\",\r\n          \"WordWrap\": true\r\n        },\r\n        {\r\n          \"Class\": \"ComboBox\",\r\n          \"Name\": \"ComboBox1\",\r\n          \"ControlSource\": \"\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 25.5,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 24,\r\n          \"RowSource\": \"\",\r\n          \"TabIndex\": 3,\r\n          \"TabStop\": true,\r\n          \"Tag\": \"\",\r\n          \"Top\": 150,\r\n          \"Visible\": true,\r\n          \"Width\": 96,\r\n          \"AutoSize\": false,\r\n          \"AutoTab\": false,\r\n          \"AutoWordSelect\": true,\r\n          \"BackColor\": -2147483643,\r\n          \"BackStyle\": 1,\r\n          \"BorderColor\": -2147483642,\r\n          \"BorderStyle\": 0,\r\n          \"BoundColumn\": 1,\r\n          \"ColumnCount\": 1,\r\n          \"ColumnHeads\": false,\r\n          \"ColumnWidths\": \"\",\r\n          \"DragBehavior\": 0,\r\n          \"DropButtonStyle\": 1,\r\n          \"Enabled\": true,\r\n          \"EnterFieldBehavior\": 0,\r\n          \"Font\": {\r\n            \"Bold\": false,\r\n            \"Charset\": 0,\r\n            \"Italic\": false,\r\n            \"Name\": \"Tahoma\",\r\n            \"Size\": 8.25,\r\n            \"Strikethrough\": false,\r\n            \"Underline\": false,\r\n            \"Weight\": 400\r\n          },\r\n          \"ForeColor\": -2147483640,\r\n          \"HideSelection\": true,\r\n          \"IMEMode\": 0,\r\n          \"ListRows\": 8,\r\n          \"ListStyle\": 0,\r\n          \"ListWidth\": \"0 pt\",\r\n          \"Locked\": false,\r\n          \"MatchEntry\": 1,\r\n          \"MatchRequired\": false,\r\n          \"MaxLength\": 0,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"SelectionMargin\": true,\r\n          \"ShowDropButtonWhen\": 2,\r\n          \"SpecialEffect\": 2,\r\n          \"Style\": 0,\r\n          \"Text\": \"\",\r\n          \"TextAlign\": 1,\r\n          \"TextColumn\": -1,\r\n          \"TopIndex\": -1,\r\n          \"Value\": \"\"\r\n        },\r\n        {\r\n          \"Class\": \"ListBox\",\r\n          \"Name\": \"ListBox1\",\r\n          \"ControlSource\": \"\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 24,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 24,\r\n          \"RowSource\": \"\",\r\n          \"TabIndex\": 4,\r\n          \"TabStop\": true,\r\n          \"Tag\": \"\",\r\n          \"Top\": 186,\r\n          \"Visible\": true,\r\n          \"Width\": 96,\r\n          \"BackColor\": -2147483643,\r\n          \"BorderColor\": -2147483642,\r\n          \"BorderStyle\": 0,\r\n          \"BoundColumn\": 1,\r\n          \"ColumnHeads\": false,\r\n          \"ColumnWidths\": \"\",\r\n          \"Enabled\": true,\r\n          \"Font\": {\r\n            \"Bold\": false,\r\n            \"Charset\": 0,\r\n            \"Italic\": false,\r\n            \"Name\": \"Tahoma\",\r\n            \"Size\": 8.25,\r\n            \"Strikethrough\": false,\r\n            \"Underline\": false,\r\n            \"Weight\": 400\r\n          },\r\n          \"ForeColor\": -2147483640,\r\n          \"IMEMode\": 0,\r\n          \"IntegralHeight\": true,\r\n          \"ListIndex\": -1,\r\n          \"ListStyle\": 0,\r\n          \"Locked\": false,\r\n          \"MatchEntry\": 0,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"MultiSelect\": 0,\r\n          \"SpecialEffect\": 2,\r\n          \"Text\": \"\",\r\n          \"TextAlign\": 1,\r\n          \"TextColumn\": -1,\r\n          \"TopIndex\": -1,\r\n          \"Value\": null\r\n        },\r\n        {\r\n          \"Class\": \"CheckBox\",\r\n          \"Name\": \"CheckBox1\",\r\n          \"ControlSource\": \"\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 18,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 138,\r\n          \"TabIndex\": 5,\r\n          \"TabStop\": true,\r\n          \"Tag\": \"\",\r\n          \"Top\": 120,\r\n          \"Visible\": true,\r\n          \"Width\": 71.25,\r\n          \"Accelerator\": \"\",\r\n          \"Alignment\": 1,\r\n          \"AutoSize\": false,\r\n          \"BackColor\": -2147483633,\r\n          \"BackStyle\": 1,\r\n          \"Caption\": \"CheckBox1\",\r\n          \"Enabled\": true,\r\n          \"Font\": {\r\n            \"Bold\": false,\r\n            \"Charset\": 0,\r\n            \"Italic\": false,\r\n            \"Name\": \"Tahoma\",\r\n            \"Size\": 8.25,\r\n            \"Strikethrough\": false,\r\n            \"Underline\": false,\r\n            \"Weight\": 400\r\n          },\r\n          \"ForeColor\": -2147483630,\r\n          \"GroupName\": \"\",\r\n          \"Locked\": false,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Picture\": \"\",\r\n          \"PicturePosition\": 7,\r\n          \"SpecialEffect\": 2,\r\n          \"TextAlign\": 1,\r\n          \"TripleState\": false,\r\n          \"Value\": false,\r\n          \"WordWrap\": true\r\n        },\r\n        {\r\n          \"Class\": \"OptionButton\",\r\n          \"Name\": \"OptionButton1\",\r\n          \"ControlSource\": \"\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 18,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 138,\r\n          \"TabIndex\": 6,\r\n          \"TabStop\": true,\r\n          \"Tag\": \"\",\r\n          \"Top\": 144,\r\n          \"Visible\": true,\r\n          \"Width\": 108,\r\n          \"Accelerator\": \"\",\r\n          \"Alignment\": 1,\r\n          \"AutoSize\": false,\r\n          \"BackColor\": -2147483633,\r\n          \"BackStyle\": 1,\r\n          \"Caption\": \"OptionButton1\",\r\n          \"Enabled\": true,\r\n          \"Font\": {\r\n            \"Bold\": false,\r\n            \"Charset\": 0,\r\n            \"Italic\": false,\r\n            \"Name\": \"Tahoma\",\r\n            \"Size\": 8.25,\r\n            \"Strikethrough\": false,\r\n            \"Underline\": false,\r\n            \"Weight\": 400\r\n          },\r\n          \"ForeColor\": -2147483630,\r\n          \"GroupName\": \"\",\r\n          \"Locked\": false,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Picture\": \"\",\r\n          \"PicturePosition\": 7,\r\n          \"SpecialEffect\": 2,\r\n          \"TextAlign\": 1,\r\n          \"TripleState\": false,\r\n          \"Value\": false,\r\n          \"WordWrap\": true\r\n        },\r\n        {\r\n          \"Class\": \"ToggleButton\",\r\n          \"Name\": \"ToggleButton1\",\r\n          \"ControlSource\": \"\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 30,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 144,\r\n          \"TabIndex\": 7,\r\n          \"TabStop\": true,\r\n          \"Tag\": \"\",\r\n          \"Top\": 168,\r\n          \"Visible\": true,\r\n          \"Width\": 64.5,\r\n          \"Accelerator\": \"\",\r\n          \"AutoSize\": false,\r\n          \"BackColor\": -2147483633,\r\n          \"BackStyle\": 1,\r\n          \"Caption\": \"ToggleButton1\",\r\n          \"Enabled\": true,\r\n          \"ForeColor\": -2147483630,\r\n          \"Locked\": false,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Picture\": \"\",\r\n          \"PicturePosition\": 7,\r\n          \"TextAlign\": 2,\r\n          \"TripleState\": false,\r\n          \"Value\": false,\r\n          \"WordWrap\": true\r\n        },\r\n        {\r\n          \"Class\": \"CommandButton\",\r\n          \"Name\": \"CommandButton1\",\r\n          \"Cancel\": false,\r\n          \"ControlTipText\": \"\",\r\n          \"Default\": false,\r\n          \"Height\": 30,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 240,\r\n          \"TabIndex\": 8,\r\n          \"TabStop\": true,\r\n          \"Tag\": \"\",\r\n          \"Top\": 36,\r\n          \"Visible\": true,\r\n          \"Width\": 108,\r\n          \"Accelerator\": \"\",\r\n          \"AutoSize\": false,\r\n          \"BackColor\": -2147483633,\r\n          \"BackStyle\": 1,\r\n          \"Caption\": \"CommandButton1\",\r\n          \"Enabled\": true,\r\n          \"Font\": {\r\n            \"Bold\": false,\r\n            \"Charset\": 0,\r\n            \"Italic\": false,\r\n            \"Name\": \"Tahoma\",\r\n            \"Size\": 8.25,\r\n            \"Strikethrough\": false,\r\n            \"Underline\": false,\r\n            \"Weight\": 400\r\n          },\r\n          \"ForeColor\": -2147483630,\r\n          \"Locked\": false,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Picture\": \"\",\r\n          \"PicturePosition\": 7,\r\n          \"TakeFocusOnClick\": true,\r\n          \"WordWrap\": false\r\n        },\r\n        {\r\n          \"Class\": \"ScrollBar\",\r\n          \"Name\": \"ScrollBar1\",\r\n          \"ControlSource\": \"\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 24,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 24,\r\n          \"TabIndex\": 9,\r\n          \"TabStop\": true,\r\n          \"Tag\": \"\",\r\n          \"Top\": 222,\r\n          \"Visible\": true,\r\n          \"Width\": 96,\r\n          \"BackColor\": -2147483633,\r\n          \"Delay\": 50,\r\n          \"Enabled\": true,\r\n          \"ForeColor\": -2147483630,\r\n          \"LargeChange\": 1,\r\n          \"Max\": 32767,\r\n          \"Min\": 0,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Orientation\": -1,\r\n          \"ProportionalThumb\": true,\r\n          \"SmallChange\": 1,\r\n          \"Value\": 0\r\n        },\r\n        {\r\n          \"Class\": \"SpinButton\",\r\n          \"Name\": \"SpinButton1\",\r\n          \"ControlSource\": \"\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 42,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 144,\r\n          \"TabIndex\": 10,\r\n          \"TabStop\": true,\r\n          \"Tag\": \"\",\r\n          \"Top\": 210,\r\n          \"Visible\": true,\r\n          \"Width\": 42,\r\n          \"BackColor\": -2147483633,\r\n          \"Delay\": 50,\r\n          \"Enabled\": true,\r\n          \"ForeColor\": -2147483630,\r\n          \"Max\": 100,\r\n          \"Min\": 0,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Orientation\": -1,\r\n          \"SmallChange\": 1,\r\n          \"Value\": 0\r\n        },\r\n        {\r\n          \"Class\": \"Image\",\r\n          \"Name\": \"Image1\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 48,\r\n          \"Left\": 240,\r\n          \"Tag\": \"\",\r\n          \"Top\": 84,\r\n          \"Visible\": true,\r\n          \"Width\": 108,\r\n          \"AutoSize\": false,\r\n          \"BackColor\": -2147483633,\r\n          \"BackStyle\": 1,\r\n          \"BorderColor\": -2147483642,\r\n          \"BorderStyle\": 1,\r\n          \"Enabled\": true,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Picture\": \"\",\r\n          \"PictureAlignment\": 2,\r\n          \"PictureSizeMode\": 0,\r\n          \"PictureTiling\": false,\r\n          \"SpecialEffect\": 0\r\n        },\r\n        {\r\n          \"Class\": \"Label\",\r\n          \"Name\": \"Label3\",\r\n          \"ControlTipText\": \"\",\r\n          \"Height\": 84,\r\n          \"HelpContextID\": 0,\r\n          \"Left\": 234,\r\n          \"TabIndex\": 11,\r\n          \"TabStop\": false,\r\n          \"Tag\": \"\",\r\n          \"Top\": 162,\r\n          \"Visible\": true,\r\n          \"Width\": 132,\r\n          \"Accelerator\": \"\",\r\n          \"AutoSize\": false,\r\n          \"BackColor\": -2147483633,\r\n          \"BackStyle\": 1,\r\n          \"BorderColor\": -2147483642,\r\n          \"BorderStyle\": 0,\r\n          \"Caption\": \"Frame and Tab controls unfortunately do not seem to be working anymore. This was observed in January 2025, and may be related to a Windows update.\",\r\n          \"Enabled\": true,\r\n          \"Font\": {\r\n            \"Bold\": false,\r\n            \"Charset\": 0,\r\n            \"Italic\": false,\r\n            \"Name\": \"Tahoma\",\r\n            \"Size\": 9.75,\r\n            \"Strikethrough\": false,\r\n            \"Underline\": false,\r\n            \"Weight\": 400\r\n          },\r\n          \"ForeColor\": -2147483630,\r\n          \"MouseIcon\": \"\",\r\n          \"MousePointer\": 0,\r\n          \"Picture\": \"\",\r\n          \"PicturePosition\": 7,\r\n          \"SpecialEffect\": 0,\r\n          \"TextAlign\": 1,\r\n          \"WordWrap\": true\r\n        }\r\n      ]\r\n    },\r\n    \"Properties\": {\r\n      \"BackColor\": -2147483633,\r\n      \"BorderColor\": -2147483630,\r\n      \"BorderStyle\": 0,\r\n      \"CanRedo\": false,\r\n      \"CanUndo\": false,\r\n      \"Cycle\": 0,\r\n      \"Font\": {\r\n        \"Name\": \"Tahoma\",\r\n        \"Size\": 8.25,\r\n        \"Bold\": false,\r\n        \"Italic\": false,\r\n        \"Underline\": false,\r\n        \"Strikethrough\": false,\r\n        \"Weight\": 400,\r\n        \"Charset\": 0\r\n      },\r\n      \"ForeColor\": -2147483630,\r\n      \"InsideHeight\": 270.75,\r\n      \"InsideWidth\": 379.5,\r\n      \"KeepScrollBarsVisible\": 3,\r\n      \"MousePointer\": 0,\r\n      \"PictureAlignment\": 2,\r\n      \"PictureSizeMode\": 0,\r\n      \"PictureTiling\": false,\r\n      \"ScrollBars\": 0,\r\n      \"ScrollHeight\": 0,\r\n      \"ScrollLeft\": 0,\r\n      \"ScrollTop\": 0,\r\n      \"ScrollWidth\": 0,\r\n      \"SpecialEffect\": 0,\r\n      \"VerticalScrollBarSide\": 0,\r\n      \"Zoom\": 100,\r\n      \"Name\": \"frmForm20\",\r\n      \"Caption\": \"MS Forms 2.0 Example\",\r\n      \"Left\": 0,\r\n      \"Width\": 384,\r\n      \"Enabled\": true,\r\n      \"Tag\": \"\",\r\n      \"HelpContextID\": 0,\r\n      \"WhatsThisButton\": false,\r\n      \"WhatsThisHelp\": false,\r\n      \"RightToLeft\": false,\r\n      \"StartUpPosition\": 1,\r\n      \"ShowModal\": true\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Testing/Testing.accdb.src/vcs-options.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"AddinVersion\": \"4.1.2\",\r\n    \"AccessVersion\": \"14.0 32-bit\"\r\n  },\r\n  \"Options\": {\r\n    \"ExportFolder\": \"\",\r\n    \"ShowDebug\": false,\r\n    \"UseFastSave\": false,\r\n    \"UseMergeBuild\": false,\r\n    \"UseGitIntegration\": false,\r\n    \"SavePrintVars\": true,\r\n    \"ExportPrintSettings\": {\r\n      \"Orientation\": true,\r\n      \"PaperSize\": true,\r\n      \"Duplex\": false,\r\n      \"PrintQuality\": false,\r\n      \"DisplayFrequency\": false,\r\n      \"Collate\": false,\r\n      \"Resolution\": false,\r\n      \"DisplayFlags\": false,\r\n      \"Color\": false,\r\n      \"Copies\": false,\r\n      \"ICMMethod\": false,\r\n      \"DefaultSource\": false,\r\n      \"Scale\": false,\r\n      \"ICMIntent\": false,\r\n      \"FormName\": false,\r\n      \"PaperLength\": false,\r\n      \"DitherType\": false,\r\n      \"MediaType\": false,\r\n      \"PaperWidth\": false,\r\n      \"TTOption\": false\r\n    },\r\n    \"SaveQuerySQL\": true,\r\n    \"FormatSQL\": true,\r\n    \"ForceImportOriginalQuerySQL\": false,\r\n    \"SaveTableSQL\": true,\r\n    \"SplitLayoutFromVBA\": true,\r\n    \"StripPublishOption\": true,\r\n    \"SanitizeColors\": 1,\r\n    \"SanitizeLevel\": 2,\r\n    \"ExtractThemeFiles\": false,\r\n    \"TablesToExportData\": {\r\n      \"tblInternal\": {\r\n        \"Format\": \"Tab Delimited\"\r\n      },\r\n      \"tblLinkedAccess\": {\r\n        \"Format\": \"XML Format\"\r\n      },\r\n      \"tblSaveXML\": {\r\n        \"Format\": \"XML Format\"\r\n      },\r\n      \"USysRegInfo\": {\r\n        \"Format\": \"Tab Delimited\"\r\n      },\r\n      \"USysRibbons\": {\r\n        \"Format\": \"Tab Delimited\"\r\n      }\r\n    },\r\n    \"SchemaExports\": {\r\n    },\r\n    \"RunBeforeExport\": \"\",\r\n    \"RunAfterExport\": \"\",\r\n    \"RunBeforeBuild\": \"\",\r\n    \"RunAfterBuild\": \"RunAfterBuild\",\r\n    \"RunBeforeMerge\": \"\",\r\n    \"RunAfterMerge\": \"\",\r\n    \"ShowVCSLegacy\": true,\r\n    \"HashAlgorithm\": \"SHA256\",\r\n    \"UseShortHash\": true,\r\n    \"BreakOnError\": true,\r\n    \"PreserveRubberDuckID\": false\r\n  }\r\n}\r\n"
  },
  {
    "path": "Translation/MSAccessVCS.pot",
    "content": "# Version Control System Add-in for Microsoft Access\n# https://github.com/joyfullservice/msaccess-vcs-addin\n# This file is distributed under the project's BSD-style license.\nmsgid \"\"\nmsgstr \"\"\n\n#: frmVCSTableData.Label40(txtTableIcon)\nmsgid \" \"\nmsgstr \"\"\n\n#: frmVCSMain.cmdExport\nmsgid \"   Export All Source\"\nmsgstr \"\"\n\n#: frmVCSInstall.cmdInstall\nmsgid \"   Install Add-In\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.cmdSplitFiles\nmsgid \"   Split Files\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdBuild\nmsgid \"  Build From Source\"\nmsgstr \"\"\n\n#: frmVCSConflict.cmdCancel\nmsgid \"  Cancel\"\nmsgstr \"\"\n\n#: frmVCSConflict.cmdContinue\nmsgid \"  Continue\"\nmsgstr \"\"\n\nmsgid \"  Merge From Source\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdAddDatabase\nmsgid \" Add\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdAddOtherTableData\nmsgid \" Add Other\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdClearDefaults\nmsgid \" Clear Defaults\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdDeleteDatabase\nmsgid \" Delete\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdEditDatabase\nmsgid \" Edit...\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdHelp\nmsgid \" Help\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdOptions\nmsgid \" Options...\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label66(Frame65)\nmsgid \" Remove Add-In\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdRestoreDefaults\nmsgid \" Restore Defaults\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdSaveAndClose\nmsgid \" Save && Close\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdSaveAsDefault\nmsgid \" Save as Default\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdSyncTranslations\nmsgid \" Sync Files\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label63(Frame62)\nmsgid \" System Defaults\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdUninstall\nmsgid \" Uninstall\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblForceImportSQLNote\nmsgid \"(\\\"Save Query SQL\\\" option needed when exporting)\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label46\nmsgid \"(Blank for default)\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label231\nmsgid \"(Module name optional)\"\nmsgstr \"\"\n\nmsgid \"\"\n\"* (This database will be renamed as a backup before building {0} from \"\n\"source.)\"\nmsgstr \"\"\n\n#: frmVCSInstall.cmdChangeInstallFolder\nmsgid \"...\"\nmsgstr \"\"\n\nmsgid \"\"\n\"<strong><em>Export</em></strong> source to generate source files from the \"\n\"current database.<br><br><strong><em>Import</em></strong> source files to \"\n\"rebuild this database from source.\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblVersion\nmsgid \"4.0.37\"\nmsgstr \"\"\n\nmsgid \"\"\n\"A summary of the build progress can be seen on this screen, and \"\n\"additional details are included in the log file.\"\nmsgstr \"\"\n\n#: Ribbon.grpActions.Label\nmsgid \"Actions\"\nmsgstr \"\"\n\n#: Ribbon.btnActivateHook.Label\nmsgid \"Activate Hook\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.cmdAddFormsAndReports\nmsgid \"Add Forms and Reports...\"\nmsgstr \"\"\n\nmsgid \"\"\n\"Additional details can be found in the project log file.<br><br>You may \"\n\"now close this window.\"\nmsgstr \"\"\n\n#: frmVCSInstall.tabInstallType.Page41\nmsgid \"Advanced Install\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label61\nmsgid \"Advanced Options\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label65(chkAdvancedOptions)\nmsgid \"Advanced Options...\"\nmsgstr \"\"\n\n#: Ribbon.mnuAdvancedTools.Label\nmsgid \"Advanced Tools\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label57(cmdRestoreDefaults)\nmsgid \"Apply system defaults to this project.\"\nmsgstr \"\"\n\n#: frmVCSInstall.tabInstallType.Page40\nmsgid \"Basic Install\"\nmsgstr \"\"\n\nmsgid \"Beginning build from Source\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeBuild\nmsgid \"Build\"\nmsgstr \"\"\n\nmsgid \"Build {0} ({1}) from source?\"\nmsgstr \"\"\n\n#: Ribbon.btnBuildAs.Label\nmsgid \"Build As…\"\nmsgstr \"\"\n\nmsgid \"Build Complete\"\nmsgstr \"\"\n\nmsgid \"Build error in: {0}\"\nmsgstr \"\"\n\n#: Ribbon.btnBuild.Label\nmsgid \"Build From Source\"\nmsgstr \"\"\n\n#: Ribbon.btnBuildAs.Supertip\nmsgid \"Build from source files to a new name or location.\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenBuildLog.Label\nmsgid \"Build Log\"\nmsgstr \"\"\n\nmsgid \"Build must be run from Add-In\"\nmsgstr \"\"\n\n#: Ribbon.btnBuild.Supertip\nmsgid \"Build the current project from source files.\"\nmsgstr \"\"\n\nmsgid \"Building From Source\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdCancel\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: frmVCSMain.lblHeading\nmsgid \"Choose Action\"\nmsgstr \"\"\n\nmsgid \"\"\n\"Click 'No' to select another project, or 'Cancel' to go back to the \"\n\"previous screen.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label303(cmdSyncTranslations)\nmsgid \"\"\n\"Click the \\\"Sync Files\\\" button to load the updated translations into the \"\n\"add-in, and then update the master template and language files with the \"\n\"latest strings from the database.\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblSubheading\nmsgid \"\"\n\"Click the Install button to install or update the add-in.\\n\"\n\"\\n\"\n\"In some more secure environments, you may need to adjust additional \"\n\"options to use this addin.\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblSubheading\nmsgid \"\"\n\"Click the Install button to install or update the add-in.\\n\"\n\"\\n\"\n\"In some more secure environments,\\n\"\n\"you may need to adjust additional\\n\"\n\"options to use this addin.\"\nmsgstr \"\"\n\nmsgid \"\"\n\"Click 'Yes' to rebuild* this database from source files in this folder:\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdClose\nmsgid \"Close\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label134(chkCollate)\nmsgid \"Collate\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label144(chkColor)\nmsgid \"Color or B/W\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label244(txtName)\nmsgid \"Connection Name:\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label248(cboConnect)\nmsgid \"Connection String:\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label256(chkContributeTranslations)\nmsgid \"Contribute to Translations\"\nmsgstr \"\"\n\nmsgid \"Created blank database for import. (v{0})\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label119(chkPaperLength)\nmsgid \"Custom Length\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label158(chkPaperWidth)\nmsgid \"Custom Width\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label252(cboType)\nmsgid \"Database Type:\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeDatabases\nmsgid \"Databases\"\nmsgstr \"\"\n\nmsgid \"db connections\"\nmsgstr \"\"\n\nmsgid \"db properties\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label219(chkBreakOnError)\nmsgid \"Debug VBA Errors\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label246(txtDescription)\nmsgid \"Description:\"\nmsgstr \"\"\n\n#: frmVCSMain.Label9\nmsgid \"\"\n\"Designed for \\n\"\n\"GitHub && GitLab\"\nmsgstr \"\"\n\n#: frmVCSMain.Label9\nmsgid \"\"\n\"Designed for \\n\"\n\"GitHub && GitLab\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label239(cboDiffTool)\nmsgid \"Diff Tool:\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label146(chkDisplayFlags)\nmsgid \"Display Flags\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label148(chkDisplayFrequency)\nmsgid \"Display Freq.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label156(chkDitherType)\nmsgid \"Dither Type\"\nmsgstr \"\"\n\nmsgid \"doc properties\"\nmsgstr \"\"\n\nmsgid \"Done. ({0} seconds)\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkDownload.Label\nmsgid \"Download Latest Version\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label130(chkDuplex)\nmsgid \"Duplex\"\nmsgstr \"\"\n\n#: Ribbon.btnSplitFiles.Description\nmsgid \"Duplicate files while preserving Git line history\"\nmsgstr \"\"\n\n#: Ribbon.btnActivateHook.Description\nmsgid \"\"\n\"Enable Access Application hook to report saving of objects and enabling \"\n\"automatic export on save.\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label261(chkEnabled)\nmsgid \"Enabled\"\nmsgstr \"\"\n\nmsgid \"Error running {0}\"\nmsgstr \"\"\n\n#: frmVCSDatabase.cmdExamples\nmsgid \"Examples...\"\nmsgstr \"\"\n\n#: frmVCSInstall.cmdExplainOptions\nmsgid \"Explain options...\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeExport\nmsgid \"Export\"\nmsgstr \"\"\n\n#: Ribbon.btnExport.Supertip\nmsgid \"\"\n\"Export all source files for the current project using the current options.\"\nmsgstr \"\"\n\n#: frmVCSTableData.Label15(cboFormatType)\nmsgid \"Export As\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label31(txtExportFolder)\nmsgid \"Export Folder:\"\nmsgstr \"\"\n\nmsgid \"Export Folder: {0}\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenExportLog.Label\nmsgid \"Export Log\"\nmsgstr \"\"\n\n#: Ribbon.btnExportVBA.Supertip\nmsgid \"\"\n\"Export only the components that may contain VBA code. Includes forms, \"\n\"reports, and VBA code modules.\"\nmsgstr \"\"\n\n#: Ribbon.btnExportSelected.Label\nmsgid \"Export Selected\"\nmsgstr \"\"\n\n#: Ribbon.btnExport.Label\nmsgid \"Export Source Files\"\nmsgstr \"\"\n\n#: Ribbon.btnExportSelected.Supertip\nmsgid \"\"\n\"Export the database component currently selected in the navigation pane.\"\nmsgstr \"\"\n\n#: Ribbon.btnExportVBA.Label\nmsgid \"Export VBA Code\"\nmsgstr \"\"\n\n#: frmVCSDatabase.lblOptions\nmsgid \"External Database Connection\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label243(lstDatabases)\nmsgid \"External Databases:\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label112(chkExtractThemeFiles)\nmsgid \"Extract Theme Files\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label250(txtFilter)\nmsgid \"Filter for database objects (source file paths):\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label253\nmsgid \"\"\n\"Filter paths to source files. Start line with ! to exclude match. Use * \"\n\"for wildcard matches (LIKE comparison).\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.Label244(txtCommitMessage)\nmsgid \"Final Commit Message:\"\nmsgstr \"\"\n\nmsgid \"Finished\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblForceImportOriginalQuerySQL(chkForceImportOriginalQuerySQL)\nmsgid \"Force import of original SQL for queries\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label142(chkFormName)\nmsgid \"Form Name\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label249(chkFormatSQL)\nmsgid \"Format SQL\"\nmsgstr \"\"\n\nmsgid \"forms\"\nmsgstr \"\"\n\n#: frmVCSMain.Label31(chkFullBuild)\nmsgid \"Full Build\"\nmsgstr \"\"\n\n#: frmVCSMain.Label29(chkFullExport)\nmsgid \"Full Export\"\nmsgstr \"\"\n\nmsgid \"Full Path: {0}\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeGeneral\nmsgid \"General\"\nmsgstr \"\"\n\n#: Ribbon.grpGitHub.Label\nmsgid \"GitHub Community\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label221(cboHashAlgorithm)\nmsgid \"Hash Algorithm:\"\nmsgstr \"\"\n\nmsgid \"hidden attributes\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label152(chkICMIntent)\nmsgid \"ICM Intent\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label150(chkICMMethod)\nmsgid \"ICM Method\"\nmsgstr \"\"\n\nmsgid \"imex specs\"\nmsgstr \"\"\n\n#: Ribbon.btnLoadSelected.Supertip\nmsgid \"\"\n\"Import from source files the database component currently selected in the \"\n\"navigation pane.\"\nmsgstr \"\"\n\nmsgid \"Importing {0}...\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblHeading(Page40)\nmsgid \"Install Add-In\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label63(txtInstallFolder)\nmsgid \"Install Folder:   \"\nmsgstr \"\"\n\nmsgid \"\"\n\"Instead of opening this form to build the add-in,\\n\"\n\"please install and use the Version Control add-in from the Add-in menu\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label32\nmsgid \"joyfullservice/msaccess-vcs-addin\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label252(cboLanguage)\nmsgid \"Language:\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.Label241(txtFileList)\nmsgid \"List of files to split:\"\nmsgstr \"\"\n\n#: Ribbon.btnLoadSelected.Label\nmsgid \"Load Selected\"\nmsgstr \"\"\n\n#: Ribbon.btnLocalizeLibraryReferences.Label\nmsgid \"Localize Library References\"\nmsgstr \"\"\n\nmsgid \"macros\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label154(chkMediaType)\nmsgid \"Media Type\"\nmsgstr \"\"\n\nmsgid \"Merge\"\nmsgstr \"\"\n\n#: Ribbon.btnMergeBuild.Label\nmsgid \"Merge Build\"\nmsgstr \"\"\n\n#: Ribbon.btnMergeBuild.Supertip\nmsgid \"Merge changed source files into the current database.\"\nmsgstr \"\"\n\nmsgid \"Merge Complete\"\nmsgstr \"\"\n\nmsgid \"Merging From Source\"\nmsgstr \"\"\n\nmsgid \"modules\"\nmsgstr \"\"\n\n#: frmVCSInstall.Caption\nmsgid \"MSAccessVCS\"\nmsgstr \"\"\n\n# Do not remove the semicolon\n#: lstDatabases.RefreshSchemaList\nmsgid \"Name;Description\"\nmsgstr \"\"\n\nmsgid \"nav pane groups\"\nmsgstr \"\"\n\nmsgid \"No {0} source files found.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label123(chkCopies)\nmsgid \"Number of Copies\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkDocumentation.Label\nmsgid \"Online Documentation\"\nmsgstr \"\"\n\n#: Ribbon.btnShow.Label\nmsgid \"Open Add-in\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label36(chkOpenAfterInstall)\nmsgid \"Open add-in after installing to trust the add-in file\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdOpenInstallFolder\nmsgid \"Open Install Folder\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdOpenLogFile\nmsgid \"Open Log File...\"\nmsgstr \"\"\n\n#: Ribbon.btnShow.Supertip\nmsgid \"\"\n\"Open the main form for the add-in. From the main form you can run an \"\n\"export or build a project from source.\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenBuildLog.Supertip\nmsgid \"Open the most recent build log file.\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenExportLog.Supertip\nmsgid \"Open the most recent export log file.\"\nmsgstr \"\"\n\n#: Ribbon.btnShowOptions.Supertip\nmsgid \"\"\n\"Open the Options form to view or change options for the current project.\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenSourceFolder.Supertip\nmsgid \"Open the source folder for the current project.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label241(cmdOpenInstallFolder)\nmsgid \"Open VCS add-In Install Folder.\"\nmsgstr \"\"\n\n#: Ribbon.grpOptions.Label\nmsgid \"Options\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdPrintSettingsOptions\nmsgid \"Options...\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label79(txtOtherTableName)\nmsgid \"Other Table Name:\"\nmsgstr \"\"\n\n#: frmVCSConflict.cmdOverwriteAll\nmsgid \"Overwrite All\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label115(chkOrientation)\nmsgid \"Page Orientation\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label117(chkPaperSize)\nmsgid \"Paper Size\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label125(chkDefaultSource)\nmsgid \"Paper Tray\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label105\nmsgid \"\"\n\"PLEASE NOTE:\\n\"\n\"Building from source will completely rebuild your database project from \"\n\"source files. It is very important that you test this carefully in your \"\n\"environment to make sure everything you need is being created in your \"\n\"database during the build. (A backup copy of your current database will \"\n\"be made as a part of the build process.)\\n\"\n\"\\n\"\n\"Please see the online documentation for additional details on the build \"\n\"process. If you encounter a problem, please feel free to open an issues \"\n\"on the GitHub project.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label105\nmsgid \"\"\n\"PLEASE NOTE:\\n\"\n\"Building from source will completely rebuild your database project from \"\n\"source files. It is very important that you test this carefully in your \"\n\"environment to make sure everything you need is being created in your \"\n\"database during the build. (A backup copy of your current database will \"\n\"be made as a part of the build process.)\\n\"\n\"\\n\"\n\"Please see the online documentation for additional details on the build \"\n\"process. If you encounter a problem, please feel free to open an issues \"\n\"on the GitHub project.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label128(chkPrintQuality)\nmsgid \"Print Quality\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label121(chkScale)\nmsgid \"Print Scale\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgePrinterSettings\nmsgid \"Printer Settings\"\nmsgstr \"\"\n\nmsgid \"proj properties\"\nmsgstr \"\"\n\nmsgid \"project\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkHome.Label\nmsgid \"Project Home\"\nmsgstr \"\"\n\nmsgid \"queries\"\nmsgstr \"\"\n\n#: Ribbon.btnRepairColors.Description\nmsgid \"\"\n\"Rebuild color definition blocks in form objects (See documentation for \"\n\"more info)\"\nmsgstr \"\"\n\n#: Ribbon.btnReloadRibbon.Description\nmsgid \"Refresh the add-in ribbon menu to reflect changes in XML source\"\nmsgstr \"\"\n\nmsgid \"relations\"\nmsgstr \"\"\n\n#: Ribbon.btnLocalizeLibraryReferences.Description\nmsgid \"\"\n\"Relink add-in library databases to the files located in the same folder \"\n\"as the current database\"\nmsgstr \"\"\n\n#: Ribbon.btnReloadRibbon.Label\nmsgid \"Reload Ribbon\"\nmsgstr \"\"\n\nmsgid \"Removing non built-in references...\"\nmsgstr \"\"\n\n#: Ribbon.btnRepairColors.Label\nmsgid \"Repair Colors\"\nmsgstr \"\"\n\nmsgid \"reports\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label59(cmdClearDefaults)\nmsgid \"Reset all default settings to original values.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label132(chkResolution)\nmsgid \"Resolution\"\nmsgstr \"\"\n\n#: frmVCSConflict.Label4\nmsgid \"Resolve Conflicts to Continue...\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label104(txtRunAfterBuild)\nmsgid \"Run Sub After Build:\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label48(txtRunAfterExport)\nmsgid \"Run Sub After Export:\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label229(txtRunBeforeBuild)\nmsgid \"Run Sub Before Build:\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label44(txtRunBeforeExport)\nmsgid \"Run Sub Before Export:\"\nmsgstr \"\"\n\nmsgid \"Running {0}...\"\nmsgstr \"\"\n\nmsgid \"Running...\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label233(cboSanitizeColors)\nmsgid \"Sanitize Colors\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label235(cboSanitizeLevel)\nmsgid \"Sanitize Level\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label257(chkSaveDotEnv)\nmsgid \"Save in .env file*\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label27(chkSavePrintVars)\nmsgid \"Save Printer Settings\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label29(chkSaveQuerySQL)\nmsgid \"Save Query SQL\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label38(chkSaveTableSQL)\nmsgid \"Save Table SQL\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label53(cmdSaveAsDefault)\nmsgid \"Save these settings as default for new projects.\"\nmsgstr \"\"\n\nmsgid \"Saved as {0}.\"\nmsgstr \"\"\n\nmsgid \"saved specs\"\nmsgstr \"\"\n\nmsgid \"Saving backup of original database...\"\nmsgstr \"\"\n\nmsgid \"Scanning source files...\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdSeeDocs\nmsgid \"See Docs...\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkAdvancedTools.Description\nmsgid \"See documentation on the purpose and usage of these custom tools...\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label126\nmsgid \"\"\n\"Select any additional settings that you would like saved to version \"\n\"control and used when building a database from source files.\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeSettings\nmsgid \"Settings\"\nmsgstr \"\"\n\nmsgid \"shared images\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label23(chkShowDebug)\nmsgid \"Show Detailed Output\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblTableShowHidden(chkTableShowHidden)\nmsgid \"Show Hidden\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label215(chkShowVCSLegacy)\nmsgid \"Show Legacy Prompts\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblTableShowOther(chkTableShowOther)\nmsgid \"Show Other\"\nmsgstr \"\"\n\nmsgid \"Show Other  \"\nmsgstr \"\"\n\n#: frmVCSOptions.lblTableShowSystem(chkTableShowSystem)\nmsgid \"Show System\"\nmsgstr \"\"\n\n#: frmVCSConflict.cmdSkipAll\nmsgid \"Skip All\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenSourceFolder.Label\nmsgid \"Source Folder\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblWorkingPathExplanation\nmsgid \"Specify the path for the working language files.\"\nmsgstr \"\"\n\n#: Ribbon.btnSplitFiles.Label\nmsgid \"Split Files\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.lblOptions\nmsgid \"Split Files and Preserve History\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label251(chkSplitLayoutFromVBA)\nmsgid \"Split Layout from VBA\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkSupport.Label\nmsgid \"Support Issues\"\nmsgstr \"\"\n\nmsgid \"table data\"\nmsgstr \"\"\n\n#: frmVCSTableData.Caption\nmsgid \"Table Data\"\nmsgstr \"\"\n\nmsgid \"table data macros\"\nmsgstr \"\"\n\n#: frmVCSTableData.Label3(txtTableName)\nmsgid \"Table Name\"\nmsgstr \"\"\n\nmsgid \"tables\"\nmsgstr \"\"\n\n#: frmVCSDatabase.cmdTest\nmsgid \"Test Filter...\"\nmsgstr \"\"\n\nmsgid \"themes\"\nmsgstr \"\"\n\n#: frmVCSConflict.lblHeading(sfrmConflictList)\nmsgid \"These source files have changed since the last export\"\nmsgstr \"\"\n\n#: Ribbon.mnuAdvancedTools.Supertip\nmsgid \"\"\n\"These tools are intended for more advanced development purposes. Please \"\n\"review the documentation for these tools before using them.\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label227\nmsgid \"\"\n\"This add-in provides limited support for exporting object definitions \"\n\"from external databases for use in version control.\\n\"\n\"\\n\"\n\"* If the connection string contains a password, please use the .env \"\n\"option, and exclude this file from version control.\"\nmsgstr \"\"\n\n#: Ribbon.PLEASE NOTE:.Label\nmsgid \"\"\n\"This file is automatically generated by the add-in. Please do not \"\n\"translate or edit this file directly.\"\nmsgstr \"\"\n\n#: Ribbon.grpTools.Label\nmsgid \"Tools\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeTranslations\nmsgid \"Translation\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label297(txtTranslationsPath)\nmsgid \"Translations Path: \"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdTranslations\nmsgid \"Translations...\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label34(chkAddTrustedLocation)\nmsgid \"Trust Add-In Folder\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label140(chkTTOption)\nmsgid \"TT Font Handling\"\nmsgstr \"\"\n\nmsgid \"Unable to load Ribbon COM add-in\"\nmsgstr \"\"\n\nmsgid \"Unable to rename original file\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label108(cmdUninstall)\nmsgid \"Uninstall this add-in\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label230\nmsgid \"Use \\\"module.sub\\\" to specify module\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label25(chkUseFastSave)\nmsgid \"Use Fast Save\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.Label242\nmsgid \"\"\n\"Use full file paths with a pipe character (|) between the original and \"\n\"new file paths.\\n\"\n\"\\n\"\n\"Each entry should be on a new line. The list will be validated before \"\n\"running the operation.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label163(chkUseGitIntegration)\nmsgid \"Use Git Integration\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label40(chkUseRibbon)\nmsgid \"Use Ribbbon Addin\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label223(chkUseShortHash)\nmsgid \"Use short hashes in index\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label227\nmsgid \"\"\n\"Use this form to set your preferred options for exporting and building \"\n\"your database project to and from source files. Note that you can have \"\n\"different options for different projects, and can save a set of options \"\n\"as default for new projects.\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblTranslationExplanation\nmsgid \"\"\n\"Use this page to manage language translations for this add-in. Check this \"\n\"box if you would like to help improve the translations.\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.Label227\nmsgid \"\"\n\"Use this tool to duplicate files while preserving commit history in Git.\\n\"\n\"\\n\"\n\"Paste in a list of Pipe delimited paths and click the button below.\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label259(chkUtcDates)\nmsgid \"Use UTC Dates\"\nmsgstr \"\"\n\nmsgid \"vb project\"\nmsgstr \"\"\n\nmsgid \"vbe forms\"\nmsgstr \"\"\n\nmsgid \"vbe references\"\nmsgstr \"\"\n\nmsgid \"VCS Version {0}\"\nmsgstr \"\"\n\nmsgid \"Version {0}\"\nmsgstr \"\"\n\nmsgid \"Version {0} currently installed.\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblInstalled\nmsgid \"Version 4.0.11 currently installed.\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblVersion\nmsgid \"Version 4.0.12\"\nmsgstr \"\"\n\n#: Ribbon.tabVersionControl.Label\nmsgid \"Version Control\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label9\nmsgid \"Version Control System \"\nmsgstr \"\"\n\n#: frmVCSInstall.Label4\nmsgid \"Version Control System\"\nmsgstr \"\"\n\n#: frmVCSConflict.lblVersion\nmsgid \"Version Control System ${version}\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkAdvancedTools.Label\nmsgid \"View Documentation\"\nmsgstr \"\"\n\n#: Ribbon.btnShowOptions.Label\nmsgid \"View Options\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkHome.Supertip\nmsgid \"View the add-in project home on GitHub.\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkSupport.Supertip\nmsgid \"View the add-in project support issues on GitHub.\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkDocumentation.Supertip\nmsgid \"View the documentation Wiki on GitHub.\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkDownload.Supertip\nmsgid \"View the GitHub download page for this add-in project.\"\nmsgstr \"\"\n\n#: frmVCSConflict.lblHeaderExplain\nmsgid \"\"\n\"WARNING: The following changes may be overwritten. Please select how you \"\n\"would \\n\"\n\"like to proceed.\"\nmsgstr \"\"\n\n#: frmVCSMain.lblSubheading\nmsgid \"What would you like to do?\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label82\nmsgid \"\"\n\"You may optionally include data from specific tables as part of the \"\n\"export process.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label244\nmsgid \"\"\n\"Your database application may connect to external database systems such \"\n\"as Microsoft SQL Server.\\n\"\n\"\\n\"\n\"If you define a connection to an external database, you can use version \"\n\"control to track changes in those external database objects.\\n\"\n\"\\n\"\n\"Please see documentation for additional notes.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label244\nmsgid \"\"\n\"Your database application may connect to external database systems such \"\n\"as Microsoft SQL Server.\\n\"\n\"\\n\"\n\"If you define a connection to an external database, you can use version \"\n\"control to track changes in those external database objects.\\n\"\n\"\\n\"\n\"Please see documentation for additional notes.\"\nmsgstr \"\"\n"
  },
  {
    "path": "Translation/en_TEST.po",
    "content": "# Version Control System Add-in for Microsoft Access\n# https://github.com/joyfullservice/msaccess-vcs-addin\n# This file is distributed under the project's BSD-style license.\nmsgid \"\"\nmsgstr \"\"\n\n#: frmVCSTableData.Label40(txtTableIcon)\nmsgid \" \"\nmsgstr \" \"\n\n#: frmVCSMain.cmdExport\nmsgid \"   Export All Source\"\nmsgstr \"   EXPORT ALL SOURCE\"\n\n#: frmVCSInstall.cmdInstall\nmsgid \"   Install Add-In\"\nmsgstr \"   INSTALL ADD-IN\"\n\n#: frmVCSSplitFiles.cmdSplitFiles\nmsgid \"   Split Files\"\nmsgstr \"   SPLIT FILES\"\n\n#: frmVCSMain.cmdBuild\nmsgid \"  Build From Source\"\nmsgstr \"  BUILD FROM SOURCE\"\n\n#: frmVCSConflict.cmdCancel\nmsgid \"  Cancel\"\nmsgstr \"  CANCEL\"\n\n#: frmVCSConflict.cmdContinue\nmsgid \"  Continue\"\nmsgstr \"  CONTINUE\"\n\nmsgid \"  Merge From Source\"\nmsgstr \"  MERGE FROM SOURCE\"\n\n#: frmVCSOptions.cmdAddDatabase\nmsgid \" Add\"\nmsgstr \" ADD\"\n\n#: frmVCSOptions.cmdAddOtherTableData\nmsgid \" Add Other\"\nmsgstr \" ADD OTHER\"\n\n#: frmVCSOptions.cmdClearDefaults\nmsgid \" Clear Defaults\"\nmsgstr \" CLEAR DEFAULTS\"\n\n#: frmVCSOptions.cmdDeleteDatabase\nmsgid \" Delete\"\nmsgstr \" DELETE\"\n\n#: frmVCSOptions.cmdEditDatabase\nmsgid \" Edit...\"\nmsgstr \" EDIT...\"\n\n#: frmVCSMain.cmdHelp\nmsgid \" Help\"\nmsgstr \" HELP\"\n\n#: frmVCSMain.cmdOptions\nmsgid \" Options...\"\nmsgstr \" OPTIONS...\"\n\n#: frmVCSOptions.Label66(Frame65)\nmsgid \" Remove Add-In\"\nmsgstr \" REMOVE ADD-IN\"\n\n#: frmVCSOptions.cmdRestoreDefaults\nmsgid \" Restore Defaults\"\nmsgstr \" RESTORE DEFAULTS\"\n\n#: frmVCSOptions.cmdSaveAndClose\nmsgid \" Save && Close\"\nmsgstr \" SAVE && CLOSE\"\n\n#: frmVCSOptions.cmdSaveAsDefault\nmsgid \" Save as Default\"\nmsgstr \" SAVE AS DEFAULT\"\n\n#: frmVCSOptions.cmdSyncTranslations\nmsgid \" Sync Files\"\nmsgstr \" SYNC FILES\"\n\n#: frmVCSOptions.Label63(Frame62)\nmsgid \" System Defaults\"\nmsgstr \" SYSTEM DEFAULTS\"\n\n#: frmVCSOptions.cmdUninstall\nmsgid \" Uninstall\"\nmsgstr \" UNINSTALL\"\n\n#: frmVCSOptions.lblForceImportSQLNote\nmsgid \"(\\\"Save Query SQL\\\" option needed when exporting)\"\nmsgstr \"(\\\"SAVE QUERY SQL\\\" OPTION NEEDED WHEN EXPORTING)\"\n\n#: frmVCSOptions.Label46\nmsgid \"(Blank for default)\"\nmsgstr \"(BLANK FOR DEFAULT)\"\n\n#: frmVCSOptions.Label231\nmsgid \"(Module name optional)\"\nmsgstr \"(MODULE NAME OPTIONAL)\"\n\nmsgid \"\"\n\"* (This database will be renamed as a backup before building {0} from \"\n\"source.)\"\nmsgstr \"\"\n\"* (THIS DATABASE WILL BE RENAMED AS A BACKUP BEFORE BUILDING {0} FROM \"\n\"SOURCE.)\"\n\n#: frmVCSInstall.cmdChangeInstallFolder\nmsgid \"...\"\nmsgstr \"...\"\n\nmsgid \"\"\n\"<strong><em>Export</em></strong> source to generate source files from the \"\n\"current database.<br><br><strong><em>Import</em></strong> source files to \"\n\"rebuild this database from source.\"\nmsgstr \"\"\n\"<STRONG><EM>EXPORT</EM></STRONG> SOURCE TO GENERATE SOURCE FILES FROM THE \"\n\"CURRENT DATABASE.<BR><BR><STRONG><EM>IMPORT</EM></STRONG> SOURCE FILES TO \"\n\"REBUILD THIS DATABASE FROM SOURCE.\"\n\n#: frmVCSOptions.lblVersion\nmsgid \"4.0.37\"\nmsgstr \"4.0.37\"\n\nmsgid \"\"\n\"A summary of the build progress can be seen on this screen, and \"\n\"additional details are included in the log file.\"\nmsgstr \"\"\n\"A SUMMARY OF THE BUILD PROGRESS CAN BE SEEN ON THIS SCREEN, AND \"\n\"ADDITIONAL DETAILS ARE INCLUDED IN THE LOG FILE.\"\n\n#: Ribbon.grpActions.Label\nmsgid \"Actions\"\nmsgstr \"ACTIONS\"\n\n#: Ribbon.btnActivateHook.Label\nmsgid \"Activate Hook\"\nmsgstr \"ACTIVATE HOOK\"\n\n#: frmVCSSplitFiles.cmdAddFormsAndReports\nmsgid \"Add Forms and Reports...\"\nmsgstr \"ADD FORMS AND REPORTS...\"\n\nmsgid \"\"\n\"Additional details can be found in the project log file.<br><br>You may \"\n\"now close this window.\"\nmsgstr \"\"\n\"ADDITIONAL DETAILS CAN BE FOUND IN THE PROJECT LOG FILE.<BR><BR>YOU MAY \"\n\"NOW CLOSE THIS WINDOW.\"\n\n#: frmVCSInstall.tabInstallType.Page41\nmsgid \"Advanced Install\"\nmsgstr \"ADVANCED INSTALL\"\n\n#: frmVCSInstall.Label61\nmsgid \"Advanced Options\"\nmsgstr \"ADVANCED OPTIONS\"\n\n#: frmVCSInstall.Label65(chkAdvancedOptions)\nmsgid \"Advanced Options...\"\nmsgstr \"ADVANCED OPTIONS...\"\n\n#: Ribbon.mnuAdvancedTools.Label\nmsgid \"Advanced Tools\"\nmsgstr \"ADVANCED TOOLS\"\n\n#: frmVCSOptions.Label57(cmdRestoreDefaults)\nmsgid \"Apply system defaults to this project.\"\nmsgstr \"APPLY SYSTEM DEFAULTS TO THIS PROJECT.\"\n\n#: frmVCSInstall.tabInstallType.Page40\nmsgid \"Basic Install\"\nmsgstr \"BASIC INSTALL\"\n\nmsgid \"Beginning build from Source\"\nmsgstr \"BEGINNING BUILD FROM SOURCE\"\n\n#: frmVCSOptions.tabOptions.pgeBuild\nmsgid \"Build\"\nmsgstr \"BUILD\"\n\nmsgid \"Build {0} ({1}) from source?\"\nmsgstr \"BUILD {0} ({1}) FROM SOURCE?\"\n\n#: Ribbon.btnBuildAs.Label\nmsgid \"Build As…\"\nmsgstr \"BUILD AS…\"\n\nmsgid \"Build Complete\"\nmsgstr \"BUILD COMPLETE\"\n\nmsgid \"Build error in: {0}\"\nmsgstr \"BUILD ERROR IN: {0}\"\n\n#: Ribbon.btnBuild.Label\nmsgid \"Build From Source\"\nmsgstr \"BUILD FROM SOURCE\"\n\n#: Ribbon.btnBuildAs.Supertip\nmsgid \"Build from source files to a new name or location.\"\nmsgstr \"BUILD FROM SOURCE FILES TO A NEW NAME OR LOCATION.\"\n\n#: Ribbon.btnOpenBuildLog.Label\nmsgid \"Build Log\"\nmsgstr \"BUILD LOG\"\n\nmsgid \"Build must be run from Add-In\"\nmsgstr \"BUILD MUST BE RUN FROM ADD-IN\"\n\n#: Ribbon.btnBuild.Supertip\nmsgid \"Build the current project from source files.\"\nmsgstr \"BUILD THE CURRENT PROJECT FROM SOURCE FILES.\"\n\nmsgid \"Building From Source\"\nmsgstr \"BUILDING FROM SOURCE\"\n\n#: frmVCSOptions.cmdCancel\nmsgid \"Cancel\"\nmsgstr \"CANCEL\"\n\n#: frmVCSMain.lblHeading\nmsgid \"Choose Action\"\nmsgstr \"CHOOSE ACTION\"\n\nmsgid \"\"\n\"Click 'No' to select another project, or 'Cancel' to go back to the \"\n\"previous screen.\"\nmsgstr \"\"\n\"CLICK 'NO' TO SELECT ANOTHER PROJECT, OR 'CANCEL' TO GO BACK TO THE \"\n\"PREVIOUS SCREEN.\"\n\n#: frmVCSOptions.Label303(cmdSyncTranslations)\nmsgid \"\"\n\"Click the \\\"Sync Files\\\" button to load the updated translations into the \"\n\"add-in, and then update the master template and language files with the \"\n\"latest strings from the database.\"\nmsgstr \"\"\n\"CLICK THE \\\"SYNC FILES\\\" BUTTON TO LOAD THE UPDATED TRANSLATIONS INTO THE \"\n\"ADD-IN, AND THEN UPDATE THE MASTER TEMPLATE AND LANGUAGE FILES WITH THE \"\n\"LATEST STRINGS FROM THE DATABASE.\"\n\n#: frmVCSInstall.lblSubheading\nmsgid \"\"\n\"Click the Install button to install or update the add-in.\\n\"\n\"\\n\"\n\"In some more secure environments, you may need to adjust additional \"\n\"options to use this addin.\"\nmsgstr \"\"\n\"CLICK THE INSTALL BUTTON TO INSTALL OR UPDATE THE ADD-IN.\\n\"\n\"\\n\"\n\"IN SOME MORE SECURE ENVIRONMENTS, YOU MAY NEED TO ADJUST ADDITIONAL \"\n\"OPTIONS TO USE THIS ADDIN.\"\n\n#: frmVCSInstall.lblSubheading\nmsgid \"\"\n\"Click the Install button to install or update the add-in.\\n\"\n\"\\n\"\n\"In some more secure environments,\\n\"\n\"you may need to adjust additional\\n\"\n\"options to use this addin.\"\nmsgstr \"\"\n\"CLICK THE INSTALL BUTTON TO INSTALL OR UPDATE THE ADD-IN.\\n\"\n\"\\n\"\n\"IN SOME MORE SECURE ENVIRONMENTS,\\n\"\n\"YOU MAY NEED TO ADJUST ADDITIONAL\\n\"\n\"OPTIONS TO USE THIS ADDIN.\"\n\nmsgid \"\"\n\"Click 'Yes' to rebuild* this database from source files in this folder:\"\nmsgstr \"\"\n\"CLICK 'YES' TO REBUILD* THIS DATABASE FROM SOURCE FILES IN THIS FOLDER:\"\n\n#: frmVCSMain.cmdClose\nmsgid \"Close\"\nmsgstr \"CLOSE\"\n\n#: frmVCSOptions.Label134(chkCollate)\nmsgid \"Collate\"\nmsgstr \"COLLATE\"\n\n#: frmVCSOptions.Label144(chkColor)\nmsgid \"Color or B/W\"\nmsgstr \"COLOR OR B/W\"\n\n#: frmVCSDatabase.Label244(txtName)\nmsgid \"Connection Name:\"\nmsgstr \"CONNECTION NAME:\"\n\n#: frmVCSDatabase.Label248(cboConnect)\nmsgid \"Connection String:\"\nmsgstr \"CONNECTION STRING:\"\n\n#: frmVCSOptions.Label256(chkContributeTranslations)\nmsgid \"Contribute to Translations\"\nmsgstr \"CONTRIBUTE TO TRANSLATIONS\"\n\nmsgid \"Created blank database for import. (v{0})\"\nmsgstr \"CREATED BLANK DATABASE FOR IMPORT. (V{0})\"\n\n#: frmVCSOptions.Label119(chkPaperLength)\nmsgid \"Custom Length\"\nmsgstr \"CUSTOM LENGTH\"\n\n#: frmVCSOptions.Label158(chkPaperWidth)\nmsgid \"Custom Width\"\nmsgstr \"CUSTOM WIDTH\"\n\n#: frmVCSDatabase.Label252(cboType)\nmsgid \"Database Type:\"\nmsgstr \"DATABASE TYPE:\"\n\n#: frmVCSOptions.tabOptions.pgeDatabases\nmsgid \"Databases\"\nmsgstr \"DATABASES\"\n\nmsgid \"db connections\"\nmsgstr \"DB CONNECTIONS\"\n\nmsgid \"db properties\"\nmsgstr \"DB PROPERTIES\"\n\n#: frmVCSOptions.Label219(chkBreakOnError)\nmsgid \"Debug VBA Errors\"\nmsgstr \"DEBUG VBA ERRORS\"\n\n#: frmVCSDatabase.Label246(txtDescription)\nmsgid \"Description:\"\nmsgstr \"DESCRIPTION:\"\n\n#: frmVCSMain.Label9\nmsgid \"\"\n\"Designed for \\n\"\n\"GitHub && GitLab\"\nmsgstr \"\"\n\"DESIGNED FOR \\n\"\n\"GITHUB && GITLAB\"\n\n#: frmVCSOptions.Label239(cboDiffTool)\nmsgid \"Diff Tool:\"\nmsgstr \"DIFF TOOL:\"\n\n#: frmVCSOptions.Label146(chkDisplayFlags)\nmsgid \"Display Flags\"\nmsgstr \"DISPLAY FLAGS\"\n\n#: frmVCSOptions.Label148(chkDisplayFrequency)\nmsgid \"Display Freq.\"\nmsgstr \"DISPLAY FREQ.\"\n\n#: frmVCSOptions.Label156(chkDitherType)\nmsgid \"Dither Type\"\nmsgstr \"DITHER TYPE\"\n\nmsgid \"doc properties\"\nmsgstr \"DOC PROPERTIES\"\n\nmsgid \"Done. ({0} seconds)\"\nmsgstr \"DONE. ({0} SECONDS)\"\n\n#: Ribbon.btnGoToLinkDownload.Label\nmsgid \"Download Latest Version\"\nmsgstr \"DOWNLOAD LATEST VERSION\"\n\n#: frmVCSOptions.Label130(chkDuplex)\nmsgid \"Duplex\"\nmsgstr \"DUPLEX\"\n\n#: Ribbon.btnSplitFiles.Description\nmsgid \"Duplicate files while preserving Git line history\"\nmsgstr \"DUPLICATE FILES WHILE PRESERVING GIT LINE HISTORY\"\n\n#: Ribbon.btnActivateHook.Description\nmsgid \"\"\n\"Enable Access Application hook to report saving of objects and enabling \"\n\"automatic export on save.\"\nmsgstr \"\"\n\"ENABLE ACCESS APPLICATION HOOK TO REPORT SAVING OF OBJECTS AND ENABLING \"\n\"AUTOMATIC EXPORT ON SAVE.\"\n\n#: frmVCSDatabase.Label261(chkEnabled)\nmsgid \"Enabled\"\nmsgstr \"ENABLED\"\n\nmsgid \"Error running {0}\"\nmsgstr \"ERROR RUNNING {0}\"\n\n#: frmVCSDatabase.cmdExamples\nmsgid \"Examples...\"\nmsgstr \"EXAMPLES...\"\n\n#: frmVCSInstall.cmdExplainOptions\nmsgid \"Explain options...\"\nmsgstr \"EXPLAIN OPTIONS...\"\n\n#: frmVCSOptions.tabOptions.pgeExport\nmsgid \"Export\"\nmsgstr \"EXPORT\"\n\n#: Ribbon.btnExport.Supertip\nmsgid \"\"\n\"Export all source files for the current project using the current options.\"\nmsgstr \"\"\n\"EXPORT ALL SOURCE FILES FOR THE CURRENT PROJECT USING THE CURRENT OPTIONS.\"\n\n#: frmVCSTableData.Label15(cboFormatType)\nmsgid \"Export As\"\nmsgstr \"EXPORT AS\"\n\n#: frmVCSOptions.Label31(txtExportFolder)\nmsgid \"Export Folder:\"\nmsgstr \"EXPORT FOLDER:\"\n\nmsgid \"Export Folder: {0}\"\nmsgstr \"EXPORT FOLDER: {0}\"\n\n#: Ribbon.btnOpenExportLog.Label\nmsgid \"Export Log\"\nmsgstr \"EXPORT LOG\"\n\n#: Ribbon.btnExportVBA.Supertip\nmsgid \"\"\n\"Export only the components that may contain VBA code. Includes forms, \"\n\"reports, and VBA code modules.\"\nmsgstr \"\"\n\"EXPORT ONLY THE COMPONENTS THAT MAY CONTAIN VBA CODE. INCLUDES FORMS, \"\n\"REPORTS, AND VBA CODE MODULES.\"\n\n#: Ribbon.btnExportSelected.Label\nmsgid \"Export Selected\"\nmsgstr \"EXPORT SELECTED\"\n\n#: Ribbon.btnExport.Label\nmsgid \"Export Source Files\"\nmsgstr \"EXPORT SOURCE FILES\"\n\n#: Ribbon.btnExportSelected.Supertip\nmsgid \"\"\n\"Export the database component currently selected in the navigation pane.\"\nmsgstr \"\"\n\"EXPORT THE DATABASE COMPONENT CURRENTLY SELECTED IN THE NAVIGATION PANE.\"\n\n#: Ribbon.btnExportVBA.Label\nmsgid \"Export VBA Code\"\nmsgstr \"EXPORT VBA CODE\"\n\n#: frmVCSDatabase.lblOptions\nmsgid \"External Database Connection\"\nmsgstr \"EXTERNAL DATABASE CONNECTION\"\n\n#: frmVCSOptions.Label243(lstDatabases)\nmsgid \"External Databases:\"\nmsgstr \"EXTERNAL DATABASES:\"\n\n#: frmVCSOptions.Label112(chkExtractThemeFiles)\nmsgid \"Extract Theme Files\"\nmsgstr \"EXTRACT THEME FILES\"\n\n#: frmVCSDatabase.Label250(txtFilter)\nmsgid \"Filter for database objects (source file paths):\"\nmsgstr \"FILTER FOR DATABASE OBJECTS (SOURCE FILE PATHS):\"\n\n#: frmVCSDatabase.Label253\nmsgid \"\"\n\"Filter paths to source files. Start line with ! to exclude match. Use * \"\n\"for wildcard matches (LIKE comparison).\"\nmsgstr \"\"\n\"FILTER PATHS TO SOURCE FILES. START LINE WITH ! TO EXCLUDE MATCH. USE * \"\n\"FOR WILDCARD MATCHES (LIKE COMPARISON).\"\n\n#: frmVCSSplitFiles.Label244(txtCommitMessage)\nmsgid \"Final Commit Message:\"\nmsgstr \"FINAL COMMIT MESSAGE:\"\n\nmsgid \"Finished\"\nmsgstr \"FINISHED\"\n\n#: frmVCSOptions.lblForceImportOriginalQuerySQL(chkForceImportOriginalQuerySQL)\nmsgid \"Force import of original SQL for queries\"\nmsgstr \"FORCE IMPORT OF ORIGINAL SQL FOR QUERIES\"\n\n#: frmVCSOptions.Label142(chkFormName)\nmsgid \"Form Name\"\nmsgstr \"FORM NAME\"\n\n#: frmVCSOptions.Label249(chkFormatSQL)\nmsgid \"Format SQL\"\nmsgstr \"FORMAT SQL\"\n\nmsgid \"forms\"\nmsgstr \"FORMS\"\n\n#: frmVCSMain.Label31(chkFullBuild)\nmsgid \"Full Build\"\nmsgstr \"FULL BUILD\"\n\n#: frmVCSMain.Label29(chkFullExport)\nmsgid \"Full Export\"\nmsgstr \"FULL EXPORT\"\n\nmsgid \"Full Path: {0}\"\nmsgstr \"FULL PATH: {0}\"\n\n#: frmVCSOptions.tabOptions.pgeGeneral\nmsgid \"General\"\nmsgstr \"GENERAL\"\n\n#: Ribbon.grpGitHub.Label\nmsgid \"GitHub Community\"\nmsgstr \"GITHUB COMMUNITY\"\n\n#: frmVCSOptions.Label221(cboHashAlgorithm)\nmsgid \"Hash Algorithm:\"\nmsgstr \"HASH ALGORITHM:\"\n\nmsgid \"hidden attributes\"\nmsgstr \"HIDDEN ATTRIBUTES\"\n\n#: frmVCSOptions.Label152(chkICMIntent)\nmsgid \"ICM Intent\"\nmsgstr \"ICM INTENT\"\n\n#: frmVCSOptions.Label150(chkICMMethod)\nmsgid \"ICM Method\"\nmsgstr \"ICM METHOD\"\n\nmsgid \"imex specs\"\nmsgstr \"IMEX SPECS\"\n\n#: Ribbon.btnLoadSelected.Supertip\nmsgid \"\"\n\"Import from source files the database component currently selected in the \"\n\"navigation pane.\"\nmsgstr \"\"\n\"IMPORT FROM SOURCE FILES THE DATABASE COMPONENT CURRENTLY SELECTED IN THE \"\n\"NAVIGATION PANE.\"\n\nmsgid \"Importing {0}...\"\nmsgstr \"IMPORTING {0}...\"\n\n#: frmVCSInstall.lblHeading(Page40)\nmsgid \"Install Add-In\"\nmsgstr \"INSTALL ADD-IN\"\n\n#: frmVCSInstall.Label63(txtInstallFolder)\nmsgid \"Install Folder:   \"\nmsgstr \"INSTALL FOLDER:   \"\n\nmsgid \"\"\n\"Instead of opening this form to build the add-in,\\n\"\n\"please install and use the Version Control add-in from the Add-in menu\"\nmsgstr \"\"\n\"INSTEAD OF OPENING THIS FORM TO BUILD THE ADD-IN,\\n\"\n\"PLEASE INSTALL AND USE THE VERSION CONTROL ADD-IN FROM THE ADD-IN MENU\"\n\n#: frmVCSInstall.Label32\nmsgid \"joyfullservice/msaccess-vcs-addin\"\nmsgstr \"JOYFULLSERVICE/MSACCESS-VCS-ADDIN\"\n\n#: frmVCSOptions.Label252(cboLanguage)\nmsgid \"Language:\"\nmsgstr \"LANGUAGE:\"\n\n#: frmVCSSplitFiles.Label241(txtFileList)\nmsgid \"List of files to split:\"\nmsgstr \"LIST OF FILES TO SPLIT:\"\n\n#: Ribbon.btnLoadSelected.Label\nmsgid \"Load Selected\"\nmsgstr \"LOAD SELECTED\"\n\n#: Ribbon.btnLocalizeLibraryReferences.Label\nmsgid \"Localize Library References\"\nmsgstr \"LOCALIZE LIBRARY REFERENCES\"\n\nmsgid \"macros\"\nmsgstr \"MACROS\"\n\n#: frmVCSOptions.Label154(chkMediaType)\nmsgid \"Media Type\"\nmsgstr \"MEDIA TYPE\"\n\nmsgid \"Merge\"\nmsgstr \"MERGE\"\n\n#: Ribbon.btnMergeBuild.Label\nmsgid \"Merge Build\"\nmsgstr \"MERGE BUILD\"\n\n#: Ribbon.btnMergeBuild.Supertip\nmsgid \"Merge changed source files into the current database.\"\nmsgstr \"MERGE CHANGED SOURCE FILES INTO THE CURRENT DATABASE.\"\n\nmsgid \"Merge Complete\"\nmsgstr \"MERGE COMPLETE\"\n\nmsgid \"Merging From Source\"\nmsgstr \"MERGING FROM SOURCE\"\n\nmsgid \"modules\"\nmsgstr \"MODULES\"\n\n#: frmVCSInstall.Caption\nmsgid \"MSAccessVCS\"\nmsgstr \"MSACCESSVCS\"\n\n# Do not remove the semicolon\n#: lstDatabases.RefreshSchemaList\nmsgid \"Name;Description\"\nmsgstr \"NAME;DESCRIPTION\"\n\nmsgid \"nav pane groups\"\nmsgstr \"NAV PANE GROUPS\"\n\nmsgid \"No {0} source files found.\"\nmsgstr \"NO {0} SOURCE FILES FOUND.\"\n\n#: frmVCSOptions.Label123(chkCopies)\nmsgid \"Number of Copies\"\nmsgstr \"NUMBER OF COPIES\"\n\n#: Ribbon.btnGoToLinkDocumentation.Label\nmsgid \"Online Documentation\"\nmsgstr \"ONLINE DOCUMENTATION\"\n\n#: Ribbon.btnShow.Label\nmsgid \"Open Add-in\"\nmsgstr \"OPEN ADD-IN\"\n\n#: frmVCSInstall.Label36(chkOpenAfterInstall)\nmsgid \"Open add-in after installing to trust the add-in file\"\nmsgstr \"OPEN ADD-IN AFTER INSTALLING TO TRUST THE ADD-IN FILE\"\n\n#: frmVCSOptions.cmdOpenInstallFolder\nmsgid \"Open Install Folder\"\nmsgstr \"OPEN INSTALL FOLDER\"\n\n#: frmVCSMain.cmdOpenLogFile\nmsgid \"Open Log File...\"\nmsgstr \"OPEN LOG FILE...\"\n\n#: Ribbon.btnShow.Supertip\nmsgid \"\"\n\"Open the main form for the add-in. From the main form you can run an \"\n\"export or build a project from source.\"\nmsgstr \"\"\n\"OPEN THE MAIN FORM FOR THE ADD-IN. FROM THE MAIN FORM YOU CAN RUN AN \"\n\"EXPORT OR BUILD A PROJECT FROM SOURCE.\"\n\n#: Ribbon.btnOpenBuildLog.Supertip\nmsgid \"Open the most recent build log file.\"\nmsgstr \"OPEN THE MOST RECENT BUILD LOG FILE.\"\n\n#: Ribbon.btnOpenExportLog.Supertip\nmsgid \"Open the most recent export log file.\"\nmsgstr \"OPEN THE MOST RECENT EXPORT LOG FILE.\"\n\n#: Ribbon.btnShowOptions.Supertip\nmsgid \"\"\n\"Open the Options form to view or change options for the current project.\"\nmsgstr \"\"\n\"OPEN THE OPTIONS FORM TO VIEW OR CHANGE OPTIONS FOR THE CURRENT PROJECT.\"\n\n#: Ribbon.btnOpenSourceFolder.Supertip\nmsgid \"Open the source folder for the current project.\"\nmsgstr \"OPEN THE SOURCE FOLDER FOR THE CURRENT PROJECT.\"\n\n#: frmVCSOptions.Label241(cmdOpenInstallFolder)\nmsgid \"Open VCS add-In Install Folder.\"\nmsgstr \"OPEN VCS ADD-IN INSTALL FOLDER.\"\n\n#: Ribbon.grpOptions.Label\nmsgid \"Options\"\nmsgstr \"OPTIONS\"\n\n#: frmVCSOptions.cmdPrintSettingsOptions\nmsgid \"Options...\"\nmsgstr \"OPTIONS...\"\n\n#: frmVCSOptions.Label79(txtOtherTableName)\nmsgid \"Other Table Name:\"\nmsgstr \"OTHER TABLE NAME:\"\n\n#: frmVCSConflict.cmdOverwriteAll\nmsgid \"Overwrite All\"\nmsgstr \"OVERWRITE ALL\"\n\n#: frmVCSOptions.Label115(chkOrientation)\nmsgid \"Page Orientation\"\nmsgstr \"PAGE ORIENTATION\"\n\n#: frmVCSOptions.Label117(chkPaperSize)\nmsgid \"Paper Size\"\nmsgstr \"PAPER SIZE\"\n\n#: frmVCSOptions.Label125(chkDefaultSource)\nmsgid \"Paper Tray\"\nmsgstr \"PAPER TRAY\"\n\n#: frmVCSOptions.Label105\nmsgid \"\"\n\"PLEASE NOTE:\\n\"\n\"Building from source will completely rebuild your database project from \"\n\"source files. It is very important that you test this carefully in your \"\n\"environment to make sure everything you need is being created in your \"\n\"database during the build. (A backup copy of your current database will \"\n\"be made as a part of the build process.)\\n\"\n\"\\n\"\n\"Please see the online documentation for additional details on the build \"\n\"process. If you encounter a problem, please feel free to open an issues \"\n\"on the GitHub project.\"\nmsgstr \"\"\n\"PLEASE NOTE:\\n\"\n\"BUILDING FROM SOURCE WILL COMPLETELY REBUILD YOUR DATABASE PROJECT FROM \"\n\"SOURCE FILES. IT IS VERY IMPORTANT THAT YOU TEST THIS CAREFULLY IN YOUR \"\n\"ENVIRONMENT TO MAKE SURE EVERYTHING YOU NEED IS BEING CREATED IN YOUR \"\n\"DATABASE DURING THE BUILD. (A BACKUP COPY OF YOUR CURRENT DATABASE WILL \"\n\"BE MADE AS A PART OF THE BUILD PROCESS.)\\n\"\n\"\\n\"\n\"PLEASE SEE THE ONLINE DOCUMENTATION FOR ADDITIONAL DETAILS ON THE BUILD \"\n\"PROCESS. IF YOU ENCOUNTER A PROBLEM, PLEASE FEEL FREE TO OPEN AN ISSUES \"\n\"ON THE GITHUB PROJECT.\"\n\n#: frmVCSOptions.Label128(chkPrintQuality)\nmsgid \"Print Quality\"\nmsgstr \"PRINT QUALITY\"\n\n#: frmVCSOptions.Label121(chkScale)\nmsgid \"Print Scale\"\nmsgstr \"PRINT SCALE\"\n\n#: frmVCSOptions.tabOptions.pgePrinterSettings\nmsgid \"Printer Settings\"\nmsgstr \"PRINTER SETTINGS\"\n\nmsgid \"proj properties\"\nmsgstr \"PROJ PROPERTIES\"\n\nmsgid \"project\"\nmsgstr \"PROJECT\"\n\n#: Ribbon.btnGoToLinkHome.Label\nmsgid \"Project Home\"\nmsgstr \"PROJECT HOME\"\n\nmsgid \"queries\"\nmsgstr \"QUERIES\"\n\n#: Ribbon.btnRepairColors.Description\nmsgid \"\"\n\"Rebuild color definition blocks in form objects (See documentation for \"\n\"more info)\"\nmsgstr \"\"\n\"REBUILD COLOR DEFINITION BLOCKS IN FORM OBJECTS (SEE DOCUMENTATION FOR \"\n\"MORE INFO)\"\n\n#: Ribbon.btnReloadRibbon.Description\nmsgid \"Refresh the add-in ribbon menu to reflect changes in XML source\"\nmsgstr \"REFRESH THE ADD-IN RIBBON MENU TO REFLECT CHANGES IN XML SOURCE\"\n\nmsgid \"relations\"\nmsgstr \"RELATIONS\"\n\n#: Ribbon.btnLocalizeLibraryReferences.Description\nmsgid \"\"\n\"Relink add-in library databases to the files located in the same folder \"\n\"as the current database\"\nmsgstr \"\"\n\"RELINK ADD-IN LIBRARY DATABASES TO THE FILES LOCATED IN THE SAME FOLDER \"\n\"AS THE CURRENT DATABASE\"\n\n#: Ribbon.btnReloadRibbon.Label\nmsgid \"Reload Ribbon\"\nmsgstr \"RELOAD RIBBON\"\n\nmsgid \"Removing non built-in references...\"\nmsgstr \"REMOVING NON BUILT-IN REFERENCES...\"\n\n#: Ribbon.btnRepairColors.Label\nmsgid \"Repair Colors\"\nmsgstr \"REPAIR COLORS\"\n\nmsgid \"reports\"\nmsgstr \"REPORTS\"\n\n#: frmVCSOptions.Label59(cmdClearDefaults)\nmsgid \"Reset all default settings to original values.\"\nmsgstr \"RESET ALL DEFAULT SETTINGS TO ORIGINAL VALUES.\"\n\n#: frmVCSOptions.Label132(chkResolution)\nmsgid \"Resolution\"\nmsgstr \"RESOLUTION\"\n\n#: frmVCSConflict.Label4\nmsgid \"Resolve Conflicts to Continue...\"\nmsgstr \"RESOLVE CONFLICTS TO CONTINUE...\"\n\n#: frmVCSOptions.Label104(txtRunAfterBuild)\nmsgid \"Run Sub After Build:\"\nmsgstr \"RUN SUB AFTER BUILD:\"\n\n#: frmVCSOptions.Label48(txtRunAfterExport)\nmsgid \"Run Sub After Export:\"\nmsgstr \"RUN SUB AFTER EXPORT:\"\n\n#: frmVCSOptions.Label229(txtRunBeforeBuild)\nmsgid \"Run Sub Before Build:\"\nmsgstr \"RUN SUB BEFORE BUILD:\"\n\n#: frmVCSOptions.Label44(txtRunBeforeExport)\nmsgid \"Run Sub Before Export:\"\nmsgstr \"RUN SUB BEFORE EXPORT:\"\n\nmsgid \"Running {0}...\"\nmsgstr \"RUNNING {0}...\"\n\nmsgid \"Running...\"\nmsgstr \"RUNNING...\"\n\n#: frmVCSOptions.Label233(cboSanitizeColors)\nmsgid \"Sanitize Colors\"\nmsgstr \"SANITIZE COLORS\"\n\n#: frmVCSOptions.Label235(cboSanitizeLevel)\nmsgid \"Sanitize Level\"\nmsgstr \"SANITIZE LEVEL\"\n\n#: frmVCSDatabase.Label257(chkSaveDotEnv)\nmsgid \"Save in .env file*\"\nmsgstr \"SAVE IN .ENV FILE*\"\n\n#: frmVCSOptions.Label27(chkSavePrintVars)\nmsgid \"Save Printer Settings\"\nmsgstr \"SAVE PRINTER SETTINGS\"\n\n#: frmVCSOptions.Label29(chkSaveQuerySQL)\nmsgid \"Save Query SQL\"\nmsgstr \"SAVE QUERY SQL\"\n\n#: frmVCSOptions.Label38(chkSaveTableSQL)\nmsgid \"Save Table SQL\"\nmsgstr \"SAVE TABLE SQL\"\n\n#: frmVCSOptions.Label53(cmdSaveAsDefault)\nmsgid \"Save these settings as default for new projects.\"\nmsgstr \"SAVE THESE SETTINGS AS DEFAULT FOR NEW PROJECTS.\"\n\nmsgid \"Saved as {0}.\"\nmsgstr \"SAVED AS {0}.\"\n\nmsgid \"saved specs\"\nmsgstr \"SAVED SPECS\"\n\nmsgid \"Saving backup of original database...\"\nmsgstr \"SAVING BACKUP OF ORIGINAL DATABASE...\"\n\nmsgid \"Scanning source files...\"\nmsgstr \"SCANNING SOURCE FILES...\"\n\n#: frmVCSOptions.cmdSeeDocs\nmsgid \"See Docs...\"\nmsgstr \"SEE DOCS...\"\n\n#: Ribbon.btnGoToLinkAdvancedTools.Description\nmsgid \"See documentation on the purpose and usage of these custom tools...\"\nmsgstr \"SEE DOCUMENTATION ON THE PURPOSE AND USAGE OF THESE CUSTOM TOOLS...\"\n\n#: frmVCSOptions.Label126\nmsgid \"\"\n\"Select any additional settings that you would like saved to version \"\n\"control and used when building a database from source files.\"\nmsgstr \"\"\n\"SELECT ANY ADDITIONAL SETTINGS THAT YOU WOULD LIKE SAVED TO VERSION \"\n\"CONTROL AND USED WHEN BUILDING A DATABASE FROM SOURCE FILES.\"\n\n#: frmVCSOptions.tabOptions.pgeSettings\nmsgid \"Settings\"\nmsgstr \"SETTINGS\"\n\nmsgid \"shared images\"\nmsgstr \"SHARED IMAGES\"\n\n#: frmVCSOptions.Label23(chkShowDebug)\nmsgid \"Show Detailed Output\"\nmsgstr \"SHOW DETAILED OUTPUT\"\n\n#: frmVCSOptions.lblTableShowHidden(chkTableShowHidden)\nmsgid \"Show Hidden\"\nmsgstr \"SHOW HIDDEN\"\n\n#: frmVCSOptions.Label215(chkShowVCSLegacy)\nmsgid \"Show Legacy Prompts\"\nmsgstr \"SHOW LEGACY PROMPTS\"\n\n#: frmVCSOptions.lblTableShowOther(chkTableShowOther)\nmsgid \"Show Other\"\nmsgstr \"SHOW OTHER\"\n\nmsgid \"Show Other  \"\nmsgstr \"SHOW OTHER  \"\n\n#: frmVCSOptions.lblTableShowSystem(chkTableShowSystem)\nmsgid \"Show System\"\nmsgstr \"SHOW SYSTEM\"\n\n#: frmVCSConflict.cmdSkipAll\nmsgid \"Skip All\"\nmsgstr \"SKIP ALL\"\n\n#: Ribbon.btnOpenSourceFolder.Label\nmsgid \"Source Folder\"\nmsgstr \"SOURCE FOLDER\"\n\n#: frmVCSOptions.lblWorkingPathExplanation\nmsgid \"Specify the path for the working language files.\"\nmsgstr \"SPECIFY THE PATH FOR THE WORKING LANGUAGE FILES.\"\n\n#: Ribbon.btnSplitFiles.Label\nmsgid \"Split Files\"\nmsgstr \"SPLIT FILES\"\n\n#: frmVCSSplitFiles.lblOptions\nmsgid \"Split Files and Preserve History\"\nmsgstr \"SPLIT FILES AND PRESERVE HISTORY\"\n\n#: frmVCSOptions.Label251(chkSplitLayoutFromVBA)\nmsgid \"Split Layout from VBA\"\nmsgstr \"SPLIT LAYOUT FROM VBA\"\n\n#: Ribbon.btnGoToLinkSupport.Label\nmsgid \"Support Issues\"\nmsgstr \"SUPPORT ISSUES\"\n\n#: frmVCSTableData.Caption\nmsgid \"Table Data\"\nmsgstr \"TABLE DATA\"\n\nmsgid \"table data\"\nmsgstr \"TABLE DATA\"\n\nmsgid \"table data macros\"\nmsgstr \"TABLE DATA MACROS\"\n\n#: frmVCSTableData.Label3(txtTableName)\nmsgid \"Table Name\"\nmsgstr \"TABLE NAME\"\n\nmsgid \"tables\"\nmsgstr \"TABLES\"\n\n#: frmVCSDatabase.cmdTest\nmsgid \"Test Filter...\"\nmsgstr \"TEST FILTER...\"\n\nmsgid \"themes\"\nmsgstr \"THEMES\"\n\n#: frmVCSConflict.lblHeading(sfrmConflictList)\nmsgid \"These source files have changed since the last export\"\nmsgstr \"THESE SOURCE FILES HAVE CHANGED SINCE THE LAST EXPORT\"\n\n#: Ribbon.mnuAdvancedTools.Supertip\nmsgid \"\"\n\"These tools are intended for more advanced development purposes. Please \"\n\"review the documentation for these tools before using them.\"\nmsgstr \"\"\n\"THESE TOOLS ARE INTENDED FOR MORE ADVANCED DEVELOPMENT PURPOSES. PLEASE \"\n\"REVIEW THE DOCUMENTATION FOR THESE TOOLS BEFORE USING THEM.\"\n\n#: frmVCSDatabase.Label227\nmsgid \"\"\n\"This add-in provides limited support for exporting object definitions \"\n\"from external databases for use in version control.\\n\"\n\"\\n\"\n\"* If the connection string contains a password, please use the .env \"\n\"option, and exclude this file from version control.\"\nmsgstr \"\"\n\"THIS ADD-IN PROVIDES LIMITED SUPPORT FOR EXPORTING OBJECT DEFINITIONS \"\n\"FROM EXTERNAL DATABASES FOR USE IN VERSION CONTROL.\\n\"\n\"\\n\"\n\"* IF THE CONNECTION STRING CONTAINS A PASSWORD, PLEASE USE THE .ENV \"\n\"OPTION, AND EXCLUDE THIS FILE FROM VERSION CONTROL.\"\n\n#: Ribbon.PLEASE NOTE:.Label\nmsgid \"\"\n\"This file is automatically generated by the add-in. Please do not \"\n\"translate or edit this file directly.\"\nmsgstr \"\"\n\"THIS FILE IS AUTOMATICALLY GENERATED BY THE ADD-IN. PLEASE DO NOT \"\n\"TRANSLATE OR EDIT THIS FILE DIRECTLY.\"\n\n#: Ribbon.grpTools.Label\nmsgid \"Tools\"\nmsgstr \"TOOLS\"\n\n#: frmVCSOptions.tabOptions.pgeTranslations\nmsgid \"Translation\"\nmsgstr \"TRANSLATION\"\n\n#: frmVCSOptions.Label297(txtTranslationsPath)\nmsgid \"Translations Path: \"\nmsgstr \"TRANSLATIONS PATH: \"\n\n#: frmVCSOptions.cmdTranslations\nmsgid \"Translations...\"\nmsgstr \"TRANSLATIONS...\"\n\n#: frmVCSInstall.Label34(chkAddTrustedLocation)\nmsgid \"Trust Add-In Folder\"\nmsgstr \"TRUST ADD-IN FOLDER\"\n\n#: frmVCSOptions.Label140(chkTTOption)\nmsgid \"TT Font Handling\"\nmsgstr \"TT FONT HANDLING\"\n\nmsgid \"Unable to load Ribbon COM add-in\"\nmsgstr \"UNABLE TO LOAD RIBBON COM ADD-IN\"\n\nmsgid \"Unable to rename original file\"\nmsgstr \"UNABLE TO RENAME ORIGINAL FILE\"\n\n#: frmVCSOptions.Label108(cmdUninstall)\nmsgid \"Uninstall this add-in\"\nmsgstr \"UNINSTALL THIS ADD-IN\"\n\n#: frmVCSOptions.Label230\nmsgid \"Use \\\"module.sub\\\" to specify module\"\nmsgstr \"USE \\\"MODULE.SUB\\\" TO SPECIFY MODULE\"\n\n#: frmVCSOptions.Label25(chkUseFastSave)\nmsgid \"Use Fast Save\"\nmsgstr \"USE FAST SAVE\"\n\n#: frmVCSSplitFiles.Label242\nmsgid \"\"\n\"Use full file paths with a pipe character (|) between the original and \"\n\"new file paths.\\n\"\n\"\\n\"\n\"Each entry should be on a new line. The list will be validated before \"\n\"running the operation.\"\nmsgstr \"\"\n\"USE FULL FILE PATHS WITH A PIPE CHARACTER (|) BETWEEN THE ORIGINAL AND \"\n\"NEW FILE PATHS.\\n\"\n\"\\n\"\n\"EACH ENTRY SHOULD BE ON A NEW LINE. THE LIST WILL BE VALIDATED BEFORE \"\n\"RUNNING THE OPERATION.\"\n\n#: frmVCSOptions.Label163(chkUseGitIntegration)\nmsgid \"Use Git Integration\"\nmsgstr \"USE GIT INTEGRATION\"\n\n#: frmVCSInstall.Label40(chkUseRibbon)\nmsgid \"Use Ribbbon Addin\"\nmsgstr \"USE RIBBBON ADDIN\"\n\n#: frmVCSOptions.Label223(chkUseShortHash)\nmsgid \"Use short hashes in index\"\nmsgstr \"USE SHORT HASHES IN INDEX\"\n\n#: frmVCSOptions.Label227\nmsgid \"\"\n\"Use this form to set your preferred options for exporting and building \"\n\"your database project to and from source files. Note that you can have \"\n\"different options for different projects, and can save a set of options \"\n\"as default for new projects.\"\nmsgstr \"\"\n\"USE THIS FORM TO SET YOUR PREFERRED OPTIONS FOR EXPORTING AND BUILDING \"\n\"YOUR DATABASE PROJECT TO AND FROM SOURCE FILES. NOTE THAT YOU CAN HAVE \"\n\"DIFFERENT OPTIONS FOR DIFFERENT PROJECTS, AND CAN SAVE A SET OF OPTIONS \"\n\"AS DEFAULT FOR NEW PROJECTS.\"\n\n#: frmVCSOptions.lblTranslationExplanation\nmsgid \"\"\n\"Use this page to manage language translations for this add-in. Check this \"\n\"box if you would like to help improve the translations.\"\nmsgstr \"\"\n\"USE THIS PAGE TO MANAGE LANGUAGE TRANSLATIONS FOR THIS ADD-IN. CHECK THIS \"\n\"BOX IF YOU WOULD LIKE TO HELP IMPROVE THE TRANSLATIONS.\"\n\n#: frmVCSSplitFiles.Label227\nmsgid \"\"\n\"Use this tool to duplicate files while preserving commit history in Git.\\n\"\n\"\\n\"\n\"Paste in a list of Pipe delimited paths and click the button below.\"\nmsgstr \"\"\n\"USE THIS TOOL TO DUPLICATE FILES WHILE PRESERVING COMMIT HISTORY IN GIT.\\n\"\n\"\\n\"\n\"PASTE IN A LIST OF PIPE DELIMITED PATHS AND CLICK THE BUTTON BELOW.\"\n\n#: frmVCSDatabase.Label259(chkUtcDates)\nmsgid \"Use UTC Dates\"\nmsgstr \"USE UTC DATES\"\n\nmsgid \"vb project\"\nmsgstr \"VB PROJECT\"\n\nmsgid \"vbe forms\"\nmsgstr \"VBE FORMS\"\n\nmsgid \"vbe references\"\nmsgstr \"VBE REFERENCES\"\n\nmsgid \"VCS Version {0}\"\nmsgstr \"VCS VERSION {0}\"\n\nmsgid \"Version {0}\"\nmsgstr \"VERSION {0}\"\n\nmsgid \"Version {0} currently installed.\"\nmsgstr \"VERSION {0} CURRENTLY INSTALLED.\"\n\n#: frmVCSInstall.lblInstalled\nmsgid \"Version 4.0.11 currently installed.\"\nmsgstr \"VERSION 4.0.11 CURRENTLY INSTALLED.\"\n\n#: frmVCSInstall.lblVersion\nmsgid \"Version 4.0.12\"\nmsgstr \"VERSION 4.0.12\"\n\n#: Ribbon.tabVersionControl.Label\nmsgid \"Version Control\"\nmsgstr \"VERSION CONTROL\"\n\n#: frmVCSOptions.Label9\nmsgid \"Version Control System \"\nmsgstr \"VERSION CONTROL SYSTEM \"\n\n#: frmVCSInstall.Label4\nmsgid \"Version Control System\"\nmsgstr \"VERSION CONTROL SYSTEM\"\n\n#: frmVCSConflict.lblVersion\nmsgid \"Version Control System ${version}\"\nmsgstr \"VERSION CONTROL SYSTEM ${VERSION}\"\n\n#: Ribbon.btnGoToLinkAdvancedTools.Label\nmsgid \"View Documentation\"\nmsgstr \"VIEW DOCUMENTATION\"\n\n#: Ribbon.btnShowOptions.Label\nmsgid \"View Options\"\nmsgstr \"VIEW OPTIONS\"\n\n#: Ribbon.btnGoToLinkHome.Supertip\nmsgid \"View the add-in project home on GitHub.\"\nmsgstr \"VIEW THE ADD-IN PROJECT HOME ON GITHUB.\"\n\n#: Ribbon.btnGoToLinkSupport.Supertip\nmsgid \"View the add-in project support issues on GitHub.\"\nmsgstr \"VIEW THE ADD-IN PROJECT SUPPORT ISSUES ON GITHUB.\"\n\n#: Ribbon.btnGoToLinkDocumentation.Supertip\nmsgid \"View the documentation Wiki on GitHub.\"\nmsgstr \"VIEW THE DOCUMENTATION WIKI ON GITHUB.\"\n\n#: Ribbon.btnGoToLinkDownload.Supertip\nmsgid \"View the GitHub download page for this add-in project.\"\nmsgstr \"VIEW THE GITHUB DOWNLOAD PAGE FOR THIS ADD-IN PROJECT.\"\n\n#: frmVCSConflict.lblHeaderExplain\nmsgid \"\"\n\"WARNING: The following changes may be overwritten. Please select how you \"\n\"would \\n\"\n\"like to proceed.\"\nmsgstr \"\"\n\"WARNING: THE FOLLOWING CHANGES MAY BE OVERWRITTEN. PLEASE SELECT HOW YOU \"\n\"WOULD \\n\"\n\"LIKE TO PROCEED.\"\n\n#: frmVCSMain.lblSubheading\nmsgid \"What would you like to do?\"\nmsgstr \"WHAT WOULD YOU LIKE TO DO?\"\n\n#: frmVCSOptions.Label82\nmsgid \"\"\n\"You may optionally include data from specific tables as part of the \"\n\"export process.\"\nmsgstr \"\"\n\"YOU MAY OPTIONALLY INCLUDE DATA FROM SPECIFIC TABLES AS PART OF THE \"\n\"EXPORT PROCESS.\"\n\n#: frmVCSOptions.Label244\nmsgid \"\"\n\"Your database application may connect to external database systems such \"\n\"as Microsoft SQL Server.\\n\"\n\"\\n\"\n\"If you define a connection to an external database, you can use version \"\n\"control to track changes in those external database objects.\\n\"\n\"\\n\"\n\"Please see documentation for additional notes.\"\nmsgstr \"\"\n\"YOUR DATABASE APPLICATION MAY CONNECT TO EXTERNAL DATABASE SYSTEMS SUCH \"\n\"AS MICROSOFT SQL SERVER.\\n\"\n\"\\n\"\n\"IF YOU DEFINE A CONNECTION TO AN EXTERNAL DATABASE, YOU CAN USE VERSION \"\n\"CONTROL TO TRACK CHANGES IN THOSE EXTERNAL DATABASE OBJECTS.\\n\"\n\"\\n\"\n\"PLEASE SEE DOCUMENTATION FOR ADDITIONAL NOTES.\"\n"
  },
  {
    "path": "Translation/pt_BR.po",
    "content": "# Version Control System Add-in for Microsoft Access\n# https://github.com/joyfullservice/msaccess-vcs-addin\n# This file is distributed under the project's BSD-style license.\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: 4.0.27\\n\"\n\"POT-Creation-Date: 2021-05-19 19:50\\n\"\n\"PO-Revision-Date: 2024-11-08 09:00-0600\\n\"\n\"Last-Translator: \\n\"\n\"Language-Team: \\n\"\n\"Language: pt_BR\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\"X-Generator: Poedit 3.4.4\\n\"\n\n#: frmVCSTableData.Label40(txtTableIcon)\nmsgid \" \"\nmsgstr \"\"\n\n#: frmVCSMain.cmdExport\nmsgid \"   Export All Source\"\nmsgstr \"\"\n\n#: frmVCSInstall.cmdInstall\nmsgid \"   Install Add-In\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.cmdSplitFiles\nmsgid \"   Split Files\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdBuild\nmsgid \"  Build From Source\"\nmsgstr \"\"\n\n#: frmVCSConflict.cmdCancel\nmsgid \"  Cancel\"\nmsgstr \"\"\n\n#: frmVCSConflict.cmdContinue\nmsgid \"  Continue\"\nmsgstr \"\"\n\nmsgid \"  Merge From Source\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdAddDatabase\nmsgid \" Add\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdAddOtherTableData\nmsgid \" Add Other\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdClearDefaults\nmsgid \" Clear Defaults\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdDeleteDatabase\nmsgid \" Delete\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdEditDatabase\nmsgid \" Edit...\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdHelp\nmsgid \" Help\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdOptions\nmsgid \" Options...\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label66(Frame65)\nmsgid \" Remove Add-In\"\nmsgstr \" Remover suplemento\"\n\n#: frmVCSOptions.cmdRestoreDefaults\nmsgid \" Restore Defaults\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdSaveAndClose\nmsgid \" Save && Close\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdSaveAsDefault\nmsgid \" Save as Default\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdSyncTranslations\nmsgid \" Sync Files\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label63(Frame62)\nmsgid \" System Defaults\"\nmsgstr \" Configuração padrão do sistema\"\n\n#: frmVCSOptions.cmdUninstall\nmsgid \" Uninstall\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblForceImportSQLNote\nmsgid \"(\\\"Save Query SQL\\\" option needed when exporting)\"\nmsgstr \"(\\\"Salvar consulta SQL\\\" opção necessária quando exportar)\"\n\n#: frmVCSOptions.Label46\nmsgid \"(Blank for default)\"\nmsgstr \"(Deixado em branco por padrão)\"\n\n#: frmVCSOptions.Label231\nmsgid \"(Module name optional)\"\nmsgstr \"(Nome do módulo opcional)\"\n\nmsgid \"\"\n\"* (This database will be renamed as a backup before building {0} from \"\n\"source.)\"\nmsgstr \"\"\n\n#: frmVCSInstall.cmdChangeInstallFolder\nmsgid \"...\"\nmsgstr \"\"\n\nmsgid \"\"\n\"<strong><em>Export</em></strong> source to generate source files from the \"\n\"current database.<br><br><strong><em>Import</em></strong> source files to \"\n\"rebuild this database from source.\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblVersion\nmsgid \"4.0.37\"\nmsgstr \"\"\n\nmsgid \"\"\n\"A summary of the build progress can be seen on this screen, and \"\n\"additional details are included in the log file.\"\nmsgstr \"\"\n\n#: Ribbon.grpActions.Label\nmsgid \"Actions\"\nmsgstr \"\"\n\n#: Ribbon.btnActivateHook.Label\nmsgid \"Activate Hook\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.cmdAddFormsAndReports\nmsgid \"Add Forms and Reports...\"\nmsgstr \"\"\n\nmsgid \"\"\n\"Additional details can be found in the project log file.<br><br>You may \"\n\"now close this window.\"\nmsgstr \"\"\n\n#: frmVCSInstall.tabInstallType.Page41\nmsgid \"Advanced Install\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label61\nmsgid \"Advanced Options\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label65(chkAdvancedOptions)\nmsgid \"Advanced Options...\"\nmsgstr \"\"\n\n#: Ribbon.mnuAdvancedTools.Label\nmsgid \"Advanced Tools\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label57(cmdRestoreDefaults)\nmsgid \"Apply system defaults to this project.\"\nmsgstr \"Aplicar configurações padrão para este projeto.\"\n\n#: frmVCSInstall.tabInstallType.Page40\nmsgid \"Basic Install\"\nmsgstr \"\"\n\nmsgid \"Beginning build from Source\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeBuild\nmsgid \"Build\"\nmsgstr \"Gerar\"\n\nmsgid \"Build {0} ({1}) from source?\"\nmsgstr \"\"\n\n#: Ribbon.btnBuildAs.Label\nmsgid \"Build As…\"\nmsgstr \"\"\n\nmsgid \"Build Complete\"\nmsgstr \"\"\n\nmsgid \"Build error in: {0}\"\nmsgstr \"\"\n\n#: Ribbon.btnBuild.Label\nmsgid \"Build From Source\"\nmsgstr \"\"\n\n#: Ribbon.btnBuildAs.Supertip\nmsgid \"Build from source files to a new name or location.\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenBuildLog.Label\nmsgid \"Build Log\"\nmsgstr \"\"\n\nmsgid \"Build must be run from Add-In\"\nmsgstr \"\"\n\n#: Ribbon.btnBuild.Supertip\nmsgid \"Build the current project from source files.\"\nmsgstr \"\"\n\nmsgid \"Building From Source\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdCancel\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: frmVCSMain.lblHeading\nmsgid \"Choose Action\"\nmsgstr \"Escolha a ação\"\n\nmsgid \"\"\n\"Click 'No' to select another project, or 'Cancel' to go back to the \"\n\"previous screen.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label303(cmdSyncTranslations)\nmsgid \"\"\n\"Click the \\\"Sync Files\\\" button to load the updated translations into the \"\n\"add-in, and then update the master template and language files with the \"\n\"latest strings from the database.\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblSubheading\nmsgid \"\"\n\"Click the Install button to install or update the add-in.\\n\"\n\"\\n\"\n\"In some more secure environments, you may need to adjust additional \"\n\"options to use this addin.\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblSubheading\nmsgid \"\"\n\"Click the Install button to install or update the add-in.\\n\"\n\"\\n\"\n\"In some more secure environments,\\n\"\n\"you may need to adjust additional\\n\"\n\"options to use this addin.\"\nmsgstr \"\"\n\nmsgid \"\"\n\"Click 'Yes' to rebuild* this database from source files in this folder:\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdClose\nmsgid \"Close\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label134(chkCollate)\nmsgid \"Collate\"\nmsgstr \"Ordenar\"\n\n#: frmVCSOptions.Label144(chkColor)\nmsgid \"Color or B/W\"\nmsgstr \"Preto e Branco ou Colorido\"\n\n#: frmVCSDatabase.Label244(txtName)\nmsgid \"Connection Name:\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label248(cboConnect)\nmsgid \"Connection String:\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label256(chkContributeTranslations)\nmsgid \"Contribute to Translations\"\nmsgstr \"Contribuir com traduções\"\n\nmsgid \"Created blank database for import. (v{0})\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label119(chkPaperLength)\nmsgid \"Custom Length\"\nmsgstr \"Comprimento personalizado\"\n\n#: frmVCSOptions.Label158(chkPaperWidth)\nmsgid \"Custom Width\"\nmsgstr \"Largura personalizada\"\n\n#: frmVCSDatabase.Label252(cboType)\nmsgid \"Database Type:\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeDatabases\nmsgid \"Databases\"\nmsgstr \"\"\n\nmsgid \"db connections\"\nmsgstr \"\"\n\nmsgid \"db properties\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label219(chkBreakOnError)\nmsgid \"Debug VBA Errors\"\nmsgstr \"Debugar erros VBA\"\n\n#: frmVCSDatabase.Label246(txtDescription)\nmsgid \"Description:\"\nmsgstr \"\"\n\n#: frmVCSMain.Label9\nmsgid \"\"\n\"Designed for \\n\"\n\"GitHub && GitLab\"\nmsgstr \"\"\n\"Desenvolvido para \\n\"\n\"GitHub && GitLab\"\n\n#: frmVCSOptions.Label239(cboDiffTool)\nmsgid \"Diff Tool:\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label146(chkDisplayFlags)\nmsgid \"Display Flags\"\nmsgstr \"Mostrar sinalizadores\"\n\n#: frmVCSOptions.Label148(chkDisplayFrequency)\nmsgid \"Display Freq.\"\nmsgstr \"Mostrar Freq.\"\n\n#: frmVCSOptions.Label156(chkDitherType)\nmsgid \"Dither Type\"\nmsgstr \"Tipo Dither\"\n\nmsgid \"doc properties\"\nmsgstr \"\"\n\nmsgid \"Done. ({0} seconds)\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkDownload.Label\nmsgid \"Download Latest Version\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label130(chkDuplex)\nmsgid \"Duplex\"\nmsgstr \"Duplex\"\n\n#: Ribbon.btnSplitFiles.Description\nmsgid \"Duplicate files while preserving Git line history\"\nmsgstr \"\"\n\n#: Ribbon.btnActivateHook.Description\nmsgid \"\"\n\"Enable Access Application hook to report saving of objects and enabling \"\n\"automatic export on save.\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label261(chkEnabled)\nmsgid \"Enabled\"\nmsgstr \"\"\n\nmsgid \"Error running {0}\"\nmsgstr \"\"\n\n#: frmVCSDatabase.cmdExamples\nmsgid \"Examples...\"\nmsgstr \"\"\n\n#: frmVCSInstall.cmdExplainOptions\nmsgid \"Explain options...\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeExport\nmsgid \"Export\"\nmsgstr \"Exportar\"\n\n#: Ribbon.btnExport.Supertip\nmsgid \"\"\n\"Export all source files for the current project using the current options.\"\nmsgstr \"\"\n\n#: frmVCSTableData.Label15(cboFormatType)\nmsgid \"Export As\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label31(txtExportFolder)\nmsgid \"Export Folder:\"\nmsgstr \"Exportar pasta:\"\n\nmsgid \"Export Folder: {0}\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenExportLog.Label\nmsgid \"Export Log\"\nmsgstr \"\"\n\n#: Ribbon.btnExportVBA.Supertip\nmsgid \"\"\n\"Export only the components that may contain VBA code. Includes forms, \"\n\"reports, and VBA code modules.\"\nmsgstr \"\"\n\n#: Ribbon.btnExportSelected.Label\nmsgid \"Export Selected\"\nmsgstr \"\"\n\n#: Ribbon.btnExport.Label\nmsgid \"Export Source Files\"\nmsgstr \"\"\n\n#: Ribbon.btnExportSelected.Supertip\nmsgid \"\"\n\"Export the database component currently selected in the navigation pane.\"\nmsgstr \"\"\n\n#: Ribbon.btnExportVBA.Label\nmsgid \"Export VBA Code\"\nmsgstr \"\"\n\n#: frmVCSDatabase.lblOptions\nmsgid \"External Database Connection\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label243(lstDatabases)\nmsgid \"External Databases:\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label112(chkExtractThemeFiles)\nmsgid \"Extract Theme Files\"\nmsgstr \"Extrair arquivos de temas\"\n\n#: frmVCSDatabase.Label250(txtFilter)\nmsgid \"Filter for database objects (source file paths):\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label253\nmsgid \"\"\n\"Filter paths to source files. Start line with ! to exclude match. Use * \"\n\"for wildcard matches (LIKE comparison).\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.Label244(txtCommitMessage)\nmsgid \"Final Commit Message:\"\nmsgstr \"\"\n\nmsgid \"Finished\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblForceImportOriginalQuerySQL(chkForceImportOriginalQuerySQL)\nmsgid \"Force import of original SQL for queries\"\nmsgstr \"Forçar importação das consultas SQLs originais\"\n\n#: frmVCSOptions.Label142(chkFormName)\nmsgid \"Form Name\"\nmsgstr \"Nome do formulário\"\n\n#: frmVCSOptions.Label249(chkFormatSQL)\nmsgid \"Format SQL\"\nmsgstr \"\"\n\nmsgid \"forms\"\nmsgstr \"\"\n\n#: frmVCSMain.Label31(chkFullBuild)\nmsgid \"Full Build\"\nmsgstr \"Criação completa\"\n\n#: frmVCSMain.Label29(chkFullExport)\nmsgid \"Full Export\"\nmsgstr \"Exportação completa\"\n\nmsgid \"Full Path: {0}\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeGeneral\nmsgid \"General\"\nmsgstr \"Geral\"\n\n#: Ribbon.grpGitHub.Label\nmsgid \"GitHub Community\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label221(cboHashAlgorithm)\nmsgid \"Hash Algorithm:\"\nmsgstr \"Algoritmo Hash:\"\n\nmsgid \"hidden attributes\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label152(chkICMIntent)\nmsgid \"ICM Intent\"\nmsgstr \"ICM Intent\"\n\n#: frmVCSOptions.Label150(chkICMMethod)\nmsgid \"ICM Method\"\nmsgstr \"Método ICM\"\n\nmsgid \"imex specs\"\nmsgstr \"\"\n\n#: Ribbon.btnLoadSelected.Supertip\nmsgid \"\"\n\"Import from source files the database component currently selected in the \"\n\"navigation pane.\"\nmsgstr \"\"\n\nmsgid \"Importing {0}...\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblHeading(Page40)\nmsgid \"Install Add-In\"\nmsgstr \"Instalar Suplemento\"\n\n#: frmVCSInstall.Label63(txtInstallFolder)\nmsgid \"Install Folder:   \"\nmsgstr \"\"\n\nmsgid \"\"\n\"Instead of opening this form to build the add-in,\\n\"\n\"please install and use the Version Control add-in from the Add-in menu\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label32\nmsgid \"joyfullservice/msaccess-vcs-addin\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label252(cboLanguage)\nmsgid \"Language:\"\nmsgstr \"Idioma:\"\n\n#: frmVCSSplitFiles.Label241(txtFileList)\nmsgid \"List of files to split:\"\nmsgstr \"\"\n\n#: Ribbon.btnLoadSelected.Label\nmsgid \"Load Selected\"\nmsgstr \"\"\n\n#: Ribbon.btnLocalizeLibraryReferences.Label\nmsgid \"Localize Library References\"\nmsgstr \"\"\n\nmsgid \"macros\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label154(chkMediaType)\nmsgid \"Media Type\"\nmsgstr \"Tipo de mídia\"\n\nmsgid \"Merge\"\nmsgstr \"\"\n\n#: Ribbon.btnMergeBuild.Label\nmsgid \"Merge Build\"\nmsgstr \"\"\n\n#: Ribbon.btnMergeBuild.Supertip\nmsgid \"Merge changed source files into the current database.\"\nmsgstr \"\"\n\nmsgid \"Merge Complete\"\nmsgstr \"\"\n\nmsgid \"Merging From Source\"\nmsgstr \"\"\n\nmsgid \"modules\"\nmsgstr \"\"\n\n#: frmVCSInstall.Caption\nmsgid \"MSAccessVCS\"\nmsgstr \"MSAccessVCS\"\n\n# Do not remove the semicolon\n#: lstDatabases.RefreshSchemaList\nmsgid \"Name;Description\"\nmsgstr \"\"\n\nmsgid \"nav pane groups\"\nmsgstr \"\"\n\nmsgid \"No {0} source files found.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label123(chkCopies)\nmsgid \"Number of Copies\"\nmsgstr \"Número de cópias\"\n\n#: Ribbon.btnGoToLinkDocumentation.Label\nmsgid \"Online Documentation\"\nmsgstr \"\"\n\n#: Ribbon.btnShow.Label\nmsgid \"Open Add-in\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label36(chkOpenAfterInstall)\nmsgid \"Open add-in after installing to trust the add-in file\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdOpenInstallFolder\nmsgid \"Open Install Folder\"\nmsgstr \"\"\n\n#: frmVCSMain.cmdOpenLogFile\nmsgid \"Open Log File...\"\nmsgstr \"Abrir arquivo de Log...\"\n\n#: Ribbon.btnShow.Supertip\nmsgid \"\"\n\"Open the main form for the add-in. From the main form you can run an \"\n\"export or build a project from source.\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenBuildLog.Supertip\nmsgid \"Open the most recent build log file.\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenExportLog.Supertip\nmsgid \"Open the most recent export log file.\"\nmsgstr \"\"\n\n#: Ribbon.btnShowOptions.Supertip\nmsgid \"\"\n\"Open the Options form to view or change options for the current project.\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenSourceFolder.Supertip\nmsgid \"Open the source folder for the current project.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label241(cmdOpenInstallFolder)\nmsgid \"Open VCS add-In Install Folder.\"\nmsgstr \"\"\n\n#: Ribbon.grpOptions.Label\nmsgid \"Options\"\nmsgstr \"Opções\"\n\n#: frmVCSOptions.cmdPrintSettingsOptions\nmsgid \"Options...\"\nmsgstr \"Opções...\"\n\n#: frmVCSOptions.Label79(txtOtherTableName)\nmsgid \"Other Table Name:\"\nmsgstr \"\"\n\n#: frmVCSConflict.cmdOverwriteAll\nmsgid \"Overwrite All\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label115(chkOrientation)\nmsgid \"Page Orientation\"\nmsgstr \"Orientação de página\"\n\n#: frmVCSOptions.Label117(chkPaperSize)\nmsgid \"Paper Size\"\nmsgstr \"Tamanho do papel\"\n\n#: frmVCSOptions.Label125(chkDefaultSource)\nmsgid \"Paper Tray\"\nmsgstr \"Bandeja\"\n\n#: frmVCSOptions.Label105\nmsgid \"\"\n\"PLEASE NOTE:\\n\"\n\"Building from source will completely rebuild your database project from \"\n\"source files. It is very important that you test this carefully in your \"\n\"environment to make sure everything you need is being created in your \"\n\"database during the build. (A backup copy of your current database will \"\n\"be made as a part of the build process.)\\n\"\n\"\\n\"\n\"Please see the online documentation for additional details on the build \"\n\"process. If you encounter a problem, please feel free to open an issues \"\n\"on the GitHub project.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label128(chkPrintQuality)\nmsgid \"Print Quality\"\nmsgstr \"Qualidade da impressão\"\n\n#: frmVCSOptions.Label121(chkScale)\nmsgid \"Print Scale\"\nmsgstr \"Imprimir Escala\"\n\n#: frmVCSOptions.tabOptions.pgePrinterSettings\nmsgid \"Printer Settings\"\nmsgstr \"Configurações de impressão\"\n\nmsgid \"proj properties\"\nmsgstr \"\"\n\nmsgid \"project\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkHome.Label\nmsgid \"Project Home\"\nmsgstr \"\"\n\nmsgid \"queries\"\nmsgstr \"\"\n\n#: Ribbon.btnRepairColors.Description\nmsgid \"\"\n\"Rebuild color definition blocks in form objects (See documentation for \"\n\"more info)\"\nmsgstr \"\"\n\n#: Ribbon.btnReloadRibbon.Description\nmsgid \"Refresh the add-in ribbon menu to reflect changes in XML source\"\nmsgstr \"\"\n\nmsgid \"relations\"\nmsgstr \"\"\n\n#: Ribbon.btnLocalizeLibraryReferences.Description\nmsgid \"\"\n\"Relink add-in library databases to the files located in the same folder \"\n\"as the current database\"\nmsgstr \"\"\n\n#: Ribbon.btnReloadRibbon.Label\nmsgid \"Reload Ribbon\"\nmsgstr \"\"\n\nmsgid \"Removing non built-in references...\"\nmsgstr \"\"\n\n#: Ribbon.btnRepairColors.Label\nmsgid \"Repair Colors\"\nmsgstr \"\"\n\nmsgid \"reports\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label59(cmdClearDefaults)\nmsgid \"Reset all default settings to original values.\"\nmsgstr \"Retornar todas as configurações padrão para seus valores originais.\"\n\n#: frmVCSOptions.Label132(chkResolution)\nmsgid \"Resolution\"\nmsgstr \"Resolução\"\n\n#: frmVCSConflict.Label4\nmsgid \"Resolve Conflicts to Continue...\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label104(txtRunAfterBuild)\nmsgid \"Run Sub After Build:\"\nmsgstr \"Rodar Rotina após gerar:\"\n\n#: frmVCSOptions.Label48(txtRunAfterExport)\nmsgid \"Run Sub After Export:\"\nmsgstr \"Rodar rotina após exportar:\"\n\n#: frmVCSOptions.Label229(txtRunBeforeBuild)\nmsgid \"Run Sub Before Build:\"\nmsgstr \"Rodar Rotina antes de gerar:\"\n\n#: frmVCSOptions.Label44(txtRunBeforeExport)\nmsgid \"Run Sub Before Export:\"\nmsgstr \"Rodar rotina antes de exportar:\"\n\nmsgid \"Running {0}...\"\nmsgstr \"\"\n\nmsgid \"Running...\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label233(cboSanitizeColors)\nmsgid \"Sanitize Colors\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label235(cboSanitizeLevel)\nmsgid \"Sanitize Level\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label257(chkSaveDotEnv)\nmsgid \"Save in .env file*\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label27(chkSavePrintVars)\nmsgid \"Save Printer Settings\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label29(chkSaveQuerySQL)\nmsgid \"Save Query SQL\"\nmsgstr \"Salvar consulta SQL\"\n\n#: frmVCSOptions.Label38(chkSaveTableSQL)\nmsgid \"Save Table SQL\"\nmsgstr \"Salvar Tabela SQL\"\n\n#: frmVCSOptions.Label53(cmdSaveAsDefault)\nmsgid \"Save these settings as default for new projects.\"\nmsgstr \"Salvar essas configurações como padrão para novos projetos.\"\n\nmsgid \"Saved as {0}.\"\nmsgstr \"\"\n\nmsgid \"saved specs\"\nmsgstr \"\"\n\nmsgid \"Saving backup of original database...\"\nmsgstr \"\"\n\nmsgid \"Scanning source files...\"\nmsgstr \"\"\n\n#: frmVCSOptions.cmdSeeDocs\nmsgid \"See Docs...\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkAdvancedTools.Description\nmsgid \"See documentation on the purpose and usage of these custom tools...\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label126\nmsgid \"\"\n\"Select any additional settings that you would like saved to version \"\n\"control and used when building a database from source files.\"\nmsgstr \"\"\n\"Selecione qualquer configuração adicional que queira salvar no controle \"\n\"de versões e as use quando criando um banco de dados de uma fonte de \"\n\"arquivos.\"\n\n#: frmVCSOptions.tabOptions.pgeSettings\nmsgid \"Settings\"\nmsgstr \"Configurações\"\n\nmsgid \"shared images\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label23(chkShowDebug)\nmsgid \"Show Detailed Output\"\nmsgstr \"Mostrar saída detalhada\"\n\n#: frmVCSOptions.lblTableShowHidden(chkTableShowHidden)\nmsgid \"Show Hidden\"\nmsgstr \"Mostrar Ocultos\"\n\n#: frmVCSOptions.Label215(chkShowVCSLegacy)\nmsgid \"Show Legacy Prompts\"\nmsgstr \"Mostrar Legacy Prompts\"\n\n#: frmVCSOptions.lblTableShowOther(chkTableShowOther)\nmsgid \"Show Other\"\nmsgstr \"Mostrar outros\"\n\nmsgid \"Show Other  \"\nmsgstr \"\"\n\n#: frmVCSOptions.lblTableShowSystem(chkTableShowSystem)\nmsgid \"Show System\"\nmsgstr \"Mostrar objetos do Sistema\"\n\n#: frmVCSConflict.cmdSkipAll\nmsgid \"Skip All\"\nmsgstr \"\"\n\n#: Ribbon.btnOpenSourceFolder.Label\nmsgid \"Source Folder\"\nmsgstr \"\"\n\n#: frmVCSOptions.lblWorkingPathExplanation\nmsgid \"Specify the path for the working language files.\"\nmsgstr \"\"\n\n#: Ribbon.btnSplitFiles.Label\nmsgid \"Split Files\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.lblOptions\nmsgid \"Split Files and Preserve History\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label251(chkSplitLayoutFromVBA)\nmsgid \"Split Layout from VBA\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkSupport.Label\nmsgid \"Support Issues\"\nmsgstr \"\"\n\n#: frmVCSTableData.Caption\nmsgid \"Table Data\"\nmsgstr \"Dados da tabela\"\n\nmsgid \"table data\"\nmsgstr \"\"\n\nmsgid \"table data macros\"\nmsgstr \"\"\n\n#: frmVCSTableData.Label3(txtTableName)\nmsgid \"Table Name\"\nmsgstr \"\"\n\nmsgid \"tables\"\nmsgstr \"\"\n\n#: frmVCSDatabase.cmdTest\nmsgid \"Test Filter...\"\nmsgstr \"\"\n\nmsgid \"themes\"\nmsgstr \"\"\n\n#: frmVCSConflict.lblHeading(sfrmConflictList)\nmsgid \"These source files have changed since the last export\"\nmsgstr \"\"\n\n#: Ribbon.mnuAdvancedTools.Supertip\nmsgid \"\"\n\"These tools are intended for more advanced development purposes. Please \"\n\"review the documentation for these tools before using them.\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label227\nmsgid \"\"\n\"This add-in provides limited support for exporting object definitions \"\n\"from external databases for use in version control.\\n\"\n\"\\n\"\n\"* If the connection string contains a password, please use the .env \"\n\"option, and exclude this file from version control.\"\nmsgstr \"\"\n\n#: Ribbon.PLEASE NOTE:.Label\nmsgid \"\"\n\"This file is automatically generated by the add-in. Please do not \"\n\"translate or edit this file directly.\"\nmsgstr \"\"\n\n#: Ribbon.grpTools.Label\nmsgid \"Tools\"\nmsgstr \"\"\n\n#: frmVCSOptions.tabOptions.pgeTranslations\nmsgid \"Translation\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label297(txtTranslationsPath)\nmsgid \"Translations Path: \"\nmsgstr \"Caminho das traduções: \"\n\n#: frmVCSOptions.cmdTranslations\nmsgid \"Translations...\"\nmsgstr \"\"\n\n#: frmVCSInstall.Label34(chkAddTrustedLocation)\nmsgid \"Trust Add-In Folder\"\nmsgstr \"Pasta de suplementos\"\n\n#: frmVCSOptions.Label140(chkTTOption)\nmsgid \"TT Font Handling\"\nmsgstr \"Tratamento de Fontes TT\"\n\nmsgid \"Unable to load Ribbon COM add-in\"\nmsgstr \"\"\n\nmsgid \"Unable to rename original file\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label108(cmdUninstall)\nmsgid \"Uninstall this add-in\"\nmsgstr \"Desinstalar este suplemento\"\n\n#: frmVCSOptions.Label230\nmsgid \"Use \\\"module.sub\\\" to specify module\"\nmsgstr \"Utilize \\\"module.sub\\\" para especificar o módulo\"\n\n#: frmVCSOptions.Label25(chkUseFastSave)\nmsgid \"Use Fast Save\"\nmsgstr \"Utilizar salvamento rápido\"\n\n#: frmVCSSplitFiles.Label242\nmsgid \"\"\n\"Use full file paths with a pipe character (|) between the original and \"\n\"new file paths.\\n\"\n\"\\n\"\n\"Each entry should be on a new line. The list will be validated before \"\n\"running the operation.\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label163(chkUseGitIntegration)\nmsgid \"Use Git Integration\"\nmsgstr \"Integração com Git\"\n\n#: frmVCSInstall.Label40(chkUseRibbon)\nmsgid \"Use Ribbbon Addin\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label223(chkUseShortHash)\nmsgid \"Use short hashes in index\"\nmsgstr \"Utilizar hashes curtos no índice\"\n\n#: frmVCSOptions.Label227\nmsgid \"\"\n\"Use this form to set your preferred options for exporting and building \"\n\"your database project to and from source files. Note that you can have \"\n\"different options for different projects, and can save a set of options \"\n\"as default for new projects.\"\nmsgstr \"\"\n\"Utilize esse formulário para configurar suas opções preferidas para \"\n\"exportação e criação do seu projeto de banco de dados. Arquivos fonte \"\n\"'De' > 'Para'. Repare que você poderá ter diferentes opções para \"\n\"diferentes projetos e poderá salvar um grupo de opções como sendo padrão \"\n\"para novos projetos.\"\n\n#: frmVCSOptions.lblTranslationExplanation\nmsgid \"\"\n\"Use this page to manage language translations for this add-in. Check this \"\n\"box if you would like to help improve the translations.\"\nmsgstr \"\"\n\n#: frmVCSSplitFiles.Label227\nmsgid \"\"\n\"Use this tool to duplicate files while preserving commit history in Git.\\n\"\n\"\\n\"\n\"Paste in a list of Pipe delimited paths and click the button below.\"\nmsgstr \"\"\n\n#: frmVCSDatabase.Label259(chkUtcDates)\nmsgid \"Use UTC Dates\"\nmsgstr \"\"\n\nmsgid \"vb project\"\nmsgstr \"\"\n\nmsgid \"vbe forms\"\nmsgstr \"\"\n\nmsgid \"vbe references\"\nmsgstr \"\"\n\nmsgid \"VCS Version {0}\"\nmsgstr \"\"\n\nmsgid \"Version {0}\"\nmsgstr \"\"\n\nmsgid \"Version {0} currently installed.\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblInstalled\nmsgid \"Version 4.0.11 currently installed.\"\nmsgstr \"\"\n\n#: frmVCSInstall.lblVersion\nmsgid \"Version 4.0.12\"\nmsgstr \"\"\n\n#: Ribbon.tabVersionControl.Label\nmsgid \"Version Control\"\nmsgstr \"\"\n\n#: frmVCSOptions.Label9\nmsgid \"Version Control System \"\nmsgstr \"Sistema de controle de versionamento \"\n\n#: frmVCSInstall.Label4\nmsgid \"Version Control System\"\nmsgstr \"Sistema de controle de versionamento\"\n\n#: frmVCSConflict.lblVersion\nmsgid \"Version Control System ${version}\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkAdvancedTools.Label\nmsgid \"View Documentation\"\nmsgstr \"\"\n\n#: Ribbon.btnShowOptions.Label\nmsgid \"View Options\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkHome.Supertip\nmsgid \"View the add-in project home on GitHub.\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkSupport.Supertip\nmsgid \"View the add-in project support issues on GitHub.\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkDocumentation.Supertip\nmsgid \"View the documentation Wiki on GitHub.\"\nmsgstr \"\"\n\n#: Ribbon.btnGoToLinkDownload.Supertip\nmsgid \"View the GitHub download page for this add-in project.\"\nmsgstr \"\"\n\n#: frmVCSConflict.lblHeaderExplain\nmsgid \"\"\n\"WARNING: The following changes may be overwritten. Please select how you \"\n\"would \\n\"\n\"like to proceed.\"\nmsgstr \"\"\n\n#: frmVCSMain.lblSubheading\nmsgid \"What would you like to do?\"\nmsgstr \"O que gostaria de fazer?\"\n\n#: frmVCSOptions.Label82\nmsgid \"\"\n\"You may optionally include data from specific tables as part of the \"\n\"export process.\"\nmsgstr \"\"\n\"Você pode optar por incluir dados oriundos de tabelas específicas como \"\n\"parte do processo de exportação.\"\n\n#: frmVCSOptions.Label244\nmsgid \"\"\n\"Your database application may connect to external database systems such \"\n\"as Microsoft SQL Server.\\n\"\n\"\\n\"\n\"If you define a connection to an external database, you can use version \"\n\"control to track changes in those external database objects.\\n\"\n\"\\n\"\n\"Please see documentation for additional notes.\"\nmsgstr \"\"\n"
  },
  {
    "path": "Version Control.accda.src/dbs-properties.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbProperty\",\r\n    \"Description\": \"Database Properties (DAO)\"\r\n  },\r\n  \"Items\": {\r\n    \"AccessVersion\": {\r\n      \"Value\": \"09.50\",\r\n      \"Type\": 10\r\n    },\r\n    \"AllowBuiltInToolbars\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowDatasheetSchema\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowFullMenus\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowShortcutMenus\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowSpecialKeys\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"AllowToolbarChanges\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"ANSI Query Mode\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"AppTitle\": {\r\n      \"Value\": \"Version Control System\",\r\n      \"Type\": 10\r\n    },\r\n    \"AppVersion\": {\r\n      \"Value\": \"4.1.2\",\r\n      \"Type\": 10\r\n    },\r\n    \"Auto Compact\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"Build\": {\r\n      \"Value\": 606,\r\n      \"Type\": 4\r\n    },\r\n    \"CheckTruncatedNumFields\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Clear Cache on Close\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"ClickOnce Deployment\": {\r\n      \"Value\": \"False\",\r\n      \"Type\": 10\r\n    },\r\n    \"CollatingOrder\": {\r\n      \"Value\": 1033,\r\n      \"Type\": 3\r\n    },\r\n    \"Connect\": {\r\n      \"Value\": \"\",\r\n      \"Type\": 12\r\n    },\r\n    \"Continuous Form Navigation Keys\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"DesignMasterID\": {\r\n      \"Value\": \"\",\r\n      \"Type\": 15\r\n    },\r\n    \"DesignWithData\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"HasOfflineLists\": {\r\n      \"Value\": 70,\r\n      \"Type\": 3\r\n    },\r\n    \"Name\": {\r\n      \"Value\": \"rel:Version Control.accda\",\r\n      \"Type\": 12\r\n    },\r\n    \"NavPane Category\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"NavPane Closed\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"NavPane Sort By\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"NavPane View By\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"NavPane Width\": {\r\n      \"Value\": 215,\r\n      \"Type\": 4\r\n    },\r\n    \"Never Cache\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"Picture Property Storage Format\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"ProjVer\": {\r\n      \"Value\": 119,\r\n      \"Type\": 3\r\n    },\r\n    \"QueryTimeout\": {\r\n      \"Value\": 60,\r\n      \"Type\": 3\r\n    },\r\n    \"RecordsAffected\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"ReplicaID\": {\r\n      \"Value\": \"\",\r\n      \"Type\": 15\r\n    },\r\n    \"Row Limit\": {\r\n      \"Value\": 10000,\r\n      \"Type\": 4\r\n    },\r\n    \"Show Navigation Pane Search Bar\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Show Values in Indexed\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Show Values in Non-Indexed\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Show Values in Remote\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"Show Values Limit\": {\r\n      \"Value\": 1000,\r\n      \"Type\": 4\r\n    },\r\n    \"ShowDocumentTabs\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"StartUpShowDBWindow\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"StartUpShowStatusBar\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"Theme Resource Name\": {\r\n      \"Value\": \"Office Theme\",\r\n      \"Type\": 10\r\n    },\r\n    \"Themed Form Controls\": {\r\n      \"Value\": 1,\r\n      \"Type\": 4\r\n    },\r\n    \"Transactions\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"Updatable\": {\r\n      \"Value\": true,\r\n      \"Type\": 1\r\n    },\r\n    \"Use Microsoft Access 2007 compatible cache\": {\r\n      \"Value\": 0,\r\n      \"Type\": 4\r\n    },\r\n    \"UseAppIconForFrmRpt\": {\r\n      \"Value\": false,\r\n      \"Type\": 1\r\n    },\r\n    \"UseMDIMode\": {\r\n      \"Value\": 0,\r\n      \"Type\": 2\r\n    },\r\n    \"Version\": {\r\n      \"Value\": \"14.0\",\r\n      \"Type\": 12\r\n    },\r\n    \"WebDesignMode\": {\r\n      \"Value\": 0,\r\n      \"Type\": 2\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Version Control.accda.src/documents.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbDocument\",\r\n    \"Description\": \"Database Documents Properties (DAO)\"\r\n  },\r\n  \"Items\": {\r\n    \"Databases\": {\r\n      \"SummaryInfo\": {\r\n        \"Comments\": \"Export source code and database objects for tracking in Version Control Systems like GitHub and GitLab. Also used for building a database from source files.\",\r\n        \"Company\": \"https://github.com/joyfullservice/msaccess-vcs-addin\",\r\n        \"Title\": \"Version Control\"\r\n      }\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSConflict.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    PopUp = NotDefault\r\n    Modal = NotDefault\r\n    RecordSelectors = NotDefault\r\n    AutoCenter = NotDefault\r\n    NavigationButtons = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    ScrollBars =0\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =12960\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =48\r\n    Left =3225\r\n    Top =2430\r\n    Right =28545\r\n    Bottom =14895\r\n    RecSrcDt = Begin\r\n        0x79e78b777268e540\r\n    End\r\n    Caption =\"MSAccessVCS\"\r\n    DatasheetFontName =\"Calibri\"\r\n    OnResize =\"[Event Procedure]\"\r\n    OnLoad =\"[Event Procedure]\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Rectangle\r\n            SpecialEffect =3\r\n            BackStyle =0\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Line\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Image\r\n            BackStyle =0\r\n            OldBorderStyle =0\r\n            BorderLineStyle =0\r\n            SizeMode =3\r\n            PictureAlignment =2\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CommandButton\r\n            FontSize =11\r\n            FontWeight =400\r\n            FontName =\"Calibri\"\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =1\r\n            Gradient =12\r\n            BackThemeColorIndex =4\r\n            BackTint =60.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =4\r\n            BorderTint =60.0\r\n            ThemeFontIndex =1\r\n            HoverThemeColorIndex =4\r\n            HoverTint =40.0\r\n            PressedThemeColorIndex =4\r\n            PressedShade =75.0\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n        End\r\n        Begin CheckBox\r\n            BorderLineStyle =0\r\n            LabelX =230\r\n            LabelY =-30\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Subform\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =1\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            BorderShade =65.0\r\n            ShowPageHeaderAndPageFooter =1\r\n        End\r\n        Begin Section\r\n            CanGrow = NotDefault\r\n            Height =8640\r\n            BackColor =15130848\r\n            Name =\"Detail\"\r\n            AlternateBackColor =15130848\r\n            Begin\r\n                Begin Line\r\n                    BorderWidth =5\r\n                    OverlapFlags =85\r\n                    Top =1440\r\n                    Width =12960\r\n                    BorderColor =15321539\r\n                    Name =\"Line10\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedTop =1440\r\n                    LayoutCachedWidth =12960\r\n                    LayoutCachedHeight =1440\r\n                    BorderThemeColorIndex =-1\r\n                End\r\n                Begin Rectangle\r\n                    SpecialEffect =0\r\n                    BackStyle =1\r\n                    OldBorderStyle =0\r\n                    OverlapFlags =93\r\n                    Width =12960\r\n                    Height =1380\r\n                    BackColor =5324600\r\n                    Name =\"Box1\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedWidth =12960\r\n                    LayoutCachedHeight =1380\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =223\r\n                    Left =420\r\n                    Top =300\r\n                    Width =5940\r\n                    Height =540\r\n                    FontSize =18\r\n                    FontWeight =700\r\n                    Name =\"Label4\"\r\n                    Caption =\"Resolve Conflicts to Continue...\"\r\n                    LayoutCachedLeft =420\r\n                    LayoutCachedTop =300\r\n                    LayoutCachedWidth =6360\r\n                    LayoutCachedHeight =840\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin CommandButton\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =10320\r\n                    Top =7740\r\n                    Width =2100\r\n                    Height =540\r\n                    Name =\"cmdContinue\"\r\n                    Caption =\"  Continue\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    HorizontalAnchor =1\r\n                    VerticalAnchor =1\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x00000000904820ff904820300000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x00000000a05020ffa05030ff9048203000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000d08060ff904820ffa04820ff ,\r\n                        0xa05020ffb05830ffc06840ffa05030ff90482030000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000d08860ffffb090fff09060ff ,\r\n                        0xf08850ffe08050ffd07850ffc07040ffa05830ff904820300000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000e09070ffffb890ffffa880ff ,\r\n                        0xff9060fff09060ffe08850ffe07850ffc07040ffb05830ff0000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000e09870ffffc0a0ffffc0a0ff ,\r\n                        0xffb080ffffa880ffffa070ffe08050ffb05830ffb05830200000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000f0a070ffe09870ffe09070ff ,\r\n                        0xd08860ffe09070fff09870ffb05830ffb0583020000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x00000000e09870ffc07850ffb058302000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x00000000f0a070ffd08060200000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n\r\n                    LayoutCachedLeft =10320\r\n                    LayoutCachedTop =7740\r\n                    LayoutCachedWidth =12420\r\n                    LayoutCachedHeight =8280\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =215\r\n                    Left =600\r\n                    Top =840\r\n                    Width =5040\r\n                    Height =360\r\n                    FontSize =10\r\n                    Name =\"lblVersion\"\r\n                    Caption =\"Version Control System ${version}\"\r\n                    LayoutCachedLeft =600\r\n                    LayoutCachedTop =840\r\n                    LayoutCachedWidth =5640\r\n                    LayoutCachedHeight =1200\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Image\r\n                    Left =11520\r\n                    Top =240\r\n                    Width =840\r\n                    Height =900\r\n                    Name =\"Image32\"\r\n                    Picture =\"iconfinder_error_6299.png\"\r\n                    HorizontalAnchor =1\r\n                    ImageData = Begin\r\n                        0x89504e470d0a1a0a0000000d4948445200000080000000800806000000c33e61 ,\r\n                        0xcb00002b9849444154785eed9d698c5ed779df7fcfb9f77ddfd9772e335c861c ,\r\n                        0x0e5771131789a2245b94258ba22dc9b2b5d84a9b264d0a0708daa0edc7228181 ,\r\n                        0xf44317b4fd901668922645b3006982b86db6d6711aa74eecd4b16bcb9265452b ,\r\n                        0x25719d21399c997766dee5de7b9eca9c73712e0ee61d52634926a5b9c08373ee ,\r\n                        0xf29212feffe7ff2ce7dc4b5155568f0faf19568f5502acda2a01566d9500abf6 ,\r\n                        0x61b49877e1f8e583c2cd7e888089a1d40eeddd319d83ed74f4758aa234666b3a ,\r\n                        0x7fb54e733e25a9599a75b019b7ccf1f967f57d22c0ad6f2210211889444c2caa ,\r\n                        0x88881111c12a585532405715e0836502c4c650369154d06c38ab37c6e62ecb26 ,\r\n                        0x4413cdd2d302af47b14ca6860c6802d96a0ef0c101bf6c22ba4dc4aea814fdc2 ,\r\n                        0x9a5d3bff70e7e37fffbfddfeb3ffe63fec79ea677f65ddfedbff286aeff8dd2c ,\r\n                        0xb1ffc018d68bd0fec1708e55020850ba06bee1ae4a57fcebdb8e8ffedcae07ee ,\r\n                        0xd83aba6f5b34325266d3d66e761c1eadec3c3e7ab8774ddbbf4d13fe651cb359 ,\r\n                        0xa003885609706b9b11e88c23468cf04f87d7b37fdda6350c76d5a89cfb7d78f1 ,\r\n                        0x9788cffd777af53463e39d8c6feba05379d266fcbd38a60f6803649500b7ae95 ,\r\n                        0x23430fcac94ec3899eae6e868f7d8272540705a40cc46033ca7d6bd8fed05dec ,\r\n                        0x38d04e49f93113b1df085d40699500b7a645025d26626b243cd35d8275773f44 ,\r\n                        0xb9d280b90940008b6302cc5f261a1c65fbc9030cf5b301cb674b25d60ab4df9a ,\r\n                        0x8eb24a804a1cd18b70aac3706070fb1a86ef3a0617be05e2f1441500921acc5c ,\r\n                        0xa473db1eb61deea7a47c42228e9a88ee5b2f215c25406ca0430c5b63f84c5705 ,\r\n                        0x461f7d0c935e82854b8ef7eac0b78ba318983b07a683cd77ef61cd1afa34e533 ,\r\n                        0xa5984181b65bc759560920409b89e851e5d10e616ccdbe51d61e3e0217be5904 ,\r\n                        0x1fd482b26808a47598394fdbe84eb61d1ea4ac3c80702c8ae8bc757281550294 ,\r\n                        0x8da1c718f6970c8f77762c7a3ff367b0f31300a079ec7743ce0211983b03a69d ,\r\n                        0x8dc777b36e1d654d79268ed928d005c4ab04b8f909dd6e0c030a4fb62b1bd61f ,\r\n                        0xd9cdd0de7d6467ff2f6050b560531ffb51674e0dd23acc9ea7b26917dbee584b ,\r\n                        0x45b807381147f400154056097033c77ea153844365e154771f6c79f471987e0d ,\r\n                        0x5db884625055542d68ea94c099555f11cc9e05ca6cb87317eb47c4d894a7cd4d ,\r\n                        0xdf1c5a258011688b22d68a5ef3febe91e3b7d337be8de4ec37508cc3dac57f6b ,\r\n                        0x413350778efa14229987d97394366e67db9deb688f388472328ee8032aab04b8 ,\r\n                        0x39ad640cdd0877958507bb070d5b1f7d027be515ecc2955cfeb1aa8e033909ac ,\r\n                        0x0f071aa88096d9707417c31b04329e8822b619b9197381550244029dc6302cca ,\r\n                        0x336d50d974ff5d748d6c2439ff2d00acaa031db0ea4241063605bc0aa016009a ,\r\n                        0x7330739668789c6dc786698f1857e591d8d0c707a82c341f98b2cfd0ab707f19 ,\r\n                        0x8ef70c97197df853a493dfc756275022441555501cf8561de60a59e649a0ea7d ,\r\n                        0x63f60c648691a3bbd9306a20e37189d96b846ea0b44a809bcbfbb718f85c5990 ,\r\n                        0x2d27efa77d682dc9f96f3a4c156b2900ef496015d06cd1f095010234e7afa980 ,\r\n                        0x59bb85b163c374c60cabe5f12862889ba645bc4a808a31f4283c50520e0e6eed ,\r\n                        0x61d3fd0f915c780e3b370912a10e7ceca2b90bcee92dea54c0e702f8a37a0e52 ,\r\n                        0x183ebc8b916d3126e56131dc6ecccdb050b44a8058e49af76f35f064c5c0d687 ,\r\n                        0x1fa0dcdd4b72fedb39c6088ab8b6afaa75f87b52682efdb648020b621655a07a ,\r\n                        0x1119dcc2f8f10db4b731a09627e288a10fc24291b9d59b3e91610878bc94b17d ,\r\n                        0x68d75a463efa20cdf3cf91cd4d021138b9cf3ddd37fe7225f0b9016abd159f9b ,\r\n                        0x39038965dd815d6c1c2b11593e2ec27163e803caab04f8d1357dbac4b0d7288f ,\r\n                        0x55ca30fec849e23826b9f02ca8a0805aeb4cf30eb08bff4542289a87069bf9ae ,\r\n                        0x20ea54600eaa17a06f841df76ea2a342c55a9e892246804e205a25c08fc0fbc5 ,\r\n                        0xd0afcae351c6c8fa039b597fc73d34cf3d87ad4ea262bc87a380c3d579b8e69e ,\r\n                        0xeff202d7237024f0fb041001c4e5021943fb76b269470593711c381109ddef7f ,\r\n                        0x8b7895002511ba040e1be5645b078c3f7a0ad4d2bcf01c8a80cff821af00b085 ,\r\n                        0x980fa87a8268181a7c55800834aa50bd08ddebd971cf26bada11b53c692246e5 ,\r\n                        0x165601730b2ff8ac019e8a52fa36deb99ba17d471663ffc22488c963bf977c67 ,\r\n                        0xa06102e8cc570458cd89107407cf41336160ef4e46f7b463320e283c640cbdef ,\r\n                        0x9f0aac12a02c428fc0b14879b0a317b67df2249ad4695e781eb58206e0538cf9 ,\r\n                        0xd67b7ef1397c750058b0d6570508888186cb053a86d87ecf667aba00cb93c6b0 ,\r\n                        0xf5fd592e5e2540c462d36704f831c9a88cde7b8881edb7513ff72cd9dc2510e3 ,\r\n                        0x3dda5700a0c504d05dc3a980b58504d162f384d02f18f970503d0fcd067dbbb7 ,\r\n                        0xb3755f07b1655ce15391a1ff566c11c7b760ecef13e1e326e39eeea1886d271f ,\r\n                        0x245d98a179fef93cf6838288937bd4ebb22a2a0e69771714714e9e9342dcb388 ,\r\n                        0x0b078233712a70110636b2edde314ebff03da6e7785c22be2a4a559506d05c55 ,\r\n                        0x80f7cefb3763794252d8fab13be9de3c4ee36df06ded2a60c07acff7711e7fee ,\r\n                        0xb3ff6b86e2ce2df88411eb6f3853df219cbd008d063ddbb6327eb013a30cabf2 ,\r\n                        0x69230c0215c0ac2ac07b13fbbb810724635fdf860ec61e7c9064ee2a8d0bdf07 ,\r\n                        0x35b957635c66ef11b3e0cf11375740089f55441535a0e4ea9081312006885c77 ,\r\n                        0x70f29a0a8cdf33c66bdf7d9eab554e1ae18f2d4c2aefb60aac2a4009e831861d ,\r\n                        0xa23c6d2cec38750fed6b36d038ff3db2f9695422efe1458f75f3dcf3b17eee4b ,\r\n                        0xc2b02c74f78ad54016f407e62e40b34ec796adec38da83b1f4283c1519d6011d ,\r\n                        0x805925c0bb47d23611fa81c73561dbc0781f9b3ffa1192e9099a17be07123949 ,\r\n                        0xb7ce408ba1c07a8035071f1f22508acf41de1c024f268a09a140631ee62621ea ,\r\n                        0x62fceeedf40f00960745b85ba00728af12e0dd8bfd5d46d88be53123b0eb93f7 ,\r\n                        0x51ee1ea4f136f859ad0ad0a2d71f74fcdc7d0fbcba645fc3d5c16b8623937b28 ,\r\n                        0xd84606cc5e84c602958d1bd879b40f6329abf263c6dc3a2d62734bb47c852184 ,\r\n                        0x276d93e1e1bdc36c3a768ce6f4459a932f2362f298ee1d15cdcb3934f47ceb3b ,\r\n                        0x841650148a8da0fcf7b86bcec0ef25040be254a03a015418bf779c356b41338e ,\r\n                        0x8b5c53825ea702b29a04aedc4a2ef33fac190fc725d8fdc87d984a27b537fe06 ,\r\n                        0x1a5544e242e9068a936c71d71c9890b7f505c18de293421110118f78b158b48a ,\r\n                        0xa0b8870005895c2e70113a0788d70db3f3ce4126ffe00a5679ca085fcb945920 ,\r\n                        0x01d25502ac3cf60fa9f2e9b44ef7e83d5b597ffbed34a7ce914cbeec40f0d93b ,\r\n                        0x41f62f2ece8b000692851ab6d1c4800311c43d9793212e4554baca6040548145 ,\r\n                        0x53d1c57331a0026ac10834eb307f09ca9bd976d7182fffbf2b9c3fcf5e13f380 ,\r\n                        0x08a755595825c00abfea01f4887097cd78b0d209b73d721f624a2413dfc7366b ,\r\n                        0x888901bcd7e23c55bd8c0ba002b5b397987f630e95a53559f11563f7fa98a19d ,\r\n                        0x0388808a6f17238a580b9213c100ba18063a07316bd7b3e7ee755cfcdd095479 ,\r\n                        0xc2085fce942ad07c672458258001ba8d6193c28f6735da763ebc9ba1dd7b685c ,\r\n                        0x7993e4d2ab8889c2377bdca9278405a20892e9599a5561e8d3ff98d2e07ad466 ,\r\n                        0x2d34c760eb0da6bffa9f983d3b41ff681f9af9570701a70280b16015702a509d ,\r\n                        0x8078335b8e6e65e4eb939c39abdb4dcca78d70d62a0d601eb0ab04b8f1a64f2f ,\r\n                        0xc289acc9f1f67e61cf273e8a664a32f12236a923a604f83a3d3ffcf6ee3c7e5b ,\r\n                        0xac4d19fefc6fd2befd316ee4e83af459667efb216ce32a4425107c95201641c0 ,\r\n                        0x1a104f0caa97aea9800cac61ef7d239cfbad73a8f2b808ff5b94f91b6f0ead12 ,\r\n                        0x20023a44d8a4ca935903d9f5c841fab76ea336799af4ca1b18897d6c468be087 ,\r\n                        0xdd3f6ca34ec7eefb68dffe49ffcc758e52df0e7a8efd14d9377e9128275ab17b ,\r\n                        0x8805058c000634578149288db2e9d0281bfeea026fbe6e37c4659e14e15555e6 ,\r\n                        0x80747915582d0305a8003dc0c7d326077a87cbec3af951d26693e4e28b689a80 ,\r\n                        0x00f82e9e00e2d772f14b7ba05942d4b3198840df010bbbd6222258b585ade414 ,\r\n                        0x978c832d640ab397a13e07dd031c3c314224602da7443822dcc8872656095002 ,\r\n                        0xba8c615c95276d02b73d7c94aee1619a974f934e9d454c84a079cf1e210783a0 ,\r\n                        0xef8ff75afb8e9dce810e582fff041d44ff9ea1ffca08d58b902ac30736b3657b ,\r\n                        0x4cd2a44fe12911d6dc8ccbc5e6266df93e9636d8b676ac87ed278e91d46ad762 ,\r\n                        0x3f362b00ae805e0f7c37ac508b5050eb8b8ce0c512d72174a3820a54af40bd0a ,\r\n                        0x9d7d1cfcd8062a25c8323e867027d0d9ba45bc4a80d8957d7bade57155b8edd4 ,\r\n                        0x1d54fad7905c7e1d3b7d11312607dc13c03780f289f74a0b2b664020fbc5b632 ,\r\n                        0xfe2d2307bc6f11933416978b9b196b6edbc8d8ee324993b22acf88b0d191205e ,\r\n                        0x25404844e8146110e189b4c1c8c8ee21b6de7d84647ee65aec47ad07bb98e95b ,\r\n                        0xeff96241549d01e8ca09005ee2d1400540d519ead709b08040750a6ab3d0d6c3 ,\r\n                        0x81131be8a8409671af080f084bbd62be4a80d8b57c0fda8c932682fd8f1c27ee ,\r\n                        0xec21b9f41ab67a098ca18002a20e6c2c78c07d5e208ae4cfad007b450bf8abe3 ,\r\n                        0x98faf3fc9ed582b98b69023317a1d96470f746761e6c276d22aa3c25c2a67ca1 ,\r\n                        0x689500c12e5f559e48eaf46dbe7d840d87f792cc4e915e7c2947c4838f9f83fa ,\r\n                        0x766ed0160670eeba1206f89d44d627857e47919be33b855e0904e6afc2fc34c4 ,\r\n                        0xedecfdc846ba3a21cb38083ce836b6945709e05bbe7d22dc9ba53c58aec0be47 ,\r\n                        0xee46e2b66bab7d76e12a4814c47c05f161c0a3b504f8b0a210e0378cfa909393 ,\r\n                        0xc0ab8027824f049d6519cc4c40b349dfd8207b0e56481350784a603b37c92e62 ,\r\n                        0x7333acf58b61d85a9e4e1a54b61ddfc6ba3dbb48662e934ebe021239590f3c5f ,\r\n                        0x9702df7a55c0fafb2b3cd4a3ed066714f718e0e7c5f25004e667e1ca59a855d9 ,\r\n                        0x7b472fbd5d90668c038f0afe43131f660294816e81fbb294bb3a7be0b65377a1 ,\r\n                        0x986b659f36e61111bf301b821fecfb0b5bc102c88ac1f761c716933eaf0ac573 ,\r\n                        0xff00ceea094ccdc1b933d068d0b9a98f7d872ad804acf218c29e9b4105cc4d90 ,\r\n                        0xf86d56e5e9b489ecb86f0f0363632457cf935d7e0311e3e53ccce8f149de12e0 ,\r\n                        0x07eab0b210a01a78bedf6718ee25f4154192c1740daed620b5506bc0ec2ca861 ,\r\n                        0xf7b101fafb21cb181178528421bf7ff0c34500012a22f40127b38483bd6b6276 ,\r\n                        0x3f78075992924cbc844dea8078f0d5032e6a11874ad817f05eaf3f5c19e84b3e ,\r\n                        0x677955600bbd00fc9e62ab3097c0541d16fc122216989a867a93b675ddec3fd4 ,\r\n                        0x4e9681553e091c722a50fab0112006ba45d8aeca535902bb1fd84ff7864d2457 ,\r\n                        0xcf904d9d5962b9374cecfc1e3f419d15d1f392bcb2c3010f458f0f044551807a ,\r\n                        0x864e35d099144df25bf81671bd09b3339009bbef1c60ed10a429bd4e05065d2e ,\r\n                        0x201f160218e7fdfdc0a7d22663039bdad971ff1da4f51ae9c517c1a680e41eee ,\r\n                        0x81b4362cf3103ff7d77d3f60c54731db7743a11474e7a9a2d30dec95263414cd ,\r\n                        0xe54797b0ab3350af130f7672f068475e343c081c87bc2c443ef804f0de7fc05a ,\r\n                        0x1e550bfb4ede4efbc000c9d49b64b3932051017c5f9201611ed0b224f4f757ce ,\r\n                        0x00b5046f1803b8f97c82bddc40ab364c11d0107c041a294c4f4306db0f0f30bc ,\r\n                        0x5e48532ac0df1561e38f2a17303f92051feffdebd68ff73076f741d28539d289 ,\r\n                        0x97c0fad82e1a7a7800748ba4cfcf7d6b78c587f5e51e08daccb0571a645309da ,\r\n                        0x745eef734df24709934780992a2c2c60fadab9fd5817a2a0ca5dc009dcbb041f ,\r\n                        0x7402c4aee77fd85a4e09b0efd4114a5d79cb770a8c71c95eb8a37709cf8756e0 ,\r\n                        0xfba451164760e589a00056b1330db2c906b666414025e45f60365081c4c2d569 ,\r\n                        0x4894b1037d6cd8282409004f8bb0f947d1228e7f040b3e23c033499deed1836b ,\r\n                        0xd97ce43692ea1592c9d700418a20178910e8abc0d26121240a0ab242f4053040 ,\r\n                        0x2d259b4d904411f110a1a002b238fa54c5e16d0854400466e7a1671e7abb3878 ,\r\n                        0xbc87f3bf3783b51c880ca714ce030bce3e30040877f9de9b657cac54867da70e ,\r\n                        0x23e50ac9d917a05ef599bf7a108be04bb8e55b40f089a100dae205d01515aa09 ,\r\n                        0xd8d9065ab318800844723ef9b902c68d62015378abbca8064621655105dadb19 ,\r\n                        0xddd3cbe86895d74f5b4c852745f88a2ab37e17f1078700c6edf3db70ade953a7 ,\r\n                        0xb4e3ee4d0cefdb4e323d4976e50d10b3e4a64ec2a55d0f28462c28febab38040 ,\r\n                        0x2b1200add7c926c1884562cf89e244dd54c45fc6cf515d42900c50ad41750e06 ,\r\n                        0x7a3870bc97b7deba8ab56c3386c781379c0258671f881ca022428f08f7db8c63 ,\r\n                        0x6d5db0f7d421504332f10a9ad4416449f0515fe71bc0888beb62fd2a20f8791e ,\r\n                        0x3a729315f602921432200a40f62322add543dd18e4047e9c9e8646c286dddd8c ,\r\n                        0x6d8bf25ce05322ece37ddc3f18bf8f89dfa82a4f260dd8fdd1ad0c8d8fd2b87c ,\r\n                        0x966cfa9c5fedd320fe8307100dbd3bf06c0f7651f2f140bd2313b368fe083c5d ,\r\n                        0x5b1320c45e00085460be09b35518eae7e0ddbdbc717a0a6b596f84cf002f0235 ,\r\n                        0xbf8bf8d62580006df92edf2c657fcfa061cf4347c892e49af7936520c6835a04 ,\r\n                        0x5bac977f00f5e0e6ca80f1a408c0771abca21cc07b79e0ede188149e0bc076e7 ,\r\n                        0x1ef4b08d313d0b9d1dac1def66c7ae2adf7b3ea154e661e04f80694782e6ad4c ,\r\n                        0x80d8edf2dda1ca67b326ecba6f27bd9b86a99d7b0d3b7bd9b991977a0f562ef1 ,\r\n                        0x812a08105e1785b0f317dc5f09f8c6045e2e4b3de771959001f9f556ae514f61 ,\r\n                        0xa60a6b07d87f570fafbe7c8524a32f323c097c5761e1bd5681f8fd78b913f874 ,\r\n                        0xda64ac7fa4cc8e13fb49e7e7c9265fcb4b23e7651634500067e200f66058e7d9 ,\r\n                        0x84a103014788c2b6b0154a5724a0c6032da1674b0b6e0848f8b0e6f32048cccc ,\r\n                        0x415707035bbad8b9778e67bfd5c0547848842fa1fc31d0006ab71801bcf78bb0 ,\r\n                        0xcf5a3ea919dcf6c01e3ad7aca5f6c6f3e8c24c5ef661b061b3c74bbf143ddf93 ,\r\n                        0x037f1ff1e0fb3021a06e5c0101bcb49b5001bca787f21f2a40700a6138108134 ,\r\n                        0x5b5481f6210e1cebe6b5ef37a82554a2886744785695791706b25b87007ec1a7 ,\r\n                        0x1778346db27eedd64eb6ddb38f64e60ad9a53741c479552bcfcf8174a38638b8 ,\r\n                        0xeb50244140841c245909fe188f20ba14418a32df420dbce787d7d43368ae06f3 ,\r\n                        0x357a3676b2fb6095bff97a0363382ec209e002300fd46e150208f94e1fe1b0cd ,\r\n                        0x3825c0de87f652eeeaa4f6eab7d1a48e4406c186c0f9f8be44469fcf7d79e789 ,\r\n                        0x8312a8834350159a358f8cde38038c010d327f0255901b718530048487b58b2a ,\r\n                        0xd0d1c6de3b7b79f97b9354179038e62911bea6efe18726e2f7b0e9b35ee199a4 ,\r\n                        0x49cfa6ddfd8c1edd4d3275113b7d1e310609251b10c183ae4173c7035e04ddcd ,\r\n                        0x837c214fdb6b4d740218adadac02308be6234e705f8264d05b4bac5b5267be06 ,\r\n                        0xd5793ad77771dba10ebef6170b4411fb051e04de04ea4006e8cd4a80b0e57b4f ,\r\n                        0x96f1401cc3de87f71295629a13af814d31c638e5d6621cf76aa0ee5c7cf9570c ,\r\n                        0x1710c47d40f33fc3c5559d69a0732992809815848030d6ab5703b4453298732f ,\r\n                        0xc4389c87f5014ea9a6abd0d9ce9e3b7a78f1b9056666218e7852e02b0af34e05 ,\r\n                        0x9a3733010cd02579cbb74179fce83a36ec1f27b974069dbbecc00f651e2428f5 ,\r\n                        0xa408b20058ffbb300b473179ef75ae89ce36a1a918e7c1b2c232c008e0cd3728 ,\r\n                        0x83d2af65f2179a2cc70181461366e6681beae5c0d14ebef2a579a288ed029f02 ,\r\n                        0xce38154800bd0909e03fec20c2c7b294e39576d877721f6253d24baf01ea57fc ,\r\n                        0x9c19081b40451280faeba21e1c097bfd8d04661a50b388a3b6f818bce23e0006 ,\r\n                        0x3468ebc95221a155f927cb9519414208509d83ae4e761eeae185ef2c70f98a12 ,\r\n                        0xc73c0efc1f60de91a07933122076b17fb32a4f244dd8f1d18dacd9be89c6b997 ,\r\n                        0xd15abeda57f4727c281045d42b8384a400773da8f7330bb30da82660414c58ae ,\r\n                        0x8118b3e24ea07a0f4700d463a66147b0a5c7df202944a0b9d81c2aadebe7f663 ,\r\n                        0x9d7cf90fe70046043e0dbca430eb9b43370f0104a800bdaee5bbafb347d8f7f1 ,\r\n                        0x7dd85a95ecca19440c124a7c50cb8b6851fa4197ecfc79722c3461ba014d0543 ,\r\n                        0x71d5ceff2e0266df74ca59bac1e80f52bd840038ce7abda14545d122f993e079 ,\r\n                        0xb94e8f50c4a94007e3077a79fe5b0b5c9cb0c431a704fe173009d46e360294dc ,\r\n                        0x479d76aaf26496c09e87b6d0b7792db5d7bf0b49dd797f18df2d08180f7ad003 ,\r\n                        0xc05f477d4c6ea630edb65f03127bd025f4bc0af0c6b7e1d52fc2f8d33796104c ,\r\n                        0x7f1b9eff0d280306d060014841f10620122485266cfcb460463817814c617a16 ,\r\n                        0x3332c4a1e35dfccf2fcea2d023f094c07714ea4015c87ee404f8e583927fcd73 ,\r\n                        0x10f8549a30d6b73666d7fd7b49a72f63a7271063405cbcf7e0076420947d10fc ,\r\n                        0xde40035845661bf003cb000326045d969867c0177f020efe29acbb0db445392d ,\r\n                        0x11542fc273bf0d57ae78c120401b105b2403b014f81290409cb5e241ae8846a0 ,\r\n                        0x5683ea025bf7f6b0e19bf39c3993512af100f0c7c01fbd5b2de2f8dd6af9027b ,\r\n                        0xace5119bc29e13dbe91ae861e1e56f2136438cf1b15b0b6120977edfb75fa229 ,\r\n                        0x248851a825305387ba450c4804d20af4f0dc00cd3a7cedd721ba4e5268ddf3e5 ,\r\n                        0x40e6c331bf176efa9070bc6eb4f1dbc542324c5761631bb71fefbab6754c95b2 ,\r\n                        0x08cf00df0616de8d1671fc437abfe4bb7c111e4f9b0cafd9dccef67b76d1bc74 ,\r\n                        0x169dbf0acefb8122f898a277041b39c067f09264305b43e7528c7ae03dc0a1b5 ,\r\n                        0x90db128ba6e133d729aad4996848043fb74b84755d860422cb3fafeacbc2e979 ,\r\n                        0x36efee66746c9ed75f492995b91bb81f9878375ac4f10f05be6ffa1cb5964f88 ,\r\n                        0x857d0feca0ad3da2f6d669f05e0e01f85ee6c35d3c05b9af361693bc54890c48 ,\r\n                        0x14806c5a49bf8084a9790ba268d19b5b011f6e015690500d3c115aaa51eb35e5 ,\r\n                        0xd6ede29939e86ee7c0b11ede3a3d9573e369e0af81ea0fab02a2aa2b2540e4fa ,\r\n                        0xfde346f8e7499d8786c7bb79e89f7c9cecf21bd7167c248a8aedde10fca01fe0 ,\r\n                        0xc820402341aed6a066110113b500d084a0bbb15537c6c94ea3aecccf59920454 ,\r\n                        0xa154828e0e435ba7209203a9001ef8d0eb352783bfe72d2029013909af05d78d ,\r\n                        0xbf892af476c1402f7ff63b13bcf842937219807f01fc2a7001a87ffe59d5f74d ,\r\n                        0x017ee5a088e499bf70dc66dc6f0c1c38b91b636b34af9c47240aeb7a0c805a8f ,\r\n                        0x53117c039229ccd4a0da0405e3e57e19f03df001e8ee9a073f4d95c97319b3d3 ,\r\n                        0x20ed1152ea0204ad2fa0134d3ada61edc61f10c1402621e820eae72aa0f9a880 ,\r\n                        0x064a10821f4a7d9839b60a1f0273f3d0d3c1febbba79fd952b58af025f016680 ,\r\n                        0x1448de370288dfe3bf11e5e9ac4969cbc101466e1ba179e66fc1a6883101f84e ,\r\n                        0xe643f08d23c97c13aed6f2162ec6041975782ee1627c8beb0efca4a99c7d25c3 ,\r\n                        0x0ead67eda91fa76bcf49e2814380215b384dede52f31f517bfc65bafbec4c6ad ,\r\n                        0xd0d117054b2f1e740f78f1baf8b79a4c98142e158ec232b1459f40804c616a96 ,\r\n                        0xb5a3fdecbcadc273df69502eb315f25dc434df56e4ec6d15b0ef4b08f8d583d2 ,\r\n                        0x0eac3386bf632dbf68041efab9bb195a1bd178f345d7efd76263c7377af0cd1f ,\r\n                        0x2302cd04b9ba00f3595ed6618a1e1ecec5838c09e45d64c990a05639f36a42e9 ,\r\n                        0xd029d63dfd1f311d9b58f2b075aefcd92f30f307ff9a8da311e5f6a828f97e2c ,\r\n                        0x267d6afd33fec392ce82908437247027aea30c0aacef67eaaaf0c55fbf449282 ,\r\n                        0x315c04fe11f035e0f2db0468be7704f0e01ba0cf087b11fe7d5267dfae7bd673 ,\r\n                        0xfc9923d45f7f0e9a358c88270038f3af7861c058bb28f7330d240322888cc3cf ,\r\n                        0x380be722608adeedcfbd99625c050357cf35a86f3fc5f04ffc0f200e1bf96176 ,\r\n                        0xced4577e91c69ffc02c35bcbc5951f0f2a1e786fc1b90d1b04cb540472230450 ,\r\n                        0x2897607890bffac3299efd669d521980df017e11380b545d2ef0debc17f06b87 ,\r\n                        0x448ca1620cbd12f1b0da1fb47c61f7891d645317a0b1808878a957bcc7036274 ,\r\n                        0xd1161ae8f959986a2016881df8a1b74745e9371049e15cdc796e7ee9af785dd3 ,\r\n                        0x84a46f036b3efd4b0e7c5d1a7cefe50c9cf867989d2748169a10b97864c4ff37 ,\r\n                        0xb8b93fc79f3bc572cf2d0f7e68baccdca925730b1c38de4d6717580bc02780a3 ,\r\n                        0x2bfdd0845961cb7797c0e76c02e3c736d1bfa64272e92c22feddbeb0ed2b0648 ,\r\n                        0x5274720ebd308f6958c438cf2f2ea91a6f5eee0d44e4e70e14293c2701213c68 ,\r\n                        0x596a69bbe327897bc6e0469c431530f47cf41f928809424e0ebc7893e09a63b2 ,\r\n                        0xff7f0993d377428890a802d37374f71bf61c6c274d00e8063e070c031dae3a7b ,\r\n                        0xf709f06b87c5e49f751178d4a68c760f46ecfac83692c9b7c0a638e7cf41f709 ,\r\n                        0x11169d9947cf556126c1e0418e24907df1a307d6c7f9d0e3319e10de0bbdf745 ,\r\n                        0xdd9d74ecb88f777ab48d3e4869fd18603db02607d7b42042519902920248b86e ,\r\n                        0x2c374a029f20a6f61a09f6ddd1457fbf9065e01a43f73832945c8fe6dd258081 ,\r\n                        0x58842e11f6aaf2a8a6b0fbde513a2a4db2e9cb8818507ce2e770a0d6840bb370 ,\r\n                        0xa98e648a899c5217c18fc2844f4024907cefd50885fb1e7c4402b01469eb271e ,\r\n                        0x3cc23b3da4dc4669683360218a3ca05108b027215e313c318ae738136dad0a7a ,\r\n                        0x9db502233057a3a3c35edb3a96a5b932f35960c47f68e25d24c07f5ef4fe7623 ,\r\n                        0x0c18e109cd18e91fa930766403c9c41944c2582f90a6e8a5eaa2d72f644e29c3 ,\r\n                        0x2a2df07a83bfe9d8e13d8ea2e4424421ee17af2f9113acf43051f8e73b935071 ,\r\n                        0x5a78be276658c5b43c84e559a1ceaeceb1e7601b436b2427c131e063402f5079 ,\r\n                        0x5709204249a04b8443289f500bbb4f6ca1ac33d8fabc0bf0b9d72b76b6863d3b ,\r\n                        0x03534dd0e0b56a87bc31617917822f616c07a1300f3c3ef74c315ea2a30848a1 ,\r\n                        0x31c13b3e6c0acd2ac42588a282aa2c8ede026687a01743c44ab8a8b929fe10a8 ,\r\n                        0x2554ca09fb8e76e66100e0696033d0f9761888df1502fc97236204dac5b006e1 ,\r\n                        0xe934a567ed58179b77f6925c9a00915cf2a19160cfcf5c4bf2a4a91079e71501 ,\r\n                        0x72cc0024f47c02cf27f7b460de22f99225c0894b904ec3e45ff28e8fd99761e6 ,\r\n                        0x2d88cb0ef43807dd932ff47c598e14a1f4390be77a5d36f887af2eb06b778975 ,\r\n                        0x2311690ac07ee0e3400f50b9915c20fac217beb01cf8e2e464500c0fa9e5670c ,\r\n                        0x44471e19a3b7324fb630bfd8efb7169d9ac74ece2375ebd5d714f12a247d1ef4 ,\r\n                        0x20bbf7e007f13ef036035118fb4dc122772f0255486660ecb3704309b203e985 ,\r\n                        0x7f05135f87a82df06837c7f71ca015b072fded6104e3326de350964915d36628 ,\r\n                        0x759478fda566be66b201f81b600a681cf9992fd8952b8060103ac4b001f85cd6 ,\r\n                        0xa43cb2a38ff51b4b2433d3101974ae4e76661a7ba98e58efe5617b9b800861fb ,\r\n                        0x1623050096029f9051de4c94031fcc0d943b60f21bf0d22f01cbc65f0ff2c49f ,\r\n                        0xc36bbf03a5aec29f17a840140504f416a843a804d72f09951b2b570d3057677c ,\r\n                        0xcc30bc31cecbc231d7221e04dadc869d15af0594812e81fbd472ac5c869d77ad ,\r\n                        0x8385ab689aa2571660a69963e0710cf0c97193d0034c28f3f9c832e03b23f0fe ,\r\n                        0xe25f543404a27678fedf41b907c67e6a79125cfa2a7ce3e7c06610957d5b5700 ,\r\n                        0x2ca8018b2f0fd5820858ebaf61c1e28f08c8fc6e1fd440abb6bd86e375b69567 ,\r\n                        0x1693d6b9fd8e3217cfa5f972f127813f05ae709d7faeceb494ffa312031d46d8 ,\r\n                        0x242ceef3db7c6080a1a194e6d9cba46fce60a79ace61c3b763c284d7cfc35adf ,\r\n                        0x5b28fd21f84176ef465f7e798f5c6463e4ee4750aa0002dffa79f8eb9f84cb5f ,\r\n                        0x83aceeffa917db84ea4bf0dccfc35ffd04d4af42a9c37979e446fff784e106f1 ,\r\n                        0xe741ccf716c9d20d22de81e76b8b9714e713b66e808d5be23c17580f3c06f479 ,\r\n                        0x1578070af09b47450cb4017d029fb01907dbba0ce3bbda68bef016d96cb28843 ,\r\n                        0x0cd2fa2d593f3a5b7ee52e071d0fbe84e007730c10946378f220c56b314819ce ,\r\n                        0x7c192efe25746f868e1100685c86ea1968ce80695f041f7532ab7eaee246a704 ,\r\n                        0xaa60ad7f89b0b85e60d46f5b2fd6f05640148cf5aae087900cd7ef1508a002cd ,\r\n                        0x26870ec59c3f93ab008f005f02fe1c6838bb2e01c2af79ee049eb616368fc574 ,\r\n                        0x5cbe445a4d30a596cbf0cb7bbfb742cc2494764f04c2d8590437027cebb54814 ,\r\n                        0xbf6fcc93c87b620f60a17a16aa6f02f9fd18cabd8086df8a05eb46114f046b0b ,\r\n                        0x926efd2a619401b9c4e7a03b425803c60278c258966f516b0b36848962c3b271 ,\r\n                        0xad65742ce6d59752ca657a5c59f802507b5b05d2cf3fabd93204f0de9f7fcb57 ,\r\n                        0x84c76cc65857afb079c892cda7485474def0bdf91bf07e5a248112947f84c01b ,\r\n                        0x07a0f1f26edcf53019448a2ddb2524288628dc8481071e67e247d48215770e90 ,\r\n                        0x039f5f078c7124b090a9270078f03301c1ab84027aa36b01ba7cf7284939b837 ,\r\n                        0xe6add3e42af0a0cb05a68005a03501c2051f603fcaa35661f388d06933b2002b ,\r\n                        0xfc3cc8fc972685b750fa7116001a26752604df998bc5fed9a8d81b00f2b9672a ,\r\n                        0x488b2c3cfc4a9905ac239182b505f004ac23a9cd0a2450888c937f0362c12864 ,\r\n                        0x4ed92c201a948f0aba1cd02d4384274a13860753c6c723befffd8c7299b26b11 ,\r\n                        0x3f0b54df5681a4a8022101f8ad3bc408b409f4018f59cbfaee0ed8d06bc9b2c0 ,\r\n                        0xeb0307465a63dbdafb25ecff07154018020c50bc1e1513c2700e22c19c9c1001 ,\r\n                        0x0942f0ad5fdfc738d015c4cb37d6cd2327f1a8bf660c6438b01d7930fef7a285 ,\r\n                        0xd00080570cf0b9466be76f7d3d55f6ef84d3a72149c10877011f05de742a506b ,\r\n                        0x49008118a103e110ca2915185d0f9518ac868a1d2840d8f708e51f3f86721faa ,\r\n                        0x80373c88841d360fb897fb9000c13d4c018c5659b83af0d559e6405650f1311f ,\r\n                        0x07a0ba24303f6c2efd7880d58703b26272a48117f9af4ddf38f8018f5358d3af ,\r\n                        0xec1815befb92522e01f004f0e74e059a5e053c01f8ed3bc4e41f76003e67859e ,\r\n                        0xbe4e181ef424465aefb866a9842fbc6f5abc4db944e3c0278421a8411b58cc12 ,\r\n                        0xe0c7c13dbfa8e3581732d60302a0ea8960732433d0dcfbbd9291018a5732cdbc ,\r\n                        0x4a21602507d72b9cf5a1c8ff0ed062a2b94c4e10f68c35004261df0ee5d537a0 ,\r\n                        0x994164d8aff071857328f5e2b7884dd1fb4d445714b1afd2673ed2b72166db06 ,\r\n                        0x28c5a0bc23f90fbba1de582e17b881b2d01324c8077cedefbb756e1e95164789 ,\r\n                        0x1c3162f7acbb669c85d7c4591483f156fc7b5d9f61e9bc0593cf03e286d97318 ,\r\n                        0x06af9b102e5f3aaa42aaf40fc0fedb84ae35313fc0b2d2613e630ca322741417 ,\r\n                        0x8af209514cc9c474899123fde383ddeb3b9a0ccecd90593001a81e584fecd6f2 ,\r\n                        0x1fc67c694182007cf05e4c90147a390f14c2277f1e74b36432d83a075040bdec ,\r\n                        0xa97a8fd6cc65f941cc8e1432007f0fc9000f3a99cf013000e1b7e605245081b0 ,\r\n                        0x0cd4ebae0ff9b9855de3c27c5f1f5aae3077696ecfe4dfce1c51c31b69421548 ,\r\n                        0xa148803271a92c3d7167bc6bcdeeadac5d388d9d036d21ef61ad8fbf177cf320 ,\r\n                        0x644e20fb047f50480882e5d51074231efc480a9e1e01de3b316eee8993b335f0 ,\r\n                        0x30ebb27817ffd58205a498f1e7a04bf0314a53a81014c81ce182770a103f8a82 ,\r\n                        0xe4234503bb84cceb32ed61f58685aed8b275b48babdddbe85a3f23d50bcfded9 ,\r\n                        0x5c48bf6c33ae028d9c8bfcfe093171c994a332831d6bfb86fb771ca4d4dd4796 ,\r\n                        0x854d9c1660b670ead6f21f32288889104aa7bb665a978646801cfc7c34ce4ace ,\r\n                        0xe28245c573773f0813fe37c5b0e3ce638896aa3ec24e2481f407521faa1f7e74 ,\r\n                        0xa086b6f4f5f0dbe28e7f6be32a03630718da7d989e919e6d71ccfac850f9d5fd ,\r\n                        0x62720540338492964da4bd95debeaeee6d77239726d1f415881dc14dc8b41615 ,\r\n                        0x95f8c11f2de41f598a3501e87ef49e2b2d424154042288e741ecf67f97f71eac ,\r\n                        0x977fcddc3960fcd4d7efc62f046910da5482e5165f438792e9d10a645cfc8997 ,\r\n                        0xf5eb87805021dae6a71858b381a46b94aee16faeaf9e9d1a3411650b9213009b ,\r\n                        0x2136d31843c5c491893bd691b58d20d6e311e078fd65ee50fea1d84f0f64d7f7 ,\r\n                        0xd8c184c12e24860791621217267806a2d8cd8b49a2f1c95e0882026abdfcdbcc ,\r\n                        0x8fc6cdb30cc8e729d874719e261e05323702583f18eb5bc0c6b81043580a82aa ,\r\n                        0x279b05b2166f1f5faf2c54a0a9b4a53344433b28770db547319d262256532480 ,\r\n                        0x05b5886024999b65e1fcf788a3294c19445b6fde31c1bda0e4f6bdfe58203685 ,\r\n                        0xed5beea1387273976d477e058fa8b444366fbc5747e55cb6830cdd5f4722ffbc ,\r\n                        0x940ade1f968541e72f8fdf6a1dc0a9274238b7e9e2dcba79f1992c27504e9604 ,\r\n                        0xac05ebc94496ff4617c72473cf3825ca2c64eaee3b03d0e29c60ee9b4b58684e ,\r\n                        0xbc48adfb79b2c61c12631024ac0254951463666a97af5e9afefe57472bbd5d98 ,\r\n                        0xd161e2c909504544b10624124c2c9848d0c85c3b97bc0b5b12879938b0170116 ,\r\n                        0x178b55047171534d84e43b777c3916947811604023b011a8bbae31a897768c14 ,\r\n                        0xdab5a963660438628975e6084514ac4de303a9162d736352f0fc45c0d4a688da ,\r\n                        0xc5f32c0537575b548ee2dc7aa0ac167a0a06acf86453f3d22a5f6cb28bf3d48d ,\r\n                        0x46030170b3c84f053099429ad168ab70a5ba40f3c52fb33079faaa98684135b3 ,\r\n                        0x450220826a46234b75da44c9b317bff18d23fddb47d0f6219addc2c2e40c0b17 ,\r\n                        0x6b34e72c22ea1c47176dc9c69606a14e96ee1486d70061e966839b86f1d39fb6 ,\r\n                        0x7a0f4bc2525a687568d811f4d7d0a01c53140d9374ab685851a2d8b09c27f807 ,\r\n                        0x298358aeeebef5cffb0819ec11f5bff1d74b1dd0bdbe8deee141ca8303c41397 ,\r\n                        0x99bbf832f393575e0573c5665913b0c57703f9dd7ba4b3d4c696f6bef830f044 ,\r\n                        0x5c8aee3651d496355292859464dea2be2710e675ad5f776fdd2072e3f2afc5b5 ,\r\n                        0xc67d99cd94b27c5c5c7ec5f53a7d96a024b31a4611370dce6d906af8b9035743 ,\r\n                        0x827835276c500624d080040894da854a774cd456223242526bbca6c6fcc6c274 ,\r\n                        0xf2d57a555fb219b33ffd1dd59c00fcd7e3124731fd711b5bca1dd1ae3896dda2 ,\r\n                        0x3a88d592a6164dfdc218848b6be1eaee125e2ec1bd904821f8c1b8fce7570564 ,\r\n                        0x39fc6fec250c0d26e1b5902c1e6c050d4810004b08588b67ac86e40848675b10 ,\r\n                        0xc39d0abe1b2eb1fcc0ac451632e4d5fa827da131675f499b4cfcf4b7b5112e06 ,\r\n                        0x65594a951a6f699ad5d2987362e84489c810fd816928e1cb6c8a6db5678016d7 ,\r\n                        0x96f7f410e3e044f9a18f566b302149422f0d9fd55029fc5c83794ba0fd79408a ,\r\n                        0x16d7c3df983c8552158355b491253a95264ca429532849f87a78ae0202c49150 ,\r\n                        0x1143bb08a5e2dbddb28c079b50defddc9daf9400e1b5f7c6b4656808c00f49f3 ,\r\n                        0x0e48a0ad14a405217c82bf7c0808096073207c829fa952b796ba2ac9dbd21fac ,\r\n                        0x063a7bfaebaa40f27bc72515cb820a4200d82d71ac9a27a2af18f5a7beadb6d5 ,\r\n                        0x072256ed436a860fdbb16aab0458b55502acda2a01566d9500abc6ff07a28bfb ,\r\n                        0x046aa9574b0000000049454e44ae426082\r\n                    End\r\n\r\n                    LayoutCachedLeft =11520\r\n                    LayoutCachedTop =240\r\n                    LayoutCachedWidth =12360\r\n                    LayoutCachedHeight =1140\r\n                    TabIndex =5\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =215\r\n                    Left =7140\r\n                    Top =360\r\n                    Width =3960\r\n                    Height =720\r\n                    FontSize =10\r\n                    Name =\"lblHeaderExplain\"\r\n                    Caption =\"WARNING: The following changes may be overwritten. Please select how you would \\015\"\r\n                        \"\\012like to proceed.\"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =7140\r\n                    LayoutCachedTop =360\r\n                    LayoutCachedWidth =11100\r\n                    LayoutCachedHeight =1080\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin CommandButton\r\n                    Cancel = NotDefault\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =7920\r\n                    Top =7740\r\n                    Width =2100\r\n                    Height =540\r\n                    TabIndex =1\r\n                    Name =\"cmdCancel\"\r\n                    Caption =\"  Cancel\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    HorizontalAnchor =1\r\n                    VerticalAnchor =1\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000020202030000800900000004000080010 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000010 ,\r\n                        0x0000000000000000000000001010100010181080101010ff202020c010081020 ,\r\n                        0x1010100000000000000000000000000000000000101010004038309010101030 ,\r\n                        0x3030200000000000000000000000000020202050201810ff302820ff30282080 ,\r\n                        0x100810100000000000000000000000000008000030282020202820e010101020 ,\r\n                        0x3030200000000000000000000000000040303010202020a0201820f0303030e0 ,\r\n                        0x3030303000000000000000001010101010081010303030d02020209020202000 ,\r\n                        0x000000000000000000000000000000000000000030283020202020d0202020ff ,\r\n                        0x303030c020182010101010101010101020202090303030f03028202000000000 ,\r\n                        0x00000000000000000000000000000000000000000000000030303020302820d0 ,\r\n                        0x302820ff302830b020181030202020b0303030ff302820700000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000030383020 ,\r\n                        0x302820d0303030ff303030ff403830ff302830e0000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x30303040303030ff403840ff303030e030202010000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000004040400040404030 ,\r\n                        0x404040d0403830ff303030f0303030f0302820b0302820100000000000000000 ,\r\n                        0x00000000000000000000000000000000000000004040400040404070404040ff ,\r\n                        0x404040ff505050a04038403040384090404040ff303030903028201000000000 ,\r\n                        0x0000000000000000000000004040402040404040404040b0404040ff404040ff ,\r\n                        0x505050b0505050100000000040383010504840b0404040ff3038309030303010 ,\r\n                        0x00000000000000004040400040404060404040b0403830ff404040ff50485080 ,\r\n                        0x504850000000000000000000000000003028201040404040504850f0404040a0 ,\r\n                        0x00000000000000004040400040484060404040ff404040f05048505050505000 ,\r\n                        0x0000000000000000000000000000000000000000404040005050502040484070 ,\r\n                        0x00000000000000000000000050485030504850c0504850405048500000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n\r\n                    LayoutCachedLeft =7920\r\n                    LayoutCachedTop =7740\r\n                    LayoutCachedWidth =10020\r\n                    LayoutCachedHeight =8280\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin Subform\r\n                    OverlapFlags =85\r\n                    Left =540\r\n                    Top =2400\r\n                    Width =11880\r\n                    Height =5100\r\n                    TabIndex =2\r\n                    Name =\"sfrmConflictList\"\r\n                    SourceObject =\"Form.frmVCSConflictList\"\r\n                    HorizontalAnchor =2\r\n                    VerticalAnchor =2\r\n\r\n                    LayoutCachedLeft =540\r\n                    LayoutCachedTop =2400\r\n                    LayoutCachedWidth =12420\r\n                    LayoutCachedHeight =7500\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            Left =960\r\n                            Top =1800\r\n                            Width =9240\r\n                            Height =360\r\n                            FontSize =14\r\n                            FontWeight =700\r\n                            ForeColor =5324600\r\n                            Name =\"lblHeading\"\r\n                            Caption =\"These source files have changed since the last export\"\r\n                            LayoutCachedLeft =960\r\n                            LayoutCachedTop =1800\r\n                            LayoutCachedWidth =10200\r\n                            LayoutCachedHeight =2160\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin CommandButton\r\n                    FontUnderline = NotDefault\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =2280\r\n                    Top =7680\r\n                    Width =1620\r\n                    TabIndex =3\r\n                    Name =\"cmdOverwriteAll\"\r\n                    Caption =\"Overwrite All\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    VerticalAnchor =1\r\n                    BackStyle =0\r\n\r\n                    CursorOnHover =1\r\n                    LayoutCachedLeft =2280\r\n                    LayoutCachedTop =7680\r\n                    LayoutCachedWidth =3900\r\n                    LayoutCachedHeight =8040\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =5324600\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverThemeColorIndex =10\r\n                    HoverTint =100.0\r\n                    PressedThemeColorIndex =10\r\n                    PressedShade =100.0\r\n                    HoverForeThemeColorIndex =10\r\n                    HoverForeTint =100.0\r\n                    PressedForeThemeColorIndex =10\r\n                    PressedForeTint =100.0\r\n                End\r\n                Begin CommandButton\r\n                    FontUnderline = NotDefault\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =960\r\n                    Top =7680\r\n                    Width =1080\r\n                    TabIndex =4\r\n                    Name =\"cmdSkipAll\"\r\n                    Caption =\"Skip All\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    VerticalAnchor =1\r\n                    BackStyle =0\r\n\r\n                    CursorOnHover =1\r\n                    LayoutCachedLeft =960\r\n                    LayoutCachedTop =7680\r\n                    LayoutCachedWidth =2040\r\n                    LayoutCachedHeight =8040\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =5324600\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverThemeColorIndex =10\r\n                    HoverTint =100.0\r\n                    PressedThemeColorIndex =10\r\n                    PressedShade =100.0\r\n                    HoverForeThemeColorIndex =10\r\n                    HoverForeTint =100.0\r\n                    PressedForeThemeColorIndex =10\r\n                    PressedForeTint =100.0\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmVCSConflict.cls\"\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSConflict.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdCancel_Click\r\n' Author    : Adam Waller\r\n' Date      : 2/4/2021\r\n' Purpose   : Close form if user presses the escape key. (Cancel property = True)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdCancel_Click()\r\n    DoCmd.Close acForm, Me.Name\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdContinue_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : User selected to continue. Close and process conflicts.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdContinue_Click()\r\n\r\n    Dim lngRemaining As Long\r\n\r\n    lngRemaining = ActionDecisionsNeeded\r\n    If lngRemaining > 0 Then\r\n        MsgBox2 \"Please Resolve Conflicts\", _\r\n            lngRemaining & \" item(s) need to be resolved to continue.\", _\r\n            \"You can cancel this operation if you need to do further review.\", vbInformation\r\n    Else\r\n        VCSIndex.Conflicts.ApproveResolutions = True\r\n        DoCmd.Close acForm, Me.Name\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Load\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : Setting the control source causes delayed display. This way the display\r\n'           : is instant when the form is opened.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Load()\r\n\r\n    ' Load translations\r\n    Translation.ApplyTo Me\r\n\r\n    ' Display version (better performance than bound control)\r\n    lblVersion.Caption = Replace(lblVersion.Caption, \"${version}\", GetVCSVersion())\r\n\r\n    Dim frmList As Form_frmVCSConflictList\r\n    Set frmList = Me.sfrmConflictList.Form\r\n\r\n    ' Update heading caption\r\n    If Log.OperationType = eotExport Then\r\n        lblHeading.Caption = \"These source files have changed since the last export\"\r\n        With frmList.cboResolution\r\n            .AddItem eResolveConflict.ercSkip & \";\" & \"Skip\"\r\n            .AddItem eResolveConflict.ercOverwrite & \";\" & \"Overwrite source file\"\r\n        End With\r\n    Else\r\n        lblHeading.Caption = \"These database objects have changed since the last export\"\r\n        With frmList.cboResolution\r\n            .AddItem eResolveConflict.ercSkip & \";\" & \"Skip\"\r\n            .AddItem eResolveConflict.ercOverwrite & \";\" & \"Overwrite database object\"\r\n        End With\r\n    End If\r\n\r\n    ' Change to resizable form\r\n    MakeDialogResizable Me\r\n\r\n    ' Set initial column size\r\n    DoEvents\r\n    Form_Resize\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Resize\r\n' Author    : Adam Waller\r\n' Date      : 5/16/2023\r\n' Purpose   : Adjust column widths on subform.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Resize()\r\n    ScaleColumns Me.sfrmConflictList.Form, , _\r\n        Array(\"txtObjectDate\", \"txtFileDate\", \"txtDiff\")\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdOverwriteAll_Click\r\n' Author    : Adam Waller\r\n' Date      : 11/1/2021\r\n' Purpose   : Overwrite all items in the list\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdOverwriteAll_Click()\r\n    sfrmConflictList.SetFocus\r\n    CodeDb.Execute \"update tblConflicts set Resolution=\" & eResolveConflict.ercOverwrite, dbFailOnError\r\n    sfrmConflictList.Requery\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdSkipAll_Click\r\n' Author    : Adam Waller\r\n' Date      : 11/1/2021\r\n' Purpose   : Skip all items in the list\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdSkipAll_Click()\r\n    sfrmConflictList.SetFocus\r\n    CodeDb.Execute \"update tblConflicts set Resolution=\" & eResolveConflict.ercSkip, dbFailOnError\r\n    sfrmConflictList.Requery\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : NeedsActionDecision\r\n' Author    : Adam Waller\r\n' Date      : 2/22/2023\r\n' Purpose   : Count how many records need a decision before we can continue.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ActionDecisionsNeeded() As Long\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n\r\n    Set dbs = CodeDb\r\n    Set rst = dbs.OpenRecordset( _\r\n        \"select count(*) as Remaining from (select id from tblConflicts where NZ(Resolution)=0)\", _\r\n        dbOpenDynaset, dbOpenForwardOnly, dbReadOnly)\r\n\r\n    ActionDecisionsNeeded = Nz(rst!Remaining)\r\n    rst.Close\r\n    Set rst = Nothing\r\n    Set dbs = Nothing\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSConflictList.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    RecordSelectors = NotDefault\r\n    NavigationButtons = NotDefault\r\n    AllowDeletions = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowAdditions = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =2\r\n    ViewsAllowed =2\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =5040\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =31\r\n    Left =5235\r\n    Top =2490\r\n    Right =17115\r\n    Bottom =7575\r\n    RecSrcDt = Begin\r\n        0x9bf1b7f2f3a6e540\r\n    End\r\n    RecordSource =\"tblConflicts\"\r\n    DatasheetFontName =\"Calibri\"\r\n    AllowFormView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin ComboBox\r\n            AddColon = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AllowValueListEdits =1\r\n            InheritValueList =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =2\r\n            ForeShade =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin EmptyCell\r\n            Height =240\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =4117\r\n            Name =\"Detail\"\r\n            AutoHeight =1\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin TextBox\r\n                    Locked = NotDefault\r\n                    OverlapFlags =85\r\n                    TextAlign =1\r\n                    IMESentenceMode =3\r\n                    Left =1695\r\n                    Top =360\r\n                    Width =2625\r\n                    Height =360\r\n                    ColumnWidth =1820\r\n                    LeftMargin =44\r\n                    TopMargin =22\r\n                    RightMargin =44\r\n                    BottomMargin =22\r\n                    Name =\"txtComponent\"\r\n                    ControlSource =\"Component\"\r\n                    GroupTable =1\r\n                    BottomPadding =150\r\n\r\n                    LayoutCachedLeft =1695\r\n                    LayoutCachedTop =360\r\n                    LayoutCachedWidth =4320\r\n                    LayoutCachedHeight =720\r\n                    ColumnStart =1\r\n                    ColumnEnd =1\r\n                    LayoutGroup =1\r\n                    GroupTable =1\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextAlign =1\r\n                            Left =360\r\n                            Top =360\r\n                            Width =1275\r\n                            Height =360\r\n                            LeftMargin =44\r\n                            TopMargin =22\r\n                            RightMargin =44\r\n                            BottomMargin =22\r\n                            Name =\"Label3\"\r\n                            Caption =\"Component\"\r\n                            GroupTable =1\r\n                            BottomPadding =150\r\n                            LayoutCachedLeft =360\r\n                            LayoutCachedTop =360\r\n                            LayoutCachedWidth =1635\r\n                            LayoutCachedHeight =720\r\n                            LayoutGroup =1\r\n                            GroupTable =1\r\n                        End\r\n                    End\r\n                End\r\n                Begin TextBox\r\n                    Locked = NotDefault\r\n                    OverlapFlags =85\r\n                    TextAlign =1\r\n                    IMESentenceMode =3\r\n                    Left =1695\r\n                    Top =900\r\n                    Width =2625\r\n                    Height =360\r\n                    ColumnWidth =2755\r\n                    TabIndex =1\r\n                    LeftMargin =44\r\n                    TopMargin =22\r\n                    RightMargin =44\r\n                    BottomMargin =22\r\n                    Name =\"txtFileName\"\r\n                    ControlSource =\"FileName\"\r\n                    GroupTable =1\r\n                    BottomPadding =150\r\n                    AggregateType =2\r\n\r\n                    LayoutCachedLeft =1695\r\n                    LayoutCachedTop =900\r\n                    LayoutCachedWidth =4320\r\n                    LayoutCachedHeight =1260\r\n                    RowStart =1\r\n                    RowEnd =1\r\n                    ColumnStart =1\r\n                    ColumnEnd =1\r\n                    LayoutGroup =1\r\n                    GroupTable =1\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextAlign =1\r\n                            Left =360\r\n                            Top =900\r\n                            Width =1275\r\n                            Height =360\r\n                            LeftMargin =44\r\n                            TopMargin =22\r\n                            RightMargin =44\r\n                            BottomMargin =22\r\n                            Name =\"Label6\"\r\n                            Caption =\"FileName\"\r\n                            GroupTable =1\r\n                            BottomPadding =150\r\n                            LayoutCachedLeft =360\r\n                            LayoutCachedTop =900\r\n                            LayoutCachedWidth =1635\r\n                            LayoutCachedHeight =1260\r\n                            RowStart =1\r\n                            RowEnd =1\r\n                            LayoutGroup =1\r\n                            GroupTable =1\r\n                        End\r\n                    End\r\n                End\r\n                Begin TextBox\r\n                    Locked = NotDefault\r\n                    OverlapFlags =85\r\n                    TextAlign =1\r\n                    IMESentenceMode =3\r\n                    Left =1695\r\n                    Top =1440\r\n                    Width =2625\r\n                    Height =360\r\n                    ColumnWidth =2235\r\n                    TabIndex =2\r\n                    LeftMargin =44\r\n                    TopMargin =22\r\n                    RightMargin =44\r\n                    BottomMargin =22\r\n                    Name =\"txtObjectDate\"\r\n                    ControlSource =\"ObjectDate\"\r\n                    GroupTable =1\r\n                    BottomPadding =150\r\n\r\n                    LayoutCachedLeft =1695\r\n                    LayoutCachedTop =1440\r\n                    LayoutCachedWidth =4320\r\n                    LayoutCachedHeight =1800\r\n                    RowStart =2\r\n                    RowEnd =2\r\n                    ColumnStart =1\r\n                    ColumnEnd =1\r\n                    LayoutGroup =1\r\n                    GroupTable =1\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextAlign =1\r\n                            Left =360\r\n                            Top =1440\r\n                            Width =1275\r\n                            Height =360\r\n                            LeftMargin =44\r\n                            TopMargin =22\r\n                            RightMargin =44\r\n                            BottomMargin =22\r\n                            Name =\"Label9\"\r\n                            Caption =\"ObjectDate\"\r\n                            GroupTable =1\r\n                            BottomPadding =150\r\n                            LayoutCachedLeft =360\r\n                            LayoutCachedTop =1440\r\n                            LayoutCachedWidth =1635\r\n                            LayoutCachedHeight =1800\r\n                            RowStart =2\r\n                            RowEnd =2\r\n                            LayoutGroup =1\r\n                            GroupTable =1\r\n                        End\r\n                    End\r\n                End\r\n                Begin TextBox\r\n                    Locked = NotDefault\r\n                    OverlapFlags =85\r\n                    TextAlign =1\r\n                    IMESentenceMode =3\r\n                    Left =1695\r\n                    Top =1980\r\n                    Width =2625\r\n                    Height =360\r\n                    ColumnWidth =2235\r\n                    TabIndex =3\r\n                    LeftMargin =44\r\n                    TopMargin =22\r\n                    RightMargin =44\r\n                    BottomMargin =22\r\n                    Name =\"txtFileDate\"\r\n                    ControlSource =\"FileDate\"\r\n                    GroupTable =1\r\n                    BottomPadding =150\r\n\r\n                    LayoutCachedLeft =1695\r\n                    LayoutCachedTop =1980\r\n                    LayoutCachedWidth =4320\r\n                    LayoutCachedHeight =2340\r\n                    RowStart =3\r\n                    RowEnd =3\r\n                    ColumnStart =1\r\n                    ColumnEnd =1\r\n                    LayoutGroup =1\r\n                    GroupTable =1\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextAlign =1\r\n                            Left =360\r\n                            Top =1980\r\n                            Width =1275\r\n                            Height =360\r\n                            LeftMargin =44\r\n                            TopMargin =22\r\n                            RightMargin =44\r\n                            BottomMargin =22\r\n                            Name =\"Label12\"\r\n                            Caption =\"FileDate\"\r\n                            GroupTable =1\r\n                            BottomPadding =150\r\n                            LayoutCachedLeft =360\r\n                            LayoutCachedTop =1980\r\n                            LayoutCachedWidth =1635\r\n                            LayoutCachedHeight =2340\r\n                            RowStart =3\r\n                            RowEnd =3\r\n                            LayoutGroup =1\r\n                            GroupTable =1\r\n                        End\r\n                    End\r\n                End\r\n                Begin TextBox\r\n                    Locked = NotDefault\r\n                    FontUnderline = NotDefault\r\n                    IsHyperlink = NotDefault\r\n                    OverlapFlags =85\r\n                    TextAlign =1\r\n                    IMESentenceMode =3\r\n                    Left =1695\r\n                    Top =3060\r\n                    Width =2625\r\n                    Height =375\r\n                    ColumnWidth =1155\r\n                    TabIndex =5\r\n                    LeftMargin =44\r\n                    TopMargin =22\r\n                    RightMargin =44\r\n                    BottomMargin =22\r\n                    Name =\"txtDiff\"\r\n                    ControlSource =\"Diff\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    GroupTable =1\r\n                    BottomPadding =150\r\n\r\n                    LayoutCachedLeft =1695\r\n                    LayoutCachedTop =3060\r\n                    LayoutCachedWidth =4320\r\n                    LayoutCachedHeight =3435\r\n                    RowStart =5\r\n                    RowEnd =5\r\n                    ColumnStart =1\r\n                    ColumnEnd =1\r\n                    LayoutGroup =1\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    GroupTable =1\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextAlign =1\r\n                            Left =360\r\n                            Top =3060\r\n                            Width =1275\r\n                            Height =375\r\n                            LeftMargin =44\r\n                            TopMargin =22\r\n                            RightMargin =44\r\n                            BottomMargin =22\r\n                            Name =\"Label18\"\r\n                            Caption =\"Diff\"\r\n                            GroupTable =1\r\n                            BottomPadding =150\r\n                            LayoutCachedLeft =360\r\n                            LayoutCachedTop =3060\r\n                            LayoutCachedWidth =1635\r\n                            LayoutCachedHeight =3435\r\n                            RowStart =5\r\n                            RowEnd =5\r\n                            LayoutGroup =1\r\n                            GroupTable =1\r\n                        End\r\n                    End\r\n                End\r\n                Begin ComboBox\r\n                    RowSourceTypeInt =1\r\n                    OverlapFlags =85\r\n                    TextAlign =1\r\n                    IMESentenceMode =3\r\n                    ColumnCount =2\r\n                    ListWidth =2880\r\n                    Left =1695\r\n                    Top =2520\r\n                    Width =2625\r\n                    Height =360\r\n                    ColumnWidth =1379\r\n                    TabIndex =4\r\n                    Name =\"cboResolution\"\r\n                    ControlSource =\"Resolution\"\r\n                    RowSourceType =\"Value List\"\r\n                    ColumnWidths =\"0\"\r\n                    GroupTable =1\r\n                    BottomPadding =150\r\n                    LeftMargin =44\r\n                    TopMargin =22\r\n                    RightMargin =44\r\n                    BottomMargin =22\r\n\r\n                    LayoutCachedLeft =1695\r\n                    LayoutCachedTop =2520\r\n                    LayoutCachedWidth =4320\r\n                    LayoutCachedHeight =2880\r\n                    RowStart =4\r\n                    RowEnd =4\r\n                    ColumnStart =1\r\n                    ColumnEnd =1\r\n                    LayoutGroup =1\r\n                    ForeThemeColorIndex =0\r\n                    ForeTint =75.0\r\n                    ForeShade =100.0\r\n                    GroupTable =1\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextAlign =1\r\n                            Left =360\r\n                            Top =2520\r\n                            Width =1275\r\n                            Height =360\r\n                            LeftMargin =44\r\n                            TopMargin =22\r\n                            RightMargin =44\r\n                            BottomMargin =22\r\n                            Name =\"Label15\"\r\n                            Caption =\"Resolution\"\r\n                            GroupTable =1\r\n                            BottomPadding =150\r\n                            LayoutCachedLeft =360\r\n                            LayoutCachedTop =2520\r\n                            LayoutCachedWidth =1635\r\n                            LayoutCachedHeight =2880\r\n                            RowStart =4\r\n                            RowEnd =4\r\n                            LayoutGroup =1\r\n                            GroupTable =1\r\n                        End\r\n                    End\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmVCSConflictList.cls\"\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSConflictList.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : txtDiff_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Launch diff program to review changes.\r\n'           : Note that the changed file is not always the primary source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub txtDiff_Click()\r\n\r\n    Dim strTempFile As String\r\n    Dim strSourceFile As String\r\n    Dim strFileName As String\r\n    Dim cCont As IDbComponent\r\n    Dim dItems As Dictionary\r\n    Dim cItem As IDbComponent\r\n\r\n    ' Move focus back to resolution control\r\n    cboResolution.SetFocus\r\n    DoEvents\r\n\r\n    ' Make sure we have a valid tool defined\r\n    If Not (modObjects.Diff.HasValidCompareTool) Then\r\n        MsgBox2 \"No Compare Tool Defined\", _\r\n            \"Please specify a compare tool (i.e. WinMerge, VSCode) in the add-in options.\", , vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Make sure we have a file name to compare\r\n    strFileName = Nz(txtFileName)\r\n    If strFileName = vbNullString Then\r\n        MsgBox2 \"File name not found\", \"A file name is required to compare source files.\", , vbExclamation\r\n    Else\r\n        ' Build full path to source file\r\n        strSourceFile = Options.GetExportFolder & strFileName\r\n\r\n        ' Check for existing temp file\r\n        strTempFile = VCSIndex.GetTempExportFolder & strFileName\r\n        If Not FSO.FileExists(strTempFile) Then\r\n\r\n            ' Has not already been exported. Export a copy that we can use for the compare.\r\n            ' Try to find matching category and file\r\n            For Each cCont In GetContainers(ecfAllObjects)\r\n                If cCont.Category = Nz(txtComponent) Then\r\n                    Set dItems = cCont.GetAllFromDB(False)\r\n                    If cCont.SingleFile Then\r\n                        Set cItem = cCont\r\n                    Else\r\n                        If dItems.Exists(strFileName) Then\r\n                            Set cItem = dItems(strFileName)\r\n                        End If\r\n                    End If\r\n                    ' Build new export file name and export\r\n                    If Not cItem Is Nothing Then cItem.Export strTempFile\r\n                    Exit For\r\n                End If\r\n            Next cCont\r\n        End If\r\n\r\n        ' Show comparison if we were able to export a temp file\r\n        If Not FSO.FileExists(strTempFile) Then\r\n            MsgBox2 \"Unable to Diff Object\", \"Unable to produce a temporary diff file with the current database object.\", , vbExclamation\r\n        Else\r\n            If Not FSO.FileExists(strSourceFile) Then\r\n                MsgBox2 \"Source File Not Found\", \"Could not find the source file needed to diff this object:\", strSourceFile, vbExclamation\r\n            Else\r\n                ' Now that we have both files, diff the files for the user\r\n                If Log.OperationType = eotExport Then\r\n                    ' Show the database object as the modified version\r\n                    modObjects.Diff.Files strSourceFile, strTempFile\r\n                Else\r\n                    ' Show the source file as the modified version\r\n                    modObjects.Diff.Files strTempFile, strSourceFile\r\n                End If\r\n            End If\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSDatabase.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    PopUp = NotDefault\r\n    RecordSelectors = NotDefault\r\n    AutoCenter = NotDefault\r\n    NavigationButtons = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    ScrollBars =0\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =10800\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =263\r\n    Left =32025\r\n    Top =2430\r\n    Right =-14191\r\n    Bottom =14895\r\n    RecSrcDt = Begin\r\n        0x79e78b777268e540\r\n    End\r\n    Caption =\"Version Control System\"\r\n    DatasheetFontName =\"Calibri\"\r\n    OnLoad =\"[Event Procedure]\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Rectangle\r\n            SpecialEffect =3\r\n            BackStyle =0\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Line\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Image\r\n            BackStyle =0\r\n            OldBorderStyle =0\r\n            BorderLineStyle =0\r\n            SizeMode =3\r\n            PictureAlignment =2\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CommandButton\r\n            FontSize =11\r\n            FontWeight =400\r\n            FontName =\"Calibri\"\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =1\r\n            Gradient =12\r\n            BackThemeColorIndex =4\r\n            BackTint =60.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =4\r\n            BorderTint =60.0\r\n            ThemeFontIndex =1\r\n            HoverThemeColorIndex =4\r\n            HoverTint =40.0\r\n            PressedThemeColorIndex =4\r\n            PressedShade =75.0\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n        End\r\n        Begin CheckBox\r\n            BorderLineStyle =0\r\n            LabelX =230\r\n            LabelY =-30\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin OptionGroup\r\n            SpecialEffect =3\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin ListBox\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AllowValueListEdits =1\r\n            InheritValueList =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin ComboBox\r\n            AddColon = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AllowValueListEdits =1\r\n            InheritValueList =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =2\r\n            ForeShade =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Tab\r\n            TextFontFamily =18\r\n            FontSize =11\r\n            FontName =\"Cambria\"\r\n            ThemeFontIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =3\r\n            BackThemeColorIndex =1\r\n            BackShade =85.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =2\r\n            BorderTint =60.0\r\n            HoverThemeColorIndex =1\r\n            PressedThemeColorIndex =1\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n        End\r\n        Begin Page\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =7560\r\n            BackColor =15130848\r\n            Name =\"Detail\"\r\n            AlternateBackColor =15130848\r\n            Begin\r\n                Begin Line\r\n                    BorderWidth =5\r\n                    OverlapFlags =85\r\n                    Top =1080\r\n                    Width =10800\r\n                    BorderColor =15321539\r\n                    Name =\"Line10\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedTop =1080\r\n                    LayoutCachedWidth =10800\r\n                    LayoutCachedHeight =1080\r\n                    BorderThemeColorIndex =-1\r\n                End\r\n                Begin Rectangle\r\n                    SpecialEffect =0\r\n                    BackStyle =1\r\n                    OldBorderStyle =0\r\n                    OverlapFlags =93\r\n                    Width =10800\r\n                    Height =1020\r\n                    BackColor =5324600\r\n                    Name =\"Box1\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedWidth =10800\r\n                    LayoutCachedHeight =1020\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =215\r\n                    Left =540\r\n                    Top =240\r\n                    Width =6060\r\n                    Height =540\r\n                    FontSize =18\r\n                    FontWeight =700\r\n                    Name =\"lblOptions\"\r\n                    Caption =\"External Database Connection\"\r\n                    LayoutCachedLeft =540\r\n                    LayoutCachedTop =240\r\n                    LayoutCachedWidth =6600\r\n                    LayoutCachedHeight =780\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =7200\r\n                    Top =1500\r\n                    Width =3435\r\n                    Height =1140\r\n                    ForeColor =5324600\r\n                    Name =\"Label227\"\r\n                    Caption =\"This add-in provides limited support for exporting object definitions from exter\"\r\n                        \"nal databases for use in version control.\"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =7200\r\n                    LayoutCachedTop =1500\r\n                    LayoutCachedWidth =10635\r\n                    LayoutCachedHeight =2640\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin TextBox\r\n                    OverlapFlags =215\r\n                    IMESentenceMode =3\r\n                    Left =720\r\n                    Top =1740\r\n                    Width =3720\r\n                    Height =315\r\n                    Name =\"txtName\"\r\n                    HorizontalAnchor =2\r\n\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =1740\r\n                    LayoutCachedWidth =4440\r\n                    LayoutCachedHeight =2055\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =93\r\n                            Left =720\r\n                            Top =1440\r\n                            Width =2160\r\n                            Height =315\r\n                            ForeColor =5324600\r\n                            Name =\"Label244\"\r\n                            Caption =\"Connection Name:\"\r\n                            HorizontalAnchor =2\r\n                            LayoutCachedLeft =720\r\n                            LayoutCachedTop =1440\r\n                            LayoutCachedWidth =2880\r\n                            LayoutCachedHeight =1755\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin TextBox\r\n                    OverlapFlags =215\r\n                    IMESentenceMode =3\r\n                    Left =720\r\n                    Top =3300\r\n                    Width =6060\r\n                    Height =315\r\n                    TabIndex =4\r\n                    Name =\"txtDescription\"\r\n                    HorizontalAnchor =2\r\n\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =3300\r\n                    LayoutCachedWidth =6780\r\n                    LayoutCachedHeight =3615\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =93\r\n                            Left =720\r\n                            Top =3000\r\n                            Width =1920\r\n                            Height =315\r\n                            ForeColor =5324600\r\n                            Name =\"Label246\"\r\n                            Caption =\"Description:\"\r\n                            HorizontalAnchor =2\r\n                            LayoutCachedLeft =720\r\n                            LayoutCachedTop =3000\r\n                            LayoutCachedWidth =2640\r\n                            LayoutCachedHeight =3315\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin TextBox\r\n                    EnterKeyBehavior = NotDefault\r\n                    OverlapFlags =215\r\n                    IMESentenceMode =3\r\n                    Left =720\r\n                    Top =5220\r\n                    Width =6060\r\n                    Height =1380\r\n                    TabIndex =8\r\n                    Name =\"txtFilter\"\r\n                    HorizontalAnchor =2\r\n                    VerticalAnchor =2\r\n\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =5220\r\n                    LayoutCachedWidth =6780\r\n                    LayoutCachedHeight =6600\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =93\r\n                            Left =720\r\n                            Top =4920\r\n                            Width =4620\r\n                            Height =315\r\n                            ForeColor =5324600\r\n                            Name =\"Label250\"\r\n                            Caption =\"Filter for database objects (source file paths):\"\r\n                            LayoutCachedLeft =720\r\n                            LayoutCachedTop =4920\r\n                            LayoutCachedWidth =5340\r\n                            LayoutCachedHeight =5235\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin CommandButton\r\n                    Cancel = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =6540\r\n                    Top =6900\r\n                    Width =1500\r\n                    Height =420\r\n                    TabIndex =9\r\n                    Name =\"cmdCancel\"\r\n                    Caption =\"Cancel\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =1\r\n                    VerticalAnchor =1\r\n\r\n                    LayoutCachedLeft =6540\r\n                    LayoutCachedTop =6900\r\n                    LayoutCachedWidth =8040\r\n                    LayoutCachedHeight =7320\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    Left =8340\r\n                    Top =6900\r\n                    Width =1860\r\n                    Height =420\r\n                    TabIndex =10\r\n                    Name =\"cmdSaveAndClose\"\r\n                    Caption =\" Save && Close\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =1\r\n                    VerticalAnchor =1\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x000000000000000000000000d0687050c06860ffb05850ffa05050ffa05050ff ,\r\n                        0xa05050ff904850ff904840ff904840ff804040ff803840ff803840ff703840ff ,\r\n                        0x703830ff0000000000000000d06870fff09090ffe08080ffb04820ff403020ff ,\r\n                        0xc0b8b0ffc0b8b0ffd0c0c0ffd0c8c0ff505050ffa04030ffa04030ffa03830ff ,\r\n                        0x703840ff0000000000000000d07070ffff98a0fff08880ffe08080ff705850ff ,\r\n                        0x404030ff907870fff0e0e0fff0e8e0ff908070ffa04030ffa04040ffa04030ff ,\r\n                        0x803840ff0000000000000000d07870ffffa0a0fff09090fff08880ff705850ff ,\r\n                        0x000000ff404030fff0d8d0fff0e0d0ff807860ffb04840ffb04840ffa04040ff ,\r\n                        0x804040ff0000000000000000d07880ffffa8b0ffffa0a0fff09090ff705850ff ,\r\n                        0x705850ff705850ff705850ff706050ff806860ffc05850ffb05050ffb04840ff ,\r\n                        0x804040ff0000000000000000e08080ffffb0b0ffffb0b0ffffa0a0fff09090ff ,\r\n                        0xf08880ffe08080ffe07880ffd07070ffd06870ffc06060ffc05850ffb05050ff ,\r\n                        0x904840ff0000000000000000e08890ffffb8c0ffffb8b0ffd06060ffc06050ff ,\r\n                        0xc05850ffc05040ffb05030ffb04830ffa04020ffa03810ffc06060ffc05850ff ,\r\n                        0x904840ff0000000000000000e09090ffffc0c0ffd06860ffffffffffffffffff ,\r\n                        0xfff8f0fff0f0f0fff0e8e0fff0d8d0ffe0d0c0ffe0c8c0ffa03810ffc06060ff ,\r\n                        0x904850ff0000000000000000e098a0ffffc0c0ffd07070ffffffffffffffffff ,\r\n                        0xfffffffffff8f0fff0f0f0fff0e8e0fff0d8d0ffe0d0c0ffa04020ffd06860ff ,\r\n                        0xa05050ff0000000000000000f0a0a0ffffc0c0ffe07870ffffffffffffffffff ,\r\n                        0xfffffffffffffffffff8f0fff0f0f0fff0e8e0fff0d8d0ffb04830ffd07070ff ,\r\n                        0xa05050ff0000000000000000f0a8a0ffffc0c0ffe08080ffffffffffffffffff ,\r\n                        0xfffffffffffffffffffffffffff8f0fff0f0f0fff0e8e0ffb05030ffe07880ff ,\r\n                        0xa05050ff0000000000000000f0b0b0ffffc0c0fff08890ffffffffffffffffff ,\r\n                        0xfffffffffffffffffffffffffffffffffff8f0fff0f0f0ffc05040ff603030ff ,\r\n                        0xb05850ff0000000000000000f0b0b0ffffc0c0ffff9090ffffffffffffffffff ,\r\n                        0xfffffffffffffffffffffffffffffffffffffffffff8f0ffc05850ffb05860ff ,\r\n                        0xb05860ff0000000000000000f0b8b0fff0b8b0fff0b0b0fff0b0b0fff0a8b0ff ,\r\n                        0xf0a0a0ffe098a0ffe09090ffe09090ffe08890ffe08080ffd07880ffd07870ff ,\r\n                        0xd07070ff00000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n\r\n                    LayoutCachedLeft =8340\r\n                    LayoutCachedTop =6900\r\n                    LayoutCachedWidth =10200\r\n                    LayoutCachedHeight =7320\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                End\r\n                Begin CommandButton\r\n                    FontUnderline = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =3720\r\n                    Top =6960\r\n                    Width =1560\r\n                    TabIndex =11\r\n                    Name =\"cmdSeeDocs\"\r\n                    Caption =\"See Docs...\"\r\n                    HyperlinkAddress =\"https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Documentation\"\r\n                    HorizontalAnchor =1\r\n                    VerticalAnchor =1\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x000000000000000000000000000000000000000000000000e0e8e000e0c8b000 ,\r\n                        0xe0d8d000e0d0c010e0d0c010d0d0c010d0d0c000d0d0d000e0e0e00000000000 ,\r\n                        0x0000000000000000000000000000000000000000f0e8e0009068303080582080 ,\r\n                        0x905010c0804820e0804820c0804810b06040108050381030d0c8c01000000000 ,\r\n                        0x000000000000000000000000e0780000e0a05010a0683070c08860f0e0c8b0ff ,\r\n                        0xf0f0f0fffffffffffffffffff0f0f0ffe0c8c0ffa07850c040301060d0c8c010 ,\r\n                        0xe0d8d0000000000000000000e0882000b0703070e0a880fffff0e0ffe0b8a0ff ,\r\n                        0xd08050ffc05820ffc05820ffd08050ffe0b8a0fff0e8e0ffb09070f050301060 ,\r\n                        0xd0c8c000e0e0e00000000000b0783030d09870f0fff0e0ffe0a890ffc05010ff ,\r\n                        0xc05010ffe0a890ffffffffffb04810ffb04810ffd0a080fff0f0e0ffa07050d0 ,\r\n                        0x50381030d0d0d000f0f0f000b0784080f0d8c0fff0c8b0ffe05820ffd05810ff ,\r\n                        0xd05010ffe08050ffe0a880ffc05010ffb04810ffb04810ffe0b8a0ffe0c8c0ff ,\r\n                        0x50401080d0d0d010f0f0f000d08040e0fff8f0fff09870fff06020ffe05820ff ,\r\n                        0xe05820fff0a890ffffffffffd05010ffc05010ffb05010ffc07850fff0f0f0ff ,\r\n                        0x804020c0e0d0c000f0f0f000d08040f0ffffffffff7840ffff6830fff06820ff ,\r\n                        0xf06020fff08850fffffffffff0c0b0ffc05820ffb05010ffb05820ffffffffff ,\r\n                        0x804820e0e0d0c010f0f0f000d08850f0ffffffffff8050ffff7030ffff6830ff ,\r\n                        0xff6830ffff6820fff09060fffff8f0fff0d8c0ffc05020ffc05820ffffffffff ,\r\n                        0x804820e0e0d8d010f0f0f000d08050c0fff8f0ffffa880ffff7040ffff8850ff ,\r\n                        0xffb090ffff7030fff06820fff09070fffffffffff08050ffd08860fffff0f0ff ,\r\n                        0x805820b0e0d8d010f0f0f000c0804070f0d8c0ffffd0c0ffff7840ffff9870ff ,\r\n                        0xffffffffffc8b0ffff9060ffffc8b0fffff8f0fff07840fff0c8b0ffe0c8b0ff ,\r\n                        0x90602070e0c8b00000000000c0884030e0a070f0fff8f0ffffc0a0ffff7840ff ,\r\n                        0xffb8a0fffff8f0fffffffffffff0e0ffff9870fff0b8a0fffff0e0ffc08850e0 ,\r\n                        0xa0682030f0e8e0000000000000000000c0884060e0b8a0f0fff8f0ffffd0c0ff ,\r\n                        0xffa880ffff8850ffff8850ffffa880fff0d0c0fffff0e0ffd0a880f0a0683060 ,\r\n                        0xe0c0a00000000000000000000000000000000000c0884060e0a070f0f0d8c0ff ,\r\n                        0xfff8f0fffffffffffffffffffff8f0fff0d8c0ffc09060e0a0703050f0b89000 ,\r\n                        0x0000000000000000000000000000000000000000f0f0f000c0884030c0804070 ,\r\n                        0xe0a070c0d09870e0d09860f0d09870d0b0784070b0784020f0e8f00000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0xf0f0f000f0f0f000f0f0f000f0f0f000f0f0f00000000000f0f0f00000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n                    BackStyle =0\r\n\r\n                    LayoutCachedLeft =3720\r\n                    LayoutCachedTop =6960\r\n                    LayoutCachedWidth =5280\r\n                    LayoutCachedHeight =7320\r\n                    PictureCaptionArrangement =4\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =240\r\n                    Top =7020\r\n                    Width =3375\r\n                    Height =240\r\n                    FontSize =10\r\n                    Name =\"Label32\"\r\n                    Caption =\"joyfullservice/msaccess-vcs-addin\"\r\n                    VerticalAnchor =1\r\n                    LayoutCachedLeft =240\r\n                    LayoutCachedTop =7020\r\n                    LayoutCachedWidth =3615\r\n                    LayoutCachedHeight =7260\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                    ForeShade =50.0\r\n                End\r\n                Begin ComboBox\r\n                    LimitToList = NotDefault\r\n                    RowSourceTypeInt =1\r\n                    OverlapFlags =215\r\n                    IMESentenceMode =3\r\n                    ColumnCount =2\r\n                    Left =720\r\n                    Top =2520\r\n                    Width =3720\r\n                    Height =315\r\n                    TabIndex =1\r\n                    Name =\"cboType\"\r\n                    RowSourceType =\"Value List\"\r\n                    RowSource =\"\\\"1\\\";\\\"Microsoft SQL Server\\\";\\\"2\\\";\\\"MySQL Server\\\"\"\r\n                    ColumnWidths =\"0\"\r\n                    HorizontalAnchor =2\r\n\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =2520\r\n                    LayoutCachedWidth =4440\r\n                    LayoutCachedHeight =2835\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =93\r\n                            Left =720\r\n                            Top =2220\r\n                            Width =2340\r\n                            Height =315\r\n                            ForeColor =5324600\r\n                            Name =\"Label252\"\r\n                            Caption =\"Database Type:\"\r\n                            LayoutCachedLeft =720\r\n                            LayoutCachedTop =2220\r\n                            LayoutCachedWidth =3060\r\n                            LayoutCachedHeight =2535\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =7200\r\n                    Top =5100\r\n                    Width =3420\r\n                    Height =1380\r\n                    ForeColor =5324600\r\n                    Name =\"Label253\"\r\n                    Caption =\"Filter paths to source files. Start line with ! to exclude match. Use * for wild\"\r\n                        \"card matches (LIKE comparison).\"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =7200\r\n                    LayoutCachedTop =5100\r\n                    LayoutCachedWidth =10620\r\n                    LayoutCachedHeight =6480\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin CommandButton\r\n                    FontUnderline = NotDefault\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =5580\r\n                    Top =4920\r\n                    Width =1140\r\n                    Height =240\r\n                    FontSize =10\r\n                    TabIndex =7\r\n                    Name =\"cmdTest\"\r\n                    Caption =\"Test Filter...\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =1\r\n                    BackStyle =0\r\n\r\n                    CursorOnHover =1\r\n                    LayoutCachedLeft =5580\r\n                    LayoutCachedTop =4920\r\n                    LayoutCachedWidth =6720\r\n                    LayoutCachedHeight =5160\r\n                    Alignment =1\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =5324600\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverThemeColorIndex =10\r\n                    HoverTint =100.0\r\n                    PressedThemeColorIndex =10\r\n                    PressedShade =100.0\r\n                    HoverForeThemeColorIndex =10\r\n                    HoverForeTint =100.0\r\n                    PressedForeThemeColorIndex =10\r\n                    PressedForeTint =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin CheckBox\r\n                    OverlapFlags =85\r\n                    Left =780\r\n                    Top =4470\r\n                    TabIndex =5\r\n                    Name =\"chkSaveDotEnv\"\r\n                    DefaultValue =\"False\"\r\n                    HorizontalAnchor =1\r\n\r\n                    LayoutCachedLeft =780\r\n                    LayoutCachedTop =4470\r\n                    LayoutCachedWidth =1040\r\n                    LayoutCachedHeight =4710\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            Left =1080\r\n                            Top =4440\r\n                            Width =1740\r\n                            Height =315\r\n                            ForeColor =5324600\r\n                            Name =\"Label257\"\r\n                            Caption =\"Save in .env file*\"\r\n                            HorizontalAnchor =1\r\n                            LayoutCachedLeft =1080\r\n                            LayoutCachedTop =4440\r\n                            LayoutCachedWidth =2820\r\n                            LayoutCachedHeight =4755\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin CheckBox\r\n                    OverlapFlags =85\r\n                    Left =4740\r\n                    Top =2190\r\n                    TabIndex =3\r\n                    Name =\"chkUtcDates\"\r\n                    DefaultValue =\"False\"\r\n                    HorizontalAnchor =1\r\n\r\n                    LayoutCachedLeft =4740\r\n                    LayoutCachedTop =2190\r\n                    LayoutCachedWidth =5000\r\n                    LayoutCachedHeight =2430\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            Left =5040\r\n                            Top =2160\r\n                            Width =1620\r\n                            Height =315\r\n                            ForeColor =5324600\r\n                            Name =\"Label259\"\r\n                            Caption =\"Use UTC Dates\"\r\n                            HorizontalAnchor =1\r\n                            LayoutCachedLeft =5040\r\n                            LayoutCachedTop =2160\r\n                            LayoutCachedWidth =6660\r\n                            LayoutCachedHeight =2475\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin CheckBox\r\n                    OverlapFlags =85\r\n                    Left =4740\r\n                    Top =1770\r\n                    TabIndex =2\r\n                    Name =\"chkEnabled\"\r\n                    DefaultValue =\"True\"\r\n                    HorizontalAnchor =1\r\n\r\n                    LayoutCachedLeft =4740\r\n                    LayoutCachedTop =1770\r\n                    LayoutCachedWidth =5000\r\n                    LayoutCachedHeight =2010\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            Left =5040\r\n                            Top =1740\r\n                            Width =1080\r\n                            Height =315\r\n                            ForeColor =5324600\r\n                            Name =\"Label261\"\r\n                            Caption =\"Enabled\"\r\n                            HorizontalAnchor =1\r\n                            LayoutCachedLeft =5040\r\n                            LayoutCachedTop =1740\r\n                            LayoutCachedWidth =6120\r\n                            LayoutCachedHeight =2055\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin ComboBox\r\n                    RowSourceTypeInt =1\r\n                    OverlapFlags =215\r\n                    IMESentenceMode =3\r\n                    Left =720\r\n                    Top =4080\r\n                    Width =6060\r\n                    Height =315\r\n                    TabIndex =6\r\n                    Name =\"cboConnect\"\r\n                    RowSourceType =\"Value List\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    HorizontalAnchor =2\r\n\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =4080\r\n                    LayoutCachedWidth =6780\r\n                    LayoutCachedHeight =4395\r\n                    ForeThemeColorIndex =0\r\n                    ForeTint =75.0\r\n                    ForeShade =100.0\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =93\r\n                            Left =720\r\n                            Top =3780\r\n                            Width =2790\r\n                            Height =315\r\n                            ForeColor =5324600\r\n                            Name =\"Label248\"\r\n                            Caption =\"ADO (OLE) Connection String:\"\r\n                            HorizontalAnchor =2\r\n                            LayoutCachedLeft =720\r\n                            LayoutCachedTop =3780\r\n                            LayoutCachedWidth =3510\r\n                            LayoutCachedHeight =4095\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin CommandButton\r\n                    FontUnderline = NotDefault\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =720\r\n                    Top =6660\r\n                    Width =1140\r\n                    Height =240\r\n                    FontSize =10\r\n                    TabIndex =12\r\n                    Name =\"cmdExamples\"\r\n                    Caption =\"Examples...\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    VerticalAnchor =1\r\n                    BackStyle =0\r\n\r\n                    CursorOnHover =1\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =6660\r\n                    LayoutCachedWidth =1860\r\n                    LayoutCachedHeight =6900\r\n                    Alignment =1\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =5324600\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverThemeColorIndex =10\r\n                    HoverTint =100.0\r\n                    PressedThemeColorIndex =10\r\n                    PressedShade =100.0\r\n                    HoverForeThemeColorIndex =10\r\n                    HoverForeTint =100.0\r\n                    PressedForeThemeColorIndex =10\r\n                    PressedForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =7200\r\n                    Top =2760\r\n                    Width =3195\r\n                    Height =1125\r\n                    FontWeight =700\r\n                    ForeColor =5324600\r\n                    Name =\"Label264\"\r\n                    Caption =\"Provide an ADO (OLE) connection string or check \\\"attempt conversion\\\" if you on\"\r\n                        \"ly have an ODBC string.\"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =7200\r\n                    LayoutCachedTop =2760\r\n                    LayoutCachedWidth =10395\r\n                    LayoutCachedHeight =3885\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =7200\r\n                    Top =3960\r\n                    Width =3420\r\n                    Height =1080\r\n                    ForeColor =5324600\r\n                    Name =\"Label266\"\r\n                    Caption =\"* If the connection string contains a password, please use the .env option, and \"\r\n                        \"exclude this file from version control.\"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =7200\r\n                    LayoutCachedTop =3960\r\n                    LayoutCachedWidth =10620\r\n                    LayoutCachedHeight =5040\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin CheckBox\r\n                    OverlapFlags =85\r\n                    Left =3000\r\n                    Top =4470\r\n                    TabIndex =13\r\n                    Name =\"chkAttemptADOConvert\"\r\n                    DefaultValue =\"True\"\r\n                    HorizontalAnchor =1\r\n\r\n                    LayoutCachedLeft =3000\r\n                    LayoutCachedTop =4470\r\n                    LayoutCachedWidth =3260\r\n                    LayoutCachedHeight =4710\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            Left =3300\r\n                            Top =4440\r\n                            Width =3840\r\n                            Height =315\r\n                            ForeColor =5324600\r\n                            Name =\"Label268\"\r\n                            Caption =\"Attempt Conversion to ADO (OLE) String\"\r\n                            HorizontalAnchor =1\r\n                            LayoutCachedLeft =3300\r\n                            LayoutCachedTop =4440\r\n                            LayoutCachedWidth =7140\r\n                            LayoutCachedHeight =4755\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmVCSDatabase.cls\"\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSDatabase.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' Store original name, just in case we rename an existing entry.\r\nPrivate m_strOriginalName As String\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadSchema\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Load a schema by name\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadSchema(strName As String, dSchema As Dictionary)\r\n\r\n    Dim dParams As Dictionary\r\n\r\n    ' Merge values into defaults to ensure that all values are defined\r\n    Set dParams = GetDefaults\r\n    MergeDictionary dParams, dSchema\r\n    MergeDotEnv strName, dParams\r\n\r\n    ' Save original name, just in case it is renamed\r\n    m_strOriginalName = strName\r\n\r\n    ' Load values from dictionary parameters\r\n    txtName = strName\r\n    chkEnabled = dParams(\"Enabled\")\r\n    cboType = dParams(\"DatabaseType\")\r\n    chkUtcDates = dParams(\"UtcDateTime\")\r\n    txtDescription = dParams(\"Description\")\r\n    txtFilter = ParseFilter(dParams(\"Filter\"))\r\n    cboConnect = dParams(\"Connect\")\r\n    chkSaveDotEnv = dParams(\"UseDotEnv\")\r\n    chkAttemptADOConvert = dParams(\"AttemptConvert\")\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ParseFilter\r\n' Author    : Adam Waller\r\n' Date      : 1/9/2024\r\n' Purpose   : Parse the filter\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ParseFilter(varValue As Variant) As String\r\n\r\n    Dim varItem As Variant\r\n\r\n    ' Convert collection to filter lines\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        For Each varItem In varValue\r\n            .Add CStr(varItem)\r\n        Next varItem\r\n        If .Length > 2 Then .Remove 2\r\n        ParseFilter = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergeDotEnv\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2023\r\n' Purpose   : Merge any specified .env params into dictionary. (Will override schema\r\n'           : options saved in vcs-options.json)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub MergeDotEnv(strSchemaName As String, ByRef dParams As Dictionary)\r\n    Dim strFile As String\r\n    strFile = BuildPath2(Options.GetExportFolder & \"databases\", GetSafeFileName(strSchemaName), \".env\")\r\n    If FSO.FileExists(strFile) Then\r\n        With New clsDotEnv\r\n            .LoadFromFile strFile\r\n            .MergeIntoDictionary dParams, False\r\n        End With\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveConnectionString\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Saves the connection string to a .env file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SaveConnectionStringToFile()\r\n\r\n    Dim strFile As String\r\n\r\n    ' Guard clause safety check\r\n    If Nz(txtName) = vbNullString Or Nz(cboConnect) = vbNullString Then Exit Sub\r\n\r\n    ' Update the value in the .env file. (Creating the file, if needed.)\r\n    strFile = BuildPath2(Options.GetExportFolder & \"databases\", GetSafeFileName(Nz(txtName)), \".env\")\r\n    With New clsDotEnv\r\n        ' Reload file so we preserve existing values\r\n        .LoadFromFile strFile\r\n        .SetVar \"CONNECT\", Nz(cboConnect)\r\n        .SaveToFile strFile\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cboConnect_Click\r\n' Author    : Adam Waller\r\n' Date      : 2/11/2025\r\n' Purpose   : Encourage the use of a .env file if passwords are identified in the\r\n'           : connection string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cboConnect_Click()\r\n    Dim strTest As String\r\n    strTest = Nz(cboConnect)\r\n    If Len(strTest) Then\r\n        If strTest Like \"*password=*\" Or strTest Like \"*pwd=*\" Then\r\n            ' Appears to have a saved password\r\n            If Not Me.chkSaveDotEnv Then\r\n                If MsgBox2(\"Use .env File for Connection String?\", _\r\n                    \"Using a .env file to store sensitive credentials is highly recommended \" & _\r\n                    \"when your connection string contains a password.\", _\r\n                    \"This .env file should be excluded from version control.\", vbQuestion + vbYesNo) = vbYes Then\r\n                    Me.chkSaveDotEnv = True\r\n                End If\r\n            End If\r\n        End If\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdCancel_Click\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Close this form\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdCancel_Click()\r\n    DoCmd.Close acForm, Me.Name\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdExamples_Click\r\n' Author    : Adam Waller\r\n' Date      : 8/2/2023\r\n' Purpose   : Show syntax examples on the Wiki\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdExamples_Click()\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdSaveAndClose_Click\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Save the schema back to the options form.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdSaveAndClose_Click()\r\n    If SaveConnection Then\r\n        Form_frmVCSOptions.RefreshSchemaList\r\n        DoCmd.Close acForm, Me.Name\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveConnection\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Save the current connection, return true if successful.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SaveConnection() As Boolean\r\n\r\n    Dim dSchema As Dictionary\r\n    Dim strKey As String\r\n\r\n    If Not PassedValidation Then Exit Function\r\n\r\n    If IsLoaded(acForm, \"frmVCSOptions\") Then\r\n        With Form_frmVCSOptions\r\n\r\n            ' Make sure we have a dictionary object\r\n            If .DatabaseSchemas Is Nothing Then Set .DatabaseSchemas = New Dictionary\r\n\r\n            ' Save to options form\r\n            With .DatabaseSchemas\r\n\r\n                ' Get a reference to dictionary object\r\n                strKey = Nz(txtName)\r\n                If Not .Exists(strKey) Then\r\n                    ' Could be a rename\r\n                    Set dSchema = New Dictionary\r\n                    .Add strKey, dSchema\r\n                    ' Remove any previous entry\r\n                    If Len(m_strOriginalName) Then\r\n                        If .Exists(m_strOriginalName) Then .Remove m_strOriginalName\r\n                    End If\r\n                End If\r\n\r\n                ' Load form values\r\n                SetParamsFromForm .Item(strKey)\r\n\r\n                ' Connection string\r\n                If chkSaveDotEnv Then\r\n                    CheckGitignoreDotEnv\r\n                    ' Save connection string to .env file\r\n                    SaveConnectionStringToFile\r\n                    ' Remove connect parameter from dictionary\r\n                    If .Item(strKey).Exists(\"Connect\") Then .Item(strKey).Remove \"Connect\"\r\n                End If\r\n            End With\r\n        End With\r\n\r\n        ' Return success\r\n        SaveConnection = True\r\n    Else\r\n        MsgBox2 \"Options form not found\", \"The Options form must be open to save changes to external database connections\", , vbExclamation\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetParamsFromForm\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2023\r\n' Purpose   : Sets the dictionary parameters based on the current form values.\r\n'           : (This is especially helpful for testing filters before saving an entry.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SetParamsFromForm(ByRef dParams As Dictionary)\r\n    With dParams\r\n        .Item(\"Enabled\") = CBool(chkEnabled)\r\n        .Item(\"DatabaseType\") = CInt(cboType)\r\n        .Item(\"Description\") = Nz(txtDescription)\r\n        .Item(\"UtcDateTime\") = CBool(chkUtcDates)\r\n        .Item(\"Connect\") = Nz(cboConnect)\r\n        .Item(\"UseDotEnv\") = CBool(chkSaveDotEnv)\r\n        .Item(\"AttemptConvert\") = CBool(chkAttemptADOConvert)\r\n        Set .Item(\"Filter\") = ToCollection(Nz(txtFilter))\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ToCollection\r\n' Author    : Adam Waller\r\n' Date      : 1/9/2024\r\n' Purpose   : Convert a string list of items to a collection. (Split on vbCrLf)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ToCollection(strFilter) As Collection\r\n\r\n    Dim colRules As Collection\r\n    Dim varLine As Variant\r\n\r\n    Set colRules = New Collection\r\n    For Each varLine In Split(strFilter, vbCrLf)\r\n        colRules.Add CStr(varLine)\r\n    Next varLine\r\n    Set ToCollection = colRules\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDefaults\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2023\r\n' Purpose   : Set the default values for connection parameters\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDefaults() As Dictionary\r\n    Set GetDefaults = New Dictionary\r\n    With GetDefaults\r\n        .CompareMode = TextCompare\r\n        .Item(\"Enabled\") = True\r\n        .Item(\"DatabaseType\") = 1\r\n        .Item(\"Description\") = vbNullString\r\n        .Item(\"UtcDateTime\") = False\r\n        .Item(\"Connect\") = vbNullString\r\n        .Item(\"UseDotEnv\") = True\r\n        .Item(\"AttemptConvert\") = True\r\n        Set .Item(\"Filter\") = New Collection\r\n    End With\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PassedValidation\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Return true if we pass validation on the form to save the entry.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function PassedValidation() As Boolean\r\n\r\n    Dim strMsg As String\r\n\r\n    ' TODO: Could add more validation for filter entries\r\n\r\n    If Len(Nz(cboConnect)) < 5 Then strMsg = \"Please select or enter a connection string for database\"\r\n    If Nz(cboType, -1) < 0 Then strMsg = \"Please select database type\"\r\n    If Len(Nz(txtName)) = 0 Then strMsg = \"Connection name is required\"\r\n\r\n    If Len(strMsg) Then\r\n        MsgBox2 \"Please fix validation issues to continue\", strMsg, \"See online wiki for additional documentation\", vbExclamation\r\n    Else\r\n        PassedValidation = True\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdTest_Click\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Test the current filter and return the number of objects found.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdTest_Click()\r\n\r\n    Dim cSchema As IDbSchema\r\n    Dim lngCount As Long\r\n    Dim dblStart As Double\r\n    Dim dParams As Dictionary\r\n\r\n    txtFilter.SetFocus\r\n    txtFilter.SelStart = 9999\r\n    If Not PassedValidation Then Exit Sub\r\n\r\n    Select Case cboType\r\n        Case eDatabaseServerType.estMsSql\r\n            Set cSchema = New clsSchemaMsSql\r\n        Case eDatabaseServerType.estMySql\r\n            Set cSchema = New clsSchemaMySql\r\n    End Select\r\n\r\n    ' Retrieve object count from server.\r\n    If Not cSchema Is Nothing Then\r\n        Set dParams = New Dictionary\r\n        dParams.CompareMode = TextCompare\r\n        SetParamsFromForm dParams\r\n        cSchema.Initialize dParams\r\n        dblStart = Perf.MicroTimer\r\n        DoCmd.Hourglass True\r\n        lngCount = cSchema.ObjectCount(False)\r\n        DoCmd.Hourglass False\r\n        MsgBox2 lngCount & \" Objects Found\", \"A total of \" & lngCount & \" database objects were retrieved in \" & _\r\n            Round(Perf.MicroTimer - dblStart, 2) & \" seconds.\", , vbInformation\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckGitignoreDotEnv\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2023\r\n' Purpose   : If the project appears to be a .git repository, check to see if .env\r\n'           : appears in the .gitignore file.\r\n'           : (This is not a comprehensive test, but just an extra aid for most common\r\n'           :  scenarios to help users avoid inadvertently comitting a .env file to\r\n'           :  their version control system.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub CheckGitignoreDotEnv()\r\n\r\n    Dim strPath As String\r\n    Dim strContent As String\r\n\r\n    ' Guess at the standard location for a .gitignore file\r\n    strPath = Options.GetExportFolder & \"..\\.gitignore\"\r\n    If FSO.FileExists(strPath) Then\r\n        strContent = ReadFile(strPath)\r\n        If Len(strContent) Then\r\n            If InStr(1, strContent, \".env\", vbTextCompare) = 0 Then\r\n                MsgBox2 \"Potentially Sensitive File\", _\r\n                    \"Please note: .env files should not be committed to version control.\", _\r\n                    \"To avoid exposing credentials to your repository, please exclude .env files in .gitignore\", _\r\n                    vbExclamation\r\n            End If\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Load\r\n' Author    : Adam Waller\r\n' Date      : 8/1/2023\r\n' Purpose   : Load in sample connection strings from the current database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Load()\r\n    Translation.ApplyTo Me\r\n    LoadSampleConnectionStrings\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadSampleConnectionStrings\r\n' Author    : Adam Waller\r\n' Date      : 8/1/2023\r\n' Purpose   : Loads in some sample connection strings from the current database (from\r\n'           : tables or pass-through queries) that can be selected by the user.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub LoadSampleConnectionStrings()\r\n\r\n    Dim dItems As Dictionary\r\n    Dim varKey As Variant\r\n    Dim varConn As Variant\r\n    Dim strCurrent As String\r\n\r\n    ' Take advantage of our connection class to retrieve some example\r\n    ' connection strings from the current database.\r\n    With New clsDbConnection\r\n        Set dItems = .GetDictionary\r\n    End With\r\n\r\n    ' Save current value, and rebuild list\r\n    strCurrent = Nz(cboConnect)\r\n    cboConnect.RowSource = vbNullString\r\n\r\n    ' Loop through the connection strings\r\n    If Not dItems Is Nothing Then\r\n        For Each varKey In dItems.Keys\r\n            For Each varConn In dItems(varKey)\r\n                If Len(varConn) > 10 Then\r\n                    ' Looks like a connection string. Add to list.\r\n                    cboConnect.AddItem \"\"\"\" & varConn & \"\"\"\"\r\n                End If\r\n            Next varConn\r\n        Next varKey\r\n    End If\r\n\r\n    ' Restore original value\r\n    cboConnect = strCurrent\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSInstall.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    PopUp = NotDefault\r\n    Modal = NotDefault\r\n    RecordSelectors = NotDefault\r\n    AutoCenter = NotDefault\r\n    NavigationButtons = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    ScrollBars =0\r\n    BorderStyle =3\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =68\r\n    Left =32025\r\n    Top =2430\r\n    Right =-14191\r\n    Bottom =14895\r\n    RecSrcDt = Begin\r\n        0x79e78b777268e540\r\n    End\r\n    Caption =\"MSAccessVCS\"\r\n    DatasheetFontName =\"Calibri\"\r\n    OnLoad =\"[Event Procedure]\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Rectangle\r\n            SpecialEffect =3\r\n            BackStyle =0\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Line\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Image\r\n            BackStyle =0\r\n            OldBorderStyle =0\r\n            BorderLineStyle =0\r\n            SizeMode =3\r\n            PictureAlignment =2\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CommandButton\r\n            FontSize =11\r\n            FontWeight =400\r\n            FontName =\"Calibri\"\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =1\r\n            Gradient =12\r\n            BackThemeColorIndex =4\r\n            BackTint =60.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =4\r\n            BorderTint =60.0\r\n            ThemeFontIndex =1\r\n            HoverThemeColorIndex =4\r\n            HoverTint =40.0\r\n            PressedThemeColorIndex =4\r\n            PressedShade =75.0\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n        End\r\n        Begin CheckBox\r\n            BorderLineStyle =0\r\n            LabelX =230\r\n            LabelY =-30\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Tab\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =3\r\n            BackThemeColorIndex =1\r\n            BackShade =85.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =2\r\n            BorderTint =60.0\r\n            HoverThemeColorIndex =1\r\n            PressedThemeColorIndex =1\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n        End\r\n        Begin Page\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin EmptyCell\r\n            Height =240\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =4740\r\n            BackColor =15130848\r\n            Name =\"Detail\"\r\n            AlternateBackColor =15130848\r\n            Begin\r\n                Begin Rectangle\r\n                    SpecialEffect =0\r\n                    BackStyle =1\r\n                    OldBorderStyle =0\r\n                    OverlapFlags =93\r\n                    Width =7200\r\n                    Height =1200\r\n                    BackColor =5324600\r\n                    Name =\"Box1\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedWidth =7200\r\n                    LayoutCachedHeight =1200\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =223\r\n                    Left =420\r\n                    Top =180\r\n                    Width =5340\r\n                    Height =540\r\n                    FontSize =18\r\n                    FontWeight =700\r\n                    Name =\"Label4\"\r\n                    Caption =\"Version Control System\"\r\n                    LayoutCachedLeft =420\r\n                    LayoutCachedTop =180\r\n                    LayoutCachedWidth =5760\r\n                    LayoutCachedHeight =720\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =215\r\n                    Left =600\r\n                    Top =720\r\n                    Width =2940\r\n                    Height =360\r\n                    FontSize =10\r\n                    Name =\"lblVersion\"\r\n                    Caption =\"Version 4.0.12\"\r\n                    LayoutCachedLeft =600\r\n                    LayoutCachedTop =720\r\n                    LayoutCachedWidth =3540\r\n                    LayoutCachedHeight =1080\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Image\r\n                    Left =5940\r\n                    Top =120\r\n                    Width =840\r\n                    Height =900\r\n                    Name =\"Image32\"\r\n                    Picture =\"adept_installer_7039.png\"\r\n                    ImageData = Begin\r\n                        0x89504e470d0a1a0a0000000d4948445200000080000000800806000000c33e61 ,\r\n                        0xcb00000006624b474400ff00ff00ffa0bda793000000097048597300002e2300 ,\r\n                        0x002e230178a53f760000000774494d4507e502031634093875488e0000200049 ,\r\n                        0x44415478daecbd5bac64d77926f6fd6badbd77ddebd4b9349ba7bbd94d8a1449 ,\r\n                        0x89a42c8d654ab2658fc71e27f1444e10c018c40e06986080c12089810448f232 ,\r\n                        0x2f7931f294413c406c2441e064063346666c27b6c7762c5b92259b43519444ea ,\r\n                        0xc216ef3cdd7deed7baedeb5aebcfc35a7bd7ae3aa7d9871425b235bd81dd7539 ,\r\n                        0x55d555fbffd6f7dfff05dc3bee1df78e7bc7bde3de71efb877dc3bee1df78e7b ,\r\n                        0xc7bde3de71efb877dc3bee1df78e7bc7bde3de71efb877dc3bee1df78e1fcd83 ,\r\n                        0x7e147ec45ffff5ee5a9665bf9065c9fdc6e8a9d666389da67b699a1de6b93926 ,\r\n                        0xa218209d65457ee3c67e76fdfaa606c0f7c4ff230080bffccb1b6b79ae7fb3d5 ,\r\n                        0x8a3ed7edaa504a1863b8280a9d696da6c6d884881280c65af36151983dadcd1e ,\r\n                        0x331f3363622d865956ecc771b69fe7fa9899136b8dcef3a2b87efdf50440f1a3 ,\r\n                        0x0c0075b7ff80c3c3e3cf75bbed5f520a419a1a1041ba5336c250f5852008b180 ,\r\n                        0x7a7227006b2d0aad6da6b58e8db109809419e33cd7270f3eb8b69d24c99ed6c5 ,\r\n                        0x31114d0139d4da1e4ea7e97651f0a1102295521a6b394f922c0590fdb07fff6b ,\r\n                        0xafbdf5a30f00662600815f8d75ea16bffffb2f7c1c3032cbb25c905024481011 ,\r\n                        0x88c469ba23f282a7120482081111454a05bd309c07cbf2720bccd57fc7d672a1 ,\r\n                        0xb5c98bc224d6da04a014a0a931f628cff55e96155bcc381142c4809868cd0771 ,\r\n                        0x9c6dc671b6478454086188b8b0d6662fbffc46f2415043f40115785414babfbb ,\r\n                        0x7b7c496bf3f8d1d1e489a3a3c9035b5bfb7ff4852f3cfb7f03b000f0f7fffeaf ,\r\n                        0x2c0f87e33f642e3e03c00a122495222925880484a0ea674a2921848494048293 ,\r\n                        0x3e81404238602c48a3040b79aa28ef13018e55a8c624e53bb97c2f5bcbc6189b ,\r\n                        0x1785498cb129808c195363ec304d8bfde934d9288aec18406a2d2645511c2449 ,\r\n                        0xbeb3bf7f744bca20ed74daa6d16815411066804acadfbc787ced6b5fbffb19c0 ,\r\n                        0x18d33a38185dcdf3e2a3a351f2d8b3cfbef298b5fc449a16ebccb22ba58ca246 ,\r\n                        0x9ffa7d7df9d2a5d53f05700200bbbbfbe1cacad2da030f3c0821209819cc80b5 ,\r\n                        0x065a6be47901ad0b686d600cc3981c5966aac70e000441c22f05775f05aa024b ,\r\n                        0x09a09255dc2dd599a1069212144c4450444205816c45519d7dd8bfb75b02c621 ,\r\n                        0x252ff234cdd3871e5a4b8490b91032b116c33c37bb4992de644edf1c0e8f5ed9 ,\r\n                        0xd8b8f1edef7def3b9b003400acaefe8dbb1b00bffddb5fe87ce10bdffe274110 ,\r\n                        0x7e8e4876a50c9a52b6a8d56aa0db552022586bc16c61b47ef4c2850b0f9400d0 ,\r\n                        0xda34a414ad280afc6a2f57208388ab955bae4e6b2dacb530c609c118465168e4 ,\r\n                        0xb93b8bc2c0180b630cb22c873106c61858cb90427ab6a83381845201a4141042 ,\r\n                        0xf8ff7f0616f79dc80bbd8212507d06c1dd28a954d0ecf5da4d2168e04154030b ,\r\n                        0x40049b65eb71afb7bcd5eb2dfddeeeeed66f01d8984eef7223b0d108fac604ff ,\r\n                        0xfefafae5fbc230049170175e6bb78a8b025a6b305b445134e874da4f01f81600 ,\r\n                        0x0c06fdfba5940da7b76d4de8ec8551bbf0c41002104240cdfdea1080f0aa87c0 ,\r\n                        0x4cb096612dcf01c5318a6395a2d0d0da01c5b18c863106cc8ea5859010e4d8a5 ,\r\n                        0x54135296609135d5e4594708cf2e00336a42a73a50041076d6d71ff870a3d1fe ,\r\n                        0xef85109fba79f3adff12c077ef7215e0f4b39401ac2558ab61ac813506c65a30 ,\r\n                        0x33180c6b0da49451a3d1fc789a26ff1c0094120f84a16a95d45a02c081011500 ,\r\n                        0xced2d3ceae74c070179caa938820a5f02a80fceb1ba5dde959e93450ac758c92 ,\r\n                        0x65058a2247516818e3c0a1b5469a663046c35aebbf2f20a513be140412f0ffb7 ,\r\n                        0x84520ace9699d9270e2402bd5e9f1e7ae8c37f33081aff6318767ff5c517df1a ,\r\n                        0xddbd006047cdda1848e1eeb3b5d5c5758fddad9412fd7eef63dffdee77fb0086 ,\r\n                        0x972f5fbea8946ccc53fd8c099cf0798e19e605ef56fc8c6e45f5396e25c2af4a ,\r\n                        0xaa81a9048c0342691096606e341a68b550314fb9a28d9957410e14058aa280d6 ,\r\n                        0x0e2ca5ed9265058c4960adf1b61f4308821402420a10c12a15f1daeafa4747e3 ,\r\n                        0x4917c0dd0b00066099618d0198c16cab0bc53c3bdd4ad368341a8fafacacdc7f ,\r\n                        0x787838d45af7959254a7fcb3847f3b10cc30483596a033dcc79940dd6b448d51 ,\r\n                        0x4a70d0dceb98b140f3ce2e901290720eff3e20818a451ca3cc80624ce1d9c419 ,\r\n                        0xb5c66891e71ac6a68a59dfdd36003383adf53a741e00d6981918ac455118341a ,\r\n                        0x8da5a5a5fe470e0f0fbf67adbda094bcad3b5d0bf8d4d403e654c05982ae83e8 ,\r\n                        0xb4b7fcce3de7996e3f457e0b8029d50f004810c1db2b4d58bb70cd18383adaa4 ,\r\n                        0xc9e4e0fbbafee2830000478b337d599db6b4c29d3d50141a428856a3d1fce43f ,\r\n                        0xfc87ff5993882e2a25cf14e6e205b65654945f9e67bdbe6e0cceec8245ce7ac7 ,\r\n                        0x5aee6dc171d6779e57218b9f455efd7cffe2fb003080857551134ff7335de9ce ,\r\n                        0x1a088c33103b9df647befad56fad2d2d2dad4b29e668bcd4e965c4cf5d405aa0 ,\r\n                        0x7e9a13f64ccfd31d0458370a67209a199aa7055e17e4ed55c4edd4cde9c7f39f ,\r\n                        0xc588a240fec66ffce7dda2c095a3a3d193c3e1f83befc43378df0160992be347 ,\r\n                        0x48025b5e008163026b9d3ac8b31c4aa98f1d1ec657575757578498670022e18d ,\r\n                        0x343e25f452c7cf5c43f8a0ce4cb8758620a277b0a269eeb3ef24c0db3dbe1d30 ,\r\n                        0xeadf592940088bb5d5a8f1d8631ffbc7b76ec51f02d447de78637bfcf2cbdff9 ,\r\n                        0xe5bbce06305e05b80bef0c21f6c0b0d67ab7d07a26b020c29294f22788a82584 ,\r\n                        0x3c43a753cd5ae739ea3cdb0ea8afe6baf01799629e0116ed021785bcbd806f67 ,\r\n                        0x072c0a9e0895ce77efb108028b3034b0368531a9579776f9f030fc0741a088b9 ,\r\n                        0xc05b6fddf883dff99d7ffed25d058022cfa10b17f41182bc81e3ddbf8a09666a ,\r\n                        0xc0c505d027127fc75a344a15305b69b460a19f36001701b078eb588116bc04aa ,\r\n                        0x09e4f67f7b3b3be476acf0f6ea81b1b3f31a80637f3d002943446113cd564b34 ,\r\n                        0xa58094127b7bdb875b5b37fe05de61fafa7d05c0fffb07cfd1e6adddbf0560c9 ,\r\n                        0xad76807df8d3a982455bc07903da68e479f6381177a454b5c0ca2c463ff3ef67 ,\r\n                        0x7e7c3d765fd7eb337aa505019317cecc285cfcfb4cf8746af5dfc9a07b3b7628 ,\r\n                        0xef8fc747d8d878096128d16eb7d168341104124110402905a5148c31d8d9d9fc ,\r\n                        0xf3175ef8da73779511787232b96f6579f91f77bb9dae29e3002500cad5efdd44 ,\r\n                        0x771a647986e97484fdfdc3b5464389d5d516a22844188608c30841a040242a35 ,\r\n                        0x52ba4c3360a096c899a7f145509431ffba57705a25b8fbfeeb9fb9facfe3069e ,\r\n                        0xf59c3106376f7e0f440651d4f2a1e4c0dd2a092915940a70727234d9debef92f ,\r\n                        0xb6b76f26fbfbbb770f00b2b490ad66ab0566685d5436c1ccd77576401c4f311e ,\r\n                        0x0fb1b7b787f17884c924c17038962fbcf06dfcd99ffd39a414182cf571e1be55 ,\r\n                        0xf4fb1d341a1156569671e1c21a7abd0ec230421886088210522a00a28a34ce00 ,\r\n                        0x72daef77829fb711eac22fd9e0767a7f71f52fd2fce938c0fcfd83835b3839d9 ,\r\n                        0x42b3d9f4e17259ad7ae581c0ccd8dabaf1ec2bafbcf4e5575e79097715005c4c ,\r\n                        0xdd409bf9685651e488e3290e0f0f707474849393133013c2a889286c63656509 ,\r\n                        0x596690e743e8c2c268c6eede1176768faa8b17040a61184049820a2456560658 ,\r\n                        0x5919a0db6da3d97400595d5d41bbdd86520a61d84010841042fa383faa733158 ,\r\n                        0x3333f8e8b6ab78f17d776287fafb0020cb12dcbc791d528a4ae8e58a5752412a ,\r\n                        0x852008301e0fd39d9dcdff6b6767f364676713b7291bf800bb81d6a2280a6459 ,\r\n                        0x8a389e626f770f2727c798c609822042b3d9c6850b97bd602c0a5d40fbb8b914 ,\r\n                        0x6e456021575f9e00602cc1e68cadad236c6d1e395b801c408240410842182aac ,\r\n                        0xae2c6369d047b319a1d36961797919fdfe129acd26940a100411a40c3d6801ad ,\r\n                        0xeb21dcd3abfa3cc2bf1d1b008cadad579124c768b53aa7845f028288b0b7b7f3 ,\r\n                        0xcd1b37def8b31b37de802b11e0bb070061d840148560686c6edec0d1e1098824 ,\r\n                        0x9aad3efa4b179c7041559cc0254e1c008cb12e3122e49cd06769d8f9220e4162 ,\r\n                        0xe1797f82501484aded636c6e1d55c6a452124ab99c7ea31161305842bfdf4114 ,\r\n                        0x85e874da180c96d1e97411860daf974308a160adb3071619e4ac557e3b237034 ,\r\n                        0x3ac6f6f66b0882b0127e752a772a15603a9de8cdcd8d7fb5b9b9b1bbb9b9f1ae ,\r\n                        0xa294ef2b003ef4d02771df2542abc7f8f44f7e0ccc8434ce70747484789aa228 ,\r\n                        0x2c0e0f8e7170788c24c940041445862ccba08b025c4bdfbaac9c3855b97396e0 ,\r\n                        0x67cfcdb346f9fe791009e439b0bb7b82dd9d63970a12f0797d97ce6db59ae8f5 ,\r\n                        0xba68b75a683402b4db5df47a7db45a6da820441038e38d48c21882d6f300294f ,\r\n                        0xc72806376f5e87d6295aad8e5ff9a5de57357520b0bfbff3cad6d68dff676beb ,\r\n                        0x06f6f6b6eebe50701836100606525a18a94020b49a0d74d6965c7e1c0c536898 ,\r\n                        0xa2005b461267d8df3bc0c1fe118e8e8e114f334c263146e309e2698cbcd0bedc ,\r\n                        0xcb3a68d404297c850e09e100e033458b2011a2ce1ca20283bb5d5433ee33f31c ,\r\n                        0x383818639f47950929a507a51468361be8763a68344244518476a78b76bb8b28 ,\r\n                        0x6a2008021005205200243636b6707070036118cee97e2955cdf50b9024096f6f ,\r\n                        0xdffcbd5ffff5ffe6adbb3617c000980d0268a8d2da36001bc080c02030097010 ,\r\n                        0x0220b41a4d3cb83ac0872481986135c31405acd6c8d21ca3e118a3e108a3d104 ,\r\n                        0xc3e104a3d114e3d10493690c5d1868e36309dec59b07c54cc025402a4611a785 ,\r\n                        0x5ebfad58a37c4f8d899c3bc7383e89419882c1206cfbcf6428a5d08822b43b2d ,\r\n                        0xb4db6dbcfefa6b60b655f590aab97b52b9aa22a5026c6f6f6edcbab5f1aff17d ,\r\n                        0x5616bfbf46e0a90819d73c73f6413de35fe72d6e0bb026d812204a82028566ab ,\r\n                        0x89f6ca122e098214001b0bab0dacd1c8b302e3e104a3d104e3d1d40323c66432 ,\r\n                        0xc5344e7daeddc218c7c354532733618b5342afab8b123434f73a5a008bbf5f7b ,\r\n                        0x2d0008c188a200a3d12146a313b4dbed5a559063814005104242eb027b7b5bd8 ,\r\n                        0xd9b9f5677ff227bffb9dbb3e1b48a855ddd4eb31cadcbc07c32970cc01c9bd97 ,\r\n                        0x41b08660500748882088b0d2ed624d1024c155181903ab3574ae311ec7988ea7 ,\r\n                        0x984e124ca629c6e329a6d304719ca2285c1ec232d782430e20a25af1339551af ,\r\n                        0x129a63913956a903827074b487adad21b22c471445339daf148410b0d6623c3e ,\r\n                        0x0126166120219a0da8466bebbffaafff0773d703e0141a4e097f16a25d4ccecd ,\r\n                        0xd7fa01048698afee77f8b0bef2481374a55a082c038866887eb38dc1fd172085 ,\r\n                        0x33c7adb660ab61b5c17492613a89912629a6d31493498a384e9126190a6de602 ,\r\n                        0x4aa5217a5a45880a30b4008ca2c8b1b3bd05228356bb8d20702b1d00f23c03b3 ,\r\n                        0x81521683e51e565696d1eff73085c024b6884727f8d101c0423e9dea899385c2 ,\r\n                        0x89ea3ecf0880163e84c128740e6d359a41d3ad561768aebd97bc1d42b0deeea8 ,\r\n                        0x9883025010a03568a2bd3c8094eed5d630600dd818a4498e78eac0902439e269 ,\r\n                        0x8634cd9066398ac295b1f9d2ff05b522aae2cf83833d1893a3dd69797d4f0814 ,\r\n                        0xa1d76b6069a987c160099d4ebbc60c013837ef594fd107ae356c4e0bd042b145 ,\r\n                        0x29e8aa88d799545425665c0839d31926e904e36c0222c27a7f1d810cdc6bcbc8 ,\r\n                        0x1bcd2a86c93b940c862c11659d4a611fecd15c8203609220a1205b117a9d1e96 ,\r\n                        0x2520c883832dc85ae499461267c8f3c20125ce91a5390aad61b48565406b8dd1 ,\r\n                        0xe804dd5e1bfd7e0ffd7e174b4beeb6d96c559e8010a22a22152205cbc67b76bd ,\r\n                        0xd5074af2a8d7f09d163eead5db55134669205aa479867136c6349ba2b0baca2b ,\r\n                        0xc4458c25b9e454cbc267577abdca3fd4fa07cb488320906548f6fc61bdb70282 ,\r\n                        0x65977fb5febe038704548068a985b672461e1b065b03c116bad0c8d202d3c918 ,\r\n                        0xd3e916fafd8ecff445ced5930a44f0c1afcca7c25d6f44b3d984ec453f8200a8 ,\r\n                        0x31f8a2f08970ca402c9160ad455ac4986413c4790c63cd8ce60158b618a763f4 ,\r\n                        0xa22e04c9998351137ef9d9c247d2672a68beb680e790ea6d0e9f1012600700c3 ,\r\n                        0x33e66046ee6f0db38bd20b024881c2009db500eb97d7404c504a819951e40532 ,\r\n                        0x9b411b17ee760d2d1241e09241eff5f1815301e7133e60ac419227986413a445 ,\r\n                        0x06cb5ef095cc66d540711e232952b4a336880176ffd4d481937ac904553ad87b ,\r\n                        0xed756662ae1ad0fcf7a91ba1e4ec11aebd827d33aa65405b185fdb6099913500 ,\r\n                        0x4b029ce74892acaa8226c1beed4c4129e93b966f5fb3f8230180b38cbc52a7bb ,\r\n                        0xa89d93a7361a711e639a4d9199acea1caafa7eaa9a82d9dad55663948ed00a5b ,\r\n                        0x4ec84efe1508ea06d542e948e984d6fe409565ca73099eba015a4316cd1807f5 ,\r\n                        0x28a2ff9e6060329d22493217ea0d2414fdf08ab5d50775f5bb6a1e9f7707a3d0 ,\r\n                        0x05e22cc6349fa230452572c6ccd2e65af757bdb004cc186763ac9865842a02d7 ,\r\n                        0x68bfee71942e2795491ab62036de8f74591db60244124c0100397353501a9304 ,\r\n                        0x3e8d24cc9a4667bf95aad432cf98e8877cbcefa1e045fb9f6a2e1d33233319a6 ,\r\n                        0x598ca488abba81fa6a77861dd79cbf920f6a0da20072ed0cc415157913822ae1 ,\r\n                        0x73e567128835849e80f223507108d223c0c4804d01d6000b3042b068c3a80bb0 ,\r\n                        0xe14558b50a4b91638152057019eaae099c6640385da6f6fec8e07d05802e0aaa ,\r\n                        0xf7d997746fd9222d324cb309529dc25833e7dfcf6eb9964ead9593d56f317b7c ,\r\n                        0x120fb1d41c400a35ebf32be1660ba03800a5b740c5be177a812a39c11a600d32 ,\r\n                        0xda3d6f0a28b6608a60838b289a1f856e3e022bba73c6626917bc6d77c8fb78bc ,\r\n                        0xbf2561594cd6862e6002826583b44831cda748753ab7b2170100e6d9faae53fe ,\r\n                        0xa21dc033668873a742facdfe8c71c0a0e20814bf0114bb80cd4b5f0020516312 ,\r\n                        0x014082c902e4123e3006648650c501e4f43a4c740d59ef2791471f06933a1dd4 ,\r\n                        0xa86880ee010000f6f7b7e4a52beb946605e2224552c4286c3ebb6e73bcc86744 ,\r\n                        0x0c676c709a01e681006614ac711c1fa3dbe8822000ab41e90610bf01e8a9b3e8 ,\r\n                        0x8900e7d461d604caeef56ce78548e44122409c41c5df814cde82ec7e0669ff67 ,\r\n                        0x6065f7948139efc6f2bfdb00f8eb6fff71d0bef819b19c06d0d6b8b4ac9ccf9e ,\r\n                        0xc11b655597ce622f1df80c158039eaafdb05a36484b4c8d0561298be0c246f81 ,\r\n                        0xd9cc0985410b11a85ab348d930e28d15c2fcca16e6048de33f86c8f711affc47 ,\r\n                        0x306a65c1cda0d33eedbfab0078eefae7a3071e6b8b47f89a9fdc21217cf5aba8 ,\r\n                        0x7ae1452dbbe6326f736e38c8f9f598d1ff1c2080aae7b00c139f4cf7d0162740 ,\r\n                        0xf296a7fbd967ce728e2e2cc42010d39cb5cf655868ee31cdfc002e108e9e016c ,\r\n                        0x86e9dadf8551cb950d405cc7c38f2003fcd66ffd56104551df18931d1d1d25f0 ,\r\n                        0xc38cce3a7efb8ffe75e3e4f8441eb78e3c00ca9a370925676090d28141085915 ,\r\n                        0x7094553aa56f5d8f93b0b7112c66ed61956dc00666f43d70183b219ec52c759f ,\r\n                        0xbff6b8aed21934377fe8546d031b84e3afc2520bf1da2fc38ae6ed725f773f00 ,\r\n                        0x7eedd77e2d78fae9a7ffc6f2f2f24f48299f20a20799f9e5cb972f6f1745b113 ,\r\n                        0xc7f15b711cdfb2d61e596be39d9d9d180067d3389c8c4662d83e71c3139417b8 ,\r\n                        0x525052ce1ed7ee0bdf0a25fc6026210448d64ab5042dd0f99ccf802e9fa0a5b7 ,\r\n                        0x5150844045b5b8703ddc87997b593322a9669412ca7260ae218651cf50136b34 ,\r\n                        0x465f8689d691f4ffd68fa617f0e8a38faae79e7bee1f1d1f1fffb78f3ffef8fa ,\r\n                        0xdada9ab870e18269369b3fd5e974a8d56a1d033862661bc7f1e6643279657d7d ,\r\n                        0xfdd05afbc6cfdcbc3930895647c7c773420ea4f2422e57ff3c08ce04831fbe54 ,\r\n                        0x9564893a3338d185c8d1b7bb0052a4e4277fd546c4d582105ece3ef8c3f5b882 ,\r\n                        0xadad78aec0300348ed6402718ce6d1ff87a2f130f2e02a00f381e281ef1b009d ,\r\n                        0x4e27caf3fce7af5fbf7ef9e0e000cd6613bd5e4f3ef2c823f2c68d1b0070717d ,\r\n                        0x7dfde2caca0a5b6b1ffdf8c73ffeb38d464300d8ff7bbff2abd978326e0dc763 ,\r\n                        0xec1f1d62381d2235a9cbe49145188450812b877215b13346380b1ca780201d18 ,\r\n                        0x8470c2edf03e08c7c805b999442411a8a0a6466623dc66422ca381aed69bad8f ,\r\n                        0x0ad6034dec855f038b532f0e0432df4463f897c857fe53173dc48f9e0dc0a50e ,\r\n                        0x67669c9c9c606363036b6b6b78edb5d770f3e64d00202965f0fcf3cf63757515 ,\r\n                        0xebebebf72f2d2d617575150f5cba84871f7c08c6684ce30427c3134c93294ec6 ,\r\n                        0x239c4c86984c47c84d014bec1b3a428441e88051aa8b8a093c084a15e1411091 ,\r\n                        0x06d1366291fbe7086c18ed46bb56d645b522d032ffe70040ac9ddb680dd81ac0 ,\r\n                        0x1a1ff1b395f02b86a8757af80807a2f1f3509dcf4207d73e20a2ff0119816534 ,\r\n                        0xefe0e00044846bd7aee1e6cd9b4892044a29c4718c8d8d0dbcf5d65b55758cd6 ,\r\n                        0x1acd6613ababab0882003bdb3b182c0f70f1be8bb874df05341a0d1866183618 ,\r\n                        0x4d46389e0c314946c8740ec3d6154d8601c220441446550dfdcc569018880932 ,\r\n                        0x71022809e91b3ef2ac80c9ddfb9d3af15e870424f9492360085f2b24587b3018 ,\r\n                        0x107b36b00caa7205d66702678e689919147a1fe1f405644b0fbcebc0b91b4e29 ,\r\n                        0x3ed800489204c618482991a6298e8e8eaa0ad752e0cc8c244970787888e3e3e3 ,\r\n                        0x0a004b4b4bb870e1027abd1e8410d8dddbc58d9b37fc500842b3d944bbd5c2f2 ,\r\n                        0xca0a1e585977c20f14c6c9047bc7fb4892298ef421341b078a20401844e8365a ,\r\n                        0xe80543c4728ac2d7d60b490031b244a3114650ca19a142f81981c2a5eeddad85 ,\r\n                        0x04439081848580818481802bf0705d1de5caaf1987d52d00d608e2ef82ba3f07 ,\r\n                        0xc63babe81142208a9ae874bae8747a98b0f8e00240fb099f4551548f4b811211 ,\r\n                        0x9224c1cece0e8e0e8fd0ebf5f1134f7f0a4f7fea693cf9d127b0be7e3fdcb450 ,\r\n                        0x671ce5798e344d912409c6e331363737f1faebafe3a5975ec2e6e6268410f8e4 ,\r\n                        0x273f89a79e7a0a1f7df8713033469331c69331c6c904bbc77b984e62c489c1ae ,\r\n                        0xd448a3065a2110891c816290309022412374b68603aa80f24322a500942048c1 ,\r\n                        0x90c49064a104430946202c02611d33c080ec8c0500ebed02a05e97a0b25b10c5 ,\r\n                        0x01105cbea312202284618876bb834ea72c118b9ccd52e80f0e007821c9b14851 ,\r\n                        0xd6f73c31330e0f0f70f3e62db45a1dfc9d5ffe45fccc7ff8193cf4c8352cb757 ,\r\n                        0xd1e1650408e7fa00cbc2c9288ad0e97470e1c2053cf9e49388e3183b3b3b78e1 ,\r\n                        0x8517f0e28b2fe2cb5ffe32daed36ae5dbb86f5f5750c06030cba7d5cbd78054a ,\r\n                        0x2a647986c9648a2ccb30c912ecc7634fe731606374238b565820540942651128 ,\r\n                        0xc7004ad20c0cc40804200523944023009a01408a4164bd1a3095e1383322cbc2 ,\r\n                        0x1240983164b10b0457e673433e6c4d709541ed760bed7617ed761ba157690023 ,\r\n                        0x4da7c889c041eb870780a79efa34bdfcf20b61962567f92cbcbfbfdf58595991 ,\r\n                        0xe54897288a4044d5a83700288a02bbbbbbd8dbdbc7131f7b127ffb1f7c0aab3f ,\r\n                        0x2e8070177bc6a08863e4acd1c100546391db9d4484ab57afe2e1871fc62ffcc2 ,\r\n                        0x2fe0b9e79ec3e73fff793cfbecb3e8743ae8743a7e1e40804ea783d5d5552c2f ,\r\n                        0x2f63696909fda5017a4b03446184388911c70932ab314ca6b0930481b4089022 ,\r\n                        0x14291a4aa3216284ca205498014009b42201db94100d01a19c2b581a85cc5cd9 ,\r\n                        0x02549f57cc39843e9e25a8fcf3c2f717763b5db4da6d34a2082a087c5f628638 ,\r\n                        0x9ec09802cc06cd660bb2d7fce101e0739ffba56bffe49ffcfaaf0e06fd6071b5 ,\r\n                        0x13116759163efbecb30f8fc76374bbddaa6ead0cbd1211e238c67038c2e52b57 ,\r\n                        0xf0f8a73f84f4c226746ed0ccba68720fe0001967204c202016c6c4d98a12cb0a ,\r\n                        0xd992698808abababf8dce73e87a79f7e1a2fbef8220e0e0e9cee0fc3ea7bd43f ,\r\n                        0xa7d56a61381c02cc585b5dc5caca4a65931009c4718cd17884e964827196e024 ,\r\n                        0x4f60d32902c4503c4553a4688b117a0d0dd36b437208d91490829d8fcf3537b1 ,\r\n                        0x5e9ac60c580d6126f3c52b44e876bb50ca7d6721248aa24092267e48b60111f9 ,\r\n                        0x9ac0f73e727fc74f5c5e1edcf7e8a38ffeeadada4a974fe7b4d91823ae5ebdba ,\r\n                        0x9ce77935dcb82ea092099acd161add085a2430781204826009010901010155dd ,\r\n                        0x5fac11a8c7f7eb3ab2bc25225cba74094f3ef9249224419ee7be5143cc818099 ,\r\n                        0x3d9d3a96a9abacf2bb5b3fb5344d534ce31893f1187b7bfb288a02fbfbfbd8da ,\r\n                        0xdb41574db09ebe0c428ca662349542101024b9b1b6e443d1b4080258808b9997 ,\r\n                        0x38db6002d65a4ca7d36ac0b41b07a77c9bbafa81058dee0800630c6559c6699a ,\r\n                        0x26cc6ce6129a44383e3e5edadbdb53455154ab3f0802b45aad4a78fda52520b4 ,\r\n                        0x98e427b0d0b064ab14ab642778090501050909f220581c177396755c9fe597e7 ,\r\n                        0x39acb51042a0280aa4695afe068cc7e35342ef743a68369b153bd441678c0101 ,\r\n                        0xe8764b5d1c22cf73bcf0c28b78e93b2f228a0274a20c492a513480480a48616b ,\r\n                        0xd43fbffacb89e40c596529cb92a1e9344692a620f61e88121550dff738801052 ,\r\n                        0x1389677abddeab7025f002b5d44b18869f7cf1c5177fe6d6ad5b6bfd7e1f4a29 ,\r\n                        0x5cb972051b1b1bd8d9dec1873efc610c2ead220ec7c8c504854850d01485c820 ,\r\n                        0x4088b88d06771171070db411720721223f48a13e389ae7d48110a28a0a1211b4 ,\r\n                        0xd6984ea7b599c38c22cf70747c02a514565656707474547d5eb3d944abd5c2c9 ,\r\n                        0xc9c9dce79643239efdea73f8e33ffea3b9cfffc55ffc45bcf1fa1b78e1c51771 ,\r\n                        0xe16391b3498a02c608b0953e9dc0736e20cf95a649586a81ed3c988b3c479117 ,\r\n                        0x0882e0871e1d3e0f0320cbf20300db3e8e590240002021c44b4b4b4b4fefefef ,\r\n                        0x430881a5a5256459869b376fa2d168e3e1873f0a4101c804200e00ce002160c1 ,\r\n                        0x28288581ad66ef08165e0d3846984df99ad7e3652ca1ce0c799ea3288a0a2cb0 ,\r\n                        0x068d4061b9dfc5c1f110711ca3dd6e23491208212a5b204dd3b99132657bf874 ,\r\n                        0x1ae3e0e0a062b52ccb70636303dffdee772b01db7276a1b5becc986ad4cff399 ,\r\n                        0x4266300268d9afc508667c4a772c0aac33cb0f1100b5ef554b9c573d1422cbb2 ,\r\n                        0xa3bdbdbd937ebf7fa5dfefa3280abcf1c61b4892043ffee39fc5a0bf066b0a58 ,\r\n                        0xb60bab81910b0b43390a4aa13884440089000a2104e49c606ea7fb4bca2ee9df ,\r\n                        0xcd1874011a418c5e23805a59c2def1c8cff277a5e193c9c41b7e34f7d9cc0c26 ,\r\n                        0x9e75007bb0551dbea29631e4596e8059d64a8c799efefdad956d6875a1aa5520 ,\r\n                        0xd0b957bc1012ad56079dee1252fae107826c0d00f5223906600e0e0eecc9c909 ,\r\n                        0x96979721a5c4e1e121d6d6eec7d5ab8ff86ba210d8b6af86b0f0997a57ee2da6 ,\r\n                        0x282883440a89108a2304c8a1108070be4e18b775cb0c00642d882c84ef16ee45 ,\r\n                        0x0262b98fad83638cc713b811f3b8c32c60be5300a472f9b8bea2b95eab545707 ,\r\n                        0x1645789a713f750000200049444154095aadf8f7cdc588de3618d4e9f466c1a0 ,\r\n                        0x20425a981f2600aa9256535bfd55d69c99adb596ab8a9bcc35443ef2c8136834 ,\r\n                        0x5ab0d6af262828d372d53970ab8cad0383162934521408a12844ce01140508a8 ,\r\n                        0x71db0bb3187d2c8ac2af4617a72f85ef4a390d06a180581be0d6fe7115aa3eab ,\r\n                        0x229988fce7dce99ad02c4dec1a065ddd605daa35e13309a48dc761a8ed7e37cf ,\r\n                        0xda5aeb2f2d3d9256ab8d4ea78b76bb537506973d12ef652ae95c0ce0935a664e ,\r\n                        0x19791b80994ddd508be329badd255cbaf4a0aff69d75740a84086cdb091f0c96 ,\r\n                        0xb51d41c8a0400a85100a01728490a440a4e668bf0e82f27196b9064ab62e5123 ,\r\n                        0xc010c4105e1fbb1e3e83e55042af2c616bffa8ca2fd485bfb076efa08d6b9150 ,\r\n                        0xb6004be702d6b7862b1b3ed842ab3524cd8fb96fb360bf80dd5e84611456812c ,\r\n                        0x371236f881f403be0b15c076c106a84bd65a6bd95a0bad35e238c6638f3d8e56 ,\r\n                        0xab3737bc9999fc448d0881292ff24c1d14228181ae812045400114bdfd05b0d6 ,\r\n                        0x22cb5c5f1d79009000047b10c0010100c81aacb423e466197b078790529c123e ,\r\n                        0xbc923a7718bc5e30e21a03818502120621697d027978056053bdaf3ca24603ed ,\r\n                        0x4ea772375d77f00fc71d78b70c206a2ac01863b81cf8680ce3e2c5074024fd2a ,\r\n                        0x2b3b7e5cf72c5982a41001da4ef8642b3058d2303403418110020a5403c1e220 ,\r\n                        0x48b79d5b065897af17c4de5571c2a7d23023014b02c21aac0dba88d31493c9e4 ,\r\n                        0x54eea29cf4718ee4ec6ca557f17f3167f495eca0d53a26ddbf098b006e8f9f5a ,\r\n                        0xa53201ad66035205ef79aaf7bd34024b002c1a81730c90a629940ab1b27ca116 ,\r\n                        0x5ca19a7e9de147da0682ba4148169a1280191a0572240810428a00d2346ebb22 ,\r\n                        0xb22c832d0a277c369004087600a89793330980244002012cee5b5bad52d7f588 ,\r\n                        0xe1e2aea077b4034f958cf15c3591a506c6fd7f0f7978cd551431dc48996a7a08 ,\r\n                        0xbdaf9561e29c5457aa0053bb35008cb701d85a8b3ccfd1e9f4d19121449a015a ,\r\n                        0xfbb1ef540d5e2e3785801550b689c07610da3642d386b20d104b0004e341a029 ,\r\n                        0x030b7d5bf72f4b13589383acd7fdb010d52c0f54ab9f85048402a40204a1d36a ,\r\n                        0x607930988b3154bad9da73185a3497ff9fd13a57bb9f3104a69dcf62d2fd69ff ,\r\n                        0xbbaddf5c92e742c1ef6779d03b6580ba0a40cd0b400580761f4d5d209c16d041 ,\r\n                        0x081b84b0410096024c62ae438aac8442d35f10c70690be820621c804d005608b ,\r\n                        0xdc356d92984b0619a3912731603404ac5bfd986fd660124ef0a5f06500089754 ,\r\n                        0x5a5d5dc17034429665a76c00bea31748b57e033b2b3f675f210442d2791aa3c1 ,\r\n                        0x7f02432db035b3d55f6b0d9f05d63fa000a05950fb7646a029377db26cd16eb4 ,\r\n                        0xd14e1308534006114c10c204216c18782028b0ac59f35641a20505eb72040428 ,\r\n                        0x1b42c50d98c422630321dc5c602583ca7d1342204f53e82c0159edb685c52c58 ,\r\n                        0x530d6a14d20b5e816500123310349a11969797b1b5b535a702ce6703ccb673b1 ,\r\n                        0x5530c8e7fe4962dafe0c86cb7f17855c01d7b6c443d5b66e71d7148532f3ed8c ,\r\n                        0x40c1ccd618e301c008558830cf20f318aac86054081d8430b96303137a4650ca ,\r\n                        0xb182702008d006c0084c03766291a529842037364dd677f49c59edc24fd2b4da ,\r\n                        0xfa71af626e88a3dba53170829701c8330049550d995e5959c1e1e1e11c0bb0b5 ,\r\n                        0x77a67f78e1570ce0548155034c7a3f8f71ff3f80a6ae2b20052fec4f50678dbb ,\r\n                        0x000035bd4f8b20a8db00440251d0702111066491436aed81103836c81d235440 ,\r\n                        0xf0ea815821e02ef27c8c2c398194c2cfcc1315f597065a19f56b369b58bd7809 ,\r\n                        0xa3bd4dd822f3abdf876bab951f8264e0eeab0042067e42a75be9cd560b83c100 ,\r\n                        0xdbdbdb33d7ee9c5e409973606b60b985b4f50492c12f22697d0c8625d80faa2a ,\r\n                        0x055f02a1ae32ee26062841b0e80656368010beb73f88a0985d3cde9fb2c82174 ,\r\n                        0x0195677340b06108e381602490a75308392bfea81781d4134165fe3c8a22b457 ,\r\n                        0xee437abcef4ab505b9d52d154879e1ab190388dac06700904260656505fbfbfb ,\r\n                        0x551de379e8b9b4f93475308d3e0c33f869a8b59f02872b6ed5b39913760904a0 ,\r\n                        0xbe2d2eee0600545d126731c05c1c00007253602203c89020c1906c21d9df5a5f ,\r\n                        0x4459e420a3218b1ca6082054001b46282202c340c999f017adffb9902d33d234 ,\r\n                        0x75c59ced25d874e48670c90042852015805408480521e5a9f1f0e5e7ba9c7f07 ,\r\n                        0xc7c7475e05f01d1767a12e60d47a18e83d0eddfd08fa8dabe8530fd21ab0177e ,\r\n                        0xa9efebfb20971b5cb1e5bb4705f840d0db3100974514e3788ca180aff1011424 ,\r\n                        0x149c71af7c3984b4a5bbc6206d202dbb009a721b35d429bf4cf6a4698ae9748a ,\r\n                        0x66b389288aaada43d7576000a140611b021a42851e002148a9dab0e77a4879d6 ,\r\n                        0xdca95480c1608093936337c1eb1ce49caa07300eaf40d12a1a5aa195e7b05643 ,\r\n                        0x0855db03992b20d4f5bffd8008ff9d86826fc70055204808813899605224555e ,\r\n                        0x5d12411241114181204940097870c001a2fc1f0895955fae706b2d4e4e4e707c ,\r\n                        0x7c8c2449fc162e0af7df7f3fa228f28521ae0288495655b48efe955ff5f53cc2 ,\r\n                        0xfce64f25c7f57a3d4829ab82923b1a45cc7edbf71c7996416b0700b7477169f4 ,\r\n                        0xcd763e77d46fe65402e3ee63007b9b5070e59a4d2627489291a76f519bb1ef2c ,\r\n                        0x7459814240112170c15e4872431794174229b0344db1b3b383dddd5decefef63 ,\r\n                        0x3018e0e2c58bc8f31cd7ae5dab98c035a2b8d843a3d1f2b3faeb3b7d9e6dc997 ,\r\n                        0x7f6b345a68349aa76202b7f302ac35280a8d3c2f501419f23cf359463b97eca9 ,\r\n                        0xef885e37026d7d8edd073f10c4751b40bc1d034c264324c910511461361a8de6 ,\r\n                        0xc030dbb5c3dd4ae180d1e7161a2aa8cac901d7699465196eddba856eb70b6b2d ,\r\n                        0xf6f7f7b1bebe8edddd5dacafaf57862233234e12b4dadd3362fcf5818ff53046 ,\r\n                        0x99825568b5da180e4fce65a15bc3be4c3d479e67d0baf011c579bd3f33046dcd ,\r\n                        0x2ec0bc1b481f7c06283d24eb3bad2b06b0d6566ea0941293c908d3e931804e75 ,\r\n                        0xc1cb9d334eefea3503068810b557d05f5a0391bb98a53a48d3144110f839bad2 ,\r\n                        0xa79d63f47a3d14455115500a215c9d7f56208ad49942ac6ffd363f9c52a0d56a ,\r\n                        0x2ffee6b751016e78b3d6058aa2a895a3cd84ee6e4d2dcc6cef3e37706e44aaaf ,\r\n                        0x46a88180ca4050bd1a77383c46abd5a8d2c0f3a55d626ee3858aa619c8f3b4da ,\r\n                        0x28c1d5c4339acd2606830186c3a1cbfbfbd8c0743ac5eeee1ea22842b3d9ac00 ,\r\n                        0x5614399224477046f7cc59f9a4fa58da66b3ed771fb7778c0395e96f27fc1c5a ,\r\n                        0xbb5d476605ac33eaaf1b84d57376be1ee003cf00e53e794280850088dcece3ba ,\r\n                        0x0a28853c1c0e71f9f2e539976df12c1b468320a8acfab299432955f9e48d46a3 ,\r\n                        0xfa5b9ee788e3184a29efbab5aa96f492a266ab11a84fe2aa6fd078d666cfcc6e ,\r\n                        0x132b21c88d7d3f472d80dbc15b574ce0f63faeeb7d736ad5579e403d0ef0c157 ,\r\n                        0x0144c6b7c34b57fbc852ba4159d65a5b6eebcecc905256e5d7a51f5f966f8761 ,\r\n                        0x5809b4147ca9bbcbdc7e1cc788a2a8d2a34208f47a3d5cbd7a15b76edd8210c2 ,\r\n                        0x374db6c1cc0882a05e99e2ad7882314ef8b3fd7f315fad83d3a565522a0821c1 ,\r\n                        0x7ce70db8cbffabdeb236f3f7f994f0e71981efae4820002a0abf45830301bb95 ,\r\n                        0x0aae3380b516411060381cc25a8b959515341a6e6bb466b339679895f459760e ,\r\n                        0x954229ebf5ca4d12880883c100524a9c9c9cb859013e2e70df7df7a1d96c55d2 ,\r\n                        0xd55a7b2106150394429f1fd57ab61a2012903280e5f88e8bb28c7b1863aa0d2d ,\r\n                        0x676965fbb6c2af0385ee0600781520ea215066a70a9cfa9f01404a89388ea1b5 ,\r\n                        0xaed440b942d2349d5b2de52aac37790821aa58bfd6ba2acdeef57a78e2892730 ,\r\n                        0x994c5014053a9d0e5aadd6a9151946115836911b8222ae6200f5a21477ff2cb7 ,\r\n                        0xd079265a9b3bae4d27485da90153f608f892afb717fe796b0edeeeffb7f4c304 ,\r\n                        0x80d0da4d652bf73d70abd50fc9b0b3c047d94973fdfa753cfcf0c3de30ab356c ,\r\n                        0xd4845e0ade6d8f266b1b254a8c7c9e5e6b8d56ab05215c72687979f9545a8619 ,\r\n                        0xfeff30508d1e922042ca84160321b10b0f571b4f71b54bd98c15d8838861acb9 ,\r\n                        0x733088ddebc0b662016374cde2c7db0bbfe619bcbb83a0b5967fef577e86e09a ,\r\n                        0x75aa32a47ff4911fe35fbafa307ef2be4b787279edbd52014425006acdbf5608 ,\r\n                        0x906fa1f3410ebf0ac3101b1b1b78e5955770f9f2e5532bbd2efcc56dd14b772f ,\r\n                        0x0802dcb87113a3d13e3a9d0e9696969cbe3fa341546b8d2c4ba1548449b8042d ,\r\n                        0x244262149ad0b0408b50b1c17c30b3b66b88bf6f8d7ddb743001306c312d62b4 ,\r\n                        0x6404b68bc61e9fe1059c217ccbdf573228cfd200400bb3261d03c0fee64b2ff0 ,\r\n                        0x6fbef4c2fc14abef0f00cc000963a0302b84b5cc2ef36a8c9fee5fab8f935242 ,\r\n                        0x6b8d975f7e19ebebebb3b46e0d0075caafef9337db1e35c2a54b5771f3e606f6 ,\r\n                        0xf70f301a8db0bcbc5c6daa58d27aa9838590d068223611921c68878486624c0d ,\r\n                        0x21338c36111ae43677225adc8aceb9abd546cf6f7bcd5c6dfed44c108aaeb7e6 ,\r\n                        0xebee9e9d2b149917fecc0534df070310802c4f0300ed45006056ae6717ceb96a ,\r\n                        0xf67303c00b5c588ba0ac0af25be08008f00040b5b3a7ff515114e1c68d1b383c ,\r\n                        0x3cc4952b57bc952d4f81a04effa5f001016b81306ce2ca958711456d1c1eee60 ,\r\n                        0x7f7f1f93c9d8efb9eb2c762905a454c8738334cfa11a2924090c59a008086dc5 ,\r\n                        0x3044280cd0b28c9620046088b2841b3375e0cadab2739564e7c890710c706fa1 ,\r\n                        0x7f912b365c143eb3851b29643d70f8dd6a0018ad158086570175419faadb3ce3 ,\r\n                        0xef153b9c5705486b11fa3757417621a0d915dd6111044a294c2613bcf4d24bb8 ,\r\n                        0x7cf972c5028b0c505ffdae0982e676d41622c0dada03e8f7ef431c8f50140900 ,\r\n                        0xeb3f43c118578b381c8e20441301049a44b061131308684be8060c0860a40919 ,\r\n                        0x333aecd840a21e2b20686d9066e9b9cab32d2ca63cc54a15e42957bca9aec169 ,\r\n                        0xda779b58d9f7201964ad1198e5d46cad6ccf9e2170532becd1feb400ecbb0180 ,\r\n                        0xf613504808c05a21a99acf3abf1346a3d1c01b6fbc815bb76ee191471e998b09 ,\r\n                        0x9c751209585baf209e856e1b8d163a9d368460c4b1451cbbade4d3f418441a41 ,\r\n                        0x2091a65388d4b598b640e0b0898481c212fa2110048c4413320374c068332120 ,\r\n                        0xbf91175c8979516408176c8ddb1505261c23e30c2d5ea0f89a3aaceb7c335779 ,\r\n                        0xccdf5718c05a2baa84ea2c347f1610e4dcc225143250b9c9b50660c5f94c4052 ,\r\n                        0xcc8898115a8b901981b5088d410008258414428853212d29158a42e3f9e7bf8e ,\r\n                        0x3ccfab55be68fc95c22f055faefe7910f8495b4af851290a4421884218e3c6a8 ,\r\n                        0x0106791ec3666384d9182d9d2260466e194719106bb72ba70d8063100e993065 ,\r\n                        0xb7292483301a9db8966f731e7a66682e30e5b13704ed19357ff3c29f1b7d53aa ,\r\n                        0x00fabe01a06a40286fe5c2df14801040138ca6d5ba012000a0ce010006409219 ,\r\n                        0x610d0491b5089c5d20032903124256fbe4964cc0ec6c819b376fe25bdffad699 ,\r\n                        0x967fa9124ac1d785bef8dcfc779a056e98ddef8da2085aa7288a189c4e102513 ,\r\n                        0xb48a0c816568660c736098bb219041004c0570c084130b2416383a3972710b63 ,\r\n                        0xce7555188c294f5070b190ec311528d8cc84bf982266bc7b16b0d6d26d84af16 ,\r\n                        0x802016fe2efc141b013706f15c1c5032407986cc88ac45442402a50221a5dbf4 ,\r\n                        0xb80e025778e904fddc735fc3ad5b9b55c363dd0ba85be03381d3dc566cf5bcfe ,\r\n                        0x0c0c02440188148a0288a216004651a4d04502641334d2095a3a47605d0fd244 ,\r\n                        0x03c7396099d00c085a024710d8cd358e46275052a1c88b735d156646c619624c ,\r\n                        0x6b3adfcc56b9b1300bde4015052c7f28de33065814bc5c14fccce021f8c7e78e ,\r\n                        0x03486644de7828d1e39a6f21541084a28ca3974196caea6540a910d3698c2f7e ,\r\n                        0xf18b585b5bc5eaea5ab523c84ce8f5cea1d34c50cfe6393ba18c2d280811fa4c ,\r\n                        0xa244a3d1c0743a753b70934040024d72bd4236086000249aa02dd00b80962264 ,\r\n                        0x02381a8e311d1f3b5098f3f7df5b184c3086611f0a36be0164ce083cc315ac54 ,\r\n                        0x00bd0b1620f0cc0814355790ce38eb2ba83ebecc0230e2ce289f01e0360c1029 ,\r\n                        0x150847e7ca6fea2041549e8e11a228c2c6c60d7cf18b5f423951acae2ae6854f ,\r\n                        0xa7843fb70114d7d5808210cab300fb6a203816d08e094436412b9ba2a50b3fa2 ,\r\n                        0x09c82d709203e3dc5dc5e9ce0d08eb52bac6589c5307000ca41423e5d4d3fe3c ,\r\n                        0xe5cfb79d2dc60bf8f4c685e7051ecf31803c0713a066186ab8794ff939034150 ,\r\n                        0x00a24a87f8b90b2503942ac01967ecbb828da776aa98200c237cf39b2f603018 ,\r\n                        0xe0e77ffe6f434a75db553f7329cfa6de7a2187034100ad73cf024d4ca713689d ,\r\n                        0x55209410681181d1c6540630206806c605904ea7186ebe8146a09024e93b6666 ,\r\n                        0x0d833186d0c6ce5bfa0b4d202e62380f8a77bb61a0b52c6ad47e3b06a85359e1 ,\r\n                        0xcfac76ea77aa0224004104c98cc20180a45221293503009181b504ad8bca0ee0 ,\r\n                        0x728812185ff9ca5f218a1af8cc677edae7f3cf5afd5cbd675120f36aa154030e ,\r\n                        0xf44551a0d96c2349e23900105c0d6213022624245255e54d876fbe0a9b4d8066 ,\r\n                        0x1379aea1ce3590b136bb9019233b446a63346dd36f395f8ec8b50b69625bb981 ,\r\n                        0xdf4f369067364035af096737f094ab3e0790f8332b6301e70540c5007ef50b22 ,\r\n                        0x3f838184502a944a8590d2050b9981248991e7996fef2a3741a66a5bf4bff88b ,\r\n                        0x2f8059e0339ff96c8d0966825f5401f5a28eba5a70e95d559d5ae708028546a3 ,\r\n                        0x89c9640ca2ac5243aa100821d0820b14652a803e3ec0c99bdf41af1521491267 ,\r\n                        0xc5cbf38965b6672190d80427c521fa7650db9fe80cc1d7d2c1a7a685bd3b1520 ,\r\n                        0x303fc3a91edd325ed02980d89f592d3a78ee7a8092012ac1c38fd437c6046118 ,\r\n                        0x4683c132eebfff22d234c1f6f616822044bbddf26552da0bcfc25a402982311a ,\r\n                        0x5ffad217a0b5c1a73ffd5984616361660fce4cd6cc57f270555758b280d61245 ,\r\n                        0x61d06cb67d5a3aafec11f87194115ca732171a37befb1c02d2b0562149f2d976 ,\r\n                        0xf2e7599ba52dc20ccb06fbd92eee6f3f0045eacc4aa03910f8f131ef7412089d ,\r\n                        0x6680b38cbe12101980298089177e5e0b059f3b1954c5014a7d43547901a2d168 ,\r\n                        0xdbc71ffff12fecefdf7a338e4757a594f7f7fb4bddfbeebb4f359b0dc4f11493 ,\r\n                        0xc91871ec180130559da0311a5ff9ca97301e8ff1533ff573e8f596aa8d944b16 ,\r\n                        0xa80bbc5e9f387f082f640782a2d0504a795b603c070222014502614a387cf365 ,\r\n                        0xe4c75b58eaf7301a8d7d9d803897f0cb9dc9ea85bd237382617e8ce560a50af4 ,\r\n                        0x94aa603e27c0d5a0a877560300bf7fb28075f5007281fa5133f0a600465ef889 ,\r\n                        0x17be7dc7d940ff1da58f24899a11280048a582e2231ff9c406f0f1d7279391dc ,\r\n                        0xdbdbea6e6fbf79653c3eba361a4d3fa494585d5bbbd088e39886c31318a39124 ,\r\n                        0x318c71d53bd65a7cf39bcfe3f8f8083ff5533f874b97ae5585a395914c67db00 ,\r\n                        0x753b8048561e8831024561bd2d90f8c28da25213cc196ebcfe32f677dec460a9 ,\r\n                        0x8fe9344151982a26c1e75cfdd5c4435f2d5bd802fbf90e7ab25f8d80af836031 ,\r\n                        0x65fc8e0c4d5d603a1db95d433a2b60ae8cc0f2cb96741f7bc10fbdf0b35aecff ,\r\n                        0x5dd703884506a881401803414445a7d32f7abdfee1a38f3ebe9d24e9739b9b6f ,\r\n                        0x756fdc7875fde060e75a96c50ff67afd4bad56b3c76c5592c4341c0e91245364 ,\r\n                        0x598a37df7c1d474787f8c4273e85279ef8713f648adf0e940b7680ac5880c8a9 ,\r\n                        0x8156cb35894ca71318a32184469ea7b879eb55ecef6d627979803c2f10c7898b ,\r\n                        0x1b9ce93e9f4b0354883c2e0e100797d114ad85e85f3d4730ab213847d2079349 ,\r\n                        0xe242dcd6a2dd6e4380c0332fa05cf539803180632ffce95994ff6e2b82a48f1d ,\r\n                        0xd3a21158bf6f2d0433a4b59041d0281e7cf0b1f1830f3efa9d2c4bbe7578b81b ,\r\n                        0x6d6ebeb97470b0f540924c3f24041eba70e1c27dccdc6236623c1e613a9de2d9 ,\r\n                        0x67bf8437df7c0d3ff6639fc6030f3ceca37b677900bc202831670c1aa351148c ,\r\n                        0x66b38d34cd60adc66834c4e6e61692648ac160803cd7984ee35a797a7d0aee39 ,\r\n                        0x39b9b6731833909a1487c53ed6832b0b2e60bd45dcdc3117c06c912431d23476 ,\r\n                        0x954e4a2c8c8c63d4843f057002e0c8affed8ab813b22ecdc0521cc0e005ed854 ,\r\n                        0xbbbf6817d4e3cf05118946a3555cb9f2607ae5cab55bc6e8b746a3e3bfdadc7c ,\r\n                        0xb3b3b1f1dac5c9e4f8435a670f4751f4d07df7759784a090d9d2f3cf7f116fbd ,\r\n                        0xf5321e78e0515cbe7c0d4a75ab0693d3c22f9b4fa457050e04795ea0d17086f2 ,\r\n                        0xe6d6360ef60f104611068301d234431c277045aeca4f05f18d2a7764009adfb3 ,\r\n                        0x98eb73012d0e8b3dac8a0b5050b3d4ef6dbc82fa7f558ee4ccf2ccd74f161082 ,\r\n                        0xaac61729159acd368cdb4697bd753f0170e857fed83fa7cf5b70785e2f403057 ,\r\n                        0x16677df56ba2192b943182053b41008e1588a89032102b2b17f4eaea85e3a79e ,\r\n                        0xfae47e9ac62feeecdc88de7cf3d5c1d6d6c6b52c9b3e0ad8877bbdcee5c9e4b0 ,\r\n                        0xfbfaebdf90bbbbaf6365651d172f5e42b7db4751342065340702971852b03600 ,\r\n                        0x5101ad0dc6e3131c1defe0e4e400599aa2dbed412985d1688ca270516d63ca1c ,\r\n                        0x3e414ae7b2e29c86e0e2be2fa5ca8acd04437d8c815c9d13fca984d0c290a042 ,\r\n                        0x6b649329b23cad6d90e1aa9f1a8d36969656d0e9747194e460f010c08117fcf1 ,\r\n                        0x02e5bfe765e1732e475960eb9960ce2b58b013ea6c50fdcd97fb0b22215aad8e ,\r\n                        0x7ef8e18fa48f3cf2f84dadf3b78e8ef6bff2d65bafb65f7ffde5fbf7f6f61e35 ,\r\n                        0xa6f87014050fefeede5adbd8e836bbbd0185411b41d0065108508052953a0373 ,\r\n                        0x88c9e40893c931927482305068365be876bbc8b21ca3d1c4fd20a16a179faa01 ,\r\n                        0x4ed6a2c634e7c300d7bc01064343e350efa3474bde46381d0ba88fa335c62049 ,\r\n                        0x5264690606d752de2e7adaef0fd0ebf5110421b6b777d3679efdea9ffce1effd ,\r\n                        0xb3df01b0e3f57df27686de7b51145ad17e9dfafdadbe9d71b8f87881110a21aa ,\r\n                        0xfb4208924a45fae2c5cbc5fafaa5e34f7ffaa70fe278fcad1b37de68bcfaeaf5 ,\r\n                        0xa537df7cedc1eded9d8f00fc68afd7bddae974961a8d969252d1741afb8e200d ,\r\n                        0x6b8d5f314db45b4d3003719ca028748dee45e539b8f1aea296b7a0730380e775 ,\r\n                        0xf15c7a77644f303563b4d0aef4fd62b770c91893c904719cbadd51035995cff5 ,\r\n                        0x7a7df4fb03349b4de479816f7ef3db077ffaa77ffadbbff33bfffbbf1c8f0f6e ,\r\n                        0xd5acfc775d637e5e23b0a4f9aa30b66400720a539c651cd655422d8a28bd8122 ,\r\n                        0xac8514a2ba2f8470b7004921a4ee7697f4934f7e2279f2c91f1b174576e3e060 ,\r\n                        0xf7af5e79e57aefb5d75eb9b8b3bdfb9865fbd120081f5e5e5ebebfd7eb365bad ,\r\n                        0x8e280a8df1788ce9748a2c9b821955f998101279eed4431084beb630ac3c8032 ,\r\n                        0xa04477300269be586616cca976ff0272ce706c0e108966d50350af18b65c370a ,\r\n                        0xed9c4bdb6cb6b1b43440bbdd86520a1b1bb7f899679efdf61fff9b3ffc5f9ef9 ,\r\n                        0xb7ffe6cfbdce4fce6be8bd172aa00c3aa0c604b74b3fd2ed56fe19cfcb52f025 ,\r\n                        0x2bd40023d88df915420811864d73e9d2357be9d2d5e3cf7ef6e78f2793c92b37 ,\r\n                        0x6f6e7cfe8d375e5fb97163e3c1a3a3adc788e8b166b379b5dfeff75756d60229 ,\r\n                        0x2525498a34cda1b5419665c8b2c2efc117fa955e4611a5670697463e4f848e08 ,\r\n                        0x7e2895a8ed5aee43d9601c9b230c780d01827937d032d8d8b911f34440188658 ,\r\n                        0x5aeaa3dbed228c222449866f7deb1bd3bff88b2ffdfe97bffcf9dfbc75ebbbd7 ,\r\n                        0xbd9ecfeee4debde73640c90035dd5fae7eaaa98352b0faed40b0c006734c50fe ,\r\n                        0xcd198d33cfc25a18d7904a0c04b6d319e0f1c797f20f7ff8a9ed2ccbf786c3e1 ,\r\n                        0x376fddbab1b4b979ebe2dedef687b48e3f2c041e6ab73b170683e5965281383c ,\r\n                        0x3c44bb0df47afd6aaef1ac455d780f42dc4105b86248150490cc50a1820ce4dc ,\r\n                        0x46124c6e65a71c636c4f30c0ca6cc5fbd53fab0a767592dd6e174bfd3e1a0d37 ,\r\n                        0x16f7e6cd2d7ee699afbdf4cc5f3ff34fbff6fc577e773add3e9a2b43788f0e75 ,\r\n                        0x7e43a7ea0caa1b80f5b4e39c8be8854d6779046701a11e5db4d6a90e2120ad85 ,\r\n                        0xf62ae7544973594fa05403cbcb0d0c0617c68f3df6f1384db35bc7c7c7cfeded ,\r\n                        0x6daf1c1cec5e393e3eb8c61c5fb3962e371a8d5eb3d90a96961a04b87d8b2793 ,\r\n                        0xd8f72196aea4785bbddf0823f454fba5ed9ddd1b03251e52cd70cd5adb35c6a8 ,\r\n                        0xb23692c855001ff321bad40759e1ea05d80fc6b66eae2240e8743b08821041a0 ,\r\n                        0x90a429aebff4eae8af9f79ee5f7dfdeb5fff8debd7bff4dd0581bfa71da5e7cc ,\r\n                        0x05cc091c0b91126ab5d06936d1f6bb9fe59309f6ad45763b15518fbad458e454 ,\r\n                        0x34c6d9039044b3da76227f0d67e1f8f224662222251a0d251e7aa8bdf2d86397 ,\r\n                        0x1e30866592643736360ebfb9bfbfdf393939b83c1e8fae8e46c9a52008561a8d ,\r\n                        0x46a3d96cc9286a90d66eec8bab0aa7db5e8e3008b11a2d5d7fe10f9ff9ddce6a ,\r\n                        0xbf75e143f7070f7de2f125acd3a540853f2195bc2a845c9152a8318d30c5046d ,\r\n                        0xae35919481211fcf28cbd0b7b677f537bff19daf7eed6b2ffc4fcf7ffdf93f3d ,\r\n                        0x3a3a8ab1d8caf41e1f770480948aa4448b4837a8b635b6bf6f838054b72bd646 ,\r\n                        0x237b640c4cab85a5c1402c1f1d996d6636cc30001b2744b644d0b5c7babcf5cf ,\r\n                        0xe9f2bedf87d5cc065431d78656b39f5d5873c589ca0d3ba34875a48c9e3a3aca ,\r\n                        0x6f1a83acd552eb8f3d76df8796967aaf66d9030745918fa7d3e98de974bc3c9d ,\r\n                        0x8eefd7ba5833c62cb55acda8d1689294c0d1d1ab675e73cb16ed661bdd6ed700 ,\r\n                        0x38991c0cdf9c1c0c37dff8eaf7f67ef6bffb8ff54a676d09060f28a8a749d0c7 ,\r\n                        0x04c94f6df3e6fa9abe3808c220a8f621f2316422429666fcc69baf6d7ef5abdf ,\r\n                        0xf83f9f7ffe1bffdbf5eb5fbc81f9713c3fb03ef23b02a0d56aca5eaf712d08d4 ,\r\n                        0x521579a7d9250f43adb2ac8094bc2225e0cae944b8b4a4ae300b57ede7e63fc1 ,\r\n                        0xd75f948fcf788eeb1348b836a798e736f4984fc8cdd965ee71d1d2da72ab259f ,\r\n                        0x72cfb120329d8b175b1fb75698b2afc19d4cc658591445668cb1cc1051a4d4c6 ,\r\n                        0xc60b72b15e430881cb972ee1333ff993763299dc04f0522d085306600efdf94d ,\r\n                        0x935aa5a51e1c260757cc041f5722782a0cc3cf0461705529b5acb591c3517afc ,\r\n                        0x9dafbff8075ff9ab7ffbbffef99fffcbe7bd655feff6f9811ee7d9364ea76932 ,\r\n                        0x3666aa1600c000d370781026c9a456b5cb084355f47a1773a240cfc032137a0d ,\r\n                        0x04580044f9f995903d2866ba88aa5135b70bcad06874104ea7130950503eaf94 ,\r\n                        0x4c07838b2951a84b9551aa9e59d332113393d63a502a3861e68099ef871b7844 ,\r\n                        0xebebf7f3cffeeccfbe757c72fccfbef18d6ffc1f006edd21f2a601ecfbf31be3 ,\r\n                        0xd14832632908820783407dfae878b8922493affdd3dff89fbf7c78341cd3fcfb ,\r\n                        0x7e70bcff4e00f0eaabafbe321c1efd2d3ca3fe000000bb49444154174a0561bd ,\r\n                        0x1e6fd6696b68b63944bdfd5bf1bca0f80e39b5db875a6e137de5db7a67c464ad ,\r\n                        0xa53a3bb86a24c96780874ee715886edd7a2b0f0245599677ffff31313199fff9 ,\r\n                        0xf3c7e4e1a3478fd838d867f7f7f59fbf7cf9f26f32c2fb2f52097186818181a1 ,\r\n                        0x303d14b95d44f7234388e9057c6660603880a795001d48c1334c4e4540dc021a ,\r\n                        0x469cfbfbb0ebff8fc197919163b872e51203030303c3b56bd7b6f2f3f30bac5b ,\r\n                        0xb7eebbaa9aeab7cb972f53db5b03765e0c00bbaf90621990b549000000004945 ,\r\n                        0x4e44ae426082\r\n                    End\r\n\r\n                    LayoutCachedLeft =5940\r\n                    LayoutCachedTop =120\r\n                    LayoutCachedWidth =6780\r\n                    LayoutCachedHeight =1020\r\n                    TabIndex =6\r\n                End\r\n                Begin CommandButton\r\n                    Transparent = NotDefault\r\n                    Cancel = NotDefault\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =215\r\n                    Left =6840\r\n                    Top =120\r\n                    Width =240\r\n                    Height =180\r\n                    Name =\"cmdCancel\"\r\n                    OnClick =\"[Event Procedure]\"\r\n\r\n                    LayoutCachedLeft =6840\r\n                    LayoutCachedTop =120\r\n                    LayoutCachedWidth =7080\r\n                    LayoutCachedHeight =300\r\n                    Overlaps =1\r\n                End\r\n                Begin Tab\r\n                    OverlapFlags =95\r\n                    TextFontFamily =18\r\n                    Left =225\r\n                    Top =1200\r\n                    Width =6750\r\n                    Height =3480\r\n                    FontSize =8\r\n                    TabIndex =3\r\n                    Name =\"tabInstallType\"\r\n                    FontName =\"Cambria\"\r\n\r\n                    LayoutCachedLeft =225\r\n                    LayoutCachedTop =1200\r\n                    LayoutCachedWidth =6975\r\n                    LayoutCachedHeight =4680\r\n                    BackColor =15130848\r\n                    BackThemeColorIndex =-1\r\n                    BackShade =100.0\r\n                    BorderColor =5324600\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15130848\r\n                    HoverThemeColorIndex =-1\r\n                    PressedColor =15130848\r\n                    PressedThemeColorIndex =-1\r\n                    HoverForeColor =15130848\r\n                    HoverForeThemeColorIndex =-1\r\n                    HoverForeTint =100.0\r\n                    PressedForeColor =5324600\r\n                    PressedForeThemeColorIndex =-1\r\n                    PressedForeTint =100.0\r\n                    ForeColor =5324600\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                    Begin\r\n                        Begin Page\r\n                            OverlapFlags =87\r\n                            Left =300\r\n                            Top =1590\r\n                            Width =6600\r\n                            Height =3015\r\n                            Name =\"Page40\"\r\n                            Caption =\"Basic Install\"\r\n                            LayoutCachedLeft =300\r\n                            LayoutCachedTop =1590\r\n                            LayoutCachedWidth =6900\r\n                            LayoutCachedHeight =4605\r\n                            Begin\r\n                                Begin Label\r\n                                    OverlapFlags =215\r\n                                    TextAlign =1\r\n                                    Left =540\r\n                                    Top =1620\r\n                                    Width =2100\r\n                                    Height =360\r\n                                    FontSize =14\r\n                                    FontWeight =700\r\n                                    ForeColor =5324600\r\n                                    Name =\"lblHeading\"\r\n                                    Caption =\"Install Add-In\"\r\n                                    LayoutCachedLeft =540\r\n                                    LayoutCachedTop =1620\r\n                                    LayoutCachedWidth =2640\r\n                                    LayoutCachedHeight =1980\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =215\r\n                                    TextAlign =1\r\n                                    Left =540\r\n                                    Top =2100\r\n                                    Width =3480\r\n                                    Height =1440\r\n                                    FontSize =10\r\n                                    ForeColor =5324600\r\n                                    Name =\"lblSubheading\"\r\n                                    Caption =\"Click the Install button to install or update the add-in.\\015\\012\\015\\012In some\"\r\n                                        \" more secure environments,\\015\\012you may need to adjust additional\\015\\012optio\"\r\n                                        \"ns to use this addin.\"\r\n                                    LayoutCachedLeft =540\r\n                                    LayoutCachedTop =2100\r\n                                    LayoutCachedWidth =4020\r\n                                    LayoutCachedHeight =3540\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =215\r\n                                    TextAlign =1\r\n                                    Left =540\r\n                                    Top =3720\r\n                                    Width =3060\r\n                                    Height =300\r\n                                    FontSize =10\r\n                                    ForeColor =5324600\r\n                                    Name =\"lblInstalled\"\r\n                                    Caption =\"Version 4.0.11 currently installed.\"\r\n                                    LayoutCachedLeft =540\r\n                                    LayoutCachedTop =3720\r\n                                    LayoutCachedWidth =3600\r\n                                    LayoutCachedHeight =4020\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =215\r\n                                    Left =300\r\n                                    Top =4200\r\n                                    Width =3720\r\n                                    Height =300\r\n                                    FontSize =10\r\n                                    Name =\"Label67\"\r\n                                    Caption =\"joyfullservice/msaccess-vcs-addin\"\r\n                                    LayoutCachedLeft =300\r\n                                    LayoutCachedTop =4200\r\n                                    LayoutCachedWidth =4020\r\n                                    LayoutCachedHeight =4500\r\n                                    BorderThemeColorIndex =1\r\n                                    BorderTint =100.0\r\n                                    BorderShade =50.0\r\n                                    ForeThemeColorIndex =1\r\n                                    ForeTint =100.0\r\n                                    ForeShade =50.0\r\n                                End\r\n                            End\r\n                        End\r\n                        Begin Page\r\n                            OverlapFlags =215\r\n                            Left =300\r\n                            Top =1590\r\n                            Width =6600\r\n                            Height =3015\r\n                            Name =\"Page41\"\r\n                            Caption =\"Advanced Install\"\r\n                            LayoutCachedLeft =300\r\n                            LayoutCachedTop =1590\r\n                            LayoutCachedWidth =6900\r\n                            LayoutCachedHeight =4605\r\n                            Begin\r\n                                Begin CheckBox\r\n                                    TabStop = NotDefault\r\n                                    OverlapFlags =215\r\n                                    Left =660\r\n                                    Top =2670\r\n                                    Name =\"chkOpenAfterInstall\"\r\n                                    DefaultValue =\"False\"\r\n\r\n                                    LayoutCachedLeft =660\r\n                                    LayoutCachedTop =2670\r\n                                    LayoutCachedWidth =920\r\n                                    LayoutCachedHeight =2910\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =215\r\n                                            Left =945\r\n                                            Top =2640\r\n                                            Width =2595\r\n                                            Height =465\r\n                                            FontSize =10\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label36\"\r\n                                            Caption =\"Open add-in after installing to trust the add-in file\"\r\n                                            LayoutCachedLeft =945\r\n                                            LayoutCachedTop =2640\r\n                                            LayoutCachedWidth =3540\r\n                                            LayoutCachedHeight =3105\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    TabStop = NotDefault\r\n                                    OverlapFlags =215\r\n                                    Left =660\r\n                                    Top =2250\r\n                                    TabIndex =1\r\n                                    Name =\"chkUseRibbon\"\r\n                                    DefaultValue =\"True\"\r\n\r\n                                    LayoutCachedLeft =660\r\n                                    LayoutCachedTop =2250\r\n                                    LayoutCachedWidth =920\r\n                                    LayoutCachedHeight =2490\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =215\r\n                                            Left =945\r\n                                            Top =2220\r\n                                            Width =2595\r\n                                            Height =300\r\n                                            FontSize =10\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label40\"\r\n                                            Caption =\"Use Ribbon Addin\"\r\n                                            LayoutCachedLeft =945\r\n                                            LayoutCachedTop =2220\r\n                                            LayoutCachedWidth =3540\r\n                                            LayoutCachedHeight =2520\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =215\r\n                                    TextAlign =1\r\n                                    Left =540\r\n                                    Top =1620\r\n                                    Width =3240\r\n                                    Height =360\r\n                                    FontSize =14\r\n                                    FontWeight =700\r\n                                    ForeColor =5324600\r\n                                    Name =\"Label61\"\r\n                                    Caption =\"Advanced Options\"\r\n                                    LayoutCachedLeft =540\r\n                                    LayoutCachedTop =1620\r\n                                    LayoutCachedWidth =3780\r\n                                    LayoutCachedHeight =1980\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin TextBox\r\n                                    TabStop = NotDefault\r\n                                    OverlapFlags =215\r\n                                    IMESentenceMode =3\r\n                                    Left =660\r\n                                    Top =3960\r\n                                    Width =2760\r\n                                    Height =300\r\n                                    FontSize =10\r\n                                    TabIndex =2\r\n                                    Name =\"txtInstallFolder\"\r\n\r\n                                    LayoutCachedLeft =660\r\n                                    LayoutCachedTop =3960\r\n                                    LayoutCachedWidth =3420\r\n                                    LayoutCachedHeight =4260\r\n                                    BackShade =95.0\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =223\r\n                                            Left =660\r\n                                            Top =3660\r\n                                            Width =1485\r\n                                            Height =300\r\n                                            FontSize =10\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label63\"\r\n                                            Caption =\"Install Folder:   \"\r\n                                            LayoutCachedLeft =660\r\n                                            LayoutCachedTop =3660\r\n                                            LayoutCachedWidth =2145\r\n                                            LayoutCachedHeight =3960\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    TabStop = NotDefault\r\n                                    OverlapFlags =215\r\n                                    Left =3480\r\n                                    Top =3960\r\n                                    Width =420\r\n                                    Height =300\r\n                                    TabIndex =3\r\n                                    Name =\"cmdChangeInstallFolder\"\r\n                                    Caption =\"...\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000708890ff608090ff607880ff507080ff506070ff405860ff ,\r\n                                        0x404850ff303840ff203030ff202030ff101820ff101010ff101020ff00000000 ,\r\n                                        0x0000000000000000708890ff90a0b0ff70b0d0ff0090d0ff0090d0ff0090d0ff ,\r\n                                        0x0090c0ff1088c0ff1080b0ff1080b0ff2078a0ff207090ff204860ff20303050 ,\r\n                                        0x0000000000000000808890ff80c0d0ff90a8b0ff80e0ffff60d0ffff50c8ffff ,\r\n                                        0x50c8ffff40c0f0ff30b0f0ff30a8f0ff20a0e0ff1090d0ff206880ff202830b0 ,\r\n                                        0x00000000000000008090a0ff80d0f0ff90a8b0ff90c0d0ff70d8ffff60d0ffff ,\r\n                                        0x60d0ffff50c8ffff50c0ffff40b8f0ff30b0f0ff30a8f0ff1088d0ff204860ff ,\r\n                                        0x10283020000000008090a0ff80d8f0ff80c8e0ff90a8b0ff80e0ffff70d0ffff ,\r\n                                        0x60d8ffff60d0ffff60d0ffff50c8ffff40c0f0ff40b8f0ff30b0f0ff206880ff ,\r\n                                        0x10486090000000008098a0ff90e0f0ff90e0ffff90a8b0ff90b8c0ff70d8ffff ,\r\n                                        0x60d8ffff60d8ffff60d8ffff60d0ffff50d0ffff50c8ffff40b8f0ff30a0e0ff ,\r\n                                        0x406070f0506070308098a0ff90e0f0ffa0e8ffff80c8e0ff90a8b0ff80e0ffff ,\r\n                                        0x80e0ffff80e0ffff80e0ffff80e0ffff80e0ffff80e0ffff70d8ffff70d8ffff ,\r\n                                        0x50a8d0ff506070a090a0a0ffa0e8f0ffa0e8ffffa0e8ffff90b0c0ff90b0c0ff ,\r\n                                        0x90a8b0ff90a8b0ff80a0b0ff80a0b0ff8098a0ff8098a0ff8090a0ff8090a0ff ,\r\n                                        0x808890ff708890ff90a0b0ffa0e8f0ffa0f0ffffa0e8ffffa0e8ffff80d8ffff ,\r\n                                        0x60d8ffff60d8ffff60d8ffff60d8ffff60d8ffff60d8ffff708890ff00000000 ,\r\n                                        0x000000000000000090a0b0ffa0f0f0ffb0f0f0ffa0f0ffffa0e8ffffa0e8ffff ,\r\n                                        0x70d8ffff90a0a0ff8098a0ff8098a0ff8090a0ff809090ff708890ff00000000 ,\r\n                                        0x000000000000000090a8b0ffa0d0e0ffb0f0f0ffb0f0f0ffa0f0ffffa0e8ffff ,\r\n                                        0x90a0b0ff80a8b0800000000000000000000000000000000000000000906850ff ,\r\n                                        0x906850ff906850ff90a8b05090a8b0ff90a8b0ff90a8b0ff90a8b0ff90a8b0ff ,\r\n                                        0x90a8b090000000000000000000000000000000000000000000000000a0787050 ,\r\n                                        0x906850ff906850ff000000000000000000000000000000000000000000000000 ,\r\n                                        0x000000000000000000000000907860ff9068506000000000a0787010a09080ff ,\r\n                                        0xa0887050907860ff000000000000000000000000000000000000000000000000 ,\r\n                                        0x000000000000000000000000a0988040a09080ffa08880ffb09880ffa0908080 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =3480\r\n                                    LayoutCachedTop =3960\r\n                                    LayoutCachedWidth =3900\r\n                                    LayoutCachedHeight =4260\r\n                                    Gradient =0\r\n                                    BackColor =15130848\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                End\r\n                                Begin CheckBox\r\n                                    TabStop = NotDefault\r\n                                    OverlapFlags =215\r\n                                    Left =660\r\n                                    Top =3270\r\n                                    TabIndex =4\r\n                                    Name =\"chkCreateCompiledVersion\"\r\n                                    DefaultValue =\"False\"\r\n\r\n                                    LayoutCachedLeft =660\r\n                                    LayoutCachedTop =3270\r\n                                    LayoutCachedWidth =920\r\n                                    LayoutCachedHeight =3510\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =215\r\n                                            Left =945\r\n                                            Top =3240\r\n                                            Width =2595\r\n                                            Height =285\r\n                                            FontSize =10\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label68\"\r\n                                            Caption =\"Create compiled version\"\r\n                                            LayoutCachedLeft =945\r\n                                            LayoutCachedTop =3240\r\n                                            LayoutCachedWidth =3540\r\n                                            LayoutCachedHeight =3525\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                            End\r\n                        End\r\n                    End\r\n                End\r\n                Begin CommandButton\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =215\r\n                    Left =4200\r\n                    Top =1800\r\n                    Width =2400\r\n                    Height =780\r\n                    TabIndex =1\r\n                    Name =\"cmdInstall\"\r\n                    Caption =\"   Install Add-In\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    Picture =\"Export.png\"\r\n                    ImageData = Begin\r\n                        0x89504e470d0a1a0a0000000d494844520000001e0000001e08060000003b30ae ,\r\n                        0xa2000031697a5458745261772070726f66696c65207479706520657869660000 ,\r\n                        0x78daad9c698e2639cea4ffeb147d04edcb71b402738339fe3c468fccaaaceefe ,\r\n                        0x80194c052a33f25ddc2591349a5194bbfbbfffd773fffad7bf42c835bb5c5aaf ,\r\n                        0xa356cf7f79e41127bf74fffdf7fd1d7cb63fbf7fdc9ff7c29fafbbdf6f445e4a ,\r\n                        0xfc9dbe7fd69fd7c3e4f5f2d7175afe797dfdf9ba6bfbe73afde742e1f785edbf ,\r\n                        0xa43bebf79fcff59f0ba5f8bd1e7efeedc6cff766fedb747efeaffef734be51fe ,\r\n                        0xe3dfb9b118a770bd145dbc2924cf9f5d7749dfff93ff037f8654f8904f8ddf0b ,\r\n                        0x3f7a25ffe7b573bf7ffdc7e2fdfeed1f6be7e7cfebe9cfa570bfc75bffb1463f ,\r\n                        0xaf87f28fd7d3efdbc43fadf6d79dff786385b8fcdffffbdbdabd77fa7bf79bdd ,\r\n                        0xc48946aeee6752bfa662bff1412e92937dadf2d3f8bff07bb39fc14f678a1b8b ,\r\n                        0x1dacb9f8d92e8c1059ed177238618617aefdbdc3668839ded8f83bc61d93bdd6 ,\r\n                        0x538b236e3346d64f78b1a5918e4b1d3b6dac967839fe1e4bb0fb0ebbdf0e9d3b ,\r\n                        0x9fc02763e062816ffcdb8ffb4f2ffebffcfcbed07b72dd107cffbd568c2bcaa7 ,\r\n                        0x19862ca73ff9140609ef674d8badaffdb8bff98dff9b6113162cb6cc9d094ebf ,\r\n                        0xbe4bac12fef2ad64764e7caef8ecfc171aa19d9f0bb044dcbb309890b080afb8 ,\r\n                        0x77a8c1b7181b809162c73e9391c794e3c202a19478827bd826a58a717ad4bdf9 ,\r\n                        0x4e0bf6d958e2f732d082214aaa844ac7421363e55cf09f963b3e34899eec4a29 ,\r\n                        0xb5b4d2cb28b3a69a6ba9b5b62a8c9a2db5dc4aabadb5de469b3df5dc4bafbdf5 ,\r\n                        0xde479f238e048495514773a38f31e6e4a6934b4fbe3df9c49c2baeb4f22aabae ,\r\n                        0xb6fa1a6b6edc67e75d76dd6df73df63cf1a443f89f7a9a3bfd8c336fb8b8d2cd ,\r\n                        0xb7dc7adbed77dcf9f0b5975e7ee5d5d75e7fe3cddf56fbb1ea9f560bffb0dcff ,\r\n                        0x6cb5f06335592cdbe7da5f56e3e5d67e5d22084e8a6c86c5620e58bcc9023874 ,\r\n                        0x94cd7c0f3947594e36f32312142562b550649c1364312c986f88e585dfb6fbcb ,\r\n                        0x72ffa3dd5cc9ff57768bffcd724ea6fbff613927d3fd58eedfedf61fac76a665 ,\r\n                        0x94640652146a4d7d7a001b93676af7fa3799670fb7c7952b8bf018f52de5ad99 ,\r\n                        0x16d38b9820a453d7f169a7f65e94151f01dc09662e747badab8f9acbd9ed96c0 ,\r\n                        0x08e33a25edc048c820bdd4dd574e6160da174b6eaf9dd9c7e2264d9e70e2ccd9 ,\r\n                        0xbdb55e3f2dc7c824f1c13b63d45dd6f585dbfffd45bdd4d29e7c3d8d9df28c8c ,\r\n                        0xf5ad54e46498bfaf771fbed1ce98c0f5b881911e8279e18a61efbcce9cb8ca29 ,\r\n                        0x7ca29efd4ac7a19e61927d2b1eaee0dab9bf47201c18ccadced2d33b3857cb25 ,\r\n                        0x9679562f37f2c1b0dfc38f733e4c7a62fa183a2b50fc8c6ee10f61cfd72797cd ,\r\n                        0x6532532d9f6f8fb55b98b0c5f6e66c7ab5e5d3265f0083324b371f1e45ced983 ,\r\n                        0x21b9b378211e4659f5fd8c5d987a8453ecc3a48faeb336769c24b05ea3ef3bf3 ,\r\n                        0x0b96223c70bd55f3c3e7965bbee3247330d9135e9e0ce19ec412ec574762f5f0 ,\r\n                        0x8e3ceeab77e7be7d39797bbecbf836962cfbe896bce75e21baf01566d15aac33 ,\r\n                        0xb51d6555be992e067b04ca7917b80d07dff49a737af3f2d7bb0b3f3db51e06e3 ,\r\n                        0x18724d8fe9e196384ff1dbeed6de39fcff6e7d2fcc7e5e612ccd6cc5c51b7ed8 ,\r\n                        0xde586f5792ae9cffba91f76838ee78d81617d90760386d359fefbd2fda4d6184 ,\r\n                        0x7786b6b8feac4577793b69981907397797c385f6637507899435bfcc3ee1b7c4 ,\r\n                        0x25f1b7fad997a5c4d687907f31df1dce19bd9edbf3258e27810acf7ab31537db ,\r\n                        0xc135b1410bf9f0d184090022ecd8083da14d0b7b608c97c5b1f67df932d57e43 ,\r\n                        0x660473dda7d54acd5dd825c4a1acdca69f27f7c1f4f8da67c633a11adb56b6dd ,\r\n                        0x511879888c27ca27f0593e3be5fe6325070aa44c9a4ecc83f5c3e34e03be887b ,\r\n                        0x0852b8bf0c4dd0847ecac5cfd6226623467edd0c93e6ab9ee827b79eebef02b6 ,\r\n                        0xa706f91e18526a638cbdb37227a6459a15eeb0ec89abf23bebb85e62b8987410 ,\r\n                        0xc840edc0574faf998bccba3c2e0087ca27e5351ee1f92c34b85e0d63168574d7 ,\r\n                        0x2bafd92dde82383502e84e87fbde0d0f7b38096eb41fa83708dfc7bd2baef905 ,\r\n                        0x341e90f94452f0f28d6997c717f7008e1b7093dce77e8c39dff1eac4e9827c97 ,\r\n                        0xf5da1578df7b74681bd920322abc6fb7a699cfd86a1dd94702e76522c21d7ca4 ,\r\n                        0x1e3201d3605ce732a94bae192fc57915887807082cd3ed4a367c3bc661beb99f ,\r\n                        0x793a64aeae4d16610255f4925cc8eac056b0f35d832b06ae3d0a8e72d6f795d0 ,\r\n                        0x09497ccbd712671b3e570f6491468a7729415f44392f34f544b2eccb6713a895 ,\r\n                        0x3053d4ee0b1891538157ec16d3d927dcc212b70b021d390ae0bf9c709241fe06 ,\r\n                        0x5c160af049ac62e9f2c3d3279174f852ec17e0279e3a59a2969c088c3e62b6f5 ,\r\n                        0x7e80ffcb5c238025c01029288d151631fff33a1e8572da8321cd51234e8ad5f3 ,\r\n                        0xfbc108c12411ccfc01ff99df256326be06161d0d03e87dbde40d627920a6b1ca ,\r\n                        0x2b5afce21c596e36b934c981d47e513a073692c928a04764718901260a285c92 ,\r\n                        0xd32e84c26dac269eb9160638f8b448e308650f9c5cc0cfcc5b349477494391d3 ,\r\n                        0x814901fc6860234c84051863e12abef232e65b78df1d033691c060a64e78939e ,\r\n                        0xc9792991f62617f2508323cb64be1beedde4ae463a237cf3987c88d154b2f719 ,\r\n                        0xfe9153b9760c950b6f9c66231f1e14aa3e470c1d793d7e18cf4971439c403480 ,\r\n                        0x2b4052ee8dab9231ba7cab01371788c66bc95cfe40391a6883addbc94e300b9b ,\r\n                        0x0c0ab6046a0ac662bd99a4dc611e8a8741da9f5808de958dda5df920d28ab892 ,\r\n                        0x7ba162f370c63e8688f66b5c0db51e78ab295decbb0477a5e5201af16ae9ab23 ,\r\n                        0xd370d28905363e0b3c25af1c8b5e03a1ed9d93fa047448347d4d12c4818bdc56 ,\r\n                        0xee4e24739963845405f95acfdb71fe178075dc6e00a8d37169fb78f96f1fdf2b ,\r\n                        0xf805ea9228b124c440c586a75ccae858d4f994a0b263c589003cbd8108967f4b ,\r\n                        0x6a5c377003a6bb374c260d98095f196b6127d11c56035c2067e17cc302dae124 ,\r\n                        0x24d6d377c1efccf161983561b6dcc223775f628fac82764997a4c9b7703d4b94 ,\r\n                        0x0727271541f5742103f60d7d81f4110b05db6a1c18b5a4d1f283bafebe817122 ,\r\n                        0x90c64ff4d02bbb178d077fe6eb8ea821a3363c0f941fe004947840b1e5850a2a ,\r\n                        0x38fb7a6de6bb082a406d284cfa3c704458701ab5e0ef085e57269882df47300f ,\r\n                        0x2f793dd5b73bc3255f6b04b912c778545a72a300b3e44a7c633745b17d9a2578 ,\r\n                        0xd1edbf7d9c35de7b8baccd00a86442964f4fd3709194045a8532062031e4dd40 ,\r\n                        0x5321da8853d0db3d243666d012b1148ba8a941b6ade0208c7e754617e0eeaf2e ,\r\n                        0xc2cd5c768a5a6db020a9cef07ceeb73707a28bd4870e6742d8e31c9325042970 ,\r\n                        0x37c0866bf9983a76d6a7f93084008e798b92f7088d70d38b61b34644162ee803 ,\r\n                        0xa1ca4af30dd0a0e9c3c4db636d59b2273e45e02ecc483ac22eb94bef72990d92 ,\r\n                        0xe206db05011024332e80050f3c48b3cfadf7629d40571245922e0cbcb1dbb880 ,\r\n                        0x3c6f4300e7947e63589d24efa02d38f6dae0d74aa2bf90fef9a02bc0da688524 ,\r\n                        0x3950c66be48478ae17172134218f10ff10f90a5fee17d66e99b68a262e187624 ,\r\n                        0x51b1c6902f6970251b600413ec7ca033782ca2a8219f4038e06430928d1c3aa4 ,\r\n                        0x780f63ab81f5875c15c606a6f20d980d73c1b8a4a2051442dec82ec40030015a ,\r\n                        0xc0c533cab0f655cf1c64fab013fc684f09b3debeba0e13002013698cc4b22f5a ,\r\n                        0xe7b038abc152e6c69da5db482aa78cde310d783e9bca1ecd914cee53ae25bfb2 ,\r\n                        0xfe283d40e6276062de38cc65bec05b9dababf8034bf6d04dc416172c533c53de ,\r\n                        0xe76076093f28dc344b50418bc5eb10f37b1387658b2b91556206e30ff02170dd ,\r\n                        0x418c7a2c2fc28e0e251c1c266cf24db40fd412179ab8e31ed743cf4163ec85ef ,\r\n                        0xd7ad61eb8315360520951f0ac9b20f7905ea0867f108ae54179151f287ba08a9 ,\r\n                        0x406a52154581b585241fc19498419c0fecb1a1490339c2044b83d57ac06f90f8 ,\r\n                        0x898d036dcf30506895a86697de1aad9bba43cfc72d8f44ac20a79fc07f4b3a01 ,\r\n                        0x731aae2b49a21e8213e044b5a05e375183b8917bc1ae02799e98476f0f3e0a12 ,\r\n                        0x8983a62780ada4b24c7659f8547547bcab02c59f22aba4cbb6f3b5a967ff240e ,\r\n                        0x353c2575af8b10b313188085c14bbbac4180a5555d4f92452c8dbedcbe2feb6b ,\r\n                        0x7ffb3a2813994b33a86528f51337d2f190088852c7291c3e078d26abd58c3627 ,\r\n                        0xe45bd99714f984c5467f483ee54b554889c73245f2f42e1eb210ce506d73919e ,\r\n                        0x911024a7610c714431745139df9a0813ce87806df8a58a2930ad6b492adea3bf ,\r\n                        0xf7fc2eae22e9ae6eea9b10d02ad5089efffdcdd54aaed8138ef4027a8ce5eb25 ,\r\n                        0x323e163dad240683574308fb04fc711a323d24187641082a931d1413e10ac5f4 ,\r\n                        0x6da8c49158af84ef27342ff86284805c0decdc6a62795e871e1e11c87f9e8caf ,\r\n                        0x5a2097e75fbadf5cc846185ec510625bb025e115cbc53d2641d4f37e30f70b89 ,\r\n                        0x7b6e8ae0152582e2530a35be5833805482986824a12cade791d438b696d9d225 ,\r\n                        0x76092b730be5f4198e5ba0d91b23b574af2540dc186e23c1064726514e51a9a2 ,\r\n                        0x845ecd24ccfd48412cc8ecb6ec5766d9f06c44513d9a264454a28dc523b69a0d ,\r\n                        0x08e201deaf9053c8c8083c115a5aaf9cb403630dcd1361bea05e72025cf31526 ,\r\n                        0x0d344ed861dd156f5d2be1e82cfe106d00bf414c120de4307aa413bc85acbe49 ,\r\n                        0xabe03d6beac24d4b0ade0802ab8f2042d4884281c517db18afb9704b04b85679 ,\r\n                        0x836f1217f27ee5a50b77a81deac72b9d487e75db22f251fd99207c25c5ee3fd8 ,\r\n                        0x7dad9bf26a7601b0edbd2f631cae4f1248d58950ec569859aac8fcb0da0cb9e2 ,\r\n                        0x748091273d1dfb6a203a81be7c7b85c31ce008006e8c00715158a11dc4211326 ,\r\n                        0x46722ce22b2852fa95269c42502201f0dc4821438f40f643c2ce0c147662eb80 ,\r\n                        0x23a2e7c43dd11fb80a89690ebe009e004490f80156799c1861717264346053aa ,\r\n                        0x4844d85d9a20378aa8554c7b441de7f10ec4c1af182b11a0d504019575bb3207 ,\r\n                        0x790ebb1010b66eb581628dc5ec30c28b34c0b2a9d644aac2f155d250b5ad10bc ,\r\n                        0xd23990de26a50054749244f71f0759e853e4d5ed569a9aaa97e0e84875b25d78 ,\r\n                        0x78fc72a80ec13c61a9f947c9ed4a767d5f61d0824adb1609ca8be73115b90f53 ,\r\n                        0x219d6d15d9cf180736511da141da3c640992c78355d5da018410e00c613ee8f7 ,\r\n                        0x2a52b7e7c9bef00240afcaf82400b15450160e7a1891854e79f190ce6a2e524f ,\r\n                        0x912c03c66187a84259666604340aa2291c062b054562d4a402d80a8e4a527780 ,\r\n                        0x0ce104fa6ca84b5dcae42c1f5c10aa2631e211f1e429d57156272331f11755a1 ,\r\n                        0x628a193a8e34031b2e0e8909a5aed7edb0ef51f09e71d7f692c9e40b82f07a10 ,\r\n                        0x413c1352f424c671982cbac51ac6f325021f8c1f5d18019188c3b1c88c1bccab ,\r\n                        0x09306a983a0b9370ea9b200f2a93d53d085ca80d6999bc971049c7f7a9527d6a ,\r\n                        0x43c3251fa1ab2b9ec27d41eca68228fa9414009d824235aebd9302237612ea12 ,\r\n                        0xd35cb508642609b2492fbf26300578bade4730e2edb05b15247d2910e6015a6f ,\r\n                        0xd6f6b28480cc43fd0e0b723fc4ad0779ad98ee5572ae5a899c937804149ffc27 ,\r\n                        0xae88197141857efcb271f18c797f05a183ab609ade83eb358a23a42f0c90e15f ,\r\n                        0x221a1532889fa1bdc89911ae821d81523c0e99a192a50402fc111078a7593dfb ,\r\n                        0x215be7a8622a0756385468571a61b6901480b97a565a10a10261422bcfc48a79 ,\r\n                        0xe07449072284f97178d108aab3c0659548a48d1b97bc56fc5b155ee0cf7cda83 ,\r\n                        0xc1fa78e4510db28305616b1d01350cc4d4c81f156e0e31e8385b10d55bcfe842 ,\r\n                        0x06807ae08679113e3f77ca658b1309ef2cabc7079d1aa82be751773d6af94f47 ,\r\n                        0x8335382601cb8dee005532866d402e04034b83645d0015d1c3307178218b0035 ,\r\n                        0x6414cb8140aaf8aea07a10d0024d05a00e42917c4396bd617bb166184b60102c ,\r\n                        0xcce62e90cd66548be85419b65e6416c2fa8898c1301027b1a82c8a0e811ed7a5 ,\r\n                        0x2d1b837b5583548b140a61040802026073cff6c41c5776a3c134f09adbb395ec ,\r\n                        0x6b812a2837f76fa5ae143b8b4ac49d0caa6f9574db12234f8033b395eadecf09 ,\r\n                        0xb05554315842cac1db21a0e559de54fe80c30a35912ae4741c9b2901c47c97f4 ,\r\n                        0x95faef1b3add71e287bd10a1781d815ab80d37323a65044ce436c119ee0aa008 ,\r\n                        0x291d2ba9686f095cee7e637543c933e28ca0b8871901c8c2fdfc85c0ce050294 ,\r\n                        0xc1267c6229e711515e24eae5f3714c74ea4003390224618ad281e4d1cfed6468 ,\r\n                        0x6e93b73429a1f23a68b77bf86aeba877d5d6ed5f5064812e89a99c5adc92f3f0 ,\r\n                        0xfe504a566c6a81d16615051243b5fc8394eff192f5996f26bbc4cad4e1064b04 ,\r\n                        0x0c49a97d0be57eb82b29a3f8fa0773351b5e1c92e4c57cc99107b549a89518fa ,\r\n                        0x906442e6dddc8974d67c38e938338258f6c24f875618f637b5bfd7150853f577 ,\r\n                        0xa80974e2a93c1ce7d64ed7579953257f43131cfa2d292aa6d5cb57503a52a578 ,\r\n                        0xc01bfaf6bd5e392c627fb3e8bde648cee693712f34554552a18d4909cb1dd205 ,\r\n                        0xb72e2ad692b24e41e3a82602fe4b67024c01fa97aa70c4f6981b0e3aef565247 ,\r\n                        0x9d4002b80f599d0bddfe6c2962478369dd55d36cd3b2c3c31f4939004ab7b209 ,\r\n                        0xd905e8fc5d872fbaf12e6b62fede208916ab9a0ed2df0f4f5a22dfa83c0eee90 ,\r\n                        0x2cb8a56d76a0de1b4111592488c45d3835df247c414b776235fa83a0fd65fad1 ,\r\n                        0xc7f3842ddc33f535e580a7fe7a379233e52a68e63e1095687206d0b7c3797e8a ,\r\n                        0x5bf8d7013fc07c2870e196981217039ff505a6cb20711bb071aa54583b041305 ,\r\n                        0x806c6fb02927cfaca7462561f2570ff89a72fecf0ae5970509537b818436f1ac ,\r\n                        0x72e0f9c20769dacb162d38c7e90e5c796ef8762091413c3c24ba11427713ea81 ,\r\n                        0x7806a409c20b114a3000fe13fde92a6a21a403b7851ab890ce6611bab6ca10a1 ,\r\n                        0x6327bfb173953f04092e6d756ab34c8a181aa584a0cd5548747c160b15118b5c ,\r\n                        0xdfdab2d486d820794d1886b60031d6a8fcd204fbabb344fd443825b92954651a ,\r\n                        0x8f9a11b413c5f7aa965adc04d50944a478e91d172310a0c9acf39877a90a9b89 ,\r\n                        0x8097bd76098af61355b801b3060ce888b9171427da11bd460ada409bded37e01 ,\r\n                        0x9c76117f43757e2840cf27707fedb480306faa77e0fd73d14b6ace362cc85529 ,\r\n                        0x0651db08a4a55e35f9297e02eb0f52cbdafc80307a79e2ee971ba17822c402c5 ,\r\n                        0x60a903ab911e2044a8a9016bf3c03af043cac73f8e76e1d3c41903b965a110cb ,\r\n                        0x530ad0b63777ed0b24220a206f974c5bb94b8a843d28cbfbca5d80443c630d6d ,\r\n                        0xce934c4b065fd61186b1fa4736092603b4ab3c8e716def00f20c79005355ef2d ,\r\n                        0xc776ef557f0220710f322440753ba84ebc679596587c3e8ecb41d8228ed12d24 ,\r\n                        0x1ddac26bcf05ce06c06a186faa625137249dabf528f7acda46583df6e9a18512 ,\r\n                        0x585fc51c26f67db638441cc4aa0b4b994a5a3045400880470324befb46cc2a2d ,\r\n                        0xa60e5412cb2871a3fe18f6ca5a4ab0509be746fe82e4f674b4f5348bc75853db ,\r\n                        0x2844520e04096469554060abc381fc02afc81eb6c4b28a8a32913201b6a10ac7 ,\r\n                        0x845640ad065a1e57058e9800cc0168e4ce952c8ca8256f91ed1489da2e67cafe ,\r\n                        0x42a6470076964a636a840a558953dd53109aaa024f81f1495bc16bf712280c20 ,\r\n                        0xe8242966e4048c90953ee2dd1921018ab3d80891719ed09cb44f6056d82401a3 ,\r\n                        0xdd9784f1b833a8403ac0d04b0d5e7064b2c84515f529a8cb8fbba66fbf1f91c8 ,\r\n                        0x32566519fcb025353e912cda5618b0de688d2726711f40a50dff42c8005780eb ,\r\n                        0x2486b59f46ace1f2100c6d8bb447dc2b51a095551c929e99b1426db32a65d000 ,\r\n                        0xd22376d0b6f0ccda1ff45214bd04cce4783177f85e02d554b958093ce0530d2d ,\r\n                        0x2d27552b06928860046dd6b77981f0823a0edc669edd05cecfbb92080b6dd50e ,\r\n                        0x6f59e867370410b8da71e212e46782ae13ae2bc4fe6dd03104b4346ac05b5708 ,\r\n                        0x69db448deaadf87350ed1347653c90386d0d4b957408176452aa182f20e51c72 ,\r\n                        0x0979fa6005c4ba57e9fee4e20c465304818f4a3a492aae2971f4ab4dc179936d ,\r\n                        0xc5a2c354ccd2d68a8d257edc3224af1d73d4a4935a2b28aa9623699aa8243925 ,\r\n                        0x220eb6462a65148867d0a52320b62ace38234e3d14af2c1c5441c04a50382833 ,\r\n                        0xb1d951c6cc87c867c148c3159bc2a72c2966081cd6e752247d20c6ab6106e82f ,\r\n                        0x1bb90a7b5ecd938a6d8da65a40940fc93ae0d3cdd78321aa5b4f2210a4455290 ,\r\n                        0xfd53455d43ba878a94785c52cbc65559821192693fc5b5072c0d6428a3fb859e ,\r\n                        0x44f24296540801179236cadac8beaa65361dc5fe22c5f92d1fd15bde79e29d78 ,\r\n                        0xc1eda1f0e52ff27498c68e2c035197ea50600167e262229d72908befc2ceb555 ,\r\n                        0xcf951d6180469f91bcb9bd585b0970d06d2d986d6a5be265bc674910aa2095d4 ,\r\n                        0xf5433e19954909ea2baba4e60a1825b939c6abe64e70d4d43497541d164c671d ,\r\n                        0x94303ace874e423a8089284e7ff6fe5838e94c6e925d24be713f2893eab7f85d ,\r\n                        0x52ff655e64ed833d6e278250eb5dfba333ca0b2e8908444395217f419d837c16 ,\r\n                        0x666379b807bc63a538861a942a6ccb834ce0dcd194ab40bb10a7e54e546815b1 ,\r\n                        0x082a39552450ef2bd79cba83306b8700a2303cd6042293c43ab3bac513efdacb ,\r\n                        0x10364da8e601044990f00f820acd8d7392d353031461fe2bada2620140688d1a ,\r\n                        0xa61d498c45729280c36d4fc66ea21e18beaa8086b3431c36bfb2c00b96500136 ,\r\n                        0x00a1940539eee20fd2478da10b50b6409a9861b1f695dd56d056394292512105 ,\r\n                        0xb86b662a6568e7c4494ea3b93392547b9c2cab5a2dbd1a67c437990db90e2578 ,\r\n                        0x6a203d1fdb181fac63a8a9374bfec776c65d51f755b82aa27ba23608efc847e5 ,\r\n                        0xabf55ded6c3388733e511342cb425674d7bd181a92e0b5fd03abd5ab0a2d043f ,\r\n                        0xec3fb57c5785c1ab55648ce4b55f9986b827575b38523b8a5f12052eb6a31a04 ,\r\n                        0x81b3d6c9fd89779e80566d0c84cb7e0d324f5c471821244f8d5eda10b132cf54 ,\r\n                        0x95cbb78f3a910b6e0cdf9e6d760778310fbd2d0966016638a536d4d1bd28a0fd ,\r\n                        0x1ed08d13437ae1ac2c34e9af54e488b57bc10d483704968314aa2c12614566fa ,\r\n                        0xa992dc963176cd24635503d42806ebebda786284acd3d4b8efd65a11d340d676 ,\r\n                        0x6a09b0be0014a88d2c5a2122cd58922a9b90ae8074603ebd5ced4f54d87f177b ,\r\n                        0x5431aae32c52da73412210968819394588d24604c8aad674cbc8bcaa5056c41b ,\r\n                        0xf5b3e41db2b6061dd567b7d54982027049bb5cf2eed583ff4af15015354c29d7 ,\r\n                        0xd6062ac805e22325a0bb14f8e0a07c32df4f90e2e16a1b228aa0d14ffb41e459 ,\r\n                        0xc8331f1bbae589084f0674e457db307c6a03e85d5bcb8ed08321b15a65fae2a3 ,\r\n                        0x83e342b2996551010b4fd4265c9d8843a94a8cac6e09d03749890a541738176a ,\r\n                        0x1c37da2ef154fd17a6e102441f1bab1cc84783b505107b92c85fd38bb95161bd ,\r\n                        0x6033da717c5a155547f6971cd6d7bbec90ad79a96974f70207de68316f7eac96 ,\r\n                        0x91a37dee033f87199180161c10966f9de5380ab86cc9416d30dd5927b4c20989 ,\r\n                        0x3f4a9171e3d606a976af19cf4e2ca26555b19ea96ec7821080478ea8ed95c4e4 ,\r\n                        0xb4808e6933957226f416fc384cb140c0e1e313dda506b0aeae82aa1309601caa ,\r\n                        0x9ec10deb4b1ca9547caa4a6d79c778d451a5f63aaf0a9cba7b02c8ac9d92c418 ,\r\n                        0x36ec480c19334007bcd6c0c68772549b924d12d81b4e7df20206f1f5a87e972e ,\r\n                        0x0fc8ad1ee5de6f9347f57008bb7ab864801ee59b24b6a67c5b54ab1ec9bd066b ,\r\n                        0x220e1b860aaded3cb5a783768613ab6471a51954a3520148484cfe441bd4a226 ,\r\n                        0x0ca83e3349479d2c7ba36899238c0adfdf1fe16bb21c64089a03a999442e10ab ,\r\n                        0xe302b6d18af6c23709ede5bff6dc06743b2b1f20c47093f095940bc4d8ac14a4 ,\r\n                        0xa1b16ed546480eca45da73a848c65ba568bf96d8a49c7e9c5a3c4837f9a72516 ,\r\n                        0x9da8506caa3703d18403a18812405d75b9d33cb77601736de4a2cff3a088feba ,\r\n                        0xae8a957a1dd54bd193daa0e1fe65aaff03a16063c35648102bd090d6501f7009 ,\r\n                        0x0160108f9205914404ed9b5e3da0d6a0d352b1b18129f2dbec5361e26817b14b ,\r\n                        0x35bfa68fb1142b4183b5934156653227e2a2b2fc2c2ca5e84ad56ad429ae8f4a ,\r\n                        0xbba3a34776bbb9880d83713eec6ec7868e644654239f9a039d7a26bce2545d41 ,\r\n                        0x685fb92cbc26239248a8aa264d0b22b205495f05117fc969b984bba73535a3a8 ,\r\n                        0xf8db756bfd628451ec5e8b3c18d3929fab35eb1450132643ac5e75bf77213901 ,\r\n                        0x8d1e28d6ddac2dbcaf6de8a92ca2645923f10ccb1edafe500d4f0de34bdbaaac ,\r\n                        0x4f569556cd800bcdd119db56e59fb1abf76c908add4006ea636aacd43d354fed ,\r\n                        0xa30177f56615785003997c866e458d608f89abc28074777e0595b5ed585d9f33 ,\r\n                        0xaa7f59e5d18d47a8d119d752238679165f568d34b06884bcee0a72a2582e134e ,\r\n                        0x3addc53a260ce1e618aa900b52fa57856c5bacafa4d5c70099b5410be5573e06 ,\r\n                        0x38824a56f1f74dd068522590643744171bb627f633c43cf5837602a1328ab0eb ,\r\n                        0x8c8ab6ee6056deda9859eba6de1ff594a99b1a46a015698fc556d318cb087891 ,\r\n                        0x43fa4fb5a3bef55541921a7444f0484155cd15227deaeef5f9ab8301e840e045 ,\r\n                        0xd484ae843a600c9f77837116d5ab325361945adb8a7a0ad4d0f3541ee6757505 ,\r\n                        0x3f357505759deee6a00e0347279daa9a46aa948e391daea53c3034adbc175ae0 ,\r\n                        0x08528fd2e8541f235002c9e45a2fc0514b74fde007aadbe53e968682222f16ff ,\r\n                        0x3ac41022392eccdc5a5213a5bc265ff8a4ed9e726f65d51333c88784c0cb0924 ,\r\n                        0x24de17e9517b830bb5ac68c975a8e31723342bfd4499041c1c564a86e1d79408 ,\r\n                        0xd19eae1b45d9232103f0fd58978a5a074a9bd4068df426d0ac5063b352391772 ,\r\n                        0xd451abda92239c2f014498b6ab9ed167ed2498f54aa69137e7b76de0cb8f5d94 ,\r\n                        0x11d183a88500a1603212abda1dd47edb4bb6f5e318cdedda983c79911fd5915f ,\r\n                        0xe7d791bf09ba00f4ab15c1202d9f25bd53d4292f7ebae10c8154eb21a68e0c40 ,\r\n                        0xb6de2a75a0ba805a263202543e831472f88b9f4f75b24098fa47e8017872ce9f ,\r\n                        0xdcd2895c0ea16412bf2327cae3dad64aa77d45ce2fa9f2a9c139bea64d98ad2d ,\r\n                        0x35d690f0570d0c6f1546ba934de832a9aae62fe55ae2f70ae1042b082c321d3e ,\r\n                        0xcfa02a9c3adb41475fc9d492f93a57837299373be117a88112550743d1ce90bf ,\r\n                        0xcc9788323cc0cf082d35baab5f8d35d3d1ab52481c04368e7db1122ad1bbdc7f ,\r\n                        0x5a19563ce29cf8205fcd048e7a544de52825b2c855fbd38a4f64e1b739a4a173 ,\r\n                        0x71b5fb37743f7021671b9568470e89e88d42fe80880ac98b1a2452e5cf68c525 ,\r\n                        0xed6a06092ff57de257d1db26976bea5645c7a114ae04dc862a1f72c7d11e1349 ,\r\n                        0x43eb0bd131d62191a5feb9667d9a38b64468d561104fa665d96f350f8adefaa0 ,\r\n                        0x7ea8264803e5caea1353537db346fa5d3b5ea0b617b58f7ee470cb039a23adab ,\r\n                        0x8437b565d6588daf8b5ec576f55f4fb5190d73c7af16322b4b3b7f3a94242c12 ,\r\n                        0xd786120ec7d8e60767e3fe358115675569269279f220bc23e8c49af4a5d38919 ,\r\n                        0x370cb0173bf170b44d7e862b224de943ebf073a8037547588be37515c96c4a0b ,\r\n                        0x12a1f35c24061673cebe8276c926b23e94004282606a20d0495b865f8e0e61a9 ,\r\n                        0xef1b01873fe172b0a182705479315765127503cffa548122d3adb610d0afaac4 ,\r\n                        0xca98b569cfe2698f565c0625b833432b47c5ca8318411556b5b814f5c801a47c ,\r\n                        0xb557eb644712f7908ed37999423e8c76368c68d4513e32363a40cdcfb821f00e ,\r\n                        0x7ef27d5890b7966724ee4065463089448fcbac86a6ad8d245d13509e77af92e2 ,\r\n                        0xeab780b5a9e75c5bc47e288cac5916aa3dc0beedc113404515e09ba238fb7611 ,\r\n                        0x220cad4ab892d7c612790c90d76e18ec8189317c1517c57448355ddabaaa6db6 ,\r\n                        0x82e2eaabc2eb6350ebf99a2ac6da6679094a0bd019042936ee60fbf14c9440c3 ,\r\n                        0x296ab9c2dca86d465c02727dacf69135dfe49dd8efb844ac0adb009e0eb291cf ,\r\n                        0x77a9197abab3ba30b65aa1ad7b5cf56d2e25a91b9ada9f205ebb89b7bb5beb45 ,\r\n                        0xb6f2efd990daea35689d9864bd598aaa7f91a60044b162581c708bc04baa7a8c ,\r\n                        0xc854f91eab31aff3da28b30e4502927c08603e7e993dabd8a402a74ea834062a ,\r\n                        0xbad39b3535202bc140943f23c838d9ecc35549caa0c205a44a552211e4057ad5 ,\r\n                        0xbcd5001e59cf52bf9e88e0b56ac4e1a9da3e6f649da3aeab1e03504b0a28c8c6 ,\r\n                        0xa07340f8512a5081ac9a505deab7dedaa1f22aa3a93c0b6cbfa7b31d883b421c ,\r\n                        0x928430dc24a4e1747a8aeca8b0b732838e7228bda96fab44357d049829f93fde ,\r\n                        0xef24198102b81ff80f9069b7d7f9bf09f32773a8150f27ac980f59b200366803 ,\r\n                        0x4befd5e679216f7d7b6d429ff8f068de6e0166190a5402e2be4249dd89f4964e ,\r\n                        0xdaa8631755a774364ded346091423d2c61e633390b586a17fb036b58048c068b ,\r\n                        0x106e44ad8bfe536444f9c4b152c781de0a358b30ed3b7edad2d41da5f79421d4 ,\r\n                        0x8905c557414c21ae5378b53b5e544fd219d050c8a30e650ed5e0750c147a5eb5 ,\r\n                        0x1b1c200e857ccca850ac45a7e1a024704d24300a8a85ddc76937f56bacc40b96 ,\r\n                        0x0e01f09de6b71a01adfb5dc702c612fc2cb5b62367ec60cb842caa8906a29130 ,\r\n                        0xe9d710f7b18f2536a73d7a5cac19fa3266f5411336da396950c1abde4ff0f5bb ,\r\n                        0xc36ae74b00e858673dfe2c3029e3e8ecda2dbfbafc7fda020ce8d50d24d20313 ,\r\n                        0xd77106a9ec23e4fa2ec3e7af4bdd8ee68c677b8f62d17af97bb54cf59622a755 ,\r\n                        0x388cd5aab4768f0e1e235f60c42bfe1cba72f64e5ee083c2a03688a80e465c70 ,\r\n                        0xda7f9cf120e2a63ae988546d3c4575cf4397cf43eb00c368cae437320b04d58e ,\r\n                        0x406965470517208c42da51dc265d96eda1399734db435a0313e7583733f98e0f ,\r\n                        0x0206a3032f2c3679ade1aa222fa4fa0e580868a1250bd7af00981ec090ccc719 ,\r\n                        0x9e58ac4e45c2da2499210e92972dba89d9c16418868e1591eb51fcd0373556e2 ,\r\n                        0x7b040d97bd5bbbccb80d836e10286b9aebb05a73e65dec2414d9137a4392d571 ,\r\n                        0x89900231a009aa635b35d762671910db034b759d2726b9699f39ecbd0b010405 ,\r\n                        0xe07e77381d28843856b57e933d81963d51af6830aff298205b12492d3a5a356d ,\r\n                        0x37be23b20efee16e0f1caf25e9ac2872980ff0ed1b81b63596b7f2edd0de286e ,\r\n                        0xa5e61932c350431a2acf645c8f3fcdf78cfd2b9431a2c4b0201350aa7bab9e4e ,\r\n                        0x40e8c7a55d0c06b259becca0b6aad08a95d074a6418d23f250342509b2a9b3f4 ,\r\n                        0xb8edbf93b2455d3341878c85d899b41b0b86dbac2b90eb478e7857812dad9101 ,\r\n                        0x12951bd4681fa0cd3784d65c6859054f48af9f6aab569aa8da6a6de08cb625a1 ,\r\n                        0x83ea8e085b12c47af3c9674d872736a61f3fc3a80e5798304694ac76d1ca4fab ,\r\n                        0xb01f788bd7fd59aa2ff6da5871a84577020c1878a9fb87c14b74e2230e4c0c3a ,\r\n                        0x13c06a284299319247471a7c5597950e2814a1fccf011acbf5a42255a3796f80 ,\r\n                        0xb724163ee3d459287130a69a10d53fa79ca0665c84d7b22603cb1aeaa4d7fee2 ,\r\n                        0x82c023ed8f7a58499e2aa658fdd61d554d8e4448cf370eebd4420a8c5e7c5547 ,\r\n                        0x3a5cbe684f17961efa2c5a633daba4e8dce52c88463ba854b6e365958907804a ,\r\n                        0x1a7d151aa966231d3bc9eae0d09eb3295955c0c5e6f3d35e12ae075692c8a067 ,\r\n                        0x03e0538bde0189cb55c577a357d473c805e549da510640ca0afe046d9fa8724f ,\r\n                        0xb62bf1926975c45e4ddd244f75233bb516eda29e26b5389345542724c50270ea ,\r\n                        0xaec82420ed1a273510553b3d072995f571469d9147cf93b45e7053b5047556c0 ,\r\n                        0x6181038ca95d28b17eb5662c1c7aabbfb0ff747212c02ad15bb97a7cc544a9dd ,\r\n                        0x36dd54f93d2ed5aeef6a9e80fc750fd562575335680dffbe734103424a9a1e7c ,\r\n                        0x9b3cd4742cb67d8545a7336f76ec5d3a505ac86e613547989bee0142dad96ab5 ,\r\n                        0xadfd8cd80633747e842caa031ff7c98f984a94b730c4af1a71e5f95c49d28104 ,\r\n                        0x054d470aab173c7f07b05481d3b9b480358ed832d8e6546db02e65a632740a88 ,\r\n                        0x54802572b1f9295b0f5fbbaa5bb03550f4cdfa6bc4a815754aa92f28e9882723 ,\r\n                        0x611509d39dc13230c8936c41d40049271796ea25a4484a3a63050ec1e7217f5d ,\r\n                        0x4f76d8c065ce5bcac1214ebe533dea0719434f3f0003dfcfb18fa946009d5b19 ,\r\n                        0x5dac9b240ffc6a7d0b7e9aad7b4c4df060aa8b4221b401c10ce869ffbdf7bc59 ,\r\n                        0x8cbd6024806655975a269b75e023a874af3db29624846a55f3fdd61929d79aa8 ,\r\n                        0xb70e02abb8a706487580e25445c753b4d90ebb4264a9cdabb26e7a64829ad355 ,\r\n                        0x5a2b8aee78745ef1bafdb5623386a5bec454c5045513d157a0b2a25c85145742 ,\r\n                        0x537d9921415f032b1188ff226d0a06eac8b9b60293c72c1b013fb4bf5dd7546f ,\r\n                        0x80dadbc8111d11c32f914054f763229cd06aaa9bca55a192ad5adf3df43874b2 ,\r\n                        0xb1ce9c5675c76bbf4acddb2a072d3bf7d0c90cd8117c4677b1864883a23845d0 ,\r\n                        0x879fb36d1054870ac4af7414f48bb24a30a1a8ec01062650fd77c00167ac4ca7 ,\r\n                        0x32bea7b7d5df69d65b706dd2b563b196941a8404c2b511988c051e7a559638ac ,\r\n                        0xf05233959718f73a19473a044f00706d1941518eb6ec660621a1c64967e4b4b9 ,\r\n                        0x13a0a805a7fca1e952dee97c85e7df20ba744ce041027e6096c06d5689b0d600 ,\r\n                        0x95ad7530161e3388d68e27e8d94bf65bf7fffc3b8199a2f1578f22b0830169bb ,\r\n                        0xadc28ee8139918b582c7a24a8f8e01212b4efa40d2dace83ace8e1ffe88a8f37 ,\r\n                        0xc39af51880811b145746d6231b4e0edf04f43407b52aa515b58f2dad9954b0b1 ,\r\n                        0xc6d6a1b4ab266c0c1dd43c35d40ddf59c2eb54eaaa6b2f2b1300ad30b7d8f448 ,\r\n                        0x8875c2a759e17b5063f2ead6aedb5288ebd912b8187a53ccd976707f9ae1f5ce ,\r\n                        0x08c47dd2c356d49dbed56e26604d4dcdff0fe5aa87a228f18daedea1ae22b375 ,\r\n                        0x950c25183d01450b64fdc5d9ec93efaf37edbd924561afdcaca9dbe5a8df716b ,\r\n                        0x47c2f627e095aae966d7bfe76588228b4e5b7bbb61e5c77b88054d25436db47f ,\r\n                        0x6337face6959d5a9ce5f1b1eeed715ff7641916bc03ad97cdeb649eaa0918057 ,\r\n                        0xfc7b7c3dbe7ac68300807c76cfd65908f5d1c9dd7fa6f2317ecb1e48e92b3888 ,\r\n                        0x3397e351d12aa6d69cbe9e59550d3d324c60d0a1c72bfe3189a55ee056279905 ,\r\n                        0x59fbfe3a63ac9b0c6b8db4ba5efa04dabad6e29b9c766feec5b1508b8a4d6f65 ,\r\n                        0x78e692a28eae9354a1955b2cbf02de07b061aa19176ada5444fb2f98d0d561c1 ,\r\n                        0xaf82b70c4c245189a4a3ee8f8c8ed0596fb5edd9342258330955fc528f22f91c ,\r\n                        0xc447b97fefcb5d83a5f9e7f454f559726b1c0ec756fdf65bd8de72565bcfbd2c ,\r\n                        0x4dd2c1ad6ffce0d15343c3d6a385a2a2a49d6fd76c9267367018aea0a70ae915 ,\r\n                        0xf426bdd54a9df5c656172568b2587cf36cb98392fa58075c48f13b0bf2cbf3f4 ,\r\n                        0x9c87a57ad9539bbe1a49f3e7f5bea8654d1c13a6ebc84c6a96055e97182380cc ,\r\n                        0x5a44c9f1a9d316046c1d3a2d4a3abc7a8c854e2d6ad7409b3f59c93133151d16 ,\r\n                        0x04b08abac8c723c14eb9e5f813cad4c508a2def16bb34374a0a4ef589815274c ,\r\n                        0xa53beddc2945684b3c773d71a80abce6eec71b8c4dff6f7f5f6ddfaa99f3583d ,\r\n                        0x20fbdd1035ea7d0621b31e3353f7c312f0693dd6e348c0372bf9420d9387b647 ,\r\n                        0x55049e8e4a9177181e7e3bf4ac881ba36b275fb179681ccaece78b104494a91e ,\r\n                        0x688024db3a080395b6424582b46615c7f46024af2744a81f0e790e3ff22fe157 ,\r\n                        0x53872e407c1244d5ce8ab61aba6a7b6842925799ea1d8495d67b06b756897ed8 ,\r\n                        0x611d960b19909dba5d3ff5fedaee3f67d58e9ac6f5e81252f0a7368e113fedc6 ,\r\n                        0xd499be2d33a04227eb7efa11f49c08347f9058530bd3538959dbebdda8976861 ,\r\n                        0xf4df17877596a86fc4969a24041552ca7b6453a0f6fceaa3193a191b9f1d2994 ,\r\n                        0xf4d7e1c2a4366192849d7933ea99dad5f11dc695a6fa8775c80fd61add77c2e9 ,\r\n                        0x10544d65e5dfe55fbd9e6fd0334284f9f9e3965bd7e83e6abf64ea7126a83aa3 ,\r\n                        0xafda85e8eb6af4a5a8cafdb7c1276d8c73ff9bbf54d5b51cf6f016c8bbca2df3 ,\r\n                        0xab2ddbd35bbc5377a8687926eda9a9f63e6b302952633a8f870ad34dbfe3543a ,\r\n                        0x610d35e9809736f09eff16a54164d06b304409f85f8ba22afad0b11e35f49699 ,\r\n                        0xf4448da5da8c9dcb5af60eb2923869c1b64fb6cae8c569933263e82dd859dfb5 ,\r\n                        0x729b489a75c9abd2210ffdf2730bf2f6d281c4b1b50ae9da9aa9033b3a92730e ,\r\n                        0xc30ee4f7f8cb8f46d5538c8e94a83729b27e2cdbc1967a321990805aea4fd0d3 ,\r\n                        0x79b44a0e6e515546ad0002601390634b0f3090cf563d010b9c20c0d250fafd59 ,\r\n                        0xa9da75dc388fa293cc92118cde19d34853099bec8e2ac49d75bcf1b310d11c8f ,\r\n                        0x3570219c8bb6e292f6c09b965cfdb4a845750d835a4e47e8b2ce6ce971342a4f ,\r\n                        0xb560f0a6ce7b9d69d70337b958fb3c49c786be64f0b323f6f55ac11b9df6784a ,\r\n                        0xd3aef236aceae4a6f655ced6df3efa75b4ab9313fa24590bd92c5d7f9bc20dd9 ,\r\n                        0xfdfa457f077cb4df2aaead0718c9624aa92b2a89f4acd2af0a223a0aa41d83ef ,\r\n                        0x28ec89da14ec0e9ff056c0ecaa987dd4414f5c1060e9c00759a37c426ffca172 ,\r\n                        0x193fccbde2b323e86089ce42a041c6d673385853e56fbc2cc3f443981841c54a ,\r\n                        0xd48b9eaaf28cba45f5cde9b8dfd0497742454fb9d07346970a78a4679b99ba7b ,\r\n                        0x95d25a872fb37acd67bd57f53c90881a567339b2da23a8c915e093a44fd173e7 ,\r\n                        0xf40c4d309f94b348f3cad25cd06bf73c65ecdfd58ea2a7847045188ae75bd133 ,\r\n                        0x9d264652d49ed32ae9ada5e3e65415dc0b66f4ccba147584353cf2223f627eb6 ,\r\n                        0x4362dd1e475b5b5c51f5b6c67041633d4f485b2ce1b8887f4ad2a88d30a21a47 ,\r\n                        0x1e3ae9c182303406a063b35b8ccfebc0fd282c9ceae17aaa5689ac365c570d2c ,\r\n                        0x7a6859d383279ab29a76b04653c69c7b6a2513d9b49040c8664bddfce15afd23 ,\r\n                        0xc0d172fd49960df427adbaf2475e85c4d9939db4b5fcd3cba3bc9a4df5ffdd97 ,\r\n                        0xa76516530ad5f48003f2fb7fcfa820af975857f9b97e679fb4f1ff7a8d4d9d9b ,\r\n                        0x3801d354ea7460d1e01eacb72a4da4d604eb846e5bded0d356feba40b3f2cb9d ,\r\n                        0x5b4f23b1c31f5947b61060701807bac89fe0d05fea4256a0dd337456c7ca742e ,\r\n                        0x04f216ed400cae7de4aec1ba6654ab42b743be3afc29bbd3164ea2137c651a32 ,\r\n                        0xcf27fea373c9e2ab10be9fc7dda4c62f764069dbe1653d7902fea767d0e1277a ,\r\n                        0xa6efcf06810a0e46be73685ae7ed875db03523681f393c5e4f0dfa3a158ac1f5 ,\r\n                        0x0e7ab2630f70487d5b57b19eebf6d9eabb8aa5cd039fd4835bc409f5b0b57f0c ,\r\n                        0x4bc821ac73cd5a03d50a3f42ef9a9fdaec6157df78be03412a8b8a1d64b0ff1f ,\r\n                        0x377a5edd191a11c321419e903f02de879e93a315fe8e7123390960f5adf9fdd9 ,\r\n                        0xefc15bd5e8a8a391590f5c4290bde034278dc0fff17d3514da3075ce9f51dab3 ,\r\n                        0x707f2ff211085e1de55d6792ca748ccc2d3d06a93ebb070166836b96a2e39f8b ,\r\n                        0xae460582edeb28337fc9ea982255f2ef75dc773071b55f732e833133d61a7e26 ,\r\n                        0x5aecf1bdf96734e9a3ceac248066cf8c53191e12e72079b6d6be9a55fecd6a6d ,\r\n                        0xd87d55fffabce9cf5192894855c486d39978d0bbd8f33fd4634b1ec0e67adc34 ,\r\n                        0x77cf082bb2a07a1fb34e7bab7f28ebb94424382900eb7021e31e622dfc6a4fb6 ,\r\n                        0x29aae487ca3e0a55381358fc7f0089a10a8f8e746eba00000006624b474400ff ,\r\n                        0x00ff00ffa0bda793000000097048597300000ec400000ec401952b0e1b000000 ,\r\n                        0x0774494d4507e4010f0d371648094ce4000009644944415448c785975b8c2547 ,\r\n                        0x79c77fd5ddd5dda74f9feb9c99d9d9bbbdbbdcb2f6ceda6b0b13222b262bcc45 ,\r\n                        0x58044c804896a52084bd8ae3585a8cc44572501240414858e22136c6d8eb8090 ,\r\n                        0x6563ae1171b4a0c4582678cdda8e19f0ac27cc7abd6766e7ccb9749fbe57150f ,\r\n                        0xb3c47989f6ff540f55fad5f77d555ffd4bdc75c73f9aa0d64429c3cd1f7a3f96 ,\r\n                        0x93e27a1a236284a5c8d302a35d2ceab49b0b7cfdfe130c36c6f4e666f8cc673f ,\r\n                        0x21a4076565705c0340515638b64b961a824050161ae90a84100028a5b02c0b47 ,\r\n                        0x3a355acd1e795e72c5c1ed081b6c09466440854082f128a690252090ecde7519 ,\r\n                        0xa3c990a5a5be198e5f6363708e52a75455415e1a16b6ed244d348b575ec1e597 ,\r\n                        0xcd1d2fabf25ea554eefb3ec618269389703c3720a835196ebe86563099689419 ,\r\n                        0x51e911da1454a922cfa0d5d84ebbd10163b3b4b44cb3dde08a83dbc0da46a9ae ,\r\n                        0xc27101341a8b382e79e4c4a39cd2a789a31d775e71e59bcf0b211ecff33cf13c ,\r\n                        0x8f56ab659c289a32370b795ee2ba5037165ed0c5b21b4001468276d105a812b4 ,\r\n                        0xb6d8bd6b0fa52ac932f003b06d284b4559e578b580309474da331c39722d4f3c ,\r\n                        0xf6c80e6d8a138b8b8bff668c39ae947adeb66d6349a786ef35c13860603c2ee9 ,\r\n                        0xf707bc7aee1c675656585959e177abab2cbdfc3b640d9224c1f5ea8c4611d285 ,\r\n                        0x340363812d6d842dd10a540528c1f6b916ef7edffbb9ef1b0ff1e2d2cb472d29 ,\r\n                        0xef1e4fe283066c476bc334be1819303f2f11ce0c956aa1748e10209d80221324 ,\r\n                        0x2918c0f33cd252d3bf00fdc10671ba41bd61916653ae3974185340209b980a1a ,\r\n                        0xad056ef9d831be76df37f8e8473ffc91ab0f1fec29b8dd914e0dcf0da9075d9e ,\r\n                        0xfd65c655d7fa4c63f06b0e9e7430c078a410da261a83e7058cc6535acd0ebd39 ,\r\n                        0xe86ceba1ec1e690ea10fe9149a01543a41ba202af0c3161ff8d05ff2d0c30fe3 ,\r\n                        0x38b71cddbb67c731274d4b2ce1e3b921ff72e2311efbee94bcdcc4f324519422 ,\r\n                        0x748399ee0eb269419615341a6d94ae180c37d9d884c240654196959c89363874 ,\r\n                        0x7081f5cd18e58ea81c58db788d42419665dc70c30d3cf8e083dcf1d7b7dd293e ,\r\n                        0x77fc5153731710c2a63b536765f539ea0df03c89257cf254321916d48326aeeb ,\r\n                        0x321a0ff06a36abeb2becd9bf93cd7842a3dbe6bab7bd9d438b3dc2001c013ffa ,\r\n                        0xd1f7387dfa34935870eedc0633dd36c2941479c2e6600df1a93bbe6376ef3c48 ,\r\n                        0x9e554ca2011bc365eaa1c37832442b68865d5c19924e0c0b0b3bc8f394caca78 ,\r\n                        0xe9955ff196c36fa2b26d5cbfc164aae9f7d739b4f826def7deab989d0307e8af ,\r\n                        0x256c9f0fc8f2ad5bd10ae1aebb3e855394194551b0b6b64e964ff8ea573f0602 ,\r\n                        0x8643e874606d0d5c0b8e7dfccbac2caf72c39f1d254a07485b72f7dd7fce734b ,\r\n                        0x053f39f914db765c8ef4e7d91c1bbe76df937cf0e67770d91ec1b6f9804a8310 ,\r\n                        0x5b5d0b6c0683015618fa08aba43b13e2f91669067ff1e1cff0c9bbbfc43882ce ,\r\n                        0x2c6c4e064cd30d5aad16d2aa114d4af2ace4ec59989d73b9eeedd7f2cad915e6 ,\r\n                        0x77efc5f67ac4798387bf7592248317fe7bc2c993a7585a5aa5d3b289a69a66b3 ,\r\n                        0x89334d46b49a31ca94c4c905ca0aba3335b66f9f279ae60cc67d842df16a9234 ,\r\n                        0x4d91d2432b43abd160fb0e282584b3757e7e6a86c178406366075ea3c5d973bf ,\r\n                        0xe53f9f19f3eeeb5b1cfaa3ab186c6a342084a0280a2ce9555c18fc0fd2cb71bc ,\r\n                        0x84242f31d698520f3164d8d2a2bfbe4e599634da21e3f190b05123cd26bc76be ,\r\n                        0xe2e5331778e9371bbcf3c6455efcf569c29644db8a5ad3e7bb3f7c9c53a7d779 ,\r\n                        0xf6b9555e39b3425980aa3460e15cffa76fe527fffa33fa1746cc6dabf1ef3f7d ,\r\n                        0x82c3470ed06ecdf2f813dfa65031596ca1448af40a7ebdfc0bfa1b6769cd29e6 ,\r\n                        0xe71d4266b13c2880689ae0b8506a455ee54469c2d5d7cc6152701d500a82c0de ,\r\n                        0x4af58def3acce2e2412a95e3f9b03e38c3cc4c9ba0d662348948f28403fbde88 ,\r\n                        0xc960a30f45a6f0eb3617c6ab9c3d771e19b6d9886d66b6b9a8dc6138326c6e4e ,\r\n                        0x280acde6e688e110ac5ce14a9bc968c06caf491cc7085d190490e5906423ba5d ,\r\n                        0x49924d49134dbbd3e5c7279f34d7bdf54f68780dd289c2776d6c072a4a0a6171 ,\r\n                        0xeccecf32ceead8b57d5c77fd4d2cbfd2c7d88620140cfa4b9c5ffa0fac62401c ,\r\n                        0x8d39f9e4b7d11a6ebfed6f708405d318a40b611800259ee751f36b141a26f184 ,\r\n                        0x7ae063a179e9b7cf60b3f5a0db8e83b27c6ebde556eefde747b9e93d37f2d47f ,\r\n                        0xbd48b7b7974992432599ebec616131e3d9a77fc8030f3cc0348134491142e018 ,\r\n                        0x03f53a2000e1324d2704351f8183635968e190a3899294bd97ef6761768e681c ,\r\n                        0x931715edd9369981d6ec713e7dcf57b8e9e6bf62b059d1e9cdd15f1b226d8fa7 ,\r\n                        0x9f7986871eb89fb00e5a81ebbab45a2d1c2e2a8e14ae6f706c178147966a846b ,\r\n                        0x311c965878d43c8fe75f5867f9c50d3cd70521885e58a5b4353b0fece7d81d1f ,\r\n                        0xe0fe6fdecfe12347c9d308cfadf1e3efff807bbff07956575fa52a7316afdcc7 ,\r\n                        0xe62866349ae008017906b560ab7610a0b4c2f73d14d0ebce5156a052387cf57e ,\r\n                        0xac028afca23d7240b8d01f27fcf1917d74bab772cfdf7d8523d71ce5a99f3dcf ,\r\n                        0xbd5ffe5bae7c03a4d14e00aa0a3a9dd696e74280e75f4c350016b6655d1c8155 ,\r\n                        0x95f836c446f1eca9df50a4056559e2f992522994ae700317c77170fd3a9f3f7e ,\r\n                        0x3b7fff0fffc4f1db6fa3614df8c5cf5fc5161acb86bdbbf710f8de4530ff17fa ,\r\n                        0x07dceb1a0f07a812da4d9b37bc711f524ab4d6b8ae439e97f8be4429c8f31c63 ,\r\n                        0x0c9db6cf17eff924fbf6ed248e15fbf7be85ac80248e68b51aa84aa175f57a8d ,\r\n                        0xff3f753a1d6c1bc693823367ce906519699ae2380e555561dbf6d6762d8b344d ,\r\n                        0x715d975eafc7d34f9f623a4d992629be1f50afd7d8bf7f3ff5a086ebba97068f ,\r\n                        0x46238ac2d06cba1c387000cf93680d524259be1e69bdee53969a2449683643c6 ,\r\n                        0xe388306c60d95bf3a228a21e0694794196659706cfcecfe3b882b50b31cbcbcb ,\r\n                        0x1445419ee70441801002cbb2288a02ad3542086cdb26cb32ac8be7643a991286 ,\r\n                        0x216118b2f7b23d049e4fe0f997069f3fbf469a2a7abd904663919abfd5e5008a ,\r\n                        0xa2c2711ca484a23064594610d488a298999990f138a319fa680d5194e0ba0e45 ,\r\n                        0x9a91e5c9a5c1dd6e977add4608288a122124455121a5f3bfd119e321c4d63725 ,\r\n                        0xcf0b94526499c1b22cfafd0181e762b4c60b6ad83eb88ebc34388ac62489c6f3 ,\r\n                        0x2c6cdb464ad0da410830c6d06efb0c875b6dd0f77d2c0ba4944ca75302dfa3dd ,\r\n                        0x08b16d9b344d298b2db75355c5a5c19e947ceb91130c0603c6e331799ea394c2 ,\r\n                        0x1883d6fa52cb29b39c76ab411cc7a02b8410ecdab58bdf030d51933dc28c84c4 ,\r\n                        0x0000000049454e44ae426082\r\n                    End\r\n\r\n                    LayoutCachedLeft =4200\r\n                    LayoutCachedTop =1800\r\n                    LayoutCachedWidth =6600\r\n                    LayoutCachedHeight =2580\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin CheckBox\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =215\r\n                    Left =4500\r\n                    Top =2850\r\n                    TabIndex =2\r\n                    Name =\"chkAddTrustedLocation\"\r\n                    DefaultValue =\"True\"\r\n\r\n                    LayoutCachedLeft =4500\r\n                    LayoutCachedTop =2850\r\n                    LayoutCachedWidth =4760\r\n                    LayoutCachedHeight =3090\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =215\r\n                            Left =4800\r\n                            Top =2820\r\n                            Width =1935\r\n                            Height =315\r\n                            FontSize =10\r\n                            ForeColor =5324600\r\n                            Name =\"Label34\"\r\n                            Caption =\"Trust Add-In Folder\"\r\n                            LayoutCachedLeft =4800\r\n                            LayoutCachedTop =2820\r\n                            LayoutCachedWidth =6735\r\n                            LayoutCachedHeight =3135\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin Line\r\n                    BorderWidth =5\r\n                    OverlapFlags =87\r\n                    Top =1230\r\n                    Width =7200\r\n                    BorderColor =15321539\r\n                    Name =\"Line10\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedTop =1230\r\n                    LayoutCachedWidth =7200\r\n                    LayoutCachedHeight =1230\r\n                    BorderThemeColorIndex =-1\r\n                End\r\n                Begin CheckBox\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =215\r\n                    Left =4500\r\n                    Top =3285\r\n                    TabIndex =4\r\n                    Name =\"chkAdvancedOptions\"\r\n                    DefaultValue =\"False\"\r\n                    OnClick =\"[Event Procedure]\"\r\n\r\n                    LayoutCachedLeft =4500\r\n                    LayoutCachedTop =3285\r\n                    LayoutCachedWidth =4760\r\n                    LayoutCachedHeight =3525\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =215\r\n                            Left =4800\r\n                            Top =3255\r\n                            Width =1980\r\n                            Height =315\r\n                            FontSize =10\r\n                            ForeColor =5324600\r\n                            Name =\"Label65\"\r\n                            Caption =\"Advanced Options...\"\r\n                            LayoutCachedLeft =4800\r\n                            LayoutCachedTop =3255\r\n                            LayoutCachedWidth =6780\r\n                            LayoutCachedHeight =3570\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin CommandButton\r\n                    FontUnderline = NotDefault\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =215\r\n                    Left =4500\r\n                    Top =3780\r\n                    Width =2160\r\n                    FontSize =9\r\n                    TabIndex =5\r\n                    Name =\"cmdExplainOptions\"\r\n                    Caption =\"Explain options...\"\r\n                    HyperlinkAddress =\"https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Installation\"\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x000000000000000000000000000000000000000000000000e0e8e000e0c8b000 ,\r\n                        0xe0d8d000e0d0c010e0d0c010d0d0c010d0d0c000d0d0d000e0e0e00000000000 ,\r\n                        0x0000000000000000000000000000000000000000f0e8e0009068303080582080 ,\r\n                        0x905010c0804820e0804820c0804810b06040108050381030d0c8c01000000000 ,\r\n                        0x000000000000000000000000e0780000e0a05010a0683070c08860f0e0c8b0ff ,\r\n                        0xf0f0f0fffffffffffffffffff0f0f0ffe0c8c0ffa07850c040301060d0c8c010 ,\r\n                        0xe0d8d0000000000000000000e0882000b0703070e0a880fffff0e0ffe0b8a0ff ,\r\n                        0xd08050ffc05820ffc05820ffd08050ffe0b8a0fff0e8e0ffb09070f050301060 ,\r\n                        0xd0c8c000e0e0e00000000000b0783030d09870f0fff0e0ffe0a890ffc05010ff ,\r\n                        0xc05010ffe0a890ffffffffffb04810ffb04810ffd0a080fff0f0e0ffa07050d0 ,\r\n                        0x50381030d0d0d000f0f0f000b0784080f0d8c0fff0c8b0ffe05820ffd05810ff ,\r\n                        0xd05010ffe08050ffe0a880ffc05010ffb04810ffb04810ffe0b8a0ffe0c8c0ff ,\r\n                        0x50401080d0d0d010f0f0f000d08040e0fff8f0fff09870fff06020ffe05820ff ,\r\n                        0xe05820fff0a890ffffffffffd05010ffc05010ffb05010ffc07850fff0f0f0ff ,\r\n                        0x804020c0e0d0c000f0f0f000d08040f0ffffffffff7840ffff6830fff06820ff ,\r\n                        0xf06020fff08850fffffffffff0c0b0ffc05820ffb05010ffb05820ffffffffff ,\r\n                        0x804820e0e0d0c010f0f0f000d08850f0ffffffffff8050ffff7030ffff6830ff ,\r\n                        0xff6830ffff6820fff09060fffff8f0fff0d8c0ffc05020ffc05820ffffffffff ,\r\n                        0x804820e0e0d8d010f0f0f000d08050c0fff8f0ffffa880ffff7040ffff8850ff ,\r\n                        0xffb090ffff7030fff06820fff09070fffffffffff08050ffd08860fffff0f0ff ,\r\n                        0x805820b0e0d8d010f0f0f000c0804070f0d8c0ffffd0c0ffff7840ffff9870ff ,\r\n                        0xffffffffffc8b0ffff9060ffffc8b0fffff8f0fff07840fff0c8b0ffe0c8b0ff ,\r\n                        0x90602070e0c8b00000000000c0884030e0a070f0fff8f0ffffc0a0ffff7840ff ,\r\n                        0xffb8a0fffff8f0fffffffffffff0e0ffff9870fff0b8a0fffff0e0ffc08850e0 ,\r\n                        0xa0682030f0e8e0000000000000000000c0884060e0b8a0f0fff8f0ffffd0c0ff ,\r\n                        0xffa880ffff8850ffff8850ffffa880fff0d0c0fffff0e0ffd0a880f0a0683060 ,\r\n                        0xe0c0a00000000000000000000000000000000000c0884060e0a070f0f0d8c0ff ,\r\n                        0xfff8f0fffffffffffffffffffff8f0fff0d8c0ffc09060e0a0703050f0b89000 ,\r\n                        0x0000000000000000000000000000000000000000f0f0f000c0884030c0804070 ,\r\n                        0xe0a070c0d09870e0d09860f0d09870d0b0784070b0784020f0e8f00000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0xf0f0f000f0f0f000f0f0f000f0f0f000f0f0f00000000000f0f0f00000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n                    BackStyle =0\r\n\r\n                    LayoutCachedLeft =4500\r\n                    LayoutCachedTop =3780\r\n                    LayoutCachedWidth =6660\r\n                    LayoutCachedHeight =4140\r\n                    PictureCaptionArrangement =4\r\n                    Alignment =1\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmVCSInstall.cls\"\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSInstall.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : chkAdvancedOptions_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/22/2023\r\n' Purpose   : Toggle to show advanced install options.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub chkAdvancedOptions_Click()\r\n    tabInstallType = Not (chkAdvancedOptions - 1)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdCancel_Click\r\n' Author    : Adam Waller\r\n' Date      : 2/4/2021\r\n' Purpose   : Close form if user presses the escape key. (Cancel property = True)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdCancel_Click()\r\n    DoCmd.Close acForm, Me.Name\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdInstall_Click\r\n' Author    : Adam Waller\r\n' Date      : 2/4/2021\r\n' Purpose   : Install add-in using selected options.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdInstall_Click()\r\n\r\n    Dim strFolder As String\r\n    Dim strFile As String\r\n    Dim strMsg As String\r\n\r\n    ' Activate the hourglass before loading installer VBA module for somoother experince.\r\n    DoCmd.Hourglass True\r\n    DoEvents\r\n\r\n    ' Validate the folder path\r\n    strFolder = StripSlash(Nz(txtInstallFolder))\r\n    If StrComp(GetInstallSettings.strInstallFolder, strFolder, vbTextCompare) <> 0 Then\r\n        ' Using a custom install folder\r\n        If Not FSO.FolderExists(strFolder) Then\r\n            strMsg = T(\"Folder does not exist: {0}\", var0:=strFolder)\r\n        Else\r\n            ' Test writing a file to make sure we have write access to this folder.\r\n            LogUnhandledErrors\r\n            On Error Resume Next\r\n            strFile = strFolder & PathSep & \"WriteTest.txt\"\r\n            WriteFile \"Test\", strFile\r\n            CatchAny eelNoError, vbNullString\r\n            If ReadFile(strFile) <> \"Test\" & vbCrLf Then strMsg = T(\"Unable to write to folder: {0}\", var0:=strFolder)\r\n        End If\r\n    End If\r\n\r\n    ' Resume normal error handling\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Bail out if we have a problem with the install.\r\n    If strMsg <> vbNullString Then\r\n        DoCmd.Hourglass False\r\n        MsgBox2 T(\"Unable to Install\"), strMsg, , vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Run the installer\r\n    modInstall.InstallVCSAddin chkAddTrustedLocation, chkUseRibbon, chkOpenAfterInstall, txtInstallFolder, chkCreateCompiledVersion\r\n    DoCmd.Hourglass False\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdChangeInstallFolder_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/22/2023\r\n' Purpose   : Allow the user to select another location for installation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdChangeInstallFolder_Click()\r\n\r\n    ' See if the add-in is already installed\r\n    If FSO.FileExists(GetAddInFileName) Then\r\n        MsgBox2 T(\"Please Uninstall First\"), _\r\n            T(\"Please uninstall (and delete) the add-in, then reinstall to the new location. \" & _\r\n            \"If you have already uninstalled, delete the addin file. \" & _\r\n            \"The install folder will open now to allow you to delete the file.\"), _\r\n            T(\"You will have the option to keep your current settings during the uninstall process.\") _\r\n            , vbExclamation\r\n        Application.FollowHyperlink GetInstallSettings.strInstallFolder\r\n    Else\r\n        ' Show a folder picker to select the desired location.\r\n        ' (The path will be validated before installation, just in case it is changed direclty in the text box.)\r\n        With Application.FileDialog(msoFileDialogFolderPicker)\r\n            .AllowMultiSelect = False\r\n            .ButtonName = T(\"Select Folder\")\r\n            .InitialFileName = GetInstallSettings.strInstallFolder\r\n            .Title = T(\"Select {0} Install Folder\", var0:=PROJECT_NAME)\r\n            .Show\r\n            If .SelectedItems.Count > 0 Then\r\n                ' Selected a folder\r\n                txtInstallFolder = .SelectedItems(1)\r\n            Else\r\n                ' Canceled dialog\r\n                Exit Sub\r\n            End If\r\n        End With\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Load\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : Setting the control source causes delayed display. This way the display\r\n'           : is instant when the form is opened.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Load()\r\n\r\n    Const STYLE_NO_TABS As Integer = 2\r\n\r\n    ' Change install type tab control to no tabs and no border\r\n    tabInstallType.Style = STYLE_NO_TABS\r\n    tabInstallType.BorderStyle = 0\r\n\r\n    ' Load translations\r\n    Translation.ApplyTo Me\r\n\r\n    ' Display version (better performance than bound control)\r\n    lblVersion.Caption = T(\"Version {0}\", var0:=GetVCSVersion())\r\n\r\n    With GetInstallSettings\r\n        chkAddTrustedLocation = .blnTrustAddInFolder\r\n        chkOpenAfterInstall = .blnOpenAfterInstall\r\n        chkUseRibbon = .blnUseRibbonAddIn\r\n        With txtInstallFolder\r\n            .Value = GetInstallSettings.strInstallFolder\r\n            .Locked = True ' Only enable this text box if not installed.\r\n            .BackColor = IIf(.Locked, 15921906, 16777215)\r\n        End With\r\n    End With\r\n\r\n    ' Show installed version\r\n    If InstalledVersion = vbNullString Then\r\n        lblInstalled.Caption = T(\"(Add-in not currently installed)\")\r\n    Else\r\n        lblInstalled.Caption = T(\"Version {0} currently installed.\", var0:=InstalledVersion)\r\n    End If\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSMain.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    PopUp = NotDefault\r\n    RecordSelectors = NotDefault\r\n    AutoCenter = NotDefault\r\n    NavigationButtons = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    ScrollBars =0\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =9360\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =33\r\n    Left =3225\r\n    Top =2430\r\n    Right =28545\r\n    Bottom =14895\r\n    OnUnload =\"[Event Procedure]\"\r\n    RecSrcDt = Begin\r\n        0x79e78b777268e540\r\n    End\r\n    Caption =\"MSAccessVCS\"\r\n    DatasheetFontName =\"Calibri\"\r\n    OnTimer =\"[Event Procedure]\"\r\n    OnLoad =\"[Event Procedure]\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Rectangle\r\n            SpecialEffect =3\r\n            BackStyle =0\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Line\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Image\r\n            BackStyle =0\r\n            OldBorderStyle =0\r\n            BorderLineStyle =0\r\n            SizeMode =3\r\n            PictureAlignment =2\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CommandButton\r\n            FontSize =11\r\n            FontWeight =400\r\n            FontName =\"Calibri\"\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =1\r\n            Gradient =12\r\n            BackThemeColorIndex =4\r\n            BackTint =60.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =4\r\n            BorderTint =60.0\r\n            ThemeFontIndex =1\r\n            HoverThemeColorIndex =4\r\n            HoverTint =40.0\r\n            PressedThemeColorIndex =4\r\n            PressedShade =75.0\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n        End\r\n        Begin CheckBox\r\n            BorderLineStyle =0\r\n            LabelX =230\r\n            LabelY =-30\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =6480\r\n            BackColor =15130848\r\n            Name =\"Detail\"\r\n            AlternateBackColor =15130848\r\n            Begin\r\n                Begin Line\r\n                    BorderWidth =5\r\n                    OverlapFlags =93\r\n                    Top =1725\r\n                    Width =9360\r\n                    BorderColor =15321539\r\n                    Name =\"Line10\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedTop =1725\r\n                    LayoutCachedWidth =9360\r\n                    LayoutCachedHeight =1725\r\n                    BorderThemeColorIndex =-1\r\n                End\r\n                Begin Rectangle\r\n                    SpecialEffect =0\r\n                    BackStyle =1\r\n                    OldBorderStyle =0\r\n                    OverlapFlags =95\r\n                    Width =9360\r\n                    Height =1680\r\n                    BackColor =5324600\r\n                    Name =\"Box1\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedWidth =9360\r\n                    LayoutCachedHeight =1680\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =223\r\n                    Left =360\r\n                    Top =300\r\n                    Width =4290\r\n                    Height =540\r\n                    FontSize =18\r\n                    FontWeight =700\r\n                    Name =\"Label4\"\r\n                    Caption =\"Version Control System\"\r\n                    LayoutCachedLeft =360\r\n                    LayoutCachedTop =300\r\n                    LayoutCachedWidth =4650\r\n                    LayoutCachedHeight =840\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =93\r\n                    Left =5100\r\n                    Top =2400\r\n                    Width =2880\r\n                    Height =900\r\n                    TabIndex =1\r\n                    Name =\"cmdExport\"\r\n                    Caption =\"   Export All Source\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    Picture =\"Export.png\"\r\n                    ImageData = Begin\r\n                        0x89504e470d0a1a0a0000000d494844520000001e0000001e08060000003b30ae ,\r\n                        0xa2000031697a5458745261772070726f66696c65207479706520657869660000 ,\r\n                        0x78daad9c698e2639cea4ffeb147d04edcb71b402738339fe3c468fccaaaceefe ,\r\n                        0x80194c052a33f25ddc2591349a5194bbfbbfffd773fffad7bf42c835bb5c5aaf ,\r\n                        0xa356cf7f79e41127bf74fffdf7fd1d7cb63fbf7fdc9ff7c29fafbbdf6f445e4a ,\r\n                        0xfc9dbe7fd69fd7c3e4f5f2d7175afe797dfdf9ba6bfbe73afde742e1f785edbf ,\r\n                        0xa43bebf79fcff59f0ba5f8bd1e7efeedc6cff766fedb747efeaffef734be51fe ,\r\n                        0xe3dfb9b118a770bd145dbc2924cf9f5d7749dfff93ff037f8654f8904f8ddf0b ,\r\n                        0x3f7a25ffe7b573bf7ffdc7e2fdfeed1f6be7e7cfebe9cfa570bfc75bffb1463f ,\r\n                        0xaf87f28fd7d3efdbc43fadf6d79dff786385b8fcdffffbdbdabd77fa7bf79bdd ,\r\n                        0xc48946aeee6752bfa662bff1412e92937dadf2d3f8bff07bb39fc14f678a1b8b ,\r\n                        0x1dacb9f8d92e8c1059ed177238618617aefdbdc3668839ded8f83bc61d93bdd6 ,\r\n                        0x538b236e3346d64f78b1a5918e4b1d3b6dac967839fe1e4bb0fb0ebbdf0e9d3b ,\r\n                        0x9fc02763e062816ffcdb8ffb4f2ffebffcfcbed07b72dd107cffbd568c2bcaa7 ,\r\n                        0x19862ca73ff9140609ef674d8badaffdb8bff98dff9b6113162cb6cc9d094ebf ,\r\n                        0xbe4bac12fef2ad64764e7caef8ecfc171aa19d9f0bb044dcbb309890b080afb8 ,\r\n                        0x77a8c1b7181b809162c73e9391c794e3c202a19478827bd826a58a717ad4bdf9 ,\r\n                        0x4e0bf6d958e2f732d082214aaa844ac7421363e55cf09f963b3e34899eec4a29 ,\r\n                        0xb5b4d2cb28b3a69a6ba9b5b62a8c9a2db5dc4aabadb5de469b3df5dc4bafbdf5 ,\r\n                        0xde479f238e048495514773a38f31e6e4a6934b4fbe3df9c49c2baeb4f22aabae ,\r\n                        0xb6fa1a6b6edc67e75d76dd6df73df63cf1a443f89f7a9a3bfd8c336fb8b8d2cd ,\r\n                        0xb7dc7adbed77dcf9f0b5975e7ee5d5d75e7fe3cddf56fbb1ea9f560bffb0dcff ,\r\n                        0x6cb5f06335592cdbe7da5f56e3e5d67e5d22084e8a6c86c5620e58bcc9023874 ,\r\n                        0x94cd7c0f3947594e36f32312142562b550649c1364312c986f88e585dfb6fbcb ,\r\n                        0x72ffa3dd5cc9ff57768bffcd724ea6fbff613927d3fd58eedfedf61fac76a665 ,\r\n                        0x94640652146a4d7d7a001b93676af7fa3799670fb7c7952b8bf018f52de5ad99 ,\r\n                        0x16d38b9820a453d7f169a7f65e94151f01dc09662e747badab8f9acbd9ed96c0 ,\r\n                        0x08e33a25edc048c820bdd4dd574e6160da174b6eaf9dd9c7e2264d9e70e2ccd9 ,\r\n                        0xbdb55e3f2dc7c824f1c13b63d45dd6f585dbfffd45bdd4d29e7c3d8d9df28c8c ,\r\n                        0xf5ad54e46498bfaf771fbed1ce98c0f5b881911e8279e18a61efbcce9cb8ca29 ,\r\n                        0x7ca29efd4ac7a19e61927d2b1eaee0dab9bf47201c18ccadced2d33b3857cb25 ,\r\n                        0x9679562f37f2c1b0dfc38f733e4c7a62fa183a2b50fc8c6ee10f61cfd72797cd ,\r\n                        0x6532532d9f6f8fb55b98b0c5f6e66c7ab5e5d3265f0083324b371f1e45ced983 ,\r\n                        0x21b9b378211e4659f5fd8c5d987a8453ecc3a48faeb336769c24b05ea3ef3bf3 ,\r\n                        0x0b96223c70bd55f3c3e7965bbee3247330d9135e9e0ce19ec412ec574762f5f0 ,\r\n                        0x8e3ceeab77e7be7d39797bbecbf836962cfbe896bce75e21baf01566d15aac33 ,\r\n                        0xb51d6555be992e067b04ca7917b80d07dff49a737af3f2d7bb0b3f3db51e06e3 ,\r\n                        0x18724d8fe9e196384ff1dbeed6de39fcff6e7d2fcc7e5e612ccd6cc5c51b7ed8 ,\r\n                        0xde586f5792ae9cffba91f76838ee78d81617d90760386d359fefbd2fda4d6184 ,\r\n                        0x7786b6b8feac4577793b69981907397797c385f6637507899435bfcc3ee1b7c4 ,\r\n                        0x25f1b7fad997a5c4d687907f31df1dce19bd9edbf3258e27810acf7ab31537db ,\r\n                        0xc135b1410bf9f0d184090022ecd8083da14d0b7b608c97c5b1f67df932d57e43 ,\r\n                        0x660473dda7d54acd5dd825c4a1acdca69f27f7c1f4f8da67c633a11adb56b6dd ,\r\n                        0x511879888c27ca27f0593e3be5fe6325070aa44c9a4ecc83f5c3e34e03be887b ,\r\n                        0x0852b8bf0c4dd0847ecac5cfd6226623467edd0c93e6ab9ee827b79eebef02b6 ,\r\n                        0xa706f91e18526a638cbdb37227a6459a15eeb0ec89abf23bebb85e62b8987410 ,\r\n                        0xc840edc0574faf998bccba3c2e0087ca27e5351ee1f92c34b85e0d63168574d7 ,\r\n                        0x2bafd92dde82383502e84e87fbde0d0f7b38096eb41fa83708dfc7bd2baef905 ,\r\n                        0x341e90f94452f0f28d6997c717f7008e1b7093dce77e8c39dff1eac4e9827c97 ,\r\n                        0xf5da1578df7b74681bd920322abc6fb7a699cfd86a1dd94702e76522c21d7ca4 ,\r\n                        0x1e3201d3605ce732a94bae192fc57915887807082cd3ed4a367c3bc661beb99f ,\r\n                        0x793a64aeae4d16610255f4925cc8eac056b0f35d832b06ae3d0a8e72d6f795d0 ,\r\n                        0x09497ccbd712671b3e570f6491468a7729415f44392f34f544b2eccb6713a895 ,\r\n                        0x3053d4ee0b1891538157ec16d3d927dcc212b70b021d390ae0bf9c709241fe06 ,\r\n                        0x5c160af049ac62e9f2c3d3279174f852ec17e0279e3a59a2969c088c3e62b6f5 ,\r\n                        0x7e80ffcb5c238025c01029288d151631fff33a1e8572da8321cd51234e8ad5f3 ,\r\n                        0xfbc108c12411ccfc01ff99df256326be06161d0d03e87dbde40d627920a6b1ca ,\r\n                        0x2b5afce21c596e36b934c981d47e513a073692c928a04764718901260a285c92 ,\r\n                        0xd32e84c26dac269eb9160638f8b448e308650f9c5cc0cfcc5b349477494391d3 ,\r\n                        0x814901fc6860234c84051863e12abef232e65b78df1d033691c060a64e78939e ,\r\n                        0xc9792991f62617f2508323cb64be1beedde4ae463a237cf3987c88d154b2f719 ,\r\n                        0xfe9153b9760c950b6f9c66231f1e14aa3e470c1d793d7e18cf4971439c403480 ,\r\n                        0x2b4052ee8dab9231ba7cab01371788c66bc95cfe40391a6883addbc94e300b9b ,\r\n                        0x0c0ab6046a0ac662bd99a4dc611e8a8741da9f5808de958dda5df920d28ab892 ,\r\n                        0x7ba162f370c63e8688f66b5c0db51e78ab295decbb0477a5e5201af16ae9ab23 ,\r\n                        0xd370d28905363e0b3c25af1c8b5e03a1ed9d93fa047448347d4d12c4818bdc56 ,\r\n                        0xee4e24739963845405f95acfdb71fe178075dc6e00a8d37169fb78f96f1fdf2b ,\r\n                        0xf805ea9228b124c440c586a75ccae858d4f994a0b263c589003cbd8108967f4b ,\r\n                        0x6a5c377003a6bb374c260d98095f196b6127d11c56035c2067e17cc302dae124 ,\r\n                        0x24d6d377c1efccf161983561b6dcc223775f628fac82764997a4c9b7703d4b94 ,\r\n                        0x0727271541f5742103f60d7d81f4110b05db6a1c18b5a4d1f283bafebe817122 ,\r\n                        0x90c64ff4d02bbb178d077fe6eb8ea821a3363c0f941fe004947840b1e5850a2a ,\r\n                        0x38fb7a6de6bb082a406d284cfa3c704458701ab5e0ef085e57269882df47300f ,\r\n                        0x2f793dd5b73bc3255f6b04b912c778545a72a300b3e44a7c633745b17d9a2578 ,\r\n                        0xd1edbf7d9c35de7b8baccd00a86442964f4fd3709194045a8532062031e4dd40 ,\r\n                        0x5321da8853d0db3d243666d012b1148ba8a941b6ade0208c7e754617e0eeaf2e ,\r\n                        0xc2cd5c768a5a6db020a9cef07ceeb73707a28bd4870e6742d8e31c9325042970 ,\r\n                        0x37c0866bf9983a76d6a7f93084008e798b92f7088d70d38b61b34644162ee803 ,\r\n                        0xa1ca4af30dd0a0e9c3c4db636d59b2273e45e02ecc483ac22eb94bef72990d92 ,\r\n                        0xe206db05011024332e80050f3c48b3cfadf7629d40571245922e0cbcb1dbb880 ,\r\n                        0x3c6f4300e7947e63589d24efa02d38f6dae0d74aa2bf90fef9a02bc0da688524 ,\r\n                        0x3950c66be48478ae17172134218f10ff10f90a5fee17d66e99b68a262e187624 ,\r\n                        0x51b1c6902f6970251b600413ec7ca033782ca2a8219f4038e06430928d1c3aa4 ,\r\n                        0x780f63ab81f5875c15c606a6f20d980d73c1b8a4a2051442dec82ec40030015a ,\r\n                        0xc0c533cab0f655cf1c64fab013fc684f09b3debeba0e13002013698cc4b22f5a ,\r\n                        0xe7b038abc152e6c69da5db482aa78cde310d783e9bca1ecd914cee53ae25bfb2 ,\r\n                        0xfe283d40e6276062de38cc65bec05b9dababf8034bf6d04dc416172c533c53de ,\r\n                        0xe76076093f28dc344b50418bc5eb10f37b1387658b2b91556206e30ff02170dd ,\r\n                        0x418c7a2c2fc28e0e251c1c266cf24db40fd412179ab8e31ed743cf4163ec85ef ,\r\n                        0xd7ad61eb8315360520951f0ac9b20f7905ea0867f108ae54179151f287ba08a9 ,\r\n                        0x406a52154581b585241fc19498419c0fecb1a1490339c2044b83d57ac06f90f8 ,\r\n                        0x898d036dcf30506895a86697de1aad9bba43cfc72d8f44ac20a79fc07f4b3a01 ,\r\n                        0x731aae2b49a21e8213e044b5a05e375183b8917bc1ae02799e98476f0f3e0a12 ,\r\n                        0x8983a62780ada4b24c7659f8547547bcab02c59f22aba4cbb6f3b5a967ff240e ,\r\n                        0x353c2575af8b10b313188085c14bbbac4180a5555d4f92452c8dbedcbe2feb6b ,\r\n                        0x7ffb3a2813994b33a86528f51337d2f190088852c7291c3e078d26abd58c3627 ,\r\n                        0xe45bd99714f984c5467f483ee54b554889c73245f2f42e1eb210ce506d73919e ,\r\n                        0x911024a7610c714431745139df9a0813ce87806df8a58a2930ad6b492adea3bf ,\r\n                        0xf7fc2eae22e9ae6eea9b10d02ad5089efffdcdd54aaed8138ef4027a8ce5eb25 ,\r\n                        0x323e163dad240683574308fb04fc711a323d24187641082a931d1413e10ac5f4 ,\r\n                        0x6da8c49158af84ef27342ff86284805c0decdc6a62795e871e1e11c87f9e8caf ,\r\n                        0x5a2097e75fbadf5cc846185ec510625bb025e115cbc53d2641d4f37e30f70b89 ,\r\n                        0x7b6e8ae0152582e2530a35be5833805482986824a12cade791d438b696d9d225 ,\r\n                        0x76092b730be5f4198e5ba0d91b23b574af2540dc186e23c1064726514e51a9a2 ,\r\n                        0x845ecd24ccfd48412cc8ecb6ec5766d9f06c44513d9a264454a28dc523b69a0d ,\r\n                        0x08e201deaf9053c8c8083c115a5aaf9cb403630dcd1361bea05e72025cf31526 ,\r\n                        0x0d344ed861dd156f5d2be1e82cfe106d00bf414c120de4307aa413bc85acbe49 ,\r\n                        0xabe03d6beac24d4b0ade0802ab8f2042d4884281c517db18afb9704b04b85679 ,\r\n                        0x836f1217f27ee5a50b77a81deac72b9d487e75db22f251fd99207c25c5ee3fd8 ,\r\n                        0x7dad9bf26a7601b0edbd2f631cae4f1248d58950ec569859aac8fcb0da0cb9e2 ,\r\n                        0x748091273d1dfb6a203a81be7c7b85c31ce008006e8c00715158a11dc4211326 ,\r\n                        0x46722ce22b2852fa95269c42502201f0dc4821438f40f643c2ce0c147662eb80 ,\r\n                        0x23a2e7c43dd11fb80a89690ebe009e004490f80156799c1861717264346053aa ,\r\n                        0x4844d85d9a20378aa8554c7b441de7f10ec4c1af182b11a0d504019575bb3207 ,\r\n                        0x790ebb1010b66eb581628dc5ec30c28b34c0b2a9d644aac2f155d250b5ad10bc ,\r\n                        0xd23990de26a50054749244f71f0759e853e4d5ed569a9aaa97e0e84875b25d78 ,\r\n                        0x78fc72a80ec13c61a9f947c9ed4a767d5f61d0824adb1609ca8be73115b90f53 ,\r\n                        0x219d6d15d9cf180736511da141da3c640992c78355d5da018410e00c613ee8f7 ,\r\n                        0x2a52b7e7c9bef00240afcaf82400b15450160e7a1891854e79f190ce6a2e524f ,\r\n                        0x912c03c66187a84259666604340aa2291c062b054562d4a402d80a8e4a527780 ,\r\n                        0x0ce104fa6ca84b5dcae42c1f5c10aa2631e211f1e429d57156272331f11755a1 ,\r\n                        0x628a193a8e34031b2e0e8909a5aed7edb0ef51f09e71d7f692c9e40b82f07a10 ,\r\n                        0x413c1352f424c671982cbac51ac6f325021f8c1f5d18019188c3b1c88c1bccab ,\r\n                        0x09306a983a0b9370ea9b200f2a93d53d085ca80d6999bc971049c7f7a9527d6a ,\r\n                        0x43c3251fa1ab2b9ec27d41eca68228fa9414009d824235aebd9302237612ea12 ,\r\n                        0xd35cb508642609b2492fbf26300578bade4730e2edb05b15247d2910e6015a6f ,\r\n                        0xd6f6b28480cc43fd0e0b723fc4ad0779ad98ee5572ae5a899c937804149ffc27 ,\r\n                        0xae88197141857efcb271f18c797f05a183ab609ade83eb358a23a42f0c90e15f ,\r\n                        0x221a1532889fa1bdc89911ae821d81523c0e99a192a50402fc111078a7593dfb ,\r\n                        0x215be7a8622a0756385468571a61b6901480b97a565a10a10261422bcfc48a79 ,\r\n                        0xe07449072284f97178d108aab3c0659548a48d1b97bc56fc5b155ee0cf7cda83 ,\r\n                        0xc1fa78e4510db28305616b1d01350cc4d4c81f156e0e31e8385b10d55bcfe842 ,\r\n                        0x06807ae08679113e3f77ca658b1309ef2cabc7079d1aa82be751773d6af94f47 ,\r\n                        0x8335382601cb8dee005532866d402e04034b83645d0015d1c3307178218b0035 ,\r\n                        0x6414cb8140aaf8aea07a10d0024d05a00e42917c4396bd617bb166184b60102c ,\r\n                        0xcce62e90cd66548be85419b65e6416c2fa8898c1301027b1a82c8a0e811ed7a5 ,\r\n                        0x2d1b837b5583548b140a61040802026073cff6c41c5776a3c134f09adbb395ec ,\r\n                        0x6b812a2837f76fa5ae143b8b4ac49d0caa6f9574db12234f8033b395eadecf09 ,\r\n                        0xb05554315842cac1db21a0e559de54fe80c30a35912ae4741c9b2901c47c97f4 ,\r\n                        0x95faef1b3add71e287bd10a1781d815ab80d37323a65044ce436c119ee0aa008 ,\r\n                        0x291d2ba9686f095cee7e637543c933e28ca0b8871901c8c2fdfc85c0ce050294 ,\r\n                        0xc1267c6229e711515e24eae5f3714c74ea4003390224618ad281e4d1cfed6468 ,\r\n                        0x6e93b73429a1f23a68b77bf86aeba877d5d6ed5f5064812e89a99c5adc92f3f0 ,\r\n                        0xfe504a566c6a81d16615051243b5fc8394eff192f5996f26bbc4cad4e1064b04 ,\r\n                        0x0c49a97d0be57eb82b29a3f8fa0773351b5e1c92e4c57cc99107b549a89518fa ,\r\n                        0x906442e6dddc8974d67c38e938338258f6c24f875618f637b5bfd7150853f577 ,\r\n                        0xa80974e2a93c1ce7d64ed7579953257f43131cfa2d292aa6d5cb57503a52a578 ,\r\n                        0xc01bfaf6bd5e392c627fb3e8bde648cee693712f34554552a18d4909cb1dd205 ,\r\n                        0xb72e2ad692b24e41e3a82602fe4b67024c01fa97aa70c4f6981b0e3aef565247 ,\r\n                        0x9d4002b80f599d0bddfe6c2962478369dd55d36cd3b2c3c31f4939004ab7b209 ,\r\n                        0xd905e8fc5d872fbaf12e6b62fede208916ab9a0ed2df0f4f5a22dfa83c0eee90 ,\r\n                        0x2cb8a56d76a0de1b4111592488c45d3835df247c414b776235fa83a0fd65fad1 ,\r\n                        0xc7f3842ddc33f535e580a7fe7a379233e52a68e63e1095687206d0b7c3797e8a ,\r\n                        0x5bf8d7013fc07c2870e196981217039ff505a6cb20711bb071aa54583b041305 ,\r\n                        0x806c6fb02927cfaca7462561f2570ff89a72fecf0ae5970509537b818436f1ac ,\r\n                        0x72e0f9c20769dacb162d38c7e90e5c796ef8762091413c3c24ba11427713ea81 ,\r\n                        0x7806a409c20b114a3000fe13fde92a6a21a403b7851ab890ce6611bab6ca10a1 ,\r\n                        0x6327bfb173953f04092e6d756ab34c8a181aa584a0cd5548747c160b15118b5c ,\r\n                        0xdfdab2d486d820794d1886b60031d6a8fcd204fbabb344fd443825b92954651a ,\r\n                        0x8f9a11b413c5f7aa965adc04d50944a478e91d172310a0c9acf39877a90a9b89 ,\r\n                        0x8097bd76098af61355b801b3060ce888b9171427da11bd460ada409bded37e01 ,\r\n                        0x9c76117f43757e2840cf27707fedb480306faa77e0fd73d14b6ace362cc85529 ,\r\n                        0x0651db08a4a55e35f9297e02eb0f52cbdafc80307a79e2ee971ba17822c402c5 ,\r\n                        0x60a903ab911e2044a8a9016bf3c03af043cac73f8e76e1d3c41903b965a110cb ,\r\n                        0x530ad0b63777ed0b24220a206f974c5bb94b8a843d28cbfbca5d80443c630d6d ,\r\n                        0xce934c4b065fd61186b1fa4736092603b4ab3c8e716def00f20c79005355ef2d ,\r\n                        0xc776ef557f0220710f322440753ba84ebc679596587c3e8ecb41d8228ed12d24 ,\r\n                        0x1ddac26bcf05ce06c06a186faa625137249dabf528f7acda46583df6e9a18512 ,\r\n                        0x585fc51c26f67db638441cc4aa0b4b994a5a3045400880470324befb46cc2a2d ,\r\n                        0xa60e5412cb2871a3fe18f6ca5a4ab0509be746fe82e4f674b4f5348bc75853db ,\r\n                        0x2844520e04096469554060abc381fc02afc81eb6c4b28a8a32913201b6a10ac7 ,\r\n                        0x845640ad065a1e57058e9800cc0168e4ce952c8ca8256f91ed1489da2e67cafe ,\r\n                        0x42a6470076964a636a840a558953dd53109aaa024f81f1495bc16bf712280c20 ,\r\n                        0xe8242966e4048c90953ee2dd1921018ab3d80891719ed09cb44f6056d82401a3 ,\r\n                        0xdd9784f1b833a8403ac0d04b0d5e7064b2c84515f529a8cb8fbba66fbf1f91c8 ,\r\n                        0x32566519fcb025353e912cda5618b0de688d2726711f40a50dff42c8005780eb ,\r\n                        0x2486b59f46ace1f2100c6d8bb447dc2b51a095551c929e99b1426db32a65d000 ,\r\n                        0xd22376d0b6f0ccda1ff45214bd04cce4783177f85e02d554b958093ce0530d2d ,\r\n                        0x2d27552b06928860046dd6b77981f0823a0edc669edd05cecfbb92080b6dd50e ,\r\n                        0x6f59e867370410b8da71e212e46782ae13ae2bc4fe6dd03104b4346ac05b5708 ,\r\n                        0x69db448deaadf87350ed1347653c90386d0d4b957408176452aa182f20e51c72 ,\r\n                        0x0979fa6005c4ba57e9fee4e20c465304818f4a3a492aae2971f4ab4dc179936d ,\r\n                        0xc5a2c354ccd2d68a8d257edc3224af1d73d4a4935a2b28aa9623699aa8243925 ,\r\n                        0x220eb6462a65148867d0a52320b62ace38234e3d14af2c1c5441c04a50382833 ,\r\n                        0xb1d951c6cc87c867c148c3159bc2a72c2966081cd6e752247d20c6ab6106e82f ,\r\n                        0x1bb90a7b5ecd938a6d8da65a40940fc93ae0d3cdd78321aa5b4f2210a4455290 ,\r\n                        0xfd53455d43ba878a94785c52cbc65559821192693fc5b5072c0d6428a3fb859e ,\r\n                        0x44f24296540801179236cadac8beaa65361dc5fe22c5f92d1fd15bde79e29d78 ,\r\n                        0xc1eda1f0e52ff27498c68e2c035197ea50600167e262229d72908befc2ceb555 ,\r\n                        0xcf951d6180469f91bcb9bd585b0970d06d2d986d6a5be265bc674910aa2095d4 ,\r\n                        0xf5433e19954909ea2baba4e60a1825b939c6abe64e70d4d43497541d164c671d ,\r\n                        0x94303ace874e423a8089284e7ff6fe5838e94c6e925d24be713f2893eab7f85d ,\r\n                        0x52ff655e64ed833d6e278250eb5dfba333ca0b2e8908444395217f419d837c16 ,\r\n                        0x666379b807bc63a538861a942a6ccb834ce0dcd194ab40bb10a7e54e546815b1 ,\r\n                        0x082a39552450ef2bd79cba83306b8700a2303cd6042293c43ab3bac513efdacb ,\r\n                        0x10364da8e601044990f00f820acd8d7392d353031461fe2bada2620140688d1a ,\r\n                        0xa61d498c45729280c36d4fc66ea21e18beaa8086b3431c36bfb2c00b96500136 ,\r\n                        0x00a1940539eee20fd2478da10b50b6409a9861b1f695dd56d056394292512105 ,\r\n                        0xb86b662a6568e7c4494ea3b93392547b9c2cab5a2dbd1a67c437990db90e2578 ,\r\n                        0x6a203d1fdb181fac63a8a9374bfec776c65d51f755b82aa27ba23608efc847e5 ,\r\n                        0xabf55ded6c3388733e511342cb425674d7bd181a92e0b5fd03abd5ab0a2d043f ,\r\n                        0xec3fb57c5785c1ab55648ce4b55f9986b827575b38523b8a5f12052eb6a31a04 ,\r\n                        0x81b3d6c9fd89779e80566d0c84cb7e0d324f5c471821244f8d5eda10b132cf54 ,\r\n                        0x95cbb78f3a910b6e0cdf9e6d760778310fbd2d0966016638a536d4d1bd28a0fd ,\r\n                        0x1ed08d13437ae1ac2c34e9af54e488b57bc10d483704968314aa2c12614566fa ,\r\n                        0xa992dc963176cd24635503d42806ebebda786284acd3d4b8efd65a11d340d676 ,\r\n                        0x6a09b0be0014a88d2c5a2122cd58922a9b90ae8074603ebd5ced4f54d87f177b ,\r\n                        0x5431aae32c52da73412210968819394588d24604c8aad674cbc8bcaa5056c41b ,\r\n                        0xf5b3e41db2b6061dd567b7d54982027049bb5cf2eed583ff4af15015354c29d7 ,\r\n                        0xd6062ac805e22325a0bb14f8e0a07c32df4f90e2e16a1b228aa0d14ffb41e459 ,\r\n                        0xc8331f1bbae589084f0674e457db307c6a03e85d5bcb8ed08321b15a65fae2a3 ,\r\n                        0x83e342b2996551010b4fd4265c9d8843a94a8cac6e09d03749890a541738176a ,\r\n                        0x1c37da2ef154fd17a6e102441f1bab1cc84783b505107b92c85fd38bb95161bd ,\r\n                        0x6033da717c5a155547f6971cd6d7bbec90ad79a96974f70207de68316f7eac96 ,\r\n                        0x91a37dee033f87199180161c10966f9de5380ab86cc9416d30dd5927b4c20989 ,\r\n                        0x3f4a9171e3d606a976af19cf4e2ca26555b19ea96ec7821080478ea8ed95c4e4 ,\r\n                        0xb4808e6933957226f416fc384cb140c0e1e313dda506b0aeae82aa1309601caa ,\r\n                        0x9ec10deb4b1ca9547caa4a6d79c778d451a5f63aaf0a9cba7b02c8ac9d92c418 ,\r\n                        0x36ec480c19334007bcd6c0c68772549b924d12d81b4e7df20206f1f5a87e972e ,\r\n                        0x0fc8ad1ee5de6f9347f57008bb7ab864801ee59b24b6a67c5b54ab1ec9bd066b ,\r\n                        0x220e1b860aaded3cb5a783768613ab6471a51954a3520148484cfe441bd4a226 ,\r\n                        0x0ca83e3349479d2c7ba36899238c0adfdf1fe16bb21c64089a03a999442e10ab ,\r\n                        0xe302b6d18af6c23709ede5bff6dc06743b2b1f20c47093f095940bc4d8ac14a4 ,\r\n                        0xa1b16ed546480eca45da73a848c65ba568bf96d8a49c7e9c5a3c4837f9a72516 ,\r\n                        0x9da8506caa3703d18403a18812405d75b9d33cb77601736de4a2cff3a088feba ,\r\n                        0xae8a957a1dd54bd193daa0e1fe65aaff03a16063c35648102bd090d6501f7009 ,\r\n                        0x0160108f9205914404ed9b5e3da0d6a0d352b1b18129f2dbec5361e26817b14b ,\r\n                        0x35bfa68fb1142b4183b5934156653227e2a2b2fc2c2ca5e84ad56ad429ae8f4a ,\r\n                        0xbba3a34776bbb9880d83713eec6ec7868e644654239f9a039d7a26bce2545d41 ,\r\n                        0x685fb92cbc26239248a8aa264d0b22b205495f05117fc969b984bba73535a3a8 ,\r\n                        0xf8db756bfd628451ec5e8b3c18d3929fab35eb1450132643ac5e75bf77213901 ,\r\n                        0x8d1e28d6ddac2dbcaf6de8a92ca2645923f10ccb1edafe500d4f0de34bdbaaac ,\r\n                        0x4f569556cd800bcdd119db56e59fb1abf76c908add4006ea636aacd43d354fed ,\r\n                        0xa30177f56615785003997c866e458d608f89abc28074777e0595b5ed585d9f33 ,\r\n                        0xaa7f59e5d18d47a8d119d752238679165f568d34b06884bcee0a72a2582e134e ,\r\n                        0x3addc53a260ce1e618aa900b52fa57856c5bacafa4d5c70099b5410be5573e06 ,\r\n                        0x38824a56f1f74dd068522590643744171bb627f633c43cf5837602a1328ab0eb ,\r\n                        0x8c8ab6ee6056deda9859eba6de1ff594a99b1a46a015698fc556d318cb087891 ,\r\n                        0x43fa4fb5a3bef55541921a7444f0484155cd15227deaeef5f9ab8301e840e045 ,\r\n                        0xd484ae843a600c9f77837116d5ab325361945adb8a7a0ad4d0f3541ee6757505 ,\r\n                        0x3f357505759deee6a00e0347279daa9a46aa948e391daea53c3034adbc175ae0 ,\r\n                        0x08528fd2e8541f235002c9e45a2fc0514b74fde007aadbe53e968682222f16ff ,\r\n                        0x3ac41022392eccdc5a5213a5bc265ff8a4ed9e726f65d51333c88784c0cb0924 ,\r\n                        0x24de17e9517b830bb5ac68c975a8e31723342bfd4499041c1c564a86e1d79408 ,\r\n                        0xd19eae1b45d9232103f0fd58978a5a074a9bd4068df426d0ac5063b352391772 ,\r\n                        0xd451abda92239c2f014498b6ab9ed167ed2498f54aa69137e7b76de0cb8f5d94 ,\r\n                        0x11d183a88500a1603212abda1dd47edb4bb6f5e318cdedda983c79911fd5915f ,\r\n                        0xe7d791bf09ba00f4ab15c1202d9f25bd53d4292f7ebae10c8154eb21a68e0c40 ,\r\n                        0xb6de2a75a0ba805a263202543e831472f88b9f4f75b24098fa47e8017872ce9f ,\r\n                        0xdcd2895c0ea16412bf2327cae3dad64aa77d45ce2fa9f2a9c139bea64d98ad2d ,\r\n                        0x35d690f0570d0c6f1546ba934de832a9aae62fe55ae2f70ae1042b082c321d3e ,\r\n                        0xcfa02a9c3adb41475fc9d492f93a57837299373be117a88112550743d1ce90bf ,\r\n                        0xcc9788323cc0cf082d35baab5f8d35d3d1ab52481c04368e7db1122ad1bbdc7f ,\r\n                        0x5a19563ce29cf8205fcd048e7a544de52825b2c855fbd38a4f64e1b739a4a173 ,\r\n                        0x71b5fb37743f7021671b9568470e89e88d42fe80880ac98b1a2452e5cf68c525 ,\r\n                        0xed6a06092ff57de257d1db26976bea5645c7a114ae04dc862a1f72c7d11e1349 ,\r\n                        0x43eb0bd131d62191a5feb9667d9a38b64468d561104fa665d96f350f8adefaa0 ,\r\n                        0x7ea8264803e5caea1353537db346fa5d3b5ea0b617b58f7ee470cb039a23adab ,\r\n                        0x8437b565d6588daf8b5ec576f55f4fb5190d73c7af16322b4b3b7f3a94242c12 ,\r\n                        0xd786120ec7d8e60767e3fe358115675569269279f220bc23e8c49af4a5d38919 ,\r\n                        0x370cb0173bf170b44d7e862b224de943ebf073a8037547588be37515c96c4a0b ,\r\n                        0x12a1f35c24061673cebe8276c926b23e94004282606a20d0495b865f8e0e61a9 ,\r\n                        0xef1b01873fe172b0a182705479315765127503cffa548122d3adb610d0afaac4 ,\r\n                        0xca98b569cfe2698f565c0625b833432b47c5ca8318411556b5b814f5c801a47c ,\r\n                        0xb557eb644712f7908ed37999423e8c76368c68d4513e32363a40cdcfb821f00e ,\r\n                        0x7ef27d5890b7966724ee4065463089448fcbac86a6ad8d245d13509e77af92e2 ,\r\n                        0xeab780b5a9e75c5bc47e288cac5916aa3dc0beedc113404515e09ba238fb7611 ,\r\n                        0x220cad4ab892d7c612790c90d76e18ec8189317c1517c57448355ddabaaa6db6 ,\r\n                        0x82e2eaabc2eb6350ebf99a2ac6da6679094a0bd019042936ee60fbf14c9440c3 ,\r\n                        0x296ab9c2dca86d465c02727dacf69135dfe49dd8efb844ac0adb009e0eb291cf ,\r\n                        0x77a9197abab3ba30b65aa1ad7b5cf56d2e25a91b9ada9f205ebb89b7bb5beb45 ,\r\n                        0xb6f2efd990daea35689d9864bd598aaa7f91a60044b162581c708bc04baa7a8c ,\r\n                        0xc854f91eab31aff3da28b30e4502927c08603e7e993dabd8a402a74ea834062a ,\r\n                        0xbad39b3535202bc140943f23c838d9ecc35549caa0c205a44a552211e4057ad5 ,\r\n                        0xbcd5001e59cf52bf9e88e0b56ac4e1a9da3e6f649da3aeab1e03504b0a28c8c6 ,\r\n                        0xa07340f8512a5081ac9a505deab7dedaa1f22aa3a93c0b6cbfa7b31d883b421c ,\r\n                        0x928430dc24a4e1747a8aeca8b0b732838e7228bda96fab44357d049829f93fde ,\r\n                        0xef24198102b81ff80f9069b7d7f9bf09f32773a8150f27ac980f59b200366803 ,\r\n                        0x4befd5e679216f7d7b6d429ff8f068de6e0166190a5402e2be4249dd89f4964e ,\r\n                        0xdaa8631755a774364ded346091423d2c61e633390b586a17fb036b58048c068b ,\r\n                        0x106e44ad8bfe536444f9c4b152c781de0a358b30ed3b7edad2d41da5f79421d4 ,\r\n                        0x8905c557414c21ae5378b53b5e544fd219d050c8a30e650ed5e0750c147a5eb5 ,\r\n                        0x1b1c200e857ccca850ac45a7e1a024704d24300a8a85ddc76937f56bacc40b96 ,\r\n                        0x0e01f09de6b71a01adfb5dc702c612fc2cb5b62367ec60cb842caa8906a29130 ,\r\n                        0xe9d710f7b18f2536a73d7a5cac19fa3266f5411336da396950c1abde4ff0f5bb ,\r\n                        0xc36ae74b00e858673dfe2c3029e3e8ecda2dbfbafc7fda020ce8d50d24d20313 ,\r\n                        0xd77106a9ec23e4fa2ec3e7af4bdd8ee68c677b8f62d17af97bb54cf59622a755 ,\r\n                        0x388cd5aab4768f0e1e235f60c42bfe1cba72f64e5ee083c2a03688a80e465c70 ,\r\n                        0xda7f9cf120e2a63ae988546d3c4575cf4397cf43eb00c368cae437320b04d58e ,\r\n                        0x406965470517208c42da51dc265d96eda1399734db435a0313e7583733f98e0f ,\r\n                        0x0206a3032f2c3679ade1aa222fa4fa0e580868a1250bd7af00981ec090ccc719 ,\r\n                        0x9e58ac4e45c2da2499210e92972dba89d9c16418868e1591eb51fcd0373556e2 ,\r\n                        0x7b040d97bd5bbbccb80d836e10286b9aebb05a73e65dec2414d9137a4392d571 ,\r\n                        0x89900231a009aa635b35d762671910db034b759d2726b9699f39ecbd0b010405 ,\r\n                        0xe07e77381d28843856b57e933d81963d51af6830aff298205b12492d3a5a356d ,\r\n                        0x37be23b20efee16e0f1caf25e9ac2872980ff0ed1b81b63596b7f2edd0de286e ,\r\n                        0xa5e61932c350431a2acf645c8f3fcdf78cfd2b9431a2c4b0201350aa7bab9e4e ,\r\n                        0x40e8c7a55d0c06b259becca0b6aad08a95d074a6418d23f250342509b2a9b3f4 ,\r\n                        0xb8edbf93b2455d3341878c85d899b41b0b86dbac2b90eb478e7857812dad9101 ,\r\n                        0x12951bd4681fa0cd3784d65c6859054f48af9f6aab569aa8da6a6de08cb625a1 ,\r\n                        0x83ea8e085b12c47af3c9674d872736a61f3fc3a80e5798304694ac76d1ca4fab ,\r\n                        0xb01f788bd7fd59aa2ff6da5871a84577020c1878a9fb87c14b74e2230e4c0c3a ,\r\n                        0x13c06a284299319247471a7c5597950e2814a1fccf011acbf5a42255a3796f80 ,\r\n                        0xb724163ee3d459287130a69a10d53fa79ca0665c84d7b22603cb1aeaa4d7fee2 ,\r\n                        0x82c023ed8f7a58499e2aa658fdd61d554d8e4448cf370eebd4420a8c5e7c5547 ,\r\n                        0x3a5cbe684f17961efa2c5a633daba4e8dce52c88463ba854b6e365958907804a ,\r\n                        0x1a7d151aa966231d3bc9eae0d09eb3295955c0c5e6f3d35e12ae075692c8a067 ,\r\n                        0x03e0538bde0189cb55c577a357d473c805e549da510640ca0afe046d9fa8724f ,\r\n                        0xb62bf1926975c45e4ddd244f75233bb516eda29e26b5389345542724c50270ea ,\r\n                        0xaec82420ed1a273510553b3d072995f571469d9147cf93b45e7053b5047556c0 ,\r\n                        0x6181038ca95d28b17eb5662c1c7aabbfb0ff747212c02ad15bb97a7cc544a9dd ,\r\n                        0x36dd54f93d2ed5aeef6a9e80fc750fd562575335680dffbe734103424a9a1e7c ,\r\n                        0x9b3cd4742cb67d8545a7336f76ec5d3a505ac86e613547989bee0142dad96ab5 ,\r\n                        0xadfd8cd80633747e842caa031ff7c98f984a94b730c4af1a71e5f95c49d28104 ,\r\n                        0x054d470aab173c7f07b05481d3b9b480358ed832d8e6546db02e65a632740a88 ,\r\n                        0x54802572b1f9295b0f5fbbaa5bb03550f4cdfa6bc4a815754aa92f28e9882723 ,\r\n                        0x611509d39dc13230c8936c41d40049271796ea25a4484a3a63050ec1e7217f5d ,\r\n                        0x4f76d8c065ce5bcac1214ebe533dea0719434f3f0003dfcfb18fa946009d5b19 ,\r\n                        0x5dac9b240ffc6a7d0b7e9aad7b4c4df060aa8b4221b401c10ce869ffbdf7bc59 ,\r\n                        0x8cbd6024806655975a269b75e023a874af3db29624846a55f3fdd61929d79aa8 ,\r\n                        0xb70e02abb8a706487580e25445c753b4d90ebb4264a9cdabb26e7a64829ad355 ,\r\n                        0x5a2b8aee78745ef1bafdb5623386a5bec454c5045513d157a0b2a25c85145742 ,\r\n                        0x537d9921415f032b1188ff226d0a06eac8b9b60293c72c1b013fb4bf5dd7546f ,\r\n                        0x80dadbc8111d11c32f914054f763229cd06aaa9bca55a192ad5adf3df43874b2 ,\r\n                        0xb1ce9c5675c76bbf4acddb2a072d3bf7d0c90cd8117c4677b1864883a23845d0 ,\r\n                        0x879fb36d1054870ac4af7414f48bb24a30a1a8ec01062650fd77c00167ac4ca7 ,\r\n                        0x32bea7b7d5df69d65b706dd2b563b196941a8404c2b511988c051e7a559638ac ,\r\n                        0xf05233959718f73a19473a044f00706d1941518eb6ec660621a1c64967e4b4b9 ,\r\n                        0x13a0a805a7fca1e952dee97c85e7df20ba744ce041027e6096c06d5689b0d600 ,\r\n                        0x95ad7530161e3388d68e27e8d94bf65bf7fffc3b8199a2f1578f22b0830169bb ,\r\n                        0xadc28ee8139918b582c7a24a8f8e01212b4efa40d2dace83ace8e1ffe88a8f37 ,\r\n                        0xc39af51880811b145746d6231b4e0edf04f43407b52aa515b58f2dad9954b0b1 ,\r\n                        0xc6d6a1b4ab266c0c1dd43c35d40ddf59c2eb54eaaa6b2f2b1300ad30b7d8f448 ,\r\n                        0x8875c2a759e17b5063f2ead6aedb5288ebd912b8187a53ccd976707f9ae1f5ce ,\r\n                        0x08c47dd2c356d49dbed56e26604d4dcdff0fe5aa87a228f18daedea1ae22b375 ,\r\n                        0x950c25183d01450b64fdc5d9ec93efaf37edbd924561afdcaca9dbe5a8df716b ,\r\n                        0x47c2f627e095aae966d7bfe76588228b4e5b7bbb61e5c77b88054d25436db47f ,\r\n                        0x6337face6959d5a9ce5f1b1eeed715ff7641916bc03ad97cdeb649eaa0918057 ,\r\n                        0xfc7b7c3dbe7ac68300807c76cfd65908f5d1c9dd7fa6f2317ecb1e48e92b3888 ,\r\n                        0x3397e351d12aa6d69cbe9e59550d3d324c60d0a1c72bfe3189a55ee056279905 ,\r\n                        0x59fbfe3a63ac9b0c6b8db4ba5efa04dabad6e29b9c766feec5b1508b8a4d6f65 ,\r\n                        0x78e692a28eae9354a1955b2cbf02de07b061aa19176ada5444fb2f98d0d561c1 ,\r\n                        0xaf82b70c4c245189a4a3ee8f8c8ed0596fb5edd9342258330955fc528f22f91c ,\r\n                        0xc447b97fefcb5d83a5f9e7f454f559726b1c0ec756fdf65bd8de72565bcfbd2c ,\r\n                        0x4dd2c1ad6ffce0d15343c3d6a385a2a2a49d6fd76c9267367018aea0a70ae915 ,\r\n                        0xf426bdd54a9df5c656172568b2587cf36cb98392fa58075c48f13b0bf2cbf3f4 ,\r\n                        0x9c87a57ad9539bbe1a49f3e7f5bea8654d1c13a6ebc84c6a96055e97182380cc ,\r\n                        0x5a44c9f1a9d316046c1d3a2d4a3abc7a8c854e2d6ad7409b3f59c93133151d16 ,\r\n                        0x04b08abac8c723c14eb9e5f813cad4c508a2def16bb34374a0a4ef589815274c ,\r\n                        0xa53beddc2945684b3c773d71a80abce6eec71b8c4dff6f7f5f6ddfaa99f3583d ,\r\n                        0x20fbdd1035ea7d0621b31e3353f7c312f0693dd6e348c0372bf9420d9387b647 ,\r\n                        0x55049e8e4a9177181e7e3bf4ac881ba36b275fb179681ccaece78b104494a91e ,\r\n                        0x688024db3a080395b6424582b46615c7f46024af2744a81f0e790e3ff22fe157 ,\r\n                        0x53872e407c1244d5ce8ab61aba6a7b6842925799ea1d8495d67b06b756897ed8 ,\r\n                        0x611d960b19909dba5d3ff5fedaee3f67d58e9ac6f5e81252f0a7368e113fedc6 ,\r\n                        0xd499be2d33a04227eb7efa11f49c08347f9058530bd3538959dbebdda8976861 ,\r\n                        0xf4df17877596a86fc4969a24041552ca7b6453a0f6fceaa3193a191b9f1d2994 ,\r\n                        0xf4d7e1c2a4366192849d7933ea99dad5f11dc695a6fa8775c80fd61add77c2e9 ,\r\n                        0x10544d65e5dfe55fbd9e6fd0334284f9f9e3965bd7e83e6abf64ea7126a83aa3 ,\r\n                        0xafda85e8eb6af4a5a8cafdb7c1276d8c73ff9bbf54d5b51cf6f016c8bbca2df3 ,\r\n                        0xab2ddbd35bbc5377a8687926eda9a9f63e6b302952633a8f870ad34dbfe3543a ,\r\n                        0x610d35e9809736f09eff16a54164d06b304409f85f8ba22afad0b11e35f49699 ,\r\n                        0xf4448da5da8c9dcb5af60eb2923869c1b64fb6cae8c569933263e82dd859dfb5 ,\r\n                        0x729b489a75c9abd2210ffdf2730bf2f6d281c4b1b50ae9da9aa9033b3a92730e ,\r\n                        0xc30ee4f7f8cb8f46d5538c8e94a83729b27e2cdbc1967a321990805aea4fd0d3 ,\r\n                        0x79b44a0e6e515546ad0002601390634b0f3090cf563d010b9c20c0d250fafd59 ,\r\n                        0xa9da75dc388fa293cc92118cde19d34853099bec8e2ac49d75bcf1b310d11c8f ,\r\n                        0x3570219c8bb6e292f6c09b965cfdb4a845750d835a4e47e8b2ce6ce971342a4f ,\r\n                        0xb560f0a6ce7b9d69d70337b958fb3c49c786be64f0b323f6f55ac11b9df6784a ,\r\n                        0xd3aef236aceae4a6f655ced6df3efa75b4ab9313fa24590bd92c5d7f9bc20dd9 ,\r\n                        0xfdfa457f077cb4df2aaead0718c9624aa92b2a89f4acd2af0a223a0aa41d83ef ,\r\n                        0x28ec89da14ec0e9ff056c0ecaa987dd4414f5c1060e9c00759a37c426ffca172 ,\r\n                        0x193fccbde2b323e86089ce42a041c6d673385853e56fbc2cc3f443981841c54a ,\r\n                        0xd48b9eaaf28cba45f5cde9b8dfd0497742454fb9d07346970a78a4679b99ba7b ,\r\n                        0x95d25a872fb37acd67bd57f53c90881a567339b2da23a8c915e093a44fd173e7 ,\r\n                        0xf40c4d309f94b348f3cad25cd06bf73c65ecdfd58ea2a7847045188ae75bd133 ,\r\n                        0x9d264652d49ed32ae9ada5e3e65415dc0b66f4ccba147584353cf2223f627eb6 ,\r\n                        0x4362dd1e475b5b5c51f5b6c67041633d4f485b2ce1b8887f4ad2a88d30a21a47 ,\r\n                        0x1e3ae9c182303406a063b35b8ccfebc0fd282c9ceae17aaa5689ac365c570d2c ,\r\n                        0x7a6859d383279ab29a76b04653c69c7b6a2513d9b49040c8664bddfce15afd23 ,\r\n                        0xc0d172fd49960df427adbaf2475e85c4d9939db4b5fcd3cba3bc9a4df5ffdd97 ,\r\n                        0xa76516530ad5f48003f2fb7fcfa820af975857f9b97e679fb4f1ff7a8d4d9d9b ,\r\n                        0x3801d354ea7460d1e01eacb72a4da4d604eb846e5bded0d356feba40b3f2cb9d ,\r\n                        0x5b4f23b1c31f5947b61060701807bac89fe0d05fea4256a0dd337456c7ca742e ,\r\n                        0x04f216ed400cae7de4aec1ba6654ab42b743be3afc29bbd3164ea2137c651a32 ,\r\n                        0xcf27fea373c9e2ab10be9fc7dda4c62f764069dbe1653d7902fea767d0e1277a ,\r\n                        0xa6efcf06810a0e46be73685ae7ed875db03523681f393c5e4f0dfa3a158ac1f5 ,\r\n                        0x0e7ab2630f70487d5b57b19eebf6d9eabb8aa5cd039fd4835bc409f5b0b57f0c ,\r\n                        0x4bc821ac73cd5a03d50a3f42ef9a9fdaec6157df78be03412a8b8a1d64b0ff1f ,\r\n                        0x377a5edd191a11c321419e903f02de879e93a315fe8e7123390960f5adf9fdd9 ,\r\n                        0xefc15bd5e8a8a391590f5c4290bde034278dc0fff17d3514da3075ce9f51dab3 ,\r\n                        0x707f2ff211085e1de55d6792ca748ccc2d3d06a93ebb070166836b96a2e39f8b ,\r\n                        0xae460582edeb28337fc9ea982255f2ef75dc773071b55f732e833133d61a7e26 ,\r\n                        0x5aecf1bdf96734e9a3ceac248066cf8c53191e12e72079b6d6be9a55fecd6a6d ,\r\n                        0xd87d55fffabce9cf5192894855c486d39978d0bbd8f33fd4634b1ec0e67adc34 ,\r\n                        0x77cf082bb2a07a1fb34e7bab7f28ebb94424382900eb7021e31e622dfc6a4fb6 ,\r\n                        0x29aae487ca3e0a55381358fc7f0089a10a8f8e746eba00000006624b474400ff ,\r\n                        0x00ff00ffa0bda793000000097048597300000ec400000ec401952b0e1b000000 ,\r\n                        0x0774494d4507e4010f0d371648094ce4000009644944415448c785975b8c2547 ,\r\n                        0x79c77fd5ddd5dda74f9feb9c99d9d9bbbdbbdcb2f6ceda6b0b13222b262bcc45 ,\r\n                        0x58044c804896a52084bd8ae3585a8cc44572501240414858e22136c6d8eb8090 ,\r\n                        0x6563ae1171b4a0c4582678cdda8e19f0ac27cc7abd6766e7ccb9749fbe57150f ,\r\n                        0xb3c47989f6ff540f55fad5f77d555ffd4bdc75c73f9aa0d64429c3cd1f7a3f96 ,\r\n                        0x93e27a1a236284a5c8d302a35d2ceab49b0b7cfdfe130c36c6f4e666f8cc673f ,\r\n                        0x21a4076565705c0340515638b64b961a824050161ae90a84100028a5b02c0b47 ,\r\n                        0x3a355acd1e795e72c5c1ed081b6c09466440854082f128a690252090ecde7519 ,\r\n                        0xa3c990a5a5be198e5f6363708e52a75455415e1a16b6ed244d348b575ec1e597 ,\r\n                        0xcd1d2fabf25ea554eefb3ec618269389703c3720a835196ebe86563099689419 ,\r\n                        0x51e911da1454a922cfa0d5d84ebbd10163b3b4b44cb3dde08a83dbc0da46a9ae ,\r\n                        0xc27101341a8b382e79e4c4a39cd2a789a31d775e71e59bcf0b211ecff33cf13c ,\r\n                        0x8f56ab659c289a32370b795ee2ba5037165ed0c5b21b4001468276d105a812b4 ,\r\n                        0xb6d8bd6b0fa52ac932f003b06d284b4559e578b580309474da331c39722d4f3c ,\r\n                        0xf6c80e6d8a138b8b8bff668c39ae947adeb66d6349a786ef35c13860603c2ee9 ,\r\n                        0xf707bc7aee1c675656585959e177abab2cbdfc3b640d9224c1f5ea8c4611d285 ,\r\n                        0x340363812d6d842dd10a540528c1f6b916ef7edffbb9ef1b0ff1e2d2cb472d29 ,\r\n                        0xef1e4fe283066c476bc334be1819303f2f11ce0c956aa1748e10209d80221324 ,\r\n                        0x2918c0f33cd252d3bf00fdc10671ba41bd61916653ae3974185340209b980a1a ,\r\n                        0xad056ef9d831be76df37f8e8473ffc91ab0f1fec29b8dd914e0dcf0da9075d9e ,\r\n                        0xfd65c655d7fa4c63f06b0e9e7430c078a410da261a83e7058cc6535acd0ebd39 ,\r\n                        0xe86ceba1ec1e690ea10fe9149a01543a41ba202af0c3161ff8d05ff2d0c30fe3 ,\r\n                        0x38b71cddbb67c731274d4b2ce1e3b921ff72e2311efbee94bcdcc4f324519422 ,\r\n                        0x748399ee0eb269419615341a6d94ae180c37d9d884c240654196959c89363874 ,\r\n                        0x7081f5cd18e58ea81c58db788d42419665dc70c30d3cf8e083dcf1d7b7dd293e ,\r\n                        0x77fc5153731710c2a63b536765f539ea0df03c89257cf254321916d48326aeeb ,\r\n                        0x321a0ff06a36abeb2becd9bf93cd7842a3dbe6bab7bd9d438b3dc2001c013ffa ,\r\n                        0xd1f7387dfa34935870eedc0633dd36c2941479c2e6600df1a93bbe6376ef3c48 ,\r\n                        0x9e554ca2011bc365eaa1c37832442b68865d5c19924e0c0b0b3bc8f394caca78 ,\r\n                        0xe9955ff196c36fa2b26d5cbfc164aae9f7d739b4f826def7deab989d0307e8af ,\r\n                        0x256c9f0fc8f2ad5bd10ae1aebb3e855394194551b0b6b64e964ff8ea573f0602 ,\r\n                        0x8643e874606d0d5c0b8e7dfccbac2caf72c39f1d254a07485b72f7dd7fce734b ,\r\n                        0x053f39f914db765c8ef4e7d91c1bbe76df937cf0e67770d91ec1b6f9804a8310 ,\r\n                        0x5b5d0b6c0683015618fa08aba43b13e2f91669067ff1e1cff0c9bbbfc43882ce ,\r\n                        0x2c6c4e064cd30d5aad16d2aa114d4af2ace4ec59989d73b9eeedd7f2cad915e6 ,\r\n                        0x77efc5f67ac4798387bf7592248317fe7bc2c993a7585a5aa5d3b289a69a66b3 ,\r\n                        0x89334d46b49a31ca94c4c905ca0aba3335b66f9f279ae60cc67d842df16a9234 ,\r\n                        0x4d91d2432b43abd160fb0e282584b3757e7e6a86c178406366075ea3c5d973bf ,\r\n                        0xe53f9f19f3eeeb5b1cfaa3ab186c6a342084a0280a2ce9555c18fc0fd2cb71bc ,\r\n                        0x84242f31d698520f3164d8d2a2bfbe4e599634da21e3f190b05123cd26bc76be ,\r\n                        0xe2e5331778e9371bbcf3c6455efcf569c29644db8a5ad3e7bb3f7c9c53a7d779 ,\r\n                        0xf6b9555e39b3425980aa3460e15cffa76fe527fffa33fa1746cc6dabf1ef3f7d ,\r\n                        0x82c3470ed06ecdf2f813dfa65031596ca1448af40a7ebdfc0bfa1b6769cd29e6 ,\r\n                        0xe71d4266b13c2880689ae0b8506a455ee54469c2d5d7cc6152701d500a82c0de ,\r\n                        0x4af58def3acce2e2412a95e3f9b03e38c3cc4c9ba0d662348948f28403fbde88 ,\r\n                        0xc960a30f45a6f0eb3617c6ab9c3d771e19b6d9886d66b6b9a8dc6138326c6e4e ,\r\n                        0x280acde6e688e110ac5ce14a9bc968c06caf491cc7085d190490e5906423ba5d ,\r\n                        0x49924d49134dbbd3e5c7279f34d7bdf54f68780dd289c2776d6c072a4a0a6171 ,\r\n                        0xeccecf32ceead8b57d5c77fd4d2cbfd2c7d88620140cfa4b9c5ffa0fac62401c ,\r\n                        0x8d39f9e4b7d11a6ebfed6f708405d318a40b611800259ee751f36b141a26f184 ,\r\n                        0x7ae063a179e9b7cf60b3f5a0db8e83b27c6ebde556eefde747b9e93d37f2d47f ,\r\n                        0xbd48b7b7974992432599ebec616131e3d9a77fc8030f3cc0348134491142e018 ,\r\n                        0x03f53a2000e1324d2704351f8183635968e190a3899294bd97ef6761768e681c ,\r\n                        0x931715edd9369981d6ec713e7dcf57b8e9e6bf62b059d1e9cdd15f1b226d8fa7 ,\r\n                        0x9f7986871eb89fb00e5a81ebbab45a2d1c2e2a8e14ae6f706c178147966a846b ,\r\n                        0x311c965878d43c8fe75f5867f9c50d3cd70521885e58a5b4353b0fece7d81d1f ,\r\n                        0xe0fe6fdecfe12347c9d308cfadf1e3efff807bbff07956575fa52a7316afdcc7 ,\r\n                        0xe62866349ae008017906b560ab7610a0b4c2f73d14d0ebce5156a052387cf57e ,\r\n                        0xac028afca23d7240b8d01f27fcf1917d74bab772cfdf7d8523d71ce5a99f3dcf ,\r\n                        0xbd5ffe5bae7c03a4d14e00aa0a3a9dd696e74280e75f4c350016b6655d1c8155 ,\r\n                        0x95f836c446f1eca9df50a4056559e2f992522994ae700317c77170fd3a9f3f7e ,\r\n                        0x3b7fff0fffc4f1db6fa3614df8c5cf5fc5161acb86bdbbf710f8de4530ff17fa ,\r\n                        0x07dceb1a0f07a812da4d9b37bc711f524ab4d6b8ae439e97f8be4429c8f31c63 ,\r\n                        0x0c9db6cf17eff924fbf6ed248e15fbf7be85ac80248e68b51aa84aa175f57a8d ,\r\n                        0xff3f753a1d6c1bc693823367ce906519699ae2380e555561dbf6d6762d8b344d ,\r\n                        0x715d975eafc7d34f9f623a4d992629be1f50afd7d8bf7f3ff5a086ebba97068f ,\r\n                        0x46238ac2d06cba1c387000cf93680d524259be1e69bdee53969a2449683643c6 ,\r\n                        0xe388306c60d95bf3a228a21e0694794196659706cfcecfe3b882b50b31cbcbcb ,\r\n                        0x1445419ee70441801002cbb2288a02ad3542086cdb26cb32ac8be7643a991286 ,\r\n                        0x216118b2f7b23d049e4fe0f997069f3fbf469a2a7abd904663919abfd5e5008a ,\r\n                        0xa2c2711ca484a23064594610d488a298999990f138a319fa680d5194e0ba0e45 ,\r\n                        0x9a91e5c9a5c1dd6e977add4608288a122124455121a5f3bfd119e321c4d63725 ,\r\n                        0xcf0b94526499c1b22cfafd0181e762b4c60b6ad83eb88ebc34388ac62489c6f3 ,\r\n                        0x2c6cdb464ad0da410830c6d06efb0c875b6dd0f77d2c0ba4944ca75302dfa3dd ,\r\n                        0x08b16d9b344d298b2db75355c5a5c19e947ceb91130c0603c6e331799ea394c2 ,\r\n                        0x1883d6fa52cb29b39c76ab411cc7a02b8410ecdab58bdf030d51933dc28c84c4 ,\r\n                        0x0000000049454e44ae426082\r\n                    End\r\n\r\n                    LayoutCachedLeft =5100\r\n                    LayoutCachedTop =2400\r\n                    LayoutCachedWidth =7980\r\n                    LayoutCachedHeight =3300\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =540\r\n                    Top =2100\r\n                    Width =2100\r\n                    Height =360\r\n                    FontSize =14\r\n                    FontWeight =700\r\n                    ForeColor =5324600\r\n                    Name =\"lblHeading\"\r\n                    Caption =\"Choose Action\"\r\n                    LayoutCachedLeft =540\r\n                    LayoutCachedTop =2100\r\n                    LayoutCachedWidth =2640\r\n                    LayoutCachedHeight =2460\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =93\r\n                    Left =5100\r\n                    Top =3960\r\n                    Width =2880\r\n                    Height =900\r\n                    TabIndex =2\r\n                    Name =\"cmdBuild\"\r\n                    Caption =\"  Build From Source\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    Picture =\"Build.png\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    ImageData = Begin\r\n                        0x89504e470d0a1a0a0000000d494844520000001e0000001e08060000003b30ae ,\r\n                        0xa2000035447a5458745261772070726f66696c65207479706520657869660000 ,\r\n                        0x78daad9c6996263b8e5cff7315bd04823397c3f11ced40cbd7357a64bea1a66e ,\r\n                        0x4995f53222bfc19d4e00063310a43bfffb7f5df75ffff55f1662082ee5da4a2f ,\r\n                        0xc5f3bfd4530f835f9afffef7fd349fdedfdf3fcecf7bf6d7d7ddef37022f457e ,\r\n                        0xc6ef9fe5e7751bbc9efff8424d3fafcfbfbeeeeafab94efbb990fdbef0fb5fd4 ,\r\n                        0x9df5fbcfe7dacf8562f85eb79f7fbbfef3bd91fef4383fff15fffb31be51feed ,\r\n                        0xdfa932193b73bd185c38d1a2e7efa6bbc4efbfc17fc6df16331ff2b1bd57f27b ,\r\n                        0xa5fdf3b973bf7ffddbe4fdfeed6f73e7c7cfebf1af53e17e8fb7fc6d8e7e5eb7 ,\r\n                        0xfcb7d7e3efdb84bf5aed8f3bffe50d3fc3f57ffedf9fe6eededdee3ddfd38d54 ,\r\n                        0x98a9e27e1eead7a3bcdff8e0642ae3fb5ae14fe5bfcceff5fde9fc693ce2c262 ,\r\n                        0x1b6b4efe2c67dd02b37d2dd9b661d7cefbb96c31c4144ea8fc0c6185f85e6bb1 ,\r\n                        0x861ed63346d21fbba1c61eb7c322212eac167939fc1e8bbdfbf677bf658d3b6f ,\r\n                        0xe393c1b898f18d7ff8e3fed98bff377f7e5fe85eb9ae996fbfe78a7105f934c3 ,\r\n                        0x90e5f4379fc220767fe634bff97d7fdc9ffcc6ffc9b0110be637cd8d071c7e7e ,\r\n                        0x9798d9fef0adf8ec1cf95cf6c9f92f34acee9f0b3045dc3b33188b58c017dcdb ,\r\n                        0x8af91a4235631e1bf6198c3cc4142616b09cc33677b14d8c05e3b4a07bf39d6a ,\r\n                        0xefb32187ef65a00543e45862c5343d0e8c9552c67f6a6af8d0c8312797732eb9 ,\r\n                        0xe6967b1e2596547229a51661d4a8b1a69a6ba9b5b6daeb68b1a5965b69b5b5d6 ,\r\n                        0xdbe8a147202cf7d2abebadf73e06371d5c7af0edc127c69861c699669e65d6d9 ,\r\n                        0x669f63e13e2badbccaaaabadbec60e3b6ec27f975ddd6ebbef71ece04a279d7c ,\r\n                        0xcaa9a79d7ec6c5d76ebce9e65b6ebdedf63b7e5bedc7aa7fb59afdcd72ffde6a ,\r\n                        0xf66335592cbdcfd53facc6cbb5feba84094eb26c86c542322c5e65011c3ac866 ,\r\n                        0xbe594a419693cd7c0f04450e58cdb28cb34d16c382e958c8d77edbee0fcbfd5b ,\r\n                        0xbbb99cfe47760bffca724ea6fbff613927d3fd58ee1fedf64facb6c7cb28f119 ,\r\n                        0x4851a839f5f1026c7ce0b411da504efaebcf5bca650e2e411ac2c8bb5efe1f01 ,\r\n                        0xc766b6f5c0fd2ebb6d03764cf6d89d072ee154bf52bdcc534df19e15721de5b4 ,\r\n                        0x69a9563bf576006ff559cb89b5ad13c71dbbce99425e825ac1da1e73ec54dae1 ,\r\n                        0x9bf7d818995b9dc155276e3bcb884cfc9a27973d99e39a4f6b912ff11f33b35b ,\r\n                        0x211d3be6fe569c659b8dd3c7dc4ce18e05b70975efbbcaf475659e0eb8dd8c8b ,\r\n                        0xe9b997101ee7cc95aed0b4f469d3a588d395058b889a165bc5e6c65a3cfc661c ,\r\n                        0x7bf4dac69075eed927cc314b8ff5dc566001a7ed960a6e3aaac32f70f8bb42df ,\r\n                        0xb9ae9d42b98be7028872190dd7187119c660eee609abb63c178f54eb8a71e773 ,\r\n                        0xcee279763898ff9448262869fbdc171335712a4686813b2110e2b5717be73205 ,\r\n                        0x4f58e15f18d8fdab37fe3b3fe7dcd71f7c3ea6e1e6dd9e99c00bf1ccbad69d93 ,\r\n                        0xb8e88d91e31244b6bf030fb9fe122358b3063c7b8c9ddb0eedc6d48e270c6e72 ,\r\n                        0x36fbf54c2b1eb698b43d8aa512f988dd1d0fb081a1429c78c45df26cef4f4a45 ,\r\n                        0x3fb1f2c6ddb8cadef5385c2f31047eedb7c873d7d2a5efc8e00383adc289d1ee ,\r\n                        0xccb17ac3578e6506c010e7ba61d63d6e0c7cc34dac05239899792cadf345e140 ,\r\n                        0x5c389f9d45d2309937ddd8eccc9b061108189dd4e6d900d2b4be673fbebab92b ,\r\n                        0x218f13909dd23e446bacd9d2d18d18c95cb5c619c35a755c658bdd71bf9deea9 ,\r\n                        0xa00f717e78bbe2635ca865bec23d6a618281231e28330d7da6b623b3deafb5c5 ,\r\n                        0xf5536bd39f156f3d11e49d61e717cefe68721c2f6e3845e5016ec59cf774b805 ,\r\n                        0xd36084abe6121cd2c73bb1aa9fc9ca3869a6bdd3c1687d5e66bbace1ca0d2051 ,\r\n                        0x5878e8160a773e7a944607197687d19944dff9fad931c26032d6eef3b43a3d81 ,\r\n                        0x48f6d5ece7321d18706f2fa6bb8d2870e8e9307160efe5dbb30fc6085a03a17d ,\r\n                        0xdf2ad346ccce6c5c1240ad6517bf22508bc365cfe3c19f2074e02c79e74eeea3 ,\r\n                        0x8f9f98bf072a0804c29d547771aa7841a15e7fbce5c4d206930d9c26b0a211e1 ,\r\n                        0x15f626a7ef869dfa3e9b6c23a70458aab2899f7101e2835160234047f7c1572a ,\r\n                        0x19e8c5daecc0c1ff4bd0f1d3f9304f0799733cd6b6dd0c06973952061b312f26 ,\r\n                        0x5cfd00afe54672473542d000b7769a17ec30a6c637c77544e7091e872788cfae ,\r\n                        0x29e3ad71e3cbe4a0b2efb401b0414af1cb5e620f5ce58c558984a4cc19e7ca9b ,\r\n                        0x6c965c8921971e1429b581c0c1ef08b692a3c0c7d64f8649ddce176b4830561b ,\r\n                        0xdf10f9910380804391f9ce44d484cc1455126057a4cc860f7b9c1bdc27c9c295 ,\r\n                        0x8532154f2b2d16b0160039a9c32a0ec1b60f89b2b404b438f2391150b648f04d ,\r\n                        0xc9fae2960794c5990af7f67b9eb4b1d5099b341b929e6857bb84d309697c4112 ,\r\n                        0x9b13765e52348fc860492504d929d30e93407ac6bf6b2c8cbec7944704eb49ea ,\r\n                        0x48a6e517f9580ed7886090c52d06dc6fc0778f124c277d9c2f5390d56f1e5c23 ,\r\n                        0x8c01533ab864f6d5b6d797f3251d96c570499aa9d7e48816bddd0703425f3623 ,\r\n                        0x019d9c487968ca4254f0b4f910964c1061058705510e205cf642af0a37aaa0d4 ,\r\n                        0xe5aef7d70e39fb6ea52f7802b37cc91d1b42124999a49f7e3af1ba12383cc777 ,\r\n                        0x89be50b517dbca8023b81230f3590b74e1fb089d429ecd7da03cca6ab3912701 ,\r\n                        0x068d944721ad5fc269132290387c34400e86ccbe1dac824c9b93b44cb53c99d1 ,\r\n                        0xbc009976736e68d6b9fc005a5e7249849adf5083567c07eb069c900c10d3bdcd ,\r\n                        0x814289d43180941ab96d3ecad16839487e1c7c41f8c434c3b1c8b41b2f542e65 ,\r\n                        0x9419898b93c743ec6f82161f2369249c746ce456c4e1d6222eceea254cdf5ff2 ,\r\n                        0x11698473807ba2b038181e02884e8b4b3c8544e01d4ec05df344599087001f54 ,\r\n                        0xda9800f0b8e4a20c26ee029b01f8c936927fc477fe810a7efcc600f73f078d5b ,\r\n                        0x7067c8887cfa8efe10fd06688d7e9950d9b4b606b186b2d695d3c669605f159c ,\r\n                        0xa365efbe9ace3cc1519291e2f062cc436683a1386614fe922b3a0bdeb6006b72 ,\r\n                        0xe412adc24ae5cdb897fb1142b8adf052461211654651c28b19822f2e874ae0eb ,\r\n                        0xf6066a53e93c417727690b5b438ac84e040759a7c499b1cf5d6b43001966f213 ,\r\n                        0x1a44c08272bdbb3117e4f7ec50d7810e2be2f69c203f598469ee9534baf1e2f9 ,\r\n                        0xe45e1e821664c2d00da70711b278f8d90edec1c7fa5eb070e6e617fe41be2002 ,\r\n                        0x24ae8de9ac1d92b3e66217895ea4037cc227a2ab13d7070eb3dc3798fde04583 ,\r\n                        0x51d6ca3605dc878b883065d209cae4d1f75d06d143e4e4ad91a5de334a138c75 ,\r\n                        0x4c05ec005a8092012a88b9fe8275114512a3851baf1e92723aa42ae7c184663d ,\r\n                        0x43aa04781cd08fbae676934ce4451c7139c642467fcc0b8a0dfa7121b86cd1dc ,\r\n                        0xc17671c652d6ad41fcfbc02741ae3b8298989fee3c6332874cf1d97df2508c91 ,\r\n                        0x5fa2f1a47d9d7361cb18cb0e4913a7e68a38093190a328601a5031a5ecb9895e ,\r\n                        0x2e01c56993cb8d7cb70115c8838c1340391e07d17d00ea2549207200e5c70310 ,\r\n                        0x798c2ec29127e9c894d27557701a175c07e24458c62135d5e50251829154d43d ,\r\n                        0xb61363b6162aa4a7489b2408368eee981292047e17426a4cc10116909baac7e0 ,\r\n                        0xe182136615bce756c003601a9b30324642063e072ca078f07bb79832dfb8374c ,\r\n                        0xf65cecc17df4908b28873aa3d13075e53a3c01f75917d42b4ade4126e21367b6 ,\r\n                        0x68957434c8330988c217322e0dfef24ff0230d04268432203b415e441214692e ,\r\n                        0xab1d3e0bc683cb80f68292e14a7122d78943ac03f194fa5915065771b6dd60d5 ,\r\n                        0x40bb47ca31ad474e8b6be2b27b2e282a1e5dc9ee0c306083bd1c92a7daace37c ,\r\n                        0x9a83dc0ab98868c3350e8f19887f8c0e18cd3d1a2e934e25dd23960e1f04e12e ,\r\n                        0x4a7e42acdc109d09b38541a09d0486d4eda52db1faf04045ff2828ea001af77e ,\r\n                        0xcffa1bcf672248203790afce728b2c001a80d8b02186f7dcb38a00b67709828b ,\r\n                        0x8724c75e28d252b284d9f1dc9222b8fef3b2960d4dcb2f19e74749744009a20b ,\r\n                        0xcf3bcc9cf2f35531814cf150d48ea904876c6899d1f35e498878060524a9ec43 ,\r\n                        0xba13e8913670ad2b9911dfcc227a1773dd9f1ec669f08228a90f6dc38d302dee ,\r\n                        0xe8c70373ffc321f5cbbfff19cf47db5b62c20f6396c67da89ac728086298ffe6 ,\r\n                        0x93e7291478919c742337b8adfe8b2fc6158011502443c10a73f1a287785a0f25 ,\r\n                        0xc17200f59bae23384538530419a5b4f029c3e331d951c9e5424240a77bb26690 ,\r\n                        0xf93a298b737434affc8a40be44c1552582b92f786ec27d0051a44d47bfc3118e ,\r\n                        0x607442ccaf5922cb4fec11a5df995c8613611631aba8877b8ee8209ad6a04d08 ,\r\n                        0x326c8679a4a678a7a22b26805ea3a8d907260ca8c2dde01c3ef902dded3efa05 ,\r\n                        0x8de2411db6f2826c3df3eaf22040b04f282904445c495a88e942db915f13a47a ,\r\n                        0x7dce80cba0253ab38abfc36ab7080b84fa16682489b22a93e60ae5219840b3d4 ,\r\n                        0xe0aa3ce33e4d611de055e87cd403c2296d515490e6086a91395ea58a605dc599 ,\r\n                        0xabe4582455abee1485d75034aecc2392177b96de7e9cae413ba07951c2ed3a12 ,\r\n                        0xfb11a7ab4a4818b02364c836854f56d93ba3546f23f821bc302728ef021a51b5 ,\r\n                        0x9b11e8d948c2dca238d21ec98ce1139de7a88ca5d9c0edc6506ecba3ab1e154e ,\r\n                        0x000c1b82698fe08304670db92790d016bc03e107a338c40c9418b571ab2af01d ,\r\n                        0x9e0d3fc3850a12e7d1bff7fc53ac9694550040e21b3f828335bff204b01cc3b3 ,\r\n                        0x5e7254e289ccc01e12231ea766be9e0a0601ea98f85281b892da93b4025a02fd ,\r\n                        0x8c7f01d4a8bf5bc8b466513825d50094a581f0445e67a861808a0f4d1ce821b2 ,\r\n                        0xd41fd597a765a40c69ad335d1d74b8f38311ee089f47a4a58c3c8945721dde9e ,\r\n                        0xa0fc2ae9a2d03aaa4c4edd0a78044ddb509a3b0c5a7c8c54d25f88283dea7271 ,\r\n                        0x7d64ee0a881e5b24163fa9ae0a2baf8bb0611a9f55258237a30a247b1f623aa0 ,\r\n                        0xcbcf97b6d1024a56a8df2fa0f075881fcc79e8b30cef0b2166f0416dfdc2090a ,\r\n                        0x6103b872d07eaca0c40b7a112570340cc914027ba4dff25052c2fae7566f2a7e ,\r\n                        0x6ed6a00bf070d0a9461720b996c96a85892007f07222a9de902ed0895be3e08d ,\r\n                        0x498d5c1e08bb4f7bc21de4b477debe6503fcd88157a1a8d272e58972dd5cb634 ,\r\n                        0x6c069c452eacd89c9dc043a862440c8e40ea05b5a7d58bd8c44f7115620d82c5 ,\r\n                        0xcd95a15782430d645aea7e218720eaa00e5f4de0d5227aa52898e28216234ff2 ,\r\n                        0x642391c288c31e5dca96c8225e3fb47af71f7eda4d3597c31d499905d510d078 ,\r\n                        0x49b5da34109cd05e5ebf624d770ed44b0d734a7bdb1929c3520930953ab37235 ,\r\n                        0xc1b337295d1c457545150d5b85443c7b648811c88c8ebc025a08c044692b590e ,\r\n                        0x958094ec3a131d6bdf4069e8b3a3da161602c1a73eed0c410eda80ba70178607 ,\r\n                        0x106b64389ac42d2944b864c2756e064f359503196f38c432b29027d817c043af ,\r\n                        0x21c300e3215d24010afc404220dc7d8879a4059491eec556a2ea7be472a963b2 ,\r\n                        0x5a2188a0e325e925c76b25e247e22b3074a001d88815a842c529dba40b2d482a ,\r\n                        0x52a6ed63ff1225bc1b3c0b880a111344ecd4e2c10b76a8054c57498984b821ad ,\r\n                        0xe1c591e13af80e0e730379aafb9f0a64d502833f9d00f22ae157c7e4a2e075f9 ,\r\n                        0x612ab76c042fac747d451540a193198e253805067ad52869e10a9978cc050f26 ,\r\n                        0xa18de1e0cff049b83d4c015d5ef153621a348201ad882f63bcf206071a41fbd2 ,\r\n                        0x21dbb4933640ad6548cb66adefa65551a87c7b24fc6a652311d1dc150dddc040 ,\r\n                        0x783e971bd1937235a60ebc7cb508923ca1386c3484f742ae2326a129f84b0c09 ,\r\n                        0x77c787b1118217af22df727f020022456e43b75d280d1436a9ec03dd42368030 ,\r\n                        0x4becc09d1ef1b319219dcf4ce32b520eac03487eca353287d71ecb26ebe965b0 ,\r\n                        0xecbdc8c4bf97f37178fb023ba09fdf0379e8965601c8b8043c9908ea99956b81 ,\r\n                        0x30d87085126768237014e1046130a7e279f161362c476e8d9f020db845d8cc6d ,\r\n                        0x99537569e9908e46eab8fdf5016c82bd4ee4f00358ac30780849d1a26a0b3370 ,\r\n                        0x5ed1bbf60638ccb2521b5a2b519152653172166aa0975641fd000ec316f3cbdb ,\r\n                        0x5a5d362eefaa965dded3c2051e9032dea1ca9d64242145065fc0fe23a7d0033c ,\r\n                        0x5f0abf941116f69e5caf60c6e5b43800711fd8a2a2afd710585ff0981cc98588 ,\r\n                        0x9fda1abc788e8f4392fb37dc5645593c053805e93126e647294dade2d42d6103 ,\r\n                        0x0f3755fce09faae5ffdbaac48537c3d7a0369042f7b883eaab00fa5fb00df833 ,\r\n                        0xc43fb2dda6426c303065908df4002bb4bc841f0c21c9b5731d3f263917bd210a ,\r\n                        0xb2b1586dd0ee03c74949f22c211108310395545687a8662d4f55adda6dae0235 ,\r\n                        0x692800a7f7a0f7503d6b6893ac4594684b86026daa96193380bb524d45cca03c ,\r\n                        0x5195a5086038b18004c3600b8e18002598c4d698a97860619a36a8e32a9889a1 ,\r\n                        0x565d5499e2722f3c2d83e67afe70e1189adfbb6ef00e08275357ad3c235c19ad ,\r\n                        0x154f6088204de0090416dc13b626e5a3650e9eede2aa578c60ea8a5871478d68 ,\r\n                        0xaa9495d30c6da585e02db742fa91aa28474000cafeaa3db0bc10a01fb02bd134 ,\r\n                        0x3c0b43e7dc1bb4f00e240416633e709829cc1d959417a178e8ce01dc029c9b69 ,\r\n                        0xa8d80fc10cb326323dc212cdd1b7a464cc3b218c08da99c95771650f27851f35 ,\r\n                        0xf257085a3802a01271dd608f480ba68e6c3521f0f8cf46eb0fc23d01af2aab11 ,\r\n                        0xfda48aa6c76f4cae783de968ad0c2f9a7a8564ca3327e5e4b4bb16f64537f05b ,\r\n                        0x3979d78521b528de791c22bd0a8011732017423aa8f344320223246801644615 ,\r\n                        0x8e5406976ee421a21cfe8a8e2dcc18c10893cf5acc1ca0279e8df301368fb3f5 ,\r\n                        0xf3163724b355dedd8d5948d7d4e57120b7285ba6028d9c3fd0cbf838930d4721 ,\r\n                        0xab68010725972462a4b702841750e383589f9c848f4e4f862593296a25ae64e9 ,\r\n                        0x2b2e44d623533be01d9a0cd5c63288023161a4da41a9ae0c05ee186a64808ee9 ,\r\n                        0x185a48818f7171dd5bc5be06f8991ec79c96cfd08524dd012d130f45c0443816 ,\r\n                        0x0cbe290a0048bcb964a80d1a8227566d2eefb776838f6f74ddb680a81946b684 ,\r\n                        0x19745fd061027f497734a2f4ac1628a09b79a8bd664d29d9f1eae2fb2d55907d ,\r\n                        0xd517805c8d0ef7205c549a41ee41fee062103d8c871d4d457e1e0f05ab98f564 ,\r\n                        0x4e5cadc86bcb8d69d6205f8200e27bce178cd799246e817ccd5a548c050d9a55 ,\r\n                        0x5df4bd152160203008154655b89f8f93f48b1792cdb5e4a0d551c7dd504d8f61 ,\r\n                        0x68cd111455e1586be0cfb83841794b97f0dbc7c1631769177326bd1684b1ca7c ,\r\n                        0x732757e4c0220d10d592912d4cfc5bb9044ff0e8298f384c2dd311a7643aaa26 ,\r\n                        0xcca7e5dbdaca14302910c6e54372b1aa9223df20c1a0d1ba0a50aa042033ee56 ,\r\n                        0xf11bf64492dcd02b745763f2b5aa0adab4059090e187b9bb1b12543d0b6dc2ec ,\r\n                        0xb4d8a107616870b98e5392f6492c43483d212d07ee0c4c11d746e0896564d0bd ,\r\n                        0x17b7073ab9e50edf627243543d7c60799ce35ff16320076f05fbb3b8a011a0d0 ,\r\n                        0x7907a823a85e3090eb165405e55355ad227f8992c4ad74a37a36c6569f166e8f ,\r\n                        0x898fffe251944139007a3cd378328be9ffb8ec5c36405304337a5c6143847995 ,\r\n                        0xc710ceefc1f9081c584bcb68c59ee21ca3bbdd3c0c444c8fd028a22f58ff3655 ,\r\n                        0x610f06c85a75345529503c404e41ca72710311e09a5ecbcf9879819024184066 ,\r\n                        0x21a395d3898813c83e98aeaeb06312ea4a8be2f24b6f4914932554209de4c189 ,\r\n                        0x0c9ff0d7a720f52a6299b43b548ec9a0eb9a8f8a6a9178d5a2c27d2463468294 ,\r\n                        0x717ae46fdf5740ef87250bb5d6620e7704b1e024ccfcd15a10e4ff7e5d155a6d ,\r\n                        0x3a893c92a1766fc1a9423c17bf9f3a1bd14ddc19217f09d3e652801243d5d548 ,\r\n                        0x79d5940808bfca0c590b1aab860c482a49cb34c35eec8d380e887b863ae54461 ,\r\n                        0x7aeee6aab82dd7ced5c3cd715ab0a7a8cab815cc70eed74e046dce31334b6d6b ,\r\n                        0xf28bfcc23fa885af4bf896afea67aa75ab649495e15339cc2f6112f8b6524325 ,\r\n                        0x7f40e802fa4f3cb52291d4984082796b2a4788053d6eaa39c6f03c0453bc0470 ,\r\n                        0x1afa94014c0494ea6e7919c288083aa61c0445e88660509f0c3185776557de27 ,\r\n                        0xdfb8043c5276d2661d9e544acd8bf4545f5dbbbe7ae2407e00d94b5554d43039 ,\r\n                        0x3e99a918e1da68a037ef93848b2ac449b82226a69e1c269c6ca12e0038a86647 ,\r\n                        0xdd4e2ad962852e4225853e17f8e3e0fa28b2c5680de5a6c6a5edc9f65e6d4b08 ,\r\n                        0x79660d9102c5ef1eeb2c2d84b5b7c88016dd2fdd63c14b60228ea118651940ef ,\r\n                        0x553710cde9a80fb5e530e84aae3d20d920357ec5def66642c5a57bb7fa66b64a ,\r\n                        0x34c1a1ebb8382c7101e5e21ccc1904084bc14554b1ca7c97bca19e2948b296ac ,\r\n                        0x60cbcff0bc9b5fa14dd3e9625cd0fc3461d351aa65a8047155c0ec5a48039f00 ,\r\n                        0xbe91de82d731822441a3893148593ea1036faa7f36ad1d616ab240559c4bc43c ,\r\n                        0xee9ac611903c1fc7f3dbe71a538f09fbc703430633830ac61db50b1372ca01eb ,\r\n                        0xa33b2786575fda88338f2c4577d5acdcceb0b24a23afd4ab4ad8d532f9e76f10 ,\r\n                        0x97435c0497ff0996064237fd194b41fbfe0b4a7f01691490c22c44beb4bc7acb ,\r\n                        0x0f56fe82ca3f803296879c80edf8036cdfd714d1ef7105f1e7fde630f1fc0055 ,\r\n                        0x398cb709a0cfbb0d9153540d585f51354b18cb5a585595d82e341dbfd0d4e161 ,\r\n                        0xa03de2ff5bccb95bbea3e90ed86c08e24e807221a195164ad12295eac866ea9d ,\r\n                        0xadafae85074717d7d721138e7ea8f485c080cd0669de40289118313fc8f3320d ,\r\n                        0x0ef8fa47128f4de6238c48bae73c4d4bb090f37a274b9446de6ef9cbf5e27f60 ,\r\n                        0x341c4d8f71f4e040f452a95984218019e4d1ada5b375117e518c009f232dc0a4 ,\r\n                        0x6150b0e893a532ce8092632364e9808e68c905828d87af46a0e6528b9a7a8287 ,\r\n                        0xb83df06ffef3881420f9dce1c01d8a3f2b1382a14a7903d0f5a80aabfc4ef60f ,\r\n                        0xea8b52c70cf12dacf7c4815341bd803ee46bc05e2bcb4cab7dd2b7d98fd3716b ,\r\n                        0x619e97ad3dbc68a27695b814b6d04ead1a3895da1a8468541c3967940457f29f ,\r\n                        0xe5705a95773eb84413abae53847e36ee4f5752d522220241b40659827e099ddc ,\r\n                        0x5096d0ad3709d8a60e3be6187d243f99af9b24bf462a5c189c9947b5652d294e ,\r\n                        0xc422501b9ed212b9d463592def5e08949f0e28cb434d68c00b8e822c80425595 ,\r\n                        0xcb9e6df5a9ae7a8823472037058303bdd178c99adacc4cb7e7e32a86854be245 ,\r\n                        0x8aaa978d69a8df73333f2651f6b98b334d9946a0da5c78b25375e0ba03a00aeb ,\r\n                        0xc4dfc8995d051d58c630d10a71b3fb164a444a6fe19de1d4c11a2393d326d1ca ,\r\n                        0xcc68a17d1c709d11efa0922ff6f55068b5ac43a3fb79eaabb4b7661fa4f1c9a5 ,\r\n                        0x2ad5dfac1043ab34745650492d662cae3aa888d450535efc99b120dcfd0572e2 ,\r\n                        0x541e36423caeaa0b85a0351fd55cbec55dfcbe4ec2b313b9978036628e072547 ,\r\n                        0x225f06b29a789b905e2f9981eb1d86b51df4489d9ae4e306e14f9016d5bf7254 ,\r\n                        0x093d14719c1bd5f7ab2e82a0953ebc0b5540d652abfd466d493baf89cc9a6a29 ,\r\n                        0x3e522dc4034387c0d943589fff849a56f4d058499590f8ca0e010d8aaba60339 ,\r\n                        0x3f30362005b23b610b889cf4839f75d68f6ce63f01e8f91bd70c5ad347bf4dab ,\r\n                        0xd1bdb6d4aefe8a195f7de2ab5c1c7bed002ace721b98e3b06ffe7ee32717f339 ,\r\n                        0xf38efaf28647f805af54844c809c4cc60eddfb4547e19b857053fedee8368405 ,\r\n                        0xe2443d4c10043d1580e64da4087a8c65892c9b7ba93b187684ef80ed0bc2ba6e ,\r\n                        0xfa4145e603a78f5debd9a28a50c9a25a174a2d2538285cdf25f0076ba979a9a0 ,\r\n                        0x46e013b0c5aa659525303e4d6c44f35bc15635002cb57ea8170a588251be4519 ,\r\n                        0x609158cb3084422c15ed77c035bcf25057319778af041334047c829a2097e087 ,\r\n                        0x614b5a32bfc0ba9a8cbcd69c61b5a242d303879111121c5a85854c1621390eb6 ,\r\n                        0x5b5313b3479a4914eb05d5cc0010d292b427a2b6000c6e6ff8ae41c12d90106a ,\r\n                        0xf64ac06a6085f74f353e1e9bad83d8456825cab766e85af24af8385e2b994dfa ,\r\n                        0x77ab4d321bc231bfbe2c84f4265db7d7d30a494099a996bc7adbaa93cb58095a ,\r\n                        0x6d6af62b8a8e7d801ceca3662fdf90ab5640fcd683ba25b943680a65408d6980 ,\r\n                        0x99ad4992216133eeaee648f04ed596a8f6775018fee194ce0bf2195081fb2535 ,\r\n                        0xa00f0baf68af8ef3a10484d22b5f0d37f8ae14343e917545b2b91ae4ec38a877 ,\r\n                        0x80d715dec4953537b02eaedc84e4fe053ba990eca8a500ad915aac05e016b86d ,\r\n                        0xe49f8a4f21b740ee473847c36fd14db8a4f2e12b9c4273bf5144adea7735b7a8 ,\r\n                        0xc83fb400846797889681c38616808c0a8744e546b5d249b20dad96c547a1089d ,\r\n                        0x41f69ff0d0d1b433483b1890d77d6775f324ed01f0c69c356d99b8e6849b8014 ,\r\n                        0x7089d30b4d5086936174cfe5338351635a6eb9a997dd00a8ad069c7a4a22e193 ,\r\n                        0xe5918370e1ee9d3a37d5c32e00405642940ddeb291e3eae99d7c13ab5c2581f3 ,\r\n                        0x2581cee466f5e727022543286084a235f71520180798170321bb41c562cb1b0c ,\r\n                        0x04bd37b5628806c1a7d09c49bc9a746fa69168b5317d0d17e6783f453311dfa1 ,\r\n                        0xe6ba800286f18058d26c7d115043ab1a4f2aeea4de9c0ea74473ed19d5bb4462 ,\r\n                        0x8eddba5b6aa4f722ae4555332d5d233a23b1ef45dec10e500acc0ee89a8de712 ,\r\n                        0x11a837bca2a0c654af423276f292eb88df1c91ac156986c20e1dc7e75352212a ,\r\n                        0x54aaf7c3a455881e212509abdfaed5310941248225628f11a1561281af865a54 ,\r\n                        0x1ee8bb2b506c515a103fb46f0e7709cf7faa02af5dd5a533a95d8b75483d6895 ,\r\n                        0x9a064957512ba95c9fa88b137ea7258ee35f290aeae6ed6d0908fea810711121 ,\r\n                        0x3f74dd6b0f45d993181eeeaa2efa56f05b23c7912ad0f34da00cbc6426e2f51c ,\r\n                        0x26f3f33534deac49412d78f5314d6963f5ffcfa14a44450242c6aa9041704126 ,\r\n                        0x45e20753d5085f5f6ac14566aa16a936c82d862ef1d618128494844a583934f6 ,\r\n                        0x4815d647946dbc3984fb50854f5ed285ba410a08a52a1718dc072eae8c13c449 ,\r\n                        0x877a241af957ba9fc19293b31a19b4288dedfb6300f1391a59e9b5ce16ad2b2d ,\r\n                        0x941669beab268cee86e679138ac061f2163f8a3f418aceb8072df2ade9e5ad4a ,\r\n                        0x27a24c30b5cbd5568a825f8b6b9eae6ec5d2553884c94df5d6182e1cdf8e41e4 ,\r\n                        0x21746a75359faacb4ee5fd1e621d354381819dcec573503b4a186bb72c0788a5 ,\r\n                        0x7d8b560eaa0a3ddc172e4cf6828391c3e7db1ab0414318fc6b3f6b2d4020fd00 ,\r\n                        0x4ba0330f13b2fa7b1171e4459c0e2dc2d3be9a587c45b455797b484fa62a4c54 ,\r\n                        0xfbe151fd0e5b83bbdb005e48accaa5daf540f842e5f0012e14546538f0222273 ,\r\n                        0x90c45b154326cdabe15a72d3ab338fc9229f89eb8145f2f299d5e00d4ac0b1d5 ,\r\n                        0x58eea4d15498449647a5606d6182908bd169058b14aded7f30950d4443d9da04 ,\r\n                        0xa1e6564ba3d233e11f65e0e5c0609583b75630c1d99180caaa059f58d440059c ,\r\n                        0xceaf0bfa2e9524491bf915167dd42e11ec915e447577aada2a22172059242d1c ,\r\n                        0x6fae22c8c19b9b4a516ad1d3b2d5da5ddd251336c4a396d554a7d472d152d399 ,\r\n                        0x6b64696d2a425494d1544fdf4be4196aa426a1abb225cfba8f9a44804a8ca39a ,\r\n                        0x2278c89b45b53a757b3564966a7678aa3fb7abaf8754a0e6637143c5aea98b04 ,\r\n                        0xe050a5ec8250da3f043dac61919aa1dc4b52054d906023278e4d0010e4435b0b ,\r\n                        0x54fa2c3f2b7f8dd9515367d4e606351e7dcb75c8319928415c3aa4488922b8a8 ,\r\n                        0x2d468a5ab8494ca8559ea268191789c874541f46d4cab5119f4b6585d7c89b49 ,\r\n                        0xff50002329f4830bb4e24c77256d66dc1163a6a25d8a524385b1cf2f7cf7fd15 ,\r\n                        0xbc592d21594c5c0b8e01db628b4e9c04b7d4c9bb05b1e40f78ef51cb26a41aee ,\r\n                        0xa15d19db08e5af41058659bf6a89705e9d0afbe1e435a2b301b599a7f0fb8a57 ,\r\n                        0x0eb2605603b92966047964f0c66d3b98ab953f104c4dc59308d60a85e0096c85 ,\r\n                        0x0c79578a761b2034097d6d3620d049d0f3d52ab596a186c0e55598c555139091 ,\r\n                        0xb5a673217d6ae0cc6f4ca8b3a2961853276f87b1242d52aab38b7ca3a20cd24f ,\r\n                        0x25da849a9f1b1e8a50f54b0b0fa57a35d4010650ba538ad5e454ba3dfe756ff4 ,\r\n                        0xafeadee3c8803f740b332dc81014599da1b0f1240f938ae2810fff0f085cbf88 ,\r\n                        0xe153107ea062ac536d5d212bd0c549b449486e0055db927e5ed441157a204a6a ,\r\n                        0xb88becaa7aabb62fb5de1d87fafb7e1305dcfbd6d72d68f733d558739bca2644 ,\r\n                        0x5e10cb455636d40c3180e0012dd426f95aa2b11a81041801070b76096936ad80 ,\r\n                        0x93382064380f03dddaa2a21a2cf653f55592f3a8d52a2bdd33ea90f675f3a8e5 ,\r\n                        0x5babf7df7a9917df03bfb4ffb1ffb406d8dbd905d81f102a7c4be852e7c888d6 ,\r\n                        0xc5088776f9a0fa410f4403d7f56a4ec163d1a0af63f6b5068305ca6e5c9cf9b2 ,\r\n                        0xa4acc68455b28498aba90b088227a235b41f44db5e7819900aaa7bab4fa0c554 ,\r\n                        0xd4d10d8b5e35c09949ac84d114dfd89a38b00bc805de180099f6ce7655da4f51 ,\r\n                        0xcbd38c3c4baec26d48b7d68796c1272a00cb32955d6b9eb8b616774c7500adbe ,\r\n                        0xcdb8a70baa55f2d145f8a8fd970c203e9d073981ece07b3615d4a78a89455a13 ,\r\n                        0xb882c9cac6eaa2834942a27851455f9505b587950b6eb22a9c6bc58870e5f1f5 ,\r\n                        0x7d3ed6c2db99d6f11449dda63ee2a8d605edeb8232dd7b9c1a8d00b241d8bc65 ,\r\n                        0x5c0d640dd5a9cb6b1ac1a1677a521db0485ab71ada435a542b59931879ad7a88 ,\r\n                        0x1aecf5f659699bfb48c037b2542d5ae4b121694326971ed2ce3c0654886ac0b8 ,\r\n                        0x6a691bd003d115ee6854733b6843628fd0acf4952fd5b34c2e5ec867eb250575 ,\r\n                        0xe92bfa3052fe7a690d155e99472dbe8af567ece324e44c1c9b102009d40fadb5 ,\r\n                        0xe9087fd6babc2d08130e9de34e37fac5f74822088b2d44d53658555b8f834181 ,\r\n                        0x664d2b20a007929621022cf795271bf998c755fbe759103c0271d6b4d56c2ee8 ,\r\n                        0x662e911cdae6bc414832f13a15a0c0ac03a5c1d7a0c42d453d5e512d8e948ffc ,\r\n                        0xbc5fef2aded6e06f4c0150fb27c075d08aaeda12f9e9112ef1d78b5aaa6f6f64 ,\r\n                        0x78efc45fefa860aef613bda34d25453bab7864c0c505027b16edd1d68e233d04 ,\r\n                        0x997b9300165e8d92d20f3579e2755c46e53f3e011a308ca3667bc850533bb653 ,\r\n                        0x9718b389bbe09712c8e1ab828067a3cb73c8a6af85b5bc4d85f72bb0a95df6e7 ,\r\n                        0x51e1ac78527344e87a3dbdf3a859ba49a4dabfbaf4a379df5bc29bfc2efadede ,\r\n                        0x8c08678d443d24552d4a88028981e9e1232493aab0683c291430f05d9416f9ef ,\r\n                        0x01634b68676d50562fd270da2b209188f68a928849cb7baad3119001c1000bed ,\r\n                        0xfaf0d1e2d20a15c0b4b726a29504d880f610e0aad5f1b00b9b2309b410a89252 ,\r\n                        0x475ee29007c80bfb6d9a402c415701712d712096a2160109a549ca468856addb ,\r\n                        0x3bf483fa4bd2d44acb01b930a0da4bbdb65fa16f559bdaf24188da89f043ecc7 ,\r\n                        0xb333a0a2fd99241d69dbbe1cdead066a98e5b3c4d6961ca613473c72de50caf5 ,\r\n                        0x3b6b6e6e20798685e953c18b5460046e05a57c115a83c75d084453cb8c1e8a71 ,\r\n                        0x40b09307b9b4930fb6534189b83a683192b6116b4f2049dfd4545f010675a605 ,\r\n                        0x37b4a9308890205b5b0dda0c209ebbb2f6600be9d10cafe3f66dc41393ddda7c ,\r\n                        0xaedd4caaee0cb1f0a8eec319d5645dd4802686735ef54b9d0dea955783edb52a ,\r\n                        0xd90f074218aae002a31fb111e3016d2b86a6fd688e5c0d4327fb6adf09926bc0 ,\r\n                        0x2808e919d5cb469a9554e64a93d9e7a3572d2f2a26f0583ae904b0fa72ed7155 ,\r\n                        0x9d6438909ac4ced6c6511ce668151463b7db23787a7430868a4e45ed3a2ba098 ,\r\n                        0x1aaa41fdcef56de811cf66dac394049eda1c40d4fba6b631d59048b1203ad3a0 ,\r\n                        0x496a125a5a79209455d60257c57925b93b4cc63bdd05c411a51a8780b3178ca5 ,\r\n                        0x402293f6544715e3df4ea9a302ab4e39f8b80181364fe5cbcc3573e94c155de5 ,\r\n                        0xf6ad3e204c7ca1bc57ed043531056f7b85224eae1a347f0817d5dfc84eea478c ,\r\n                        0xd77c5577895236c8b049b84a098a20eccfb72fb41dde549194c2594432a264eb ,\r\n                        0xb402f527f0079640c66eafab1cfae5e23a6145b413b67beb4f5805f27a40ead6 ,\r\n                        0xa2287332ac5a80726d4c9ca115d5e4d0e26f9b96eddca67a4fb44ccfc77056f1 ,\r\n                        0x54a52e33e98cc6fc67158bc9204c02025eb83ad5875ea4ec7a95f441b94eb55e ,\r\n                        0xc799dcd71a04dcee0835575b4ae0af0edb5771574b4ec12013aa7b68233719ae ,\r\n                        0x343e86529fe1a83308282ffe908eb474a4754e5120523ac0a32d07a14845a85f ,\r\n                        0x4d362420e5504d1b609488ea1fdb9e8ad76afee94e0d4fe830e8ac9f80095ec5 ,\r\n                        0xa736c0d36b55a42031a6961080c88935548485201c311d845778cda86a7d754d ,\r\n                        0xa550edc6e4f13637456be2a0d1d4af345546c8d21ef2d11836b377964ac651d5 ,\r\n                        0xb2afdebe815b9e5abdc7cbb4f71b358c1336359cbf2c06bed54a8c33fcae55ba ,\r\n                        0xa4fea7aa5d26aab190947a0ecad1c39b31992e84a28ecca00ea46a8216493e6d ,\r\n                        0x56d01233d1a8bfb5c5f0f502eb08a2a1b310b4cdae6a47e0c4f4d84073c405b4 ,\r\n                        0x3dabfe0a0d1569e02cefdfda9db70928fcffb5961b2cea9fef04727f7e011f16 ,\r\n                        0x0dff180901ab6fa357b508a666431d338189782cc2a6e25ac4e15eaf0b685587 ,\r\n                        0x94fbb66ce25ddfe668e0680cb50301bd513a09bff7ccd5cc30c2309eb0c1905d ,\r\n                        0xfb6c03a8a10de121bbeba1bb538bf9f0e3f5147bd352f70d7bbc4dfa513a3b55 ,\r\n                        0xf509428413a49afca86e4c020f9db7c157744e733fa36176fdda7abb7ebb5901 ,\r\n                        0x37802cedc79e2161bd22664297e70ae9486a504ef485962804e32e6b134e57fb ,\r\n                        0xa16abf47fd8b00d094afd8db94135e1dd7ef72481308721482367f084f7406d7 ,\r\n                        0x884418f67708d42566abc65eadb92122b44453f682a7a0c3abb64fa8c996805e ,\r\n                        0xaa6da2bac3adb9c3d6a0283a8486a9d3a68a436e9e5af20003df0ebcfd72492d ,\r\n                        0xda6ad7a18cda6ea8e8fb96aa935875ffb6cd1211f8b1b6f4c38fb4cf500b753f ,\r\n                        0x0b71f02dad4e6eb599243513655817d2caa0fc04e5d35a49bbfdde4c9aeac60b ,\r\n                        0x9f5f9e1051df866604e5ff6e3ccbeb2d84c8e234fdf134950cb4a6dda47f9a6a ,\r\n                        0xde88da8befab69a3eb0400083b2aa52db8dec96fd3c2cbd9c59393957394a0e0 ,\r\n                        0x694a50fd8f0435fe92a09ab60b916975c156d482f77ac78ff834f045fecf5362 ,\r\n                        0x006b16d4ac4eb198af8b4ead7c0f1d91a0a68e27e56412245a08c9a57336d030 ,\r\n                        0x707a82b19602c73fda662cd587f66cd7a3045e4b93f6377b351a7e8dbc12e85a ,\r\n                        0xa8724d4a1dd4ca6b1b50ad56595452f758802ce2d19e601bc2afe2054b2b3984 ,\r\n                        0x0c1e7f745a44dc601f68848db753f5e56ae3780da21b3ad0c30b19bf83387ac8 ,\r\n                        0x925f55c524ecf3edcf4211eb141ce925de68c3dad8db15af2a00898b8b44edf3 ,\r\n                        0x527584544ab040f8f1ab1c0fc193aab655a94e8ca715e91615a397b80c5f427c ,\r\n                        0x3bed2008dacb441cbfa56931d753cbaf666e75070578a31400808be32bf96adf ,\r\n                        0x75d70ee0fdeb1011d491baababb6abf5d76a4434eacc913f6f5c42ecd5f51407 ,\r\n                        0x23ed24cfa1d2dbd2dcf7a4bd782b06a776113f26aa71688ae6d0d3e9801e6b59 ,\r\n                        0x2511f2ef94806910532d22d7f3ed41d239083ffa2ea9a3d68d50d4cc7b7458d0 ,\r\n                        0xfc6a6b5a1c3f5a062d9013ad5149234346260f1248ac78a476e36a6b378c0c2d ,\r\n                        0xffcad0ba0d61acb5badbd5e9d5d468161f35c8bba45cd415be74a21d1f81e908 ,\r\n                        0xcf43973e800be9481a5481965755ef6166e034aa1cc3e5490fa2cecc9b76ba63 ,\r\n                        0x6435407f658fead51bf121ba76a27ef208701e22a3ca1d039df08af4caa17dab ,\r\n                        0x233cc226b9ef5587dfcf6e7d001c380b49d3072f68c8e4d83abc5ecbdc2ed7a6 ,\r\n                        0xd3b7da7fdcc084c28238e8c0a7ab462562e19db0d1c7abb545d211e972300eb2 ,\r\n                        0x5e114268ef1f4aa117235ebd74ad20cb92b2d1c5d5c8b40c59cb214735ac6cda ,\r\n                        0x3d0fd4aa12e4959a5e6bccd4c6146bba37df7ea945029bfce8b5dfbf012b69c6 ,\r\n                        0xd24c87f4e4a0f317d4021c7d734b599d0f6bff1f68a2fe71021437418de87815 ,\r\n                        0x5c09d38f8ba6d64933a4109dc581db4d75e4aea58ad2f42139529ef6b5fba7d7 ,\r\n                        0x455a62ce8d696da6451bed140fef30a9ace31a834ec45032505969a8d9ac69af ,\r\n                        0xf05473056080462626f202945531d431894aaca051376dca2bdb8638bcce8728 ,\r\n                        0xaa034d83246975f6b574bf5ef7e626f888d521595bb9ca830fb266d68e11ed92 ,\r\n                        0xcf7a60799e85d611a1592c27991dd1e38d9cd7713d7103fe31892296a316cd21 ,\r\n                        0x74871eead8a3709727dadabbe8ce2480aed3c97ad04e5cee0b819bda6683a4c3 ,\r\n                        0xf7afe3635a7f97ae3a05e250d50db8800bada0b4dd3ace720961cde0e34dea01 ,\r\n                        0x2604de9963c1e2cfbeece45467526715cc8791ac9f3a31e9571f5672fdbda111 ,\r\n                        0x3daf9f1054664a9bb2c005683ba3123ebbb7297203a8edebd10edad5a8dfd415 ,\r\n                        0x7192ba395f5e839a293c913c008bdae8d54c1e9a9684aed6285cc645c8de1b8c ,\r\n                        0x225ed5854300a99a06b64c1d94d2528c9036e37bcc09e19ed52af3d2a22aa2c4 ,\r\n                        0xa3f579ab6b53475092dd83cabb2128d3ab458510aa13eea1c557d500a7d622d6 ,\r\n                        0x3b0313c21bc1eda160cb263f9a7836aa3da015c49b3d6c4a770a712a2f8c8096 ,\r\n                        0xd6a91137a82974bf7d0fa092fce2f5a568e77ac52f7222ab380984c8dbb187c7 ,\r\n                        0xab55859ca103e573781d19d27458c0ea0bd65b5ad2f14fd0236d68356d3ed646 ,\r\n                        0x84ae93f1541a23739147b5f30f5c0483b5d5961823b729bd5e95ea97645c05e4 ,\r\n                        0x54c40830a077340694f2807eda67549cd407b2311dad74abaf4ff1a00ea9b013 ,\r\n                        0xd7d2297977adf64ee348428daf90168f0aa29a4a943e9c0d84946374082a983e ,\r\n                        0x4fd2d9705bc1a19eba9c8459aacd86a26d0d499ae99a7a48e0df5720dedf7951 ,\r\n                        0x554bd0c229c970a6752bcc2b98125fb50c7dcaedd5f7b3d2e4b65a3694cc7838 ,\r\n                        0xad3df555673e2c015cdf8e14cb508bb22639f8ca6cb8dc79f5d4ac7e94466809 ,\r\n                        0xed9078202d3755f3605cb00cdfbab6d3355c7f2567c117cb706cc5ebd74ed6d6 ,\r\n                        0x7f3a37e09ffc74df81025b67d824edd410f1fb36dd1150f5dbbea09355b45b69 ,\r\n                        0x6919590b5e12d843876da89d05461cd435a6463f2167d06e343d07e2776993be ,\r\n                        0xfab3207e848a217b856c37685719ee127fba758f76dbcf439285d6746d0d8688 ,\r\n                        0x76c27898b7917039433a92aeb6cef91822494d8db2b909ded1744d4ba1874492 ,\r\n                        0x4d5568e678b8ded46671d0cdab8d77a81117ea0420fead8d82bd85d71f0955d1 ,\r\n                        0xc152a69353bc3a2409d4a805a5c9ccd6ac2e8d93b469e5da416c54ad34061d95 ,\r\n                        0xc07b702fd1a8e3bff59a09ce551d5dd6f5cf8ac60119450d02de1c9d567536fe ,\r\n                        0x8cd7f21824371d16a0630272d2115b7da987441cfc68f50e810bb4711f75e974 ,\r\n                        0xedfdd83a42b6e92c0dd848873fb7f09d6a8ce487d57525a8a2cdec5b07de10c9 ,\r\n                        0x20241414b3f6a8666f8cd7b5326e4ca81c7f39f092cb80558b8087f0aaf8f3a4 ,\r\n                        0x87e9d081bb2428d5b9fc2dda6bf595af969db53a476ed6512ab87436b288219c ,\r\n                        0x968e35262b6885471bc8dfc172c4dbf93487f08029025eb5fea6630ca676fc56 ,\r\n                        0xc54b56a1f39afb56393586a200142cc77780d01faf7fe2480b3e5f67bf69a7d6 ,\r\n                        0x7b9f318a9ac1bebbd3d22b046feb740a9d7d623a22627dad03dfdf3a0541a7ba ,\r\n                        0x454c66dacc5f549bb8aa78f7d2de6607281ae07f442876298cbb83d888277c55 ,\r\n                        0x6bf630c15af025344f5b283ea98e769abab1d54357908a9bdc8dde0081f16c9d ,\r\n                        0x5446da0e6a86d1a2d7d8084f72221ea33348b44d7dbfc245532a30e022159db6 ,\r\n                        0x2bfaa38645d3b151e2d9daa4de56632ab431fc0e29fa22df94a8f589c4a84e50 ,\r\n                        0xb2c31f9941349c98abe881a6ad8e64078743ab2f24beee7e98aa1c0fd281bc56 ,\r\n                        0x834e81839309d513ddd42344de47027a7d05682f3a3e8fb93780157ed483764b ,\r\n                        0x37fe957c9b3ade58db090ca1fbb2dd898d6f3286f33adab47acfb4c0d7fd4fb1 ,\r\n                        0x78bd857925c81711da256c555d584f2aa841b2844ff9d7a95311b92e06786bb4 ,\r\n                        0x5bec45c7b5f06c019baed687430aefab35e974bb8ee7a82a0da8da8dbc544fb5 ,\r\n                        0x8e10222ab42f59ad1c3a244add0997f0388d69dfd2756452077e8931a532d4cd ,\r\n                        0xfaa48cba1dc87c5dc553861d5eea423d77eda26c70f6a3edeeeaead726dc8045 ,\r\n                        0xb5ba9ed45527802aeac9d39a9556cd19b8a9e70ac47f7a0b5fff0fb8edfef140 ,\r\n                        0x98fda49e8ea22299a89db08aa4002143c50f78dfb7551e17d521a33a0fa46054 ,\r\n                        0xf54346ed7848ea07cfda8f56c24b59316675526eed09941455614469bdaa03b5 ,\r\n                        0x659df217d297710fb2c72966807c9db2d874d6ea6ef115e154e2a939ea085078 ,\r\n                        0xf4d664ab4c36421c3ab7e675f9446dfa815c67e6005a537498aeba99e2dbd5f5 ,\r\n                        0x3ae3c835fb7420132eab93ac3030fec468d4b80a87d78640ade1f877200edce9 ,\r\n                        0x53473ace12f5f11d0a94a6363269ab179199c43d49b7496d33b146d5e28a2459 ,\r\n                        0xf65b7d899200aa5f238eb199f6077233b23648998e34e46b485aaf47f09f03e2 ,\r\n                        0x3fe0a143a4bd967330397cb5acae4d095148b97f9a3bb6364364ed35ea5b4785 ,\r\n                        0x2ce629e39f432d97dac38ade038fa2ce0fee594b690c035310aa00a51a8702a4 ,\r\n                        0x200a32e027e313c51e16d4a4d9d478f6d6c7a37ad1b44edbae16e121cc5ddbca ,\r\n                        0xb49693d7d7df3f983ab519a83fee0f77bb55ed6a7cddb6d41bfa6014727fd682 ,\r\n                        0xbf8eba41d83067df797f2995b75949553e256f9398b59fede13ae310b214b560 ,\r\n                        0x4162f3af19a9bb2831d124ec757ad93bf0f6f877d0e8fd1a86992d6d8fd85a7c ,\r\n                        0xf05bc782a947b594c0956bd189486a795c4e2d1aaffbdadff355ce540c7e0d64 ,\r\n                        0x0a8ea3a387db7cd576ed1253277d7badf95dbb4097b65c06f5403a3517566dd4 ,\r\n                        0xe949ab314d995b34d53a09357d672e1d6d1ceef6b6a5219710f11863695bfdbc ,\r\n                        0xe45588e3a9ae7c87d76d069473573505f09c5f977d781baa9500e797a0bcba3a ,\r\n                        0x7508d8f6e4716de3bb3a9791e937d5b375d62d909e54e22f10021de575559cb2 ,\r\n                        0x2a22880f4c9d5ddce00b4987816efed2093def10a1b7d78280755fd166ffdaad ,\r\n                        0x429afe7639e0a329cde70e6aa354864591a910c3ff7a99a2caf65ca2bfe280fb ,\r\n                        0xeb96236da9f1defd1f513ad9707ee37fc600000006624b474400ff00ff00ffa0 ,\r\n                        0xbda793000000097048597300000ec400000ec401952b0e1b0000000774494d45 ,\r\n                        0x07e4010f0e0513d75552430000075d4944415448c7b5d66b5054e719c0f1ff39 ,\r\n                        0x7be5260bcb2ea0ac22a05c55045daf510901b45648636c92c676d2c9a4364e7a ,\r\n                        0x9b7432d37eccf44ba633d5345ea219ef46498c132f1111b04134204ab4882e82 ,\r\n                        0x82a8c802bbdc61cfdece39fd6027958eb6c1dae7e3799f797ef33cf39ef77d05 ,\r\n                        0x9e10ef1fea51bbdc7e46a400ae5e375e9f8f9a2dcf093ca3109fb4d03b22b172 ,\r\n                        0x690873938d142e4a6070d887cbe5d2fcdfe1ab775c74ba153c7e857e8f9faede ,\r\n                        0x51fc7eff8e67058f1bdda2df37a86d9d4300242744b268a695a13185a870912d ,\r\n                        0x472fa1684454110454fc27d70acf0c763a9d6fa7fea666db86423b82a8251054 ,\r\n                        0xf00741af1350140569cccb890be7a97c7f11b3b2b2846736eaf8f8f8ed8d1fd8 ,\r\n                        0xf9aab69ea0a2228a0206bd80808a462372bce63c27fe904d8cd9bcf1998dbabb ,\r\n                        0xbbbbc4d9d575ccf9e01e4ee73dde3da1a76479d1778947aacad85c2c11133b85 ,\r\n                        0xf8c953b14d9d763a2121e1074f0d5fbfdea4deb8f235b6880ea2ac71845b6c84 ,\r\n                        0x4559e91dd1b0e4bd0ed26c3369eab8c9d54d71448a2e3cee7b8c0db8f0782434 ,\r\n                        0xe1e960ca237beedc098f5ddb72e9107979762c531682360a040d10202a46e6d2 ,\r\n                        0x5fc3b1fffa2ab776cdc56a128118cc09a9a0ca107033363ccae9d23f3f55c75a ,\r\n                        0xb9af8d1e473f915e1d44a4a1899c816888020492ac7aba4bed688520b27f14d9 ,\r\n                        0xd38dafbf1d5fdf2d820337e9ea8b46a344d2dddd5d121717777c42b08a8ad4d7 ,\r\n                        0x8e2fb40ba46e44b90fc16041f17b5103411459872a6b51fc1281e13b78ddad04 ,\r\n                        0x4787d01bcd44c4cc619a35115996d700138285d20f5e56a7c70e913ecd0991e9 ,\r\n                        0x883173100db1c8013f2806f69f0ed22fe9e81ef4909b00b3137c24848f527ed5 ,\r\n                        0x4b47306b5cb1306590accc147273726c56abb5f33f76acc8a0aa0a02a00a1a04 ,\r\n                        0xd188a033a11114e480c2fcf4004d5d71fcf157f9385a3aa8bb7c859ed12156d8 ,\r\n                        0xa1aeb98f9eb1503267cf63614e3a3eaf4453cb5d761d3c71bfb2b29282828227 ,\r\n                        0x6e3a3108c88a0a828820ea10b566ae35ab1c39338ca03131c3164a66fc3047cb ,\r\n                        0x1b484db1b1fec72574fbccb475f959981a426e92916febaad9f4f101145564c9 ,\r\n                        0xfc2c36bef51a1e25826ddb77aadfefacd61811b4d134b77b68eb3772b3230840 ,\r\n                        0x4aac8fc9210f38577b05ad06962dcee5769717c53780b7af8d57d6bdc882aca9 ,\r\n                        0xecdc7398debe41f43a1df9cf6593f742211f6dddf1585c7cb881141004447488 ,\r\n                        0x9a30da5d2a3f5f5bc4c5eb6374baf568f50632a687d2dcd28ee40d62319be9f3 ,\r\n                        0x6810041d63034eb45a2d76bb1dbd2640736b07b2ac109415e2ac16f20a8af8cb ,\r\n                        0xe66dea633a560075dc61e6f10688301a989918c7d78d3e6e766ad185c5126614 ,\r\n                        0x50641fbe2068f491087a13a193cc88a2486363235171499863a209c832415926 ,\r\n                        0xa82858cdd164ce994bf99933eaf88e830aaaf2f09b4a1055914888d221f97c2c ,\r\n                        0xb0cf63cdaa3c1ada048e54b4b364e13ceabe7580a2a2314ec210359dc9c9b9b4 ,\r\n                        0xb6b6d27cb9144b741453275bf1076402010577ff301eaf0f040ded1df7e8eded ,\r\n                        0x4df80e0ec85abc7e15c9a7450dfa518383d867993879e61b341a1db6b8687ef9 ,\r\n                        0xb335bcf2a395b4b63bf1483e7a7a7bd04744b0bbac95fbfd322d970e93ff7c0a ,\r\n                        0x8b33daa8b97c9b4040e1f6dd1edc03c3b4dd75d2d2da46686818db77ecbd7fe1 ,\r\n                        0xc2051540a8aa28573b1bf61322df6152b4094bd21cacd3e773b149c239a0252b ,\r\n                        0x3d055bc264a2232371f70f22f97c8485e808090d63cfb17318461dac5e1c4252 ,\r\n                        0x8a86e1074d34dd18a0d659843f10608ad948408cc412a1624b4c212b3599da86 ,\r\n                        0xbfd3de72e3e1edd4d3d363bf7dabb5def9e01e833d77913dfd4499c21996c318 ,\r\n                        0xf0e9189614fc3218f446d61517628a3113f07ad1e90d1c2cbbc8cb4b83448c1c ,\r\n                        0xa3a979884f0e5550fc936da4a767603299f8f26439cb96af20232d19bf5fa6ad ,\r\n                        0xc349d9a9a33cf60777b95c1a45517265595ea3aa6afca36b7ffa70df9befbcb5 ,\r\n                        0x9ee89828a49151747a0307ca2e92a21ee7f32f3ee3a59f6e213b3b878c8c0c01 ,\r\n                        0xa0bebe5ebd7dbf9f82fca534b7765276e2082ffeb080095f671515156a757d13 ,\r\n                        0xafbfba0e734c349e9111747a033b8f9c658abe9f17f2f3494949195777cfde7d ,\r\n                        0xaadba3c7337097c2e797939898b87142707373b37ae1abcd14ae5ec0a1bf6929 ,\r\n                        0x2e5a81d9128d67f821fed1a7a75832239292e2e271753b3b3bcb24495a254912 ,\r\n                        0xb1b1b10b6263632f0913458b5f4a45af87d3d57d387a9278ada4601cbee5e029 ,\r\n                        0x56645a58b56aa5f054cfdb47c3e170fc0bd5a9785c8d58849b382adfe393bd9f ,\r\n                        0xd1e7ea2774520401bf8f77d6afa6bad149655595fa3fc10e8743fda6ec438ad7 ,\r\n                        0xa6fd13bd86c331cab6fdd5bcb17137cb7253d8b1bb943ef723f81b2594d7b753 ,\r\n                        0x5b5bf7445cfbdfe09a539b58604f432b0878dcd770348fb275ff397ef1dbfdcc ,\r\n                        0x9a35bbd16834e68685850577ec2a65c39baf62359b88080d6171ce2c8e9d39ff ,\r\n                        0xf41da7cd7f9ddabaab8c8d78713846d9baef1c1b7ef710b5d96cd9168b452e2a ,\r\n                        0x2a14d6e4e7f0f1ae52ee7474515eddc0e14f0fb16c7ec6f77bd03f29ce9e3dab ,\r\n                        0x7eb175235d2303bcfdee3e3233b31a6d365bf6bfe7555655a907beac21392e84 ,\r\n                        0xc50be7919696f6d83c807f0002cb58d9e9f027800000000049454e44ae426082\r\n                    End\r\n\r\n                    LayoutCachedLeft =5100\r\n                    LayoutCachedTop =3960\r\n                    LayoutCachedWidth =7980\r\n                    LayoutCachedHeight =4860\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =540\r\n                    Top =2580\r\n                    Width =2400\r\n                    Height =540\r\n                    FontSize =10\r\n                    ForeColor =5324600\r\n                    Name =\"lblSubheading\"\r\n                    Caption =\"What would you like to do?\"\r\n                    LayoutCachedLeft =540\r\n                    LayoutCachedTop =2580\r\n                    LayoutCachedWidth =2940\r\n                    LayoutCachedHeight =3120\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =215\r\n                    Left =540\r\n                    Top =840\r\n                    Width =1800\r\n                    Height =465\r\n                    FontSize =10\r\n                    Name =\"Label9\"\r\n                    Caption =\"Designed for \\015\\012GitHub && GitLab\"\r\n                    LayoutCachedLeft =540\r\n                    LayoutCachedTop =840\r\n                    LayoutCachedWidth =2340\r\n                    LayoutCachedHeight =1305\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin TextBox\r\n                    Locked = NotDefault\r\n                    TabStop = NotDefault\r\n                    OldBorderStyle =0\r\n                    OverlapFlags =85\r\n                    BackStyle =0\r\n                    IMESentenceMode =3\r\n                    Left =780\r\n                    Top =3300\r\n                    Width =1980\r\n                    Height =2400\r\n                    FontSize =10\r\n                    TabIndex =3\r\n                    BackColor =15130848\r\n                    ForeColor =5324600\r\n                    Name =\"txtDescription\"\r\n                    TextFormat =1\r\n                    VerticalAnchor =2\r\n\r\n                    LayoutCachedLeft =780\r\n                    LayoutCachedTop =3300\r\n                    LayoutCachedWidth =2760\r\n                    LayoutCachedHeight =5700\r\n                    BackThemeColorIndex =-1\r\n                    BorderThemeColorIndex =0\r\n                    BorderTint =50.0\r\n                    BorderShade =100.0\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin CommandButton\r\n                    Cancel = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =7680\r\n                    Top =5880\r\n                    Width =1500\r\n                    Height =420\r\n                    TabIndex =4\r\n                    Name =\"cmdClose\"\r\n                    Caption =\"Close\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =1\r\n                    VerticalAnchor =1\r\n\r\n                    LayoutCachedLeft =7680\r\n                    LayoutCachedTop =5880\r\n                    LayoutCachedWidth =9180\r\n                    LayoutCachedHeight =6300\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =223\r\n                    Left =6480\r\n                    Top =1200\r\n                    Width =1560\r\n                    TabIndex =5\r\n                    Name =\"cmdOptions\"\r\n                    Caption =\" Options...\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000c0a090ff604830ff604830ff604830ff604830ff604830ff ,\r\n                        0x604830ff604830ff604830ff604830ff604830ff604830ff0000000000000000 ,\r\n                        0x0000000000000000c0a890fffff8f0fffff0e0fff0e8e0fff0e0d0fff0d0c0ff ,\r\n                        0xf0c8b0ffe0c0a0ffe0b8a0ffe0b090ffe0a890ff604830ff0000000000000000 ,\r\n                        0x0000000000000000c0a8a0fffffff0ffc0a8a0ffb0a0a0fff0e8e0ffb0a090ff ,\r\n                        0xb09890ffb09890ffb09890ffb09890ffe0b090ff604830ff0000000000000000 ,\r\n                        0x0000000000000000c0a8a0fffffff0fffffff0fffff8f0fffff0e0fff0e8e0ff ,\r\n                        0xf0e0d0fff0d0c0fff0c8b0ffe0c0a0ffe0b8a0ff604830ff0000000000000000 ,\r\n                        0x0000000000000000c0a8a0fffffff0ffb0a8a0ffb0a0a0fffff8f0ffb0a090ff ,\r\n                        0xb09890ffb09890ffb09890ffb09890ffe0c0a0ff604830ff0000000000000000 ,\r\n                        0x0000000000000000c0b0a0fffffff0fffffff0fffffff0fffffff0fffff8f0ff ,\r\n                        0xfff0e0fff0e8e0fff0e0d0fff0d0c0fff0c8b0ff604830ff0000000000000000 ,\r\n                        0x0000000000000000c0b0a0fffffff0fffffff0fffffff0fffffff0fffffff0ff ,\r\n                        0xfff8f0ffc0c8c0ff406070fff0e0d0fff0d0c0ff604830ff0000000000000000 ,\r\n                        0x0000000040784010c0b0a0ffffffffffe0e8f0ff607880ffe0e0e0fffffff0ff ,\r\n                        0xc0c8d0ff506070ff30a8d0ff203840ffb0a0a0ff604830ff0000000000000000 ,\r\n                        0x40784070408050ffd0b0a0ffffffffff90a8b0ff80d0e0ff506070ffb0b8c0ff ,\r\n                        0x506070ff60c0e0ff506070ff30b8f0ff102830ff103040ff102830ff10203090 ,\r\n                        0x407840ff50a860ffd0b8a0ffffffffffd0d8e0ff90a8b0ff80e0f0ff506070ff ,\r\n                        0x80d0e0ff506070ff60d0f0ff406070ff30b0e0ff2098d0ff2088b0ff205070ff ,\r\n                        0x508860ff50b060ffd0b8a0ffd0b8a0ffd0b8a0ffb0b0a0ff90a8b0ff80e0f0ff ,\r\n                        0x506070ff80e0f0ff405870ff60d0f0ff50c8f0ff40c0f0ff20b0e0ff20a0d0ff ,\r\n                        0x609870ff50b870ff00000000000000000000000090a8b0ff2080a0ff90a8b0ff ,\r\n                        0x80e0f0ff405870ff80e0f0ff70d8f0ff60d0f0ff50c8f0ff40c0f0ff30b8f0ff ,\r\n                        0x70a880ff60c080ff00000000000000000000000090a8b09090a8b0ff2080a0ff ,\r\n                        0x90a8b0ff80e0f0ff80e0f0ff80e0f0ff70d8f0ff60d0f0ff60b0d0ff808890ff ,\r\n                        0xb0c0a0ff90d0a0ff000000000000000000000000000000000000000090a8b050 ,\r\n                        0x90a8b0b090a8b0ff90a0b0ff90a0a0ff8098a0ff8090a0ff808890ff80889080 ,\r\n                        0xa0b8a0ffb0c0a0ff000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n\r\n                    LayoutCachedLeft =6480\r\n                    LayoutCachedTop =1200\r\n                    LayoutCachedWidth =8040\r\n                    LayoutCachedHeight =1560\r\n                    PictureCaptionArrangement =5\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =5324600\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =300\r\n                    Top =5940\r\n                    Width =3375\r\n                    Height =240\r\n                    FontSize =10\r\n                    Name =\"Label32\"\r\n                    Caption =\"joyfullservice/msaccess-vcs-addin\"\r\n                    VerticalAnchor =1\r\n                    LayoutCachedLeft =300\r\n                    LayoutCachedTop =5940\r\n                    LayoutCachedWidth =3675\r\n                    LayoutCachedHeight =6180\r\n                    BorderThemeColorIndex =1\r\n                    BorderTint =100.0\r\n                    BorderShade =50.0\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                    ForeShade =50.0\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =223\r\n                    Left =8100\r\n                    Top =1200\r\n                    Width =1080\r\n                    TabIndex =6\r\n                    Name =\"cmdHelp\"\r\n                    Caption =\" Help\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =1\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x000000000000000000000000000000000000000000000000e0e8e000e0c8b000 ,\r\n                        0xe0d8d000e0d0c010e0d0c010d0d0c010d0d0c000d0d0d000e0e0e00000000000 ,\r\n                        0x0000000000000000000000000000000000000000f0e8e0009068303080582080 ,\r\n                        0x905010c0804820e0804820c0804810b06040108050381030d0c8c01000000000 ,\r\n                        0x000000000000000000000000e0780000e0a05010a0683070c08860f0e0c8b0ff ,\r\n                        0xf0f0f0fffffffffffffffffff0f0f0ffe0c8c0ffa07850c040301060d0c8c010 ,\r\n                        0xe0d8d0000000000000000000e0882000b0703070e0a880fffff0e0ffe0b8a0ff ,\r\n                        0xd08050ffc05820ffc05820ffd08050ffe0b8a0fff0e8e0ffb09070f050301060 ,\r\n                        0xd0c8c000e0e0e00000000000b0783030d09870f0fff0e0ffe0a890ffc05010ff ,\r\n                        0xc05010ffe0a890ffffffffffb04810ffb04810ffd0a080fff0f0e0ffa07050d0 ,\r\n                        0x50381030d0d0d000f0f0f000b0784080f0d8c0fff0c8b0ffe05820ffd05810ff ,\r\n                        0xd05010ffe08050ffe0a880ffc05010ffb04810ffb04810ffe0b8a0ffe0c8c0ff ,\r\n                        0x50401080d0d0d010f0f0f000d08040e0fff8f0fff09870fff06020ffe05820ff ,\r\n                        0xe05820fff0a890ffffffffffd05010ffc05010ffb05010ffc07850fff0f0f0ff ,\r\n                        0x804020c0e0d0c000f0f0f000d08040f0ffffffffff7840ffff6830fff06820ff ,\r\n                        0xf06020fff08850fffffffffff0c0b0ffc05820ffb05010ffb05820ffffffffff ,\r\n                        0x804820e0e0d0c010f0f0f000d08850f0ffffffffff8050ffff7030ffff6830ff ,\r\n                        0xff6830ffff6820fff09060fffff8f0fff0d8c0ffc05020ffc05820ffffffffff ,\r\n                        0x804820e0e0d8d010f0f0f000d08050c0fff8f0ffffa880ffff7040ffff8850ff ,\r\n                        0xffb090ffff7030fff06820fff09070fffffffffff08050ffd08860fffff0f0ff ,\r\n                        0x805820b0e0d8d010f0f0f000c0804070f0d8c0ffffd0c0ffff7840ffff9870ff ,\r\n                        0xffffffffffc8b0ffff9060ffffc8b0fffff8f0fff07840fff0c8b0ffe0c8b0ff ,\r\n                        0x90602070e0c8b00000000000c0884030e0a070f0fff8f0ffffc0a0ffff7840ff ,\r\n                        0xffb8a0fffff8f0fffffffffffff0e0ffff9870fff0b8a0fffff0e0ffc08850e0 ,\r\n                        0xa0682030f0e8e0000000000000000000c0884060e0b8a0f0fff8f0ffffd0c0ff ,\r\n                        0xffa880ffff8850ffff8850ffffa880fff0d0c0fffff0e0ffd0a880f0a0683060 ,\r\n                        0xe0c0a00000000000000000000000000000000000c0884060e0a070f0f0d8c0ff ,\r\n                        0xfff8f0fffffffffffffffffffff8f0fff0d8c0ffc09060e0a0703050f0b89000 ,\r\n                        0x0000000000000000000000000000000000000000f0f0f000c0884030c0804070 ,\r\n                        0xe0a070c0d09870e0d09860f0d09870d0b0784070b0784020f0e8f00000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0xf0f0f000f0f0f000f0f0f000f0f0f000f0f0f00000000000f0f0f00000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n\r\n                    LayoutCachedLeft =8100\r\n                    LayoutCachedTop =1200\r\n                    LayoutCachedWidth =9180\r\n                    LayoutCachedHeight =1560\r\n                    PictureCaptionArrangement =5\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =5324600\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =215\r\n                    TextAlign =3\r\n                    Left =5820\r\n                    Top =480\r\n                    Width =3180\r\n                    Height =240\r\n                    FontSize =10\r\n                    Name =\"lblVersion\"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =5820\r\n                    LayoutCachedTop =480\r\n                    LayoutCachedWidth =9000\r\n                    LayoutCachedHeight =720\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    Visible = NotDefault\r\n                    OverlapFlags =93\r\n                    Left =3120\r\n                    Top =5640\r\n                    Width =5160\r\n                    Height =180\r\n                    Name =\"lblProgBack\"\r\n                    HorizontalAnchor =2\r\n                    VerticalAnchor =1\r\n                    LayoutCachedLeft =3120\r\n                    LayoutCachedTop =5640\r\n                    LayoutCachedWidth =8280\r\n                    LayoutCachedHeight =5820\r\n                End\r\n                Begin Label\r\n                    Visible = NotDefault\r\n                    OverlapFlags =93\r\n                    Left =4500\r\n                    Top =6000\r\n                    Width =2220\r\n                    Height =180\r\n                    Name =\"lblProgFront\"\r\n                    LayoutCachedLeft =4500\r\n                    LayoutCachedTop =6000\r\n                    LayoutCachedWidth =6720\r\n                    LayoutCachedHeight =6180\r\n                End\r\n                Begin Label\r\n                    Visible = NotDefault\r\n                    OverlapFlags =215\r\n                    Left =4980\r\n                    Top =6120\r\n                    Width =1260\r\n                    Height =180\r\n                    FontSize =8\r\n                    Name =\"lblProgCaption\"\r\n                    LayoutCachedLeft =4980\r\n                    LayoutCachedTop =6120\r\n                    LayoutCachedWidth =6240\r\n                    LayoutCachedHeight =6300\r\n                End\r\n                Begin Line\r\n                    Visible = NotDefault\r\n                    OverlapFlags =95\r\n                    Left =2460\r\n                    Top =1080\r\n                    Width =660\r\n                    Name =\"Line27\"\r\n                    LayoutCachedLeft =2460\r\n                    LayoutCachedTop =1080\r\n                    LayoutCachedWidth =3120\r\n                    LayoutCachedHeight =1080\r\n                    BorderThemeColorIndex =1\r\n                End\r\n                Begin CheckBox\r\n                    OverlapFlags =93\r\n                    Left =5700\r\n                    Top =3390\r\n                    TabIndex =7\r\n                    Name =\"chkFullExport\"\r\n\r\n                    LayoutCachedLeft =5700\r\n                    LayoutCachedTop =3390\r\n                    LayoutCachedWidth =5960\r\n                    LayoutCachedHeight =3630\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =255\r\n                            Left =5925\r\n                            Top =3360\r\n                            Width =1335\r\n                            Height =270\r\n                            FontSize =10\r\n                            ForeColor =5324600\r\n                            Name =\"Label29\"\r\n                            Caption =\"Full Export\"\r\n                            LayoutCachedLeft =5925\r\n                            LayoutCachedTop =3360\r\n                            LayoutCachedWidth =7260\r\n                            LayoutCachedHeight =3630\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin CheckBox\r\n                    OverlapFlags =93\r\n                    Left =5700\r\n                    Top =4950\r\n                    TabIndex =8\r\n                    Name =\"chkFullBuild\"\r\n                    DefaultValue =\"True\"\r\n                    OnClick =\"[Event Procedure]\"\r\n\r\n                    LayoutCachedLeft =5700\r\n                    LayoutCachedTop =4950\r\n                    LayoutCachedWidth =5960\r\n                    LayoutCachedHeight =5190\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =255\r\n                            Left =5925\r\n                            Top =4920\r\n                            Width =1335\r\n                            Height =270\r\n                            FontSize =10\r\n                            ForeColor =5324600\r\n                            Name =\"Label31\"\r\n                            Caption =\"Full Build\"\r\n                            LayoutCachedLeft =5925\r\n                            LayoutCachedTop =4920\r\n                            LayoutCachedWidth =7260\r\n                            LayoutCachedHeight =5190\r\n                            ForeThemeColorIndex =-1\r\n                            ForeTint =100.0\r\n                        End\r\n                    End\r\n                End\r\n                Begin TextBox\r\n                    Visible = NotDefault\r\n                    EnterKeyBehavior = NotDefault\r\n                    SpecialEffect =2\r\n                    OldBorderStyle =0\r\n                    OverlapFlags =247\r\n                    TextFontFamily =49\r\n                    IMESentenceMode =3\r\n                    Left =3120\r\n                    Top =1080\r\n                    Width =5160\r\n                    Height =4500\r\n                    FontSize =10\r\n                    LeftMargin =144\r\n                    TopMargin =144\r\n                    RightMargin =144\r\n                    BottomMargin =144\r\n                    Name =\"txtLog\"\r\n                    FontName =\"Lucida Console\"\r\n                    TextFormat =1\r\n                    HorizontalAnchor =2\r\n                    VerticalAnchor =2\r\n\r\n                    LayoutCachedLeft =3120\r\n                    LayoutCachedTop =1080\r\n                    LayoutCachedWidth =8280\r\n                    LayoutCachedHeight =5580\r\n                    ThemeFontIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin CommandButton\r\n                    Visible = NotDefault\r\n                    FontUnderline = NotDefault\r\n                    TabStop = NotDefault\r\n                    OverlapFlags =215\r\n                    Left =3120\r\n                    Top =5640\r\n                    Width =1860\r\n                    Height =240\r\n                    FontSize =10\r\n                    TabIndex =9\r\n                    Name =\"cmdOpenLogFile\"\r\n                    Caption =\"Open Log File...\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    VerticalAnchor =1\r\n                    BackStyle =0\r\n\r\n                    CursorOnHover =1\r\n                    LayoutCachedLeft =3120\r\n                    LayoutCachedTop =5640\r\n                    LayoutCachedWidth =4980\r\n                    LayoutCachedHeight =5880\r\n                    Alignment =1\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =5324600\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverThemeColorIndex =10\r\n                    HoverTint =100.0\r\n                    PressedThemeColorIndex =10\r\n                    PressedShade =100.0\r\n                    HoverForeThemeColorIndex =10\r\n                    HoverForeTint =100.0\r\n                    PressedForeThemeColorIndex =10\r\n                    PressedForeTint =100.0\r\n                    Overlaps =1\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmVCSMain.cls\"\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSMain.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : Form_frmVCSMain\r\n' Author    : Adam Waller\r\n' Date      : 3/30/2022\r\n' Purpose   : Main form for performing an export or build operation.\r\n'           : This color scheme can be changed, I just wanted something more aesthetically\r\n'           : pleasing than the default wizards and forms.\r\n'           : Color scheme: https://coolors.co/383f51-e0e0e6-ffffff-ef8354-d3d7ef\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' This property can be set to export or merge a specific subset of containers\r\nPublic intContainerFilter As eContainerFilter\r\n\r\n' Used for exporting or loading a single object\r\nPublic objSingleObject As AccessObject\r\n\r\n' Path to the last log file, in case the user wants to view the log after the operation.\r\n' (The Log object has already been reset at this point, so we can't use Log.LogFilePath.)\r\nPublic strLastLogFilePath As String\r\n\r\n' Use this property to set the path to the source files (such as a build triggered from the API)\r\nPublic strSourcePath As String\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdBuild_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2020\r\n' Purpose   : Initiate the process to build from source\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub cmdBuild_Click()\r\n\r\n    Dim strFolder As String\r\n\r\n    ' Make sure we use the add-in to build the add-in.\r\n    If CodeProject.FullName = CurrentProject.FullName Then\r\n        MsgBox2 T(\"Build must be run from Add-In\"), T(\"Instead of opening this form to build the add-in,\" & vbCrLf & _\r\n            \"please install and use the Version Control add-in from the Add-in menu\"), , vbExclamation\r\n        DoCmd.Hourglass False\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Get source files folder\r\n    If Len(Me.strSourcePath) Then\r\n        ' Use specified build folder\r\n        strFolder = Me.strSourcePath\r\n    Else\r\n        ' Attempt to get the source folder from the current database, or from\r\n        ' a folder picker dialog.\r\n        strFolder = GetSourceFolder\r\n        ' Exit out of build if the user cancelled any of the confirmations.\r\n        If strFolder = vbNullString Then\r\n            DoCmd.Close acForm, Me.Name\r\n            Exit Sub\r\n        End If\r\n    End If\r\n\r\n    ' Build project using the selected source folder\r\n    ' (Use a timer so we can release the reference to this form before beginning the\r\n    '  build process, just in case we need to import a form with the same name.)\r\n    If strFolder <> vbNullString Then SetTimer \"Build\", strFolder, chkFullBuild\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSourceFolder\r\n' Author    : Adam Waller\r\n' Date      : 10/19/2023\r\n' Purpose   : Return the source files folder from either the currently open database\r\n'           : or from a folder picker dialog. (Returns an empty string if the user\r\n'           : cancels the selection.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSourceFolder() As String\r\n\r\n    Dim strMsg(0 To 2) As String\r\n    Dim intChoice As VbMsgBoxResult\r\n\r\n    DoCmd.Hourglass True\r\n    DoEvents\r\n\r\n    ' Close the current database if it is currently open.\r\n    If DatabaseFileOpen Then\r\n        If FolderHasVcsOptionsFile(Options.GetExportFolder) Then\r\n            strMsg(0) = T(\"Build {0} ({1}) from source?\", var0:=CurrentVBProject.Name, var1:=CurrentProject.Name)\r\n            If chkFullBuild Then\r\n                strMsg(1) = T(\"Click 'Yes' to rebuild* this database from source files in this folder:\") & vbCrLf & Options.GetExportFolder & vbCrLf & _\r\n                    T(\"* (This database will be renamed as a backup before building {0} from source.)\", var0:=CurrentProject.Name)\r\n            Else\r\n                strMsg(1) = T(\"Click 'Yes' to merge* any changed source files into this database.\" & vbCrLf & _\r\n                    \"* (A backup of this database will be created before importing any source files.)\")\r\n            End If\r\n            strMsg(2) = T(\"Click 'No' to select another project, or 'Cancel' to go back to the previous screen.\")\r\n            If Not chkFullBuild And Not Me.Visible Then\r\n                ' Skip confirmation for merge build initiated from Ribbon\r\n                intChoice = vbYes\r\n            Else\r\n                ' Require user confirmation for full builds, or if main form is visible.\r\n                intChoice = MsgBox2(strMsg(0), strMsg(1), strMsg(2), vbYesNoCancel + vbQuestion + vbDefaultButton3, , vbYes)\r\n            End If\r\n            If intChoice = vbYes Then\r\n                ' Rebuild the open project\r\n                GetSourceFolder = Options.GetExportFolder\r\n            ElseIf intChoice = vbCancel Then\r\n                ' Canceled out of build option.\r\n                DoCmd.Hourglass False\r\n                Exit Function\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' If we aren't doing the current database, then prompt user to find a folder\r\n    ' with source files to use for the build.\r\n    If GetSourceFolder = vbNullString Then\r\n\r\n        ' Show a folder picker to select the file with source code.\r\n        DoCmd.Hourglass False\r\n        With Application.FileDialog(msoFileDialogFolderPicker)\r\n            .AllowMultiSelect = False\r\n            .ButtonName = T(\"Select Source Files Folder\")\r\n            '.InitialFileName = Options.GetExportFolder\r\n            .Title = T(\"Select Source Folder\")\r\n            .Show\r\n            If .SelectedItems.Count > 0 Then\r\n                ' Selected a folder\r\n                If FolderHasVcsOptionsFile(.SelectedItems(1)) Then\r\n                    ' Has source files\r\n                    GetSourceFolder = .SelectedItems(1) & PathSep\r\n                    DoCmd.Hourglass True\r\n                Else\r\n                    MsgBox2 T(\"Source files not found\"), T(\"Required source files were not found in this folder.\"), _\r\n                        T(\"You selected: {0}\", var0:=.SelectedItems(1)), vbExclamation\r\n                    Exit Function\r\n                End If\r\n            Else\r\n                ' Canceled dialog\r\n                Exit Function\r\n            End If\r\n        End With\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildFromSource\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2020\r\n' Purpose   : Show the GUI for building the database from source.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub StartBuild(blnFullBuild As Boolean)\r\n\r\n    Dim strType As String\r\n\r\n    cmdClose.SetFocus\r\n    HideActionButtons\r\n    DoEvents\r\n    With txtLog\r\n        .ScrollBars = 0\r\n        .Visible = True\r\n        .SetFocus\r\n    End With\r\n    Log.SetConsole Me.txtLog, GetProgressBar\r\n    Me.Visible = True\r\n\r\n    ' Show the status\r\n    SetStatusText T(\"Running...\"), IIf(blnFullBuild, T(\"Building From Source\"), T(\"Merging From Source\")), _\r\n        T(\"A summary of the build progress can be seen on this screen, and additional details are included in the log file.\")\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FinishBuild\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2020\r\n' Purpose   : Finish the build process\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub FinishBuild(blnFullBuild As Boolean _\r\n                        , Optional blnSuccess As Boolean = True)\r\n\r\n    Dim strMessage As String\r\n\r\n    ' Turn on scroll bars in case the user wants to scroll back through the log.\r\n    txtLog.ScrollBars = 2\r\n\r\n    ' Display final UI messages.\r\n    Log.Flush\r\n    strMessage = T(IIf(blnFullBuild, \"Build\", \"Merge\")) & \" \" & T(IIf(blnSuccess, \"Complete\", \"FAILED\"))\r\n    SetStatusText T(\"Finished\"), strMessage _\r\n        , T(\"Additional details can be found in the project log file.<br><br>You may now close this window.\")\r\n    cmdOpenLogFile.Visible = (Log.LogFilePath <> vbNullString)\r\n    Me.strLastLogFilePath = Log.LogFilePath\r\n\r\n    ' Close form if running in silent mode (such as when running automated builds)\r\n    If InteractionMode = eimSilent Then DoCmd.Close acForm, Me.Name\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : chkFullBuild_Click\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2023\r\n' Purpose   :\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub chkFullBuild_Click()\r\n    SetBuildCaption\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetBuildCaption\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2023\r\n' Purpose   : Set the appropriate caption for the build/merge button.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SetBuildCaption()\r\n    cmdBuild.Caption = IIf(chkFullBuild, _\r\n        T(\"  Build From Source\"), _\r\n        T(\"  Merge From Source\"))\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdClose_Click\r\n' Author    : Adam Waller\r\n' Date      : 1/14/2020\r\n' Purpose   : Close the form\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdClose_Click()\r\n    ' Ignore the error if the user resumes (cancels the close operation)\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    DoCmd.Close acForm, Me.Name\r\n    Catch 2501  ' Close form was canceled.\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ConfirmCancel\r\n' Author    : Adam Waller\r\n' Date      : 7/8/2021\r\n' Purpose   : Confirm that the user really wants to cancel the current operation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ConfirmCancel() As Boolean\r\n    ConfirmCancel = MsgBox2(T(\"Cancel Current Operation?\"), _\r\n        T(\"You are in the midst of a running process. Are you sure you want to cancel?\"), _\r\n        T(\"Click [Yes] to cancel the process, or [No] to resume.\"), _\r\n        vbYesNo + vbDefaultButton2 + vbExclamation, , vbYes) = vbYes\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdExport_Click\r\n' Author    : Adam Waller\r\n' Date      : 1/14/2020\r\n' Purpose   : Export source code from current database\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub cmdExport_Click()\r\n\r\n    cmdClose.SetFocus\r\n    HideActionButtons\r\n    DoEvents\r\n    With txtLog\r\n        .ScrollBars = 0\r\n        .Visible = True\r\n        .SetFocus\r\n    End With\r\n    Log.SetConsole Me.txtLog, GetProgressBar\r\n\r\n    ' Show the status\r\n    SetStatusText T(\"Running...\"), T(\"Exporting source code\"), _\r\n        T(\"A summary of the export progress can be seen on this screen, \" & _\r\n            \"and additional details are included in the log file.\")\r\n\r\n    ' See if we are exporting a single object, or everything.\r\n    If Me.objSingleObject Is Nothing Then\r\n        ' Export the source code using the specified filter.\r\n        modImportExport.ExportSource chkFullExport, Me.intContainerFilter, Me\r\n    Else\r\n        modImportExport.ExportSingleObject Me.objSingleObject, Me\r\n    End If\r\n\r\n    ' Turn on scroll bars in case the user wants to scroll back through the log.\r\n    txtLog.ScrollBars = 2\r\n    Log.Flush\r\n\r\n    ' Don't attempt to access controls if we are in the process of closing the form.\r\n    If FormLoaded(Me) Then\r\n        SetStatusText T(\"Finished\"), T(\"Export Complete\"), _\r\n            T(\"Additional details can be found in the project log file.<br><br>You may now close this window.\")\r\n        cmdOpenLogFile.Visible = (Me.strLastLogFilePath <> vbNullString)\r\n        Me.strLastLogFilePath = Me.strLastLogFilePath\r\n        DoEvents\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HideActionButtons\r\n' Author    : Adam Waller\r\n' Date      : 1/14/2020\r\n' Purpose   : Hide the action buttons when running a command.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub HideActionButtons()\r\n    cmdExport.Visible = False\r\n    chkFullExport.Visible = False\r\n    cmdBuild.Visible = False\r\n    chkFullBuild.Visible = False\r\n    cmdOptions.Visible = False\r\n    cmdHelp.Visible = False\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdHelp_Click\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Go to the GitHub documentation page.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdHelp_Click()\r\n    Application.FollowHyperlink \"https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Documentation\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdOptions_Click\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Open options dialog (for this project)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdOptions_Click()\r\n    If DatabaseFileOpen Then\r\n        ' Force reload of options from current project before opening the form.\r\n        Set Options = Nothing\r\n        Form_frmVCSOptions.Visible = True\r\n    Else\r\n        ' The table data subform may crash Access when the options form is closed with no database open.\r\n        MsgBox2 T(\"No Database Open\"), T(\"Please open a database file before configuring VCS options.\"), , vbInformation\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Load\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : Setting the control source causes delayed display. This way the display\r\n'           : is instant when the form is opened.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Form_Load()\r\n\r\n    ' Load translations\r\n    Translation.ApplyTo Me\r\n\r\n    ' Display version (better performance than bound control)\r\n    lblVersion.Caption = T(\"Version {0}\", var0:=GetVCSVersion())\r\n\r\n    SetStatusText T(\"Choose Action\"), T(\"What would you like to do?\"), _\r\n        T(\"<strong><em>Export</em></strong> source to generate source files from the current database.\" & _\r\n        \"<br><br><strong><em>Import</em></strong> source files to rebuild this database from source.\")\r\n\r\n    ' Set defaults based on current options.\r\n    chkFullBuild = Not Options.UseMergeBuild\r\n    chkFullExport = Not Options.UseFastSave\r\n\r\n    ' You can only export if you have a database open.\r\n    cmdExport.Enabled = DatabaseFileOpen\r\n    chkFullExport.Enabled = DatabaseFileOpen\r\n\r\n    If DatabaseFileOpen Then\r\n\r\n        ' Require full export after options change\r\n        If VCSIndex.OptionsHash <> Options.GetHash Then\r\n            chkFullExport = True\r\n            chkFullExport.Enabled = False\r\n        End If\r\n\r\n        ' Merge build only available after full build.\r\n        ' (Attempting a merge build of the entire database may\r\n        '  not work correctly due to objects that depend upon\r\n        '  each other.)\r\n        If VCSIndex.FullBuildDate = 0 Then\r\n            chkFullBuild = True\r\n            chkFullBuild.Enabled = False\r\n        End If\r\n    End If\r\n\r\n    ' Set caption on build button\r\n    SetBuildCaption\r\n\r\n    ' Turn off the timer, just in case it was left on.\r\n    Me.TimerInterval = 0\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AutoClose\r\n' Author    : Adam Waller\r\n' Date      : 5/6/2021\r\n' Purpose   : Use the timer to automatically close the form in 2 seconds.\r\n'           : (This keeps the application from \"hanging\" during the pause between\r\n'           :  completion and close.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub AutoClose()\r\n    'The procedure may be called when the form has been closed.\r\n    'In this case, a VBA error may occur, so we check if the\r\n    'form is loaded before setting the property. We do not use\r\n    'the Me.Name because that would be also an error.\r\n    If IsLoaded(acForm, \"frmVCSMain\", False) Then\r\n        Me.TimerInterval = 2000\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Timer\r\n' Author    : Adam Waller\r\n' Date      : 5/6/2021\r\n' Purpose   : Automatically close form.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Timer()\r\n    Me.TimerInterval = 0\r\n    cmdClose_Click\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetStatusText\r\n' Author    : Adam Waller\r\n' Date      : 4/21/2020\r\n' Purpose   : Update the status text to direct the user to the next task, or show the\r\n'           : status of the current operation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetStatusText(strHeading As String, strSubHeading As String, strDescriptionHtml As String)\r\n    If Not FormLoaded(Me) Then Exit Sub\r\n    lblHeading.Caption = strHeading\r\n    lblSubheading.Caption = strSubHeading\r\n    txtDescription.Value = strDescriptionHtml\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetProgressBar\r\n' Author    : Adam Waller\r\n' Date      : 11/6/2020\r\n' Purpose   : Set up the progress bar.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetProgressBar() As clsLblProg\r\n\r\n    Dim cProg As clsLblProg\r\n\r\n    ' Create a new progress bar class, and initialize with form controls\r\n    Set cProg = New clsLblProg\r\n    cProg.Initialize lblProgBack, lblProgFront, lblProgCaption\r\n    cProg.SetRepaintInterval 0.5\r\n\r\n    ' Move caption up slightly for better alignment on this very small progress bar\r\n    lblProgCaption.Top = lblProgBack.Top + 1\r\n\r\n    ' Return reference to caller\r\n    Set GetProgressBar = cProg\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Unload\r\n' Author    : Adam Waller\r\n' Date      : 7/8/2021\r\n' Purpose   : Verify that the user wants to cancel the current operation\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Unload(Cancel As Integer)\r\n\r\n    Static intAttempt As Integer\r\n\r\n    ' Allow the form to close on the third attempt, just in case the log\r\n    ' is stuck in active status for some reason.\r\n    If intAttempt > 2 Then Exit Sub\r\n\r\n    ' Check to see if we have an active job running.\r\n    If Log.Active Then\r\n        If ConfirmCancel Then Log.Error eelCritical, T(\"Canceled Operation\"), Me.Name & \".Form_Unload\"\r\n        ' Either way, we want the log to complete first.\r\n        Cancel = True\r\n        intAttempt = intAttempt + 1\r\n    End If\r\n\r\n    ' Release the log console if we are closing the form\r\n    If Not Cancel Then Log.ReleaseConsole\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdOpenLogFile_Click\r\n' Author    : Adam Waller\r\n' Date      : 11/6/2020\r\n' Purpose   : Open the log file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdOpenLogFile_Click()\r\n    cmdClose.SetFocus\r\n    If FSO.FileExists(strLastLogFilePath) Then\r\n        ' (Note, parentheses are required for the path argument)\r\n        CreateObject(\"Shell.Application\").Open (strLastLogFilePath)\r\n    End If\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSOptions.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    PopUp = NotDefault\r\n    RecordSelectors = NotDefault\r\n    AutoCenter = NotDefault\r\n    NavigationButtons = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    ScrollBars =0\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =10080\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =310\r\n    Left =32025\r\n    Top =2430\r\n    Right =-14191\r\n    Bottom =14895\r\n    RecSrcDt = Begin\r\n        0x79e78b777268e540\r\n    End\r\n    Caption =\"Version Control System\"\r\n    OnOpen =\"[Event Procedure]\"\r\n    DatasheetFontName =\"Calibri\"\r\n    OnLoad =\"[Event Procedure]\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Rectangle\r\n            SpecialEffect =3\r\n            BackStyle =0\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Line\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Image\r\n            BackStyle =0\r\n            OldBorderStyle =0\r\n            BorderLineStyle =0\r\n            SizeMode =3\r\n            PictureAlignment =2\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CommandButton\r\n            FontSize =11\r\n            FontWeight =400\r\n            FontName =\"Calibri\"\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =1\r\n            Gradient =12\r\n            BackThemeColorIndex =4\r\n            BackTint =60.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =4\r\n            BorderTint =60.0\r\n            ThemeFontIndex =1\r\n            HoverThemeColorIndex =4\r\n            HoverTint =40.0\r\n            PressedThemeColorIndex =4\r\n            PressedShade =75.0\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n        End\r\n        Begin CheckBox\r\n            BorderLineStyle =0\r\n            LabelX =230\r\n            LabelY =-30\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin OptionGroup\r\n            SpecialEffect =3\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin ListBox\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AllowValueListEdits =1\r\n            InheritValueList =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin ComboBox\r\n            AddColon = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AllowValueListEdits =1\r\n            InheritValueList =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =2\r\n            ForeShade =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Subform\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =1\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            BorderShade =65.0\r\n            ShowPageHeaderAndPageFooter =1\r\n        End\r\n        Begin Tab\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =3\r\n            BackThemeColorIndex =1\r\n            BackShade =85.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =2\r\n            BorderTint =60.0\r\n            HoverThemeColorIndex =1\r\n            PressedThemeColorIndex =1\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n        End\r\n        Begin Page\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin EmptyCell\r\n            Height =240\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            CanGrow = NotDefault\r\n            Height =7200\r\n            BackColor =15130848\r\n            Name =\"Detail\"\r\n            AlternateBackColor =15130848\r\n            Begin\r\n                Begin Line\r\n                    BorderWidth =5\r\n                    OverlapFlags =85\r\n                    Top =1080\r\n                    Width =10080\r\n                    BorderColor =15321539\r\n                    Name =\"Line10\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedTop =1080\r\n                    LayoutCachedWidth =10080\r\n                    LayoutCachedHeight =1080\r\n                    BorderThemeColorIndex =-1\r\n                End\r\n                Begin Rectangle\r\n                    SpecialEffect =0\r\n                    BackStyle =1\r\n                    OldBorderStyle =0\r\n                    OverlapFlags =93\r\n                    Width =10080\r\n                    Height =1020\r\n                    BackColor =5324600\r\n                    Name =\"Box1\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedWidth =10080\r\n                    LayoutCachedHeight =1020\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =215\r\n                    Left =540\r\n                    Top =240\r\n                    Width =4020\r\n                    Height =540\r\n                    FontSize =18\r\n                    FontWeight =700\r\n                    Name =\"lblOptions\"\r\n                    Caption =\"Options\"\r\n                    LayoutCachedLeft =540\r\n                    LayoutCachedTop =240\r\n                    LayoutCachedWidth =4560\r\n                    LayoutCachedHeight =780\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =223\r\n                    TextAlign =2\r\n                    Left =7140\r\n                    Top =300\r\n                    Width =2040\r\n                    Height =240\r\n                    FontSize =10\r\n                    Name =\"Label9\"\r\n                    Caption =\"Version Control System \"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =7140\r\n                    LayoutCachedTop =300\r\n                    LayoutCachedWidth =9180\r\n                    LayoutCachedHeight =540\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin CommandButton\r\n                    Cancel = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =5880\r\n                    Top =6540\r\n                    Width =1500\r\n                    Height =420\r\n                    Name =\"cmdCancel\"\r\n                    Caption =\"Cancel\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =1\r\n                    VerticalAnchor =1\r\n\r\n                    LayoutCachedLeft =5880\r\n                    LayoutCachedTop =6540\r\n                    LayoutCachedWidth =7380\r\n                    LayoutCachedHeight =6960\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    Left =7680\r\n                    Top =6540\r\n                    Width =1860\r\n                    Height =420\r\n                    TabIndex =1\r\n                    Name =\"cmdSaveAndClose\"\r\n                    Caption =\" Save && Close\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    LeftPadding =135\r\n                    TopPadding =135\r\n                    RightPadding =150\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =1\r\n                    VerticalAnchor =1\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x000000000000000000000000d0687050c06860ffb05850ffa05050ffa05050ff ,\r\n                        0xa05050ff904850ff904840ff904840ff804040ff803840ff803840ff703840ff ,\r\n                        0x703830ff0000000000000000d06870fff09090ffe08080ffb04820ff403020ff ,\r\n                        0xc0b8b0ffc0b8b0ffd0c0c0ffd0c8c0ff505050ffa04030ffa04030ffa03830ff ,\r\n                        0x703840ff0000000000000000d07070ffff98a0fff08880ffe08080ff705850ff ,\r\n                        0x404030ff907870fff0e0e0fff0e8e0ff908070ffa04030ffa04040ffa04030ff ,\r\n                        0x803840ff0000000000000000d07870ffffa0a0fff09090fff08880ff705850ff ,\r\n                        0x000000ff404030fff0d8d0fff0e0d0ff807860ffb04840ffb04840ffa04040ff ,\r\n                        0x804040ff0000000000000000d07880ffffa8b0ffffa0a0fff09090ff705850ff ,\r\n                        0x705850ff705850ff705850ff706050ff806860ffc05850ffb05050ffb04840ff ,\r\n                        0x804040ff0000000000000000e08080ffffb0b0ffffb0b0ffffa0a0fff09090ff ,\r\n                        0xf08880ffe08080ffe07880ffd07070ffd06870ffc06060ffc05850ffb05050ff ,\r\n                        0x904840ff0000000000000000e08890ffffb8c0ffffb8b0ffd06060ffc06050ff ,\r\n                        0xc05850ffc05040ffb05030ffb04830ffa04020ffa03810ffc06060ffc05850ff ,\r\n                        0x904840ff0000000000000000e09090ffffc0c0ffd06860ffffffffffffffffff ,\r\n                        0xfff8f0fff0f0f0fff0e8e0fff0d8d0ffe0d0c0ffe0c8c0ffa03810ffc06060ff ,\r\n                        0x904850ff0000000000000000e098a0ffffc0c0ffd07070ffffffffffffffffff ,\r\n                        0xfffffffffff8f0fff0f0f0fff0e8e0fff0d8d0ffe0d0c0ffa04020ffd06860ff ,\r\n                        0xa05050ff0000000000000000f0a0a0ffffc0c0ffe07870ffffffffffffffffff ,\r\n                        0xfffffffffffffffffff8f0fff0f0f0fff0e8e0fff0d8d0ffb04830ffd07070ff ,\r\n                        0xa05050ff0000000000000000f0a8a0ffffc0c0ffe08080ffffffffffffffffff ,\r\n                        0xfffffffffffffffffffffffffff8f0fff0f0f0fff0e8e0ffb05030ffe07880ff ,\r\n                        0xa05050ff0000000000000000f0b0b0ffffc0c0fff08890ffffffffffffffffff ,\r\n                        0xfffffffffffffffffffffffffffffffffff8f0fff0f0f0ffc05040ff603030ff ,\r\n                        0xb05850ff0000000000000000f0b0b0ffffc0c0ffff9090ffffffffffffffffff ,\r\n                        0xfffffffffffffffffffffffffffffffffffffffffff8f0ffc05850ffb05860ff ,\r\n                        0xb05860ff0000000000000000f0b8b0fff0b8b0fff0b0b0fff0b0b0fff0a8b0ff ,\r\n                        0xf0a0a0ffe098a0ffe09090ffe09090ffe08890ffe08080ffd07880ffd07870ff ,\r\n                        0xd07070ff00000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n\r\n                    LayoutCachedLeft =7680\r\n                    LayoutCachedTop =6540\r\n                    LayoutCachedWidth =9540\r\n                    LayoutCachedHeight =6960\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin Tab\r\n                    OverlapFlags =85\r\n                    Left =525\r\n                    Top =1500\r\n                    Width =9015\r\n                    Height =4845\r\n                    TabIndex =2\r\n                    Name =\"tabOptions\"\r\n                    HorizontalAnchor =2\r\n                    VerticalAnchor =2\r\n\r\n                    LayoutCachedLeft =525\r\n                    LayoutCachedTop =1500\r\n                    LayoutCachedWidth =9540\r\n                    LayoutCachedHeight =6345\r\n                    ThemeFontIndex =1\r\n                    Shape =1\r\n                    Gradient =12\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackShade =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    PressedColor =15130848\r\n                    PressedThemeColorIndex =-1\r\n                    Begin\r\n                        Begin Page\r\n                            OverlapFlags =87\r\n                            Left =600\r\n                            Top =1980\r\n                            Width =8865\r\n                            Height =4295\r\n                            Name =\"pgeGeneral\"\r\n                            Caption =\"General\"\r\n                            LayoutCachedLeft =600\r\n                            LayoutCachedTop =1980\r\n                            LayoutCachedWidth =9465\r\n                            LayoutCachedHeight =6275\r\n                            Begin\r\n                                Begin CheckBox\r\n                                    OverlapFlags =215\r\n                                    Left =1020\r\n                                    Top =4020\r\n                                    Name =\"chkShowDebug\"\r\n\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =4020\r\n                                    LayoutCachedWidth =1280\r\n                                    LayoutCachedHeight =4260\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =215\r\n                                            Left =1320\r\n                                            Top =3960\r\n                                            Width =2640\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label23\"\r\n                                            Caption =\"Show Detailed Output\"\r\n                                            LayoutCachedLeft =1320\r\n                                            LayoutCachedTop =3960\r\n                                            LayoutCachedWidth =3960\r\n                                            LayoutCachedHeight =4275\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =215\r\n                                    Left =1020\r\n                                    Top =4440\r\n                                    TabIndex =1\r\n                                    Name =\"chkBreakOnError\"\r\n\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =4440\r\n                                    LayoutCachedWidth =1280\r\n                                    LayoutCachedHeight =4680\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =215\r\n                                            Left =1320\r\n                                            Top =4380\r\n                                            Width =2640\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label219\"\r\n                                            Caption =\"Debug VBA Errors\"\r\n                                            LayoutCachedLeft =1320\r\n                                            LayoutCachedTop =4380\r\n                                            LayoutCachedWidth =3960\r\n                                            LayoutCachedHeight =4695\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =215\r\n                                    Left =1020\r\n                                    Top =4860\r\n                                    TabIndex =2\r\n                                    Name =\"chkShowVCSLegacy\"\r\n\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =4860\r\n                                    LayoutCachedWidth =1280\r\n                                    LayoutCachedHeight =5100\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =215\r\n                                            Left =1320\r\n                                            Top =4800\r\n                                            Width =2640\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label215\"\r\n                                            Caption =\"Show Legacy Prompts\"\r\n                                            LayoutCachedLeft =1320\r\n                                            LayoutCachedTop =4800\r\n                                            LayoutCachedWidth =3960\r\n                                            LayoutCachedHeight =5115\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =215\r\n                                    Left =1020\r\n                                    Top =5280\r\n                                    TabIndex =6\r\n                                    Name =\"chkUseShortHash\"\r\n\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =5280\r\n                                    LayoutCachedWidth =1280\r\n                                    LayoutCachedHeight =5520\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =215\r\n                                            Left =1320\r\n                                            Top =5220\r\n                                            Width =2640\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label223\"\r\n                                            Caption =\"Use short hashes in index\"\r\n                                            LayoutCachedLeft =1320\r\n                                            LayoutCachedTop =5220\r\n                                            LayoutCachedWidth =3960\r\n                                            LayoutCachedHeight =5535\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin ComboBox\r\n                                    RowSourceTypeInt =1\r\n                                    OverlapFlags =215\r\n                                    IMESentenceMode =3\r\n                                    ListWidth =1440\r\n                                    Left =2640\r\n                                    Top =5640\r\n                                    Width =1560\r\n                                    Height =315\r\n                                    TabIndex =5\r\n                                    Name =\"cboHashAlgorithm\"\r\n                                    RowSourceType =\"Value List\"\r\n                                    RowSource =\"\\\"SHA1\\\";\\\"SHA256\\\";\\\"SHA512\\\"\"\r\n                                    AllowValueListEdits =0\r\n\r\n                                    LayoutCachedLeft =2640\r\n                                    LayoutCachedTop =5640\r\n                                    LayoutCachedWidth =4200\r\n                                    LayoutCachedHeight =5955\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =215\r\n                                            Left =960\r\n                                            Top =5640\r\n                                            Width =1560\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label221\"\r\n                                            Caption =\"Hash Algorithm:\"\r\n                                            LayoutCachedLeft =960\r\n                                            LayoutCachedTop =5640\r\n                                            LayoutCachedWidth =2520\r\n                                            LayoutCachedHeight =5955\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin ComboBox\r\n                                    RowSourceTypeInt =1\r\n                                    OverlapFlags =223\r\n                                    IMESentenceMode =3\r\n                                    ListWidth =1440\r\n                                    Left =7080\r\n                                    Top =4320\r\n                                    Width =1740\r\n                                    Height =315\r\n                                    TabIndex =3\r\n                                    Name =\"cboDiffTool\"\r\n                                    RowSourceType =\"Value List\"\r\n                                    RowSource =\"\\\"WinMerge\\\";\\\"VSCode\\\";\\\"TortoiseGitDiff\\\"\"\r\n                                    AllowValueListEdits =0\r\n\r\n                                    LayoutCachedLeft =7080\r\n                                    LayoutCachedTop =4320\r\n                                    LayoutCachedWidth =8820\r\n                                    LayoutCachedHeight =4635\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =223\r\n                                            Left =5040\r\n                                            Top =4320\r\n                                            Width =1980\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label239\"\r\n                                            Caption =\"File Diff Tool:\"\r\n                                            LayoutCachedLeft =5040\r\n                                            LayoutCachedTop =4320\r\n                                            LayoutCachedWidth =7020\r\n                                            LayoutCachedHeight =4635\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin ComboBox\r\n                                    RowSourceTypeInt =1\r\n                                    OverlapFlags =223\r\n                                    IMESentenceMode =3\r\n                                    ColumnCount =2\r\n                                    ListWidth =1440\r\n                                    Left =7080\r\n                                    Top =4740\r\n                                    Width =1740\r\n                                    Height =315\r\n                                    TabIndex =9\r\n                                    Name =\"cboOpenRepository\"\r\n                                    RowSourceType =\"Value List\"\r\n                                    RowSource =\"0;\\\"\\\";1;\\\"Github Desktop\\\";2;\\\"VSCode\\\";3;\\\"Sourcetree\\\";4;\\\"Tortoise Git\\\"\"\r\n                                    ColumnWidths =\"0\"\r\n                                    AllowValueListEdits =0\r\n\r\n                                    LayoutCachedLeft =7080\r\n                                    LayoutCachedTop =4740\r\n                                    LayoutCachedWidth =8820\r\n                                    LayoutCachedHeight =5055\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =223\r\n                                            Left =5040\r\n                                            Top =4740\r\n                                            Width =1980\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label305\"\r\n                                            Caption =\"Open Repository:\"\r\n                                            LayoutCachedLeft =5040\r\n                                            LayoutCachedTop =4740\r\n                                            LayoutCachedWidth =7020\r\n                                            LayoutCachedHeight =5055\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin ComboBox\r\n                                    LimitToList = NotDefault\r\n                                    OverlapFlags =223\r\n                                    IMESentenceMode =3\r\n                                    ColumnCount =2\r\n                                    ListWidth =2880\r\n                                    Left =7080\r\n                                    Top =5160\r\n                                    Width =1740\r\n                                    Height =315\r\n                                    TabIndex =7\r\n                                    Name =\"cboLanguage\"\r\n                                    RowSourceType =\"Table/Query\"\r\n                                    RowSource =\"SELECT tblLanguages.ID, tblLanguages.DisplayName FROM tblLanguages; \"\r\n                                    ColumnWidths =\"0\"\r\n                                    DefaultValue =\"\\\"en_US\\\"\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    AllowValueListEdits =0\r\n\r\n                                    LayoutCachedLeft =7080\r\n                                    LayoutCachedTop =5160\r\n                                    LayoutCachedWidth =8820\r\n                                    LayoutCachedHeight =5475\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =223\r\n                                            Left =5040\r\n                                            Top =5160\r\n                                            Width =1980\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label252\"\r\n                                            Caption =\"Language:\"\r\n                                            LayoutCachedLeft =5040\r\n                                            LayoutCachedTop =5160\r\n                                            LayoutCachedWidth =7020\r\n                                            LayoutCachedHeight =5475\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    FontUnderline = NotDefault\r\n                                    TabStop = NotDefault\r\n                                    OverlapFlags =223\r\n                                    Left =7320\r\n                                    Top =5520\r\n                                    Width =1500\r\n                                    Height =240\r\n                                    FontSize =10\r\n                                    TabIndex =8\r\n                                    Name =\"cmdTranslations\"\r\n                                    Caption =\"Translations...\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    BackStyle =0\r\n\r\n                                    CursorOnHover =1\r\n                                    LayoutCachedLeft =7320\r\n                                    LayoutCachedTop =5520\r\n                                    LayoutCachedWidth =8820\r\n                                    LayoutCachedHeight =5760\r\n                                    Alignment =3\r\n                                    ForeThemeColorIndex =10\r\n                                    ForeTint =100.0\r\n                                    Gradient =0\r\n                                    BackColor =5324600\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    OldBorderStyle =0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverThemeColorIndex =10\r\n                                    HoverTint =100.0\r\n                                    PressedThemeColorIndex =10\r\n                                    PressedShade =100.0\r\n                                    HoverForeThemeColorIndex =10\r\n                                    HoverForeTint =100.0\r\n                                    PressedForeThemeColorIndex =10\r\n                                    PressedForeTint =100.0\r\n                                End\r\n                                Begin OptionGroup\r\n                                    OverlapFlags =255\r\n                                    Left =4620\r\n                                    Top =3900\r\n                                    Width =4500\r\n                                    Height =2040\r\n                                    TabIndex =10\r\n                                    Name =\"Frame307\"\r\n\r\n                                    LayoutCachedLeft =4620\r\n                                    LayoutCachedTop =3900\r\n                                    LayoutCachedWidth =9120\r\n                                    LayoutCachedHeight =5940\r\n                                    Begin\r\n                                        Begin Label\r\n                                            BackStyle =1\r\n                                            OverlapFlags =247\r\n                                            Left =4740\r\n                                            Top =3780\r\n                                            Width =1920\r\n                                            Height =315\r\n                                            BackColor =15130848\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label308\"\r\n                                            Caption =\" Developer Settings\"\r\n                                            LayoutCachedLeft =4740\r\n                                            LayoutCachedTop =3780\r\n                                            LayoutCachedWidth =6660\r\n                                            LayoutCachedHeight =4095\r\n                                            BackThemeColorIndex =-1\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    Visible = NotDefault\r\n                                    OverlapFlags =215\r\n                                    Left =6060\r\n                                    Top =2100\r\n                                    TabIndex =4\r\n                                    Name =\"chkUseGitIntegration\"\r\n\r\n                                    LayoutCachedLeft =6060\r\n                                    LayoutCachedTop =2100\r\n                                    LayoutCachedWidth =6320\r\n                                    LayoutCachedHeight =2340\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =223\r\n                                            Left =6360\r\n                                            Top =2040\r\n                                            Width =2640\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label163\"\r\n                                            Caption =\"Use Git Integration\"\r\n                                            LayoutCachedLeft =6360\r\n                                            LayoutCachedTop =2040\r\n                                            LayoutCachedWidth =9000\r\n                                            LayoutCachedHeight =2355\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin Line\r\n                                    LineSlant = NotDefault\r\n                                    BorderWidth =1\r\n                                    OverlapFlags =87\r\n                                    Left =960\r\n                                    Top =3600\r\n                                    Width =8160\r\n                                    Name =\"Line226\"\r\n                                    HorizontalAnchor =2\r\n                                    LayoutCachedLeft =960\r\n                                    LayoutCachedTop =3600\r\n                                    LayoutCachedWidth =9120\r\n                                    LayoutCachedHeight =3600\r\n                                    BorderThemeColorIndex =1\r\n                                    BorderShade =65.0\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =215\r\n                                    Left =1020\r\n                                    Top =2340\r\n                                    Width =8160\r\n                                    Height =1095\r\n                                    ForeColor =5324600\r\n                                    Name =\"Label227\"\r\n                                    Caption =\"Use this form to set your preferred options for exporting and building your data\"\r\n                                        \"base project to and from source files. Note that you can have different options \"\r\n                                        \"for different projects, and can save a set of options as default for new project\"\r\n                                        \"s.\"\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =2340\r\n                                    LayoutCachedWidth =9180\r\n                                    LayoutCachedHeight =3435\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =4740\r\n                                    Top =5640\r\n                                    Width =1800\r\n                                    Height =240\r\n                                    FontSize =10\r\n                                    Name =\"Label309\"\r\n                                    Caption =\"(this machine only)\"\r\n                                    LayoutCachedLeft =4740\r\n                                    LayoutCachedTop =5640\r\n                                    LayoutCachedWidth =6540\r\n                                    LayoutCachedHeight =5880\r\n                                End\r\n                            End\r\n                        End\r\n                        Begin Page\r\n                            Visible = NotDefault\r\n                            OverlapFlags =247\r\n                            Left =600\r\n                            Top =1980\r\n                            Width =8865\r\n                            Height =4290\r\n                            Name =\"pgeTranslations\"\r\n                            Caption =\"Translation\"\r\n                            LayoutCachedLeft =600\r\n                            LayoutCachedTop =1980\r\n                            LayoutCachedWidth =9465\r\n                            LayoutCachedHeight =6270\r\n                            Begin\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1200\r\n                                    Top =2395\r\n                                    Name =\"chkContributeTranslations\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n\r\n                                    LayoutCachedLeft =1200\r\n                                    LayoutCachedTop =2395\r\n                                    LayoutCachedWidth =1460\r\n                                    LayoutCachedHeight =2635\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1500\r\n                                            Top =2340\r\n                                            Width =1980\r\n                                            Height =600\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label256\"\r\n                                            Caption =\"Contribute to Translations\"\r\n                                            LayoutCachedLeft =1500\r\n                                            LayoutCachedTop =2340\r\n                                            LayoutCachedWidth =3480\r\n                                            LayoutCachedHeight =2940\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =3720\r\n                                    Top =2340\r\n                                    Width =5040\r\n                                    Height =825\r\n                                    ForeColor =5324600\r\n                                    Name =\"lblTranslationExplanation\"\r\n                                    Caption =\"Use this page to manage language translations for this add-in. Check this box if\"\r\n                                        \" you would like to help improve the translations.\"\r\n                                    HorizontalAnchor =2\r\n                                    LayoutCachedLeft =3720\r\n                                    LayoutCachedTop =2340\r\n                                    LayoutCachedWidth =8760\r\n                                    LayoutCachedHeight =3165\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin Line\r\n                                    LineSlant = NotDefault\r\n                                    BorderWidth =1\r\n                                    OverlapFlags =119\r\n                                    Left =960\r\n                                    Top =3360\r\n                                    Width =8160\r\n                                    Name =\"Line296\"\r\n                                    HorizontalAnchor =2\r\n                                    LayoutCachedLeft =960\r\n                                    LayoutCachedTop =3360\r\n                                    LayoutCachedWidth =9120\r\n                                    LayoutCachedHeight =3360\r\n                                    BorderThemeColorIndex =1\r\n                                    BorderShade =65.0\r\n                                End\r\n                                Begin TextBox\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    Left =2940\r\n                                    Top =4020\r\n                                    Width =5400\r\n                                    Height =315\r\n                                    TabIndex =1\r\n                                    Name =\"txtTranslationsPath\"\r\n\r\n                                    LayoutCachedLeft =2940\r\n                                    LayoutCachedTop =4020\r\n                                    LayoutCachedWidth =8340\r\n                                    LayoutCachedHeight =4335\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1080\r\n                                            Top =4020\r\n                                            Width =1740\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label297\"\r\n                                            Caption =\"Translations Path: \"\r\n                                            LayoutCachedLeft =1080\r\n                                            LayoutCachedTop =4020\r\n                                            LayoutCachedWidth =2820\r\n                                            LayoutCachedHeight =4335\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =8400\r\n                                    Top =4020\r\n                                    Width =360\r\n                                    Height =300\r\n                                    TabIndex =2\r\n                                    Name =\"cmdBrowseTranslationsPath\"\r\n                                    Caption =\"...\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n\r\n                                    LayoutCachedLeft =8400\r\n                                    LayoutCachedTop =4020\r\n                                    LayoutCachedWidth =8760\r\n                                    LayoutCachedHeight =4320\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =1080\r\n                                    Top =4860\r\n                                    Width =2340\r\n                                    Height =660\r\n                                    TabIndex =3\r\n                                    Name =\"cmdSyncTranslations\"\r\n                                    Caption =\" Sync Files\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    HorizontalAnchor =1\r\n                                    VerticalAnchor =1\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x000000000000000000000000000000000000000000000000a08070ff604830ff ,\r\n                                        0x604830ff604830ff604830ff604830ff604830ff604830ff604830ff604830ff ,\r\n                                        0x604830ff000000000000000000000000a08070ff604830ffa08070ffffffffff ,\r\n                                        0xb0a090ffb0a090ffb0a090ffb0a090ffb0a090ffb0a090ffb0a090ffb0a090ff ,\r\n                                        0x604830ff00000000a08070ff604830ffa08070ffffffffffa08070ffffffffff ,\r\n                                        0xfffffffffff8fffff0f0f0fff0e8e0fff0e0d0ffe0d0d0ffe0c8c0ffb0a090ff ,\r\n                                        0x604830ff00000000a08070ffffffffffa08070ffffffffffa08070ffffffffff ,\r\n                                        0xffffffffd0f0e0ff106850fff0f0f0fff0e0e0fff0d8d0ffe0d0c0ffb0a090ff ,\r\n                                        0x604830ff00000000a08070ffffffffffa08070ffffffffffa08070ffffffffff ,\r\n                                        0xffffffff209870ff209870ff209870ff209870ffc0c8c0ffe0d8d0ffb0a090ff ,\r\n                                        0x604830ff00000000a08070ffffffffffa08070ffffffffffa08870ffffffffff ,\r\n                                        0xffffffffe0f0f0ff209870fffff8f0ffc0e0d0ff209870fff0d8d0ffb0a090ff ,\r\n                                        0x604830ff00000000a08070ffffffffffa08870ffffffffffa08880ffffffffff ,\r\n                                        0xfffffffffffffffffffffffffffffffffff8f0ff209870fff0e0e0ffb0a090ff ,\r\n                                        0x604830ff00000000a08870ffffffffffa08880ffffffffffb09080ffffffffff ,\r\n                                        0xffffffff209870fffffffffffffffffffff8fffff0f0f0fff0e8e0ffb0a090ff ,\r\n                                        0x604830ff00000000a08880ffffffffffb09080ffffffffffb09080ffffffffff ,\r\n                                        0xffffffff209870ffb0d8c0ffffffffff107850ffd0e0e0fff0f0f0ffb0a090ff ,\r\n                                        0x604830ff00000000b09080ffffffffffb09080ffffffffffb09880ffffffffff ,\r\n                                        0xffffffffd0e8e0ff209870ff209870ff209870ff107850ffd0b8b0ffb0a090ff ,\r\n                                        0x604830ff00000000b09080ffffffffffb09880ffffffffffb09880ffffffffff ,\r\n                                        0xffffffffffffffffffffffffffffffff209870ffd0d8d0ffa09080ff605040ff ,\r\n                                        0x604830ff00000000b09880ffffffffffb09880ffffffffffb0a090ffffffffff ,\r\n                                        0xffffffffffffffffffffffffffffffffffffffffd0b8b0ffd0c8c0ff604830ff ,\r\n                                        0xd0b0a09000000000b09880ffffffffffb0a090ffffffffffc0a090ffffffffff ,\r\n                                        0xffffffffffffffffffffffffffffffffffffffffc0a8a0ff604830ffd0b0a090 ,\r\n                                        0x0000000000000000b0a090ffffffffffc0a090ffffffffffc0a090ffd0c0b0ff ,\r\n                                        0xd0c0b0ffd0c0b0ffd0b8b0ffd0b8a0ffc0b0a0ffc0a090ffd0b0a09000000000 ,\r\n                                        0x0000000000000000c0a090ffffffffffc0a090ffe0c8b0ffe0c8c0ffe0d0c0ff ,\r\n                                        0xe0d0c0ffe0d0c0ffe0d0c0ffd0b8b0ffd0b0a090000000000000000000000000 ,\r\n                                        0x0000000000000000b09890ffd0c0b0ffd0c0b0ffd0c0b0ffd0c0b0ffd0c0b0ff ,\r\n                                        0xd0b8b0ffc0b0a0ffd0b0a0900000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =1080\r\n                                    LayoutCachedTop =4860\r\n                                    LayoutCachedWidth =3420\r\n                                    LayoutCachedHeight =5520\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =3900\r\n                                            Top =4680\r\n                                            Width =5100\r\n                                            Height =1095\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label303\"\r\n                                            Caption =\"Click the \\\"Sync Files\\\" button to load the updated translations into the add-in\"\r\n                                                \", and then update the master template and language files with the latest strings\"\r\n                                                \" from the database.\"\r\n                                            HorizontalAnchor =2\r\n                                            LayoutCachedLeft =3900\r\n                                            LayoutCachedTop =4680\r\n                                            LayoutCachedWidth =9000\r\n                                            LayoutCachedHeight =5775\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =1080\r\n                                    Top =3540\r\n                                    Width =7500\r\n                                    Height =300\r\n                                    ForeColor =5324600\r\n                                    Name =\"lblWorkingPathExplanation\"\r\n                                    Caption =\"Specify the path for the working language files.\"\r\n                                    HorizontalAnchor =2\r\n                                    LayoutCachedLeft =1080\r\n                                    LayoutCachedTop =3540\r\n                                    LayoutCachedWidth =8580\r\n                                    LayoutCachedHeight =3840\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                            End\r\n                        End\r\n                        Begin Page\r\n                            OverlapFlags =247\r\n                            Left =600\r\n                            Top =1980\r\n                            Width =8865\r\n                            Height =4290\r\n                            Name =\"pgeExport\"\r\n                            Caption =\"Export\"\r\n                            LayoutCachedLeft =600\r\n                            LayoutCachedTop =1980\r\n                            LayoutCachedWidth =9465\r\n                            LayoutCachedHeight =6270\r\n                            Begin\r\n                                Begin TextBox\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    Left =2460\r\n                                    Top =2340\r\n                                    Width =3420\r\n                                    Height =315\r\n                                    Name =\"txtExportFolder\"\r\n                                    BeforeUpdate =\"[Event Procedure]\"\r\n\r\n                                    LayoutCachedLeft =2460\r\n                                    LayoutCachedTop =2340\r\n                                    LayoutCachedWidth =5880\r\n                                    LayoutCachedHeight =2655\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =960\r\n                                            Top =2340\r\n                                            Width =1380\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label31\"\r\n                                            Caption =\"Export Folder:\"\r\n                                            LayoutCachedLeft =960\r\n                                            LayoutCachedTop =2340\r\n                                            LayoutCachedWidth =2340\r\n                                            LayoutCachedHeight =2655\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1020\r\n                                    Top =2940\r\n                                    TabIndex =1\r\n                                    Name =\"chkUseFastSave\"\r\n\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =2940\r\n                                    LayoutCachedWidth =1280\r\n                                    LayoutCachedHeight =3180\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1320\r\n                                            Top =2880\r\n                                            Width =3420\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label25\"\r\n                                            Caption =\"Use Fast Save\"\r\n                                            LayoutCachedLeft =1320\r\n                                            LayoutCachedTop =2880\r\n                                            LayoutCachedWidth =4740\r\n                                            LayoutCachedHeight =3195\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1020\r\n                                    Top =3360\r\n                                    TabIndex =4\r\n                                    Name =\"chkExtractThemeFiles\"\r\n\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =3360\r\n                                    LayoutCachedWidth =1280\r\n                                    LayoutCachedHeight =3600\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1320\r\n                                            Top =3300\r\n                                            Width =3420\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label112\"\r\n                                            Caption =\"Extract Theme Files\"\r\n                                            LayoutCachedLeft =1320\r\n                                            LayoutCachedTop =3300\r\n                                            LayoutCachedWidth =4740\r\n                                            LayoutCachedHeight =3615\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin ComboBox\r\n                                    LimitToList = NotDefault\r\n                                    RowSourceTypeInt =1\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    ColumnCount =2\r\n                                    Left =2760\r\n                                    Top =3720\r\n                                    Width =1980\r\n                                    Height =315\r\n                                    TabIndex =2\r\n                                    Name =\"cboSanitizeLevel\"\r\n                                    RowSourceType =\"Value List\"\r\n                                    ColumnWidths =\"0\"\r\n                                    AllowValueListEdits =0\r\n\r\n                                    LayoutCachedLeft =2760\r\n                                    LayoutCachedTop =3720\r\n                                    LayoutCachedWidth =4740\r\n                                    LayoutCachedHeight =4035\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1020\r\n                                            Top =3720\r\n                                            Width =1560\r\n                                            Height =305\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label235\"\r\n                                            Caption =\"Sanitize Level\"\r\n                                            LayoutCachedLeft =1020\r\n                                            LayoutCachedTop =3720\r\n                                            LayoutCachedWidth =2580\r\n                                            LayoutCachedHeight =4025\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin ComboBox\r\n                                    RowSourceTypeInt =1\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    ColumnCount =2\r\n                                    Left =2760\r\n                                    Top =4140\r\n                                    Width =1980\r\n                                    Height =315\r\n                                    TabIndex =3\r\n                                    Name =\"cboSanitizeColors\"\r\n                                    RowSourceType =\"Value List\"\r\n                                    ColumnWidths =\"0\"\r\n                                    AllowValueListEdits =0\r\n\r\n                                    LayoutCachedLeft =2760\r\n                                    LayoutCachedTop =4140\r\n                                    LayoutCachedWidth =4740\r\n                                    LayoutCachedHeight =4455\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1020\r\n                                            Top =4140\r\n                                            Width =1560\r\n                                            Height =305\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label233\"\r\n                                            Caption =\"Sanitize Colors\"\r\n                                            LayoutCachedLeft =1020\r\n                                            LayoutCachedTop =4140\r\n                                            LayoutCachedWidth =2580\r\n                                            LayoutCachedHeight =4445\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =5340\r\n                                    Top =2935\r\n                                    TabIndex =5\r\n                                    Name =\"chkSavePrintVars\"\r\n\r\n                                    LayoutCachedLeft =5340\r\n                                    LayoutCachedTop =2935\r\n                                    LayoutCachedWidth =5600\r\n                                    LayoutCachedHeight =3175\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =5640\r\n                                            Top =2880\r\n                                            Width =2400\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label27\"\r\n                                            Caption =\"Save Printer Settings\"\r\n                                            LayoutCachedLeft =5640\r\n                                            LayoutCachedTop =2880\r\n                                            LayoutCachedWidth =8040\r\n                                            LayoutCachedHeight =3195\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =5340\r\n                                    Top =3360\r\n                                    TabIndex =6\r\n                                    Name =\"chkSaveQuerySQL\"\r\n\r\n                                    LayoutCachedLeft =5340\r\n                                    LayoutCachedTop =3360\r\n                                    LayoutCachedWidth =5600\r\n                                    LayoutCachedHeight =3600\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =5640\r\n                                            Top =3300\r\n                                            Width =3540\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label29\"\r\n                                            Caption =\"Save Query SQL\"\r\n                                            LayoutCachedLeft =5640\r\n                                            LayoutCachedTop =3300\r\n                                            LayoutCachedWidth =9180\r\n                                            LayoutCachedHeight =3615\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =5340\r\n                                    Top =3780\r\n                                    TabIndex =7\r\n                                    Name =\"chkSaveTableSQL\"\r\n\r\n                                    LayoutCachedLeft =5340\r\n                                    LayoutCachedTop =3780\r\n                                    LayoutCachedWidth =5600\r\n                                    LayoutCachedHeight =4020\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =5640\r\n                                            Top =3720\r\n                                            Width =3540\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label38\"\r\n                                            Caption =\"Save Table SQL\"\r\n                                            LayoutCachedLeft =5640\r\n                                            LayoutCachedTop =3720\r\n                                            LayoutCachedWidth =9180\r\n                                            LayoutCachedHeight =4035\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =5340\r\n                                    Top =4200\r\n                                    TabIndex =8\r\n                                    Name =\"chkFormatSQL\"\r\n\r\n                                    LayoutCachedLeft =5340\r\n                                    LayoutCachedTop =4200\r\n                                    LayoutCachedWidth =5600\r\n                                    LayoutCachedHeight =4440\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =5640\r\n                                            Top =4140\r\n                                            Width =3540\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label249\"\r\n                                            Caption =\"Format SQL\"\r\n                                            LayoutCachedLeft =5640\r\n                                            LayoutCachedTop =4140\r\n                                            LayoutCachedWidth =9180\r\n                                            LayoutCachedHeight =4455\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =5340\r\n                                    Top =4620\r\n                                    TabIndex =9\r\n                                    Name =\"chkSplitLayoutFromVBA\"\r\n\r\n                                    LayoutCachedLeft =5340\r\n                                    LayoutCachedTop =4620\r\n                                    LayoutCachedWidth =5600\r\n                                    LayoutCachedHeight =4860\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =5640\r\n                                            Top =4560\r\n                                            Width =3540\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label251\"\r\n                                            Caption =\"Split Layout from VBA\"\r\n                                            LayoutCachedLeft =5640\r\n                                            LayoutCachedTop =4560\r\n                                            LayoutCachedWidth =9180\r\n                                            LayoutCachedHeight =4875\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin TextBox\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    Left =3540\r\n                                    Top =5100\r\n                                    Width =2700\r\n                                    Height =315\r\n                                    TabIndex =10\r\n                                    Name =\"txtRunBeforeExport\"\r\n\r\n                                    LayoutCachedLeft =3540\r\n                                    LayoutCachedTop =5100\r\n                                    LayoutCachedWidth =6240\r\n                                    LayoutCachedHeight =5415\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1020\r\n                                            Top =5100\r\n                                            Width =2460\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label44\"\r\n                                            Caption =\"Run Sub Before Export:\"\r\n                                            LayoutCachedLeft =1020\r\n                                            LayoutCachedTop =5100\r\n                                            LayoutCachedWidth =3480\r\n                                            LayoutCachedHeight =5415\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin TextBox\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    Left =3540\r\n                                    Top =5520\r\n                                    Width =2700\r\n                                    Height =315\r\n                                    TabIndex =11\r\n                                    Name =\"txtRunAfterExport\"\r\n\r\n                                    LayoutCachedLeft =3540\r\n                                    LayoutCachedTop =5520\r\n                                    LayoutCachedWidth =6240\r\n                                    LayoutCachedHeight =5835\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1020\r\n                                            Top =5520\r\n                                            Width =2460\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label48\"\r\n                                            Caption =\"Run Sub After Export:\"\r\n                                            LayoutCachedLeft =1020\r\n                                            LayoutCachedTop =5520\r\n                                            LayoutCachedWidth =3480\r\n                                            LayoutCachedHeight =5835\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    FontUnderline = NotDefault\r\n                                    OverlapFlags =247\r\n                                    Left =7140\r\n                                    Top =5640\r\n                                    Width =2160\r\n                                    TabIndex =12\r\n                                    Name =\"cmdExplainOptions\"\r\n                                    Caption =\"Explain options...\"\r\n                                    HyperlinkAddress =\"https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Documentation#options\"\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x000000000000000000000000000000000000000000000000e0e8e000e0c8b000 ,\r\n                                        0xe0d8d000e0d0c010e0d0c010d0d0c010d0d0c000d0d0d000e0e0e00000000000 ,\r\n                                        0x0000000000000000000000000000000000000000f0e8e0009068303080582080 ,\r\n                                        0x905010c0804820e0804820c0804810b06040108050381030d0c8c01000000000 ,\r\n                                        0x000000000000000000000000e0780000e0a05010a0683070c08860f0e0c8b0ff ,\r\n                                        0xf0f0f0fffffffffffffffffff0f0f0ffe0c8c0ffa07850c040301060d0c8c010 ,\r\n                                        0xe0d8d0000000000000000000e0882000b0703070e0a880fffff0e0ffe0b8a0ff ,\r\n                                        0xd08050ffc05820ffc05820ffd08050ffe0b8a0fff0e8e0ffb09070f050301060 ,\r\n                                        0xd0c8c000e0e0e00000000000b0783030d09870f0fff0e0ffe0a890ffc05010ff ,\r\n                                        0xc05010ffe0a890ffffffffffb04810ffb04810ffd0a080fff0f0e0ffa07050d0 ,\r\n                                        0x50381030d0d0d000f0f0f000b0784080f0d8c0fff0c8b0ffe05820ffd05810ff ,\r\n                                        0xd05010ffe08050ffe0a880ffc05010ffb04810ffb04810ffe0b8a0ffe0c8c0ff ,\r\n                                        0x50401080d0d0d010f0f0f000d08040e0fff8f0fff09870fff06020ffe05820ff ,\r\n                                        0xe05820fff0a890ffffffffffd05010ffc05010ffb05010ffc07850fff0f0f0ff ,\r\n                                        0x804020c0e0d0c000f0f0f000d08040f0ffffffffff7840ffff6830fff06820ff ,\r\n                                        0xf06020fff08850fffffffffff0c0b0ffc05820ffb05010ffb05820ffffffffff ,\r\n                                        0x804820e0e0d0c010f0f0f000d08850f0ffffffffff8050ffff7030ffff6830ff ,\r\n                                        0xff6830ffff6820fff09060fffff8f0fff0d8c0ffc05020ffc05820ffffffffff ,\r\n                                        0x804820e0e0d8d010f0f0f000d08050c0fff8f0ffffa880ffff7040ffff8850ff ,\r\n                                        0xffb090ffff7030fff06820fff09070fffffffffff08050ffd08860fffff0f0ff ,\r\n                                        0x805820b0e0d8d010f0f0f000c0804070f0d8c0ffffd0c0ffff7840ffff9870ff ,\r\n                                        0xffffffffffc8b0ffff9060ffffc8b0fffff8f0fff07840fff0c8b0ffe0c8b0ff ,\r\n                                        0x90602070e0c8b00000000000c0884030e0a070f0fff8f0ffffc0a0ffff7840ff ,\r\n                                        0xffb8a0fffff8f0fffffffffffff0e0ffff9870fff0b8a0fffff0e0ffc08850e0 ,\r\n                                        0xa0682030f0e8e0000000000000000000c0884060e0b8a0f0fff8f0ffffd0c0ff ,\r\n                                        0xffa880ffff8850ffff8850ffffa880fff0d0c0fffff0e0ffd0a880f0a0683060 ,\r\n                                        0xe0c0a00000000000000000000000000000000000c0884060e0a070f0f0d8c0ff ,\r\n                                        0xfff8f0fffffffffffffffffffff8f0fff0d8c0ffc09060e0a0703050f0b89000 ,\r\n                                        0x0000000000000000000000000000000000000000f0f0f000c0884030c0804070 ,\r\n                                        0xe0a070c0d09870e0d09860f0d09870d0b0784070b0784020f0e8f00000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0xf0f0f000f0f0f000f0f0f000f0f0f000f0f0f00000000000f0f0f00000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n                                    BackStyle =0\r\n\r\n                                    LayoutCachedLeft =7140\r\n                                    LayoutCachedTop =5640\r\n                                    LayoutCachedWidth =9300\r\n                                    LayoutCachedHeight =6000\r\n                                    PictureCaptionArrangement =4\r\n                                    ForeThemeColorIndex =10\r\n                                    ForeTint =100.0\r\n                                    Gradient =0\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    OldBorderStyle =0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                End\r\n                                Begin CommandButton\r\n                                    FontUnderline = NotDefault\r\n                                    TabStop = NotDefault\r\n                                    OverlapFlags =247\r\n                                    Left =8100\r\n                                    Top =2880\r\n                                    Width =1140\r\n                                    Height =240\r\n                                    FontSize =10\r\n                                    TabIndex =13\r\n                                    Name =\"cmdPrintSettingsOptions\"\r\n                                    Caption =\"Options...\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    BackStyle =0\r\n\r\n                                    CursorOnHover =1\r\n                                    LayoutCachedLeft =8100\r\n                                    LayoutCachedTop =2880\r\n                                    LayoutCachedWidth =9240\r\n                                    LayoutCachedHeight =3120\r\n                                    Alignment =1\r\n                                    ForeThemeColorIndex =10\r\n                                    ForeTint =100.0\r\n                                    Gradient =0\r\n                                    BackColor =5324600\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    OldBorderStyle =0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverThemeColorIndex =10\r\n                                    HoverTint =100.0\r\n                                    PressedThemeColorIndex =10\r\n                                    PressedShade =100.0\r\n                                    HoverForeThemeColorIndex =10\r\n                                    HoverForeTint =100.0\r\n                                    PressedForeThemeColorIndex =10\r\n                                    PressedForeTint =100.0\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =6120\r\n                                    Top =2400\r\n                                    Width =3000\r\n                                    Height =240\r\n                                    FontSize =10\r\n                                    Name =\"Label46\"\r\n                                    Caption =\"(Blank for default)\"\r\n                                    LayoutCachedLeft =6120\r\n                                    LayoutCachedTop =2400\r\n                                    LayoutCachedWidth =9120\r\n                                    LayoutCachedHeight =2640\r\n                                End\r\n                            End\r\n                        End\r\n                        Begin Page\r\n                            Visible = NotDefault\r\n                            OverlapFlags =247\r\n                            Left =600\r\n                            Top =1980\r\n                            Width =8865\r\n                            Height =4290\r\n                            Name =\"pgePrinterSettings\"\r\n                            Caption =\"Printer Settings\"\r\n                            LayoutCachedLeft =600\r\n                            LayoutCachedTop =1980\r\n                            LayoutCachedWidth =9465\r\n                            LayoutCachedHeight =6270\r\n                            Begin\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1200\r\n                                    Top =2395\r\n                                    Name =\"chkOrientation\"\r\n\r\n                                    LayoutCachedLeft =1200\r\n                                    LayoutCachedTop =2395\r\n                                    LayoutCachedWidth =1460\r\n                                    LayoutCachedHeight =2635\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1500\r\n                                            Top =2340\r\n                                            Width =1920\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label115\"\r\n                                            Caption =\"Page Orientation\"\r\n                                            LayoutCachedLeft =1500\r\n                                            LayoutCachedTop =2340\r\n                                            LayoutCachedWidth =3420\r\n                                            LayoutCachedHeight =2655\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1200\r\n                                    Top =2755\r\n                                    TabIndex =1\r\n                                    Name =\"chkPaperSize\"\r\n\r\n                                    LayoutCachedLeft =1200\r\n                                    LayoutCachedTop =2755\r\n                                    LayoutCachedWidth =1460\r\n                                    LayoutCachedHeight =2995\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1500\r\n                                            Top =2700\r\n                                            Width =1920\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label117\"\r\n                                            Caption =\"Paper Size\"\r\n                                            LayoutCachedLeft =1500\r\n                                            LayoutCachedTop =2700\r\n                                            LayoutCachedWidth =3420\r\n                                            LayoutCachedHeight =3015\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1200\r\n                                    Top =3775\r\n                                    TabIndex =2\r\n                                    Name =\"chkDuplex\"\r\n\r\n                                    LayoutCachedLeft =1200\r\n                                    LayoutCachedTop =3775\r\n                                    LayoutCachedWidth =1460\r\n                                    LayoutCachedHeight =4015\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1500\r\n                                            Top =3720\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label130\"\r\n                                            Caption =\"Duplex\"\r\n                                            LayoutCachedLeft =1500\r\n                                            LayoutCachedTop =3720\r\n                                            LayoutCachedWidth =2940\r\n                                            LayoutCachedHeight =4035\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =3780\r\n                                    Top =3775\r\n                                    TabIndex =3\r\n                                    Name =\"chkPrintQuality\"\r\n\r\n                                    LayoutCachedLeft =3780\r\n                                    LayoutCachedTop =3775\r\n                                    LayoutCachedWidth =4040\r\n                                    LayoutCachedHeight =4015\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =4080\r\n                                            Top =3720\r\n                                            Width =1620\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label128\"\r\n                                            Caption =\"Print Quality\"\r\n                                            LayoutCachedLeft =4080\r\n                                            LayoutCachedTop =3720\r\n                                            LayoutCachedWidth =5700\r\n                                            LayoutCachedHeight =4035\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =7020\r\n                                    Top =3775\r\n                                    TabIndex =4\r\n                                    Name =\"chkDisplayFrequency\"\r\n\r\n                                    LayoutCachedLeft =7020\r\n                                    LayoutCachedTop =3775\r\n                                    LayoutCachedWidth =7280\r\n                                    LayoutCachedHeight =4015\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =7320\r\n                                            Top =3720\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label148\"\r\n                                            Caption =\"Display Freq.\"\r\n                                            LayoutCachedLeft =7320\r\n                                            LayoutCachedTop =3720\r\n                                            LayoutCachedWidth =8760\r\n                                            LayoutCachedHeight =4035\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1200\r\n                                    Top =4135\r\n                                    TabIndex =5\r\n                                    Name =\"chkCollate\"\r\n\r\n                                    LayoutCachedLeft =1200\r\n                                    LayoutCachedTop =4135\r\n                                    LayoutCachedWidth =1460\r\n                                    LayoutCachedHeight =4375\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1500\r\n                                            Top =4080\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label134\"\r\n                                            Caption =\"Collate\"\r\n                                            LayoutCachedLeft =1500\r\n                                            LayoutCachedTop =4080\r\n                                            LayoutCachedWidth =2940\r\n                                            LayoutCachedHeight =4395\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =3780\r\n                                    Top =4135\r\n                                    TabIndex =6\r\n                                    Name =\"chkResolution\"\r\n\r\n                                    LayoutCachedLeft =3780\r\n                                    LayoutCachedTop =4135\r\n                                    LayoutCachedWidth =4040\r\n                                    LayoutCachedHeight =4375\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =4080\r\n                                            Top =4080\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label132\"\r\n                                            Caption =\"Resolution\"\r\n                                            LayoutCachedLeft =4080\r\n                                            LayoutCachedTop =4080\r\n                                            LayoutCachedWidth =5520\r\n                                            LayoutCachedHeight =4395\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =7020\r\n                                    Top =4135\r\n                                    TabIndex =7\r\n                                    Name =\"chkDisplayFlags\"\r\n\r\n                                    LayoutCachedLeft =7020\r\n                                    LayoutCachedTop =4135\r\n                                    LayoutCachedWidth =7280\r\n                                    LayoutCachedHeight =4375\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =7320\r\n                                            Top =4080\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label146\"\r\n                                            Caption =\"Display Flags\"\r\n                                            LayoutCachedLeft =7320\r\n                                            LayoutCachedTop =4080\r\n                                            LayoutCachedWidth =8760\r\n                                            LayoutCachedHeight =4395\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1200\r\n                                    Top =4495\r\n                                    TabIndex =8\r\n                                    Name =\"chkColor\"\r\n\r\n                                    LayoutCachedLeft =1200\r\n                                    LayoutCachedTop =4495\r\n                                    LayoutCachedWidth =1460\r\n                                    LayoutCachedHeight =4735\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1500\r\n                                            Top =4440\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label144\"\r\n                                            Caption =\"Color or B/W\"\r\n                                            LayoutCachedLeft =1500\r\n                                            LayoutCachedTop =4440\r\n                                            LayoutCachedWidth =2940\r\n                                            LayoutCachedHeight =4755\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =3780\r\n                                    Top =4495\r\n                                    TabIndex =9\r\n                                    Name =\"chkCopies\"\r\n\r\n                                    LayoutCachedLeft =3780\r\n                                    LayoutCachedTop =4495\r\n                                    LayoutCachedWidth =4040\r\n                                    LayoutCachedHeight =4735\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =4080\r\n                                            Top =4440\r\n                                            Width =1800\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label123\"\r\n                                            Caption =\"Number of Copies\"\r\n                                            LayoutCachedLeft =4080\r\n                                            LayoutCachedTop =4440\r\n                                            LayoutCachedWidth =5880\r\n                                            LayoutCachedHeight =4755\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =7020\r\n                                    Top =4495\r\n                                    TabIndex =10\r\n                                    Name =\"chkICMMethod\"\r\n\r\n                                    LayoutCachedLeft =7020\r\n                                    LayoutCachedTop =4495\r\n                                    LayoutCachedWidth =7280\r\n                                    LayoutCachedHeight =4735\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =7320\r\n                                            Top =4440\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label150\"\r\n                                            Caption =\"ICM Method\"\r\n                                            LayoutCachedLeft =7320\r\n                                            LayoutCachedTop =4440\r\n                                            LayoutCachedWidth =8760\r\n                                            LayoutCachedHeight =4755\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1200\r\n                                    Top =4855\r\n                                    TabIndex =11\r\n                                    Name =\"chkDefaultSource\"\r\n\r\n                                    LayoutCachedLeft =1200\r\n                                    LayoutCachedTop =4855\r\n                                    LayoutCachedWidth =1460\r\n                                    LayoutCachedHeight =5095\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1500\r\n                                            Top =4800\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label125\"\r\n                                            Caption =\"Paper Tray\"\r\n                                            LayoutCachedLeft =1500\r\n                                            LayoutCachedTop =4800\r\n                                            LayoutCachedWidth =2940\r\n                                            LayoutCachedHeight =5115\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =3780\r\n                                    Top =4855\r\n                                    TabIndex =12\r\n                                    Name =\"chkScale\"\r\n\r\n                                    LayoutCachedLeft =3780\r\n                                    LayoutCachedTop =4855\r\n                                    LayoutCachedWidth =4040\r\n                                    LayoutCachedHeight =5095\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =4080\r\n                                            Top =4800\r\n                                            Width =2220\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label121\"\r\n                                            Caption =\"Print Scale\"\r\n                                            LayoutCachedLeft =4080\r\n                                            LayoutCachedTop =4800\r\n                                            LayoutCachedWidth =6300\r\n                                            LayoutCachedHeight =5115\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =7020\r\n                                    Top =4855\r\n                                    TabIndex =13\r\n                                    Name =\"chkICMIntent\"\r\n\r\n                                    LayoutCachedLeft =7020\r\n                                    LayoutCachedTop =4855\r\n                                    LayoutCachedWidth =7280\r\n                                    LayoutCachedHeight =5095\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =7320\r\n                                            Top =4800\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label152\"\r\n                                            Caption =\"ICM Intent\"\r\n                                            LayoutCachedLeft =7320\r\n                                            LayoutCachedTop =4800\r\n                                            LayoutCachedWidth =8760\r\n                                            LayoutCachedHeight =5115\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1200\r\n                                    Top =5215\r\n                                    TabIndex =14\r\n                                    Name =\"chkFormName\"\r\n\r\n                                    LayoutCachedLeft =1200\r\n                                    LayoutCachedTop =5215\r\n                                    LayoutCachedWidth =1460\r\n                                    LayoutCachedHeight =5455\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1500\r\n                                            Top =5160\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label142\"\r\n                                            Caption =\"Form Name\"\r\n                                            LayoutCachedLeft =1500\r\n                                            LayoutCachedTop =5160\r\n                                            LayoutCachedWidth =2940\r\n                                            LayoutCachedHeight =5475\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =3780\r\n                                    Top =5215\r\n                                    TabIndex =15\r\n                                    Name =\"chkPaperLength\"\r\n\r\n                                    LayoutCachedLeft =3780\r\n                                    LayoutCachedTop =5215\r\n                                    LayoutCachedWidth =4040\r\n                                    LayoutCachedHeight =5455\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =4080\r\n                                            Top =5160\r\n                                            Width =2220\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label119\"\r\n                                            Caption =\"Custom Length\"\r\n                                            LayoutCachedLeft =4080\r\n                                            LayoutCachedTop =5160\r\n                                            LayoutCachedWidth =6300\r\n                                            LayoutCachedHeight =5475\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =7020\r\n                                    Top =5215\r\n                                    TabIndex =16\r\n                                    Name =\"chkDitherType\"\r\n\r\n                                    LayoutCachedLeft =7020\r\n                                    LayoutCachedTop =5215\r\n                                    LayoutCachedWidth =7280\r\n                                    LayoutCachedHeight =5455\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =7320\r\n                                            Top =5160\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label156\"\r\n                                            Caption =\"Dither Type\"\r\n                                            LayoutCachedLeft =7320\r\n                                            LayoutCachedTop =5160\r\n                                            LayoutCachedWidth =8760\r\n                                            LayoutCachedHeight =5475\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1200\r\n                                    Top =5575\r\n                                    TabIndex =17\r\n                                    Name =\"chkMediaType\"\r\n\r\n                                    LayoutCachedLeft =1200\r\n                                    LayoutCachedTop =5575\r\n                                    LayoutCachedWidth =1460\r\n                                    LayoutCachedHeight =5815\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1500\r\n                                            Top =5520\r\n                                            Width =1440\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label154\"\r\n                                            Caption =\"Media Type\"\r\n                                            LayoutCachedLeft =1500\r\n                                            LayoutCachedTop =5520\r\n                                            LayoutCachedWidth =2940\r\n                                            LayoutCachedHeight =5835\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =3780\r\n                                    Top =5575\r\n                                    TabIndex =18\r\n                                    Name =\"chkPaperWidth\"\r\n\r\n                                    LayoutCachedLeft =3780\r\n                                    LayoutCachedTop =5575\r\n                                    LayoutCachedWidth =4040\r\n                                    LayoutCachedHeight =5815\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =4080\r\n                                            Top =5520\r\n                                            Width =2220\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label158\"\r\n                                            Caption =\"Custom Width\"\r\n                                            LayoutCachedLeft =4080\r\n                                            LayoutCachedTop =5520\r\n                                            LayoutCachedWidth =6300\r\n                                            LayoutCachedHeight =5835\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =7020\r\n                                    Top =5575\r\n                                    TabIndex =19\r\n                                    Name =\"chkTTOption\"\r\n\r\n                                    LayoutCachedLeft =7020\r\n                                    LayoutCachedTop =5575\r\n                                    LayoutCachedWidth =7280\r\n                                    LayoutCachedHeight =5815\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =7320\r\n                                            Top =5520\r\n                                            Width =1725\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label140\"\r\n                                            Caption =\"TT Font Handling\"\r\n                                            LayoutCachedLeft =7320\r\n                                            LayoutCachedTop =5520\r\n                                            LayoutCachedWidth =9045\r\n                                            LayoutCachedHeight =5835\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =3720\r\n                                    Top =2340\r\n                                    Width =5040\r\n                                    Height =825\r\n                                    ForeColor =5324600\r\n                                    Name =\"Label126\"\r\n                                    Caption =\"Select any additional settings that you would like saved to version control and \"\r\n                                        \"used when building a database from source files.\"\r\n                                    HorizontalAnchor =2\r\n                                    LayoutCachedLeft =3720\r\n                                    LayoutCachedTop =2340\r\n                                    LayoutCachedWidth =8760\r\n                                    LayoutCachedHeight =3165\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin Line\r\n                                    LineSlant = NotDefault\r\n                                    BorderWidth =1\r\n                                    OverlapFlags =119\r\n                                    Left =960\r\n                                    Top =3360\r\n                                    Width =8160\r\n                                    Name =\"Line161\"\r\n                                    HorizontalAnchor =2\r\n                                    LayoutCachedLeft =960\r\n                                    LayoutCachedTop =3360\r\n                                    LayoutCachedWidth =9120\r\n                                    LayoutCachedHeight =3360\r\n                                    BorderThemeColorIndex =1\r\n                                    BorderShade =65.0\r\n                                End\r\n                            End\r\n                        End\r\n                        Begin Page\r\n                            OverlapFlags =247\r\n                            Left =600\r\n                            Top =1980\r\n                            Width =8865\r\n                            Height =4290\r\n                            Name =\"pgeDatabases\"\r\n                            Caption =\"Databases\"\r\n                            LayoutCachedLeft =600\r\n                            LayoutCachedTop =1980\r\n                            LayoutCachedWidth =9465\r\n                            LayoutCachedHeight =6270\r\n                            Begin\r\n                                Begin ListBox\r\n                                    ColumnHeads = NotDefault\r\n                                    RowSourceTypeInt =1\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    ColumnCount =2\r\n                                    Left =1020\r\n                                    Top =2520\r\n                                    Width =4440\r\n                                    Height =2880\r\n                                    Name =\"lstDatabases\"\r\n                                    RowSourceType =\"Value List\"\r\n                                    ColumnWidths =\"2520\"\r\n                                    OnDblClick =\"[Event Procedure]\"\r\n                                    HorizontalAnchor =2\r\n                                    VerticalAnchor =2\r\n\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =2520\r\n                                    LayoutCachedWidth =5460\r\n                                    LayoutCachedHeight =5400\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1200\r\n                                            Top =2160\r\n                                            Width =1890\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label243\"\r\n                                            Caption =\"External Databases:\"\r\n                                            LayoutCachedLeft =1200\r\n                                            LayoutCachedTop =2160\r\n                                            LayoutCachedWidth =3090\r\n                                            LayoutCachedHeight =2475\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =5880\r\n                                    Top =2580\r\n                                    Width =3240\r\n                                    Height =3525\r\n                                    ForeColor =5324600\r\n                                    Name =\"Label244\"\r\n                                    Caption =\"Your database application may connect to external database systems such as Micro\"\r\n                                        \"soft SQL Server.\\015\\012\\015\\012If you define a connection to an external databa\"\r\n                                        \"se, you can use version control to track changes in those external database obje\"\r\n                                        \"cts.\\015\\012\\015\\012Please see documentation for additional notes.\"\r\n                                    HorizontalAnchor =1\r\n                                    LayoutCachedLeft =5880\r\n                                    LayoutCachedTop =2580\r\n                                    LayoutCachedWidth =9120\r\n                                    LayoutCachedHeight =6105\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =1020\r\n                                    Top =5520\r\n                                    Width =1320\r\n                                    TabIndex =1\r\n                                    Name =\"cmdDeleteDatabase\"\r\n                                    Caption =\" Delete\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    VerticalAnchor =1\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000b0a090ff302010ff302010ff302010ff302010ff302010ff ,\r\n                                        0x302010ff302010ff302010ff302010ff302010ff302010ff302010ff00000000 ,\r\n                                        0x0000000000000000b0a090fffff8f0fffff0f0ffffe8e0fff0e8e0fff0e0d0ff ,\r\n                                        0xf0d8d0fff0d8c0fff0d8c0fff0d8c0fff0d8c0fff0d8c0ff302010ff00000000 ,\r\n                                        0x0000000000000000b0a090ffffffffffe06830ffe06830ffe06830ffd06830ff ,\r\n                                        0xd06830ffd06830ffd06030ffc06030ff904820ffffe0d0ff302010ff00000000 ,\r\n                                        0x0000000000000000b0a090ffffffffffd06830ffffb080ffffa880ffffa070ff ,\r\n                                        0xf09870fff09060ffa0b0f0ff1020e0ffc0c8f0ffffe0d0ff302010ff00000000 ,\r\n                                        0x00000000a0a8f0ffb0a090ffffffffffe06830ffe06830ffe06830ffd06830ff ,\r\n                                        0xd06830ffe0e0f0ff0028ffff1028f0ff4050d0ffffe0d0ff302010ff00000000 ,\r\n                                        0x4050e0ff0010b0ffb0a090ffffffffffffffffffffffffffffffffffffffffff ,\r\n                                        0xfff8f0ffffe8e0ff2048ffff1038ffff1028ffffe0e8f0ff302010ff7088f0ff ,\r\n                                        0x0018c0ff6078f0ffb0a090ffb0a090ffb0a090ffb0a090ffb0a090ffb0a090ff ,\r\n                                        0xb0a090ffb0a090ffe0e0f0ff3050ffff2040ffff8090f0ffb0b8f0ff0028f0ff ,\r\n                                        0x4058f0ff00000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x000000000000000000000000d0d8f0ff4060ffff3050ffff2040ffff3050ffff ,\r\n                                        0xe0e8f0ff00000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x00000000000000000000000000000000c0d0f0ff4068ffff4060ffffc0c8f0ff ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x000000000000000000000000c0c8f0ff6078ffff6078ffff6080ffff5070ffff ,\r\n                                        0xe0e0f0ff00000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000b0b8f0ff6078ffff6078ffffb0c0f0fff0f0f0ff7088ffff ,\r\n                                        0x6078ffffc0d0f0ff000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000090a0ffff6078ffff6078ffffd0d8f0ff000000000000000000000000 ,\r\n                                        0xb0b8f0ff8098ffff000000000000000000000000000000000000000000000000 ,\r\n                                        0x000000008098ffff6080ffffd0d8f0ff00000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =5520\r\n                                    LayoutCachedWidth =2340\r\n                                    LayoutCachedHeight =5880\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =4260\r\n                                    Top =5520\r\n                                    Width =1200\r\n                                    TabIndex =2\r\n                                    Name =\"cmdAddDatabase\"\r\n                                    Caption =\" Add\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    HorizontalAnchor =1\r\n                                    VerticalAnchor =1\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000b09880ff201010ff201010ff201010ff201010ff201010ff ,\r\n                                        0x201010ff201010ff201010ff201010ff201010ff201010ff201010ff00000000 ,\r\n                                        0x0000000000000000c0a090fffff8f0fffff8f0fffff0f0fffff0e0fff0e8e0ff ,\r\n                                        0xf0e8d0fff0e0d0fff0e0d0fff0e0d0fff0d8d0fff0d8d0ff201810ff00000000 ,\r\n                                        0x0000000000000000c0a090ffffffffffd07850ffd07840ffd07040ffc07040ff ,\r\n                                        0xc06840ffc06840ffc06840ffc07040ffa06040fff0e0d0ff403830ff00000000 ,\r\n                                        0x0000000000000000c0a890ffffffffffd07850fff0b8a0fff0b090fff0a880ff ,\r\n                                        0xf0a080fff09870fff09870fff0a880ffc09880fffff0f0ff909090ff00000000 ,\r\n                                        0x0000000000000000c0a890ffffffffffd07850ffd07850ffd07840ffd07040ff ,\r\n                                        0xc07040ffc07050ffd09070ff70b8c0ff90d8f0ff90f0ffff40c0e0ffa0f0ffff ,\r\n                                        0xa0e8ffff90d8f0ffc0a8a0fffffffffffffffffffffffffffffffffffff8f0ff ,\r\n                                        0xfff8f0fffff8f0fffff8f0ffb0e8ffff30b8e0ff80e8ffff60c8e0ff90f0ffff ,\r\n                                        0x30b8e0ffa0e8ffffc0a8a0ffc0a8a0ffc0a890ffc0a090ffc0a090ffc0a090ff ,\r\n                                        0xc09880ffc0a090ffd0c0b0ffa0e8ffff90f0ffffc0f8ffffb0e8f0ffc0f8ffff ,\r\n                                        0x90f0ffffa0f0ffff000000000000000000000000000000000000000000000000 ,\r\n                                        0x00000000000000000000000020a8e0ff50c0e0ffb0e8f0fff0ffffffb0e8f0ff ,\r\n                                        0x50c0e0ff30b8e0ff000000000000000000000000000000000000000000000000 ,\r\n                                        0x00000000000000000000000080e8ffc090f0ffffc0f8ffffb0e8f0ffc0f8ffff ,\r\n                                        0x90f0ffff90d8e0ff000000000000000000000000000000000000000000000000 ,\r\n                                        0x00000000000000000000000050d8ff8030b8e0ff90f0ffff60c0e0ff90f0ffff ,\r\n                                        0x30b8e0ff50d0f080000000000000000000000000000000000000000000000000 ,\r\n                                        0x00000000000000000000000030b0e0a040c8f09080e8ffc020b0e0ff70e8ffc0 ,\r\n                                        0x50d8f08030b0e080000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =4260\r\n                                    LayoutCachedTop =5520\r\n                                    LayoutCachedWidth =5460\r\n                                    LayoutCachedHeight =5880\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =3000\r\n                                    Top =5520\r\n                                    Width =1140\r\n                                    TabIndex =3\r\n                                    Name =\"cmdEditDatabase\"\r\n                                    Caption =\" Edit...\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    HorizontalAnchor =1\r\n                                    VerticalAnchor =1\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000ff00000000000000ff00000000000000ff00000000 ,\r\n                                        0x707070ff505850ff000000ff2048503000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0xa0a0a0ffffffffff5090b0ff101010ff30607030000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0xa0a0a0ff90b8c0ff70d0e0ff5098b0ff101010ff306070300000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x80a0b04050a0b0ff90e0f0ff60c0d0ff5098b0ff101010ff3060704000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000080a0b04050a0b0ff90e0f0ff60c0d0ff5098b0ff101010ff30607040 ,\r\n                                        0x000000000000000000000000000000000000000070707000707070ff404040ff ,\r\n                                        0x000800ff0000000080a0b04060a8b0ff90e0f0ff60c0d0ff5098b0ff101010ff ,\r\n                                        0x305860300000000000000000000000000000000070687000707070ff505050ff ,\r\n                                        0x100810ff000000000000000080a0b04070b0c0ff90e0f0ff70c8e0ff808880ff ,\r\n                                        0x303890ff3038805000000000000000000000000000000000707070ff707070ff ,\r\n                                        0x404040ff00000000000000000000000080a0b04080b0c0ffd0b8b0ff7088d0ff ,\r\n                                        0x6070b0ff303890ff00000000000000000000000000000000707070ff909890ff ,\r\n                                        0x504850ff4048402000000000000000000000000080a0b0406070b0ff7090e0ff ,\r\n                                        0x6078d0ff6070b0ff00000000000000000000000090989060909090ffd0c8c0ff ,\r\n                                        0x505850ff50485060000000000000000000000000000000007080c0506070b0ff ,\r\n                                        0x6070b0ff6078c030000000000000000090909050808880fff0f0f0ffe0d8d0ff ,\r\n                                        0x909890ff404840ff505050400000000000000000000000000000000000000000 ,\r\n                                        0x000000000000000000000000a098a040707070fff0e8f0fffff8fffff0e8e0ff ,\r\n                                        0xd0d0d0ff707070ff404840ff5050503000000000000000000000000000000000 ,\r\n                                        0x0000000000000000a098a050707070ffc0c8c0fffffffffff0f8f0fff0f0f0ff ,\r\n                                        0xd0d8d0ffb0a8b0ff606060ff404840ff50505040000000000000000000000000 ,\r\n                                        0x0000000000000000c0b8c0ffc0c0c0ffd0c8d0ffc0c0c0ffb0b8b0ffb0b0b0ff ,\r\n                                        0xb0b0b0ffa0a0a0ff808080ff606060ff404840ff303030000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =3000\r\n                                    LayoutCachedTop =5520\r\n                                    LayoutCachedWidth =4140\r\n                                    LayoutCachedHeight =5880\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                End\r\n                            End\r\n                        End\r\n                        Begin Page\r\n                            OverlapFlags =247\r\n                            Left =600\r\n                            Top =1980\r\n                            Width =8865\r\n                            Height =4290\r\n                            Name =\"pgeTable\"\r\n                            Caption =\"Table Data\"\r\n                            LayoutCachedLeft =600\r\n                            LayoutCachedTop =1980\r\n                            LayoutCachedWidth =9465\r\n                            LayoutCachedHeight =6270\r\n                            Begin\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1020\r\n                                    Top =5760\r\n                                    Name =\"chkTableShowHidden\"\r\n                                    DefaultValue =\"False\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    VerticalAnchor =1\r\n\r\n                                    LayoutCachedLeft =1020\r\n                                    LayoutCachedTop =5760\r\n                                    LayoutCachedWidth =1280\r\n                                    LayoutCachedHeight =6000\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1320\r\n                                            Top =5700\r\n                                            Width =1680\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"lblTableShowHidden\"\r\n                                            Caption =\"Show Hidden\"\r\n                                            VerticalAnchor =1\r\n                                            LayoutCachedLeft =1320\r\n                                            LayoutCachedTop =5700\r\n                                            LayoutCachedWidth =3000\r\n                                            LayoutCachedHeight =6015\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =3060\r\n                                    Top =5760\r\n                                    TabIndex =1\r\n                                    Name =\"chkTableShowSystem\"\r\n                                    DefaultValue =\"False\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    VerticalAnchor =1\r\n\r\n                                    LayoutCachedLeft =3060\r\n                                    LayoutCachedTop =5760\r\n                                    LayoutCachedWidth =3320\r\n                                    LayoutCachedHeight =6000\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =3360\r\n                                            Top =5700\r\n                                            Width =1740\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"lblTableShowSystem\"\r\n                                            Caption =\"Show System\"\r\n                                            VerticalAnchor =1\r\n                                            LayoutCachedLeft =3360\r\n                                            LayoutCachedTop =5700\r\n                                            LayoutCachedWidth =5100\r\n                                            LayoutCachedHeight =6015\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =5160\r\n                                    Top =5760\r\n                                    TabIndex =2\r\n                                    Name =\"chkTableShowOther\"\r\n                                    DefaultValue =\"False\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    VerticalAnchor =1\r\n\r\n                                    LayoutCachedLeft =5160\r\n                                    LayoutCachedTop =5760\r\n                                    LayoutCachedWidth =5420\r\n                                    LayoutCachedHeight =6000\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =5460\r\n                                            Top =5700\r\n                                            Width =1620\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"lblTableShowOther\"\r\n                                            Caption =\"Show Other\"\r\n                                            VerticalAnchor =1\r\n                                            LayoutCachedLeft =5460\r\n                                            LayoutCachedTop =5700\r\n                                            LayoutCachedWidth =7080\r\n                                            LayoutCachedHeight =6015\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin TextBox\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    Left =6960\r\n                                    Top =3540\r\n                                    Width =2400\r\n                                    Height =360\r\n                                    TabIndex =3\r\n                                    Name =\"txtOtherTableName\"\r\n                                    HorizontalAnchor =1\r\n\r\n                                    LayoutCachedLeft =6960\r\n                                    LayoutCachedTop =3540\r\n                                    LayoutCachedWidth =9360\r\n                                    LayoutCachedHeight =3900\r\n                                    BackShade =95.0\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =255\r\n                                            Left =6960\r\n                                            Top =3240\r\n                                            Width =1830\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label79\"\r\n                                            Caption =\"Other Table Name:\"\r\n                                            HorizontalAnchor =1\r\n                                            LayoutCachedLeft =6960\r\n                                            LayoutCachedTop =3240\r\n                                            LayoutCachedWidth =8790\r\n                                            LayoutCachedHeight =3555\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =7440\r\n                                    Top =4860\r\n                                    Width =1920\r\n                                    TabIndex =4\r\n                                    Name =\"cmdAddOtherTableData\"\r\n                                    Caption =\" Add Other\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    HorizontalAnchor =1\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x000000000000000000000000d0687050c06860ffb05850ffa05050ffa05050ff ,\r\n                                        0xa05050ff904850ff904840ff904840ff804040ff803840ff803840ff703840ff ,\r\n                                        0x703830ff0000000000000000d06870fff09090ffe08080ffb04820ff403020ff ,\r\n                                        0xc0b8b0ffc0b8b0ffd0c0c0ffd0c8c0ff505050ffa04030ffa04030ffa03830ff ,\r\n                                        0x703840ff0000000000000000d07070ffff98a0fff08880ffe08080ff705850ff ,\r\n                                        0x404030ff907870fff0e0e0fff0e8e0ff908070ffa04030ffa04040ffa04030ff ,\r\n                                        0x803840ff0000000000000000d07870ffffa0a0fff09090fff08880ff705850ff ,\r\n                                        0x000000ff404030fff0d8d0fff0e0d0ff807860ffb04840ffb04840ffa04040ff ,\r\n                                        0x804040ff0000000000000000d07880ffffa8b0ffffa0a0fff09090ff705850ff ,\r\n                                        0x705850ff705850ff705850ff706050ff806860ffc05850ffb05050ffb04840ff ,\r\n                                        0x804040ff0000000000000000e08080ffffb0b0ffffb0b0ffffa0a0fff09090ff ,\r\n                                        0xf08880ffe08080ffe07880ffd07070ffd06870ffc06060ffc05850ffb05050ff ,\r\n                                        0x904840ff0000000000000000e08890ffffb8c0ffffb8b0ffd06060ffc06050ff ,\r\n                                        0xc05850ffc05040ffb05030ffb04830ffa04020ffa03810ffc06060ffc05850ff ,\r\n                                        0x904840ff0000000000000000e09090ffffc0c0ffd06860ffffffffffffffffff ,\r\n                                        0xfff8f0fff0f0f0fff0e8e0fff0d8d0ffe0d0c0ffe0c8c0ffa03810ffc06060ff ,\r\n                                        0x904850ff0000000000000000e098a0ffffc0c0ffd07070ffffffffffffffffff ,\r\n                                        0xfffffffffff8f0fff0f0f0fff0e8e0fff0d8d0ffe0d0c0ffa04020ffd06860ff ,\r\n                                        0xa05050ff0000000000000000f0a0a0ffffc0c0ffe07870ffffffffffffffffff ,\r\n                                        0xfffffffffffffffffff8f0fff0f0f0fff0e8e0fff0d8d0ffb04830ffd07070ff ,\r\n                                        0xa05050ff0000000000000000f0a8a0ffffc0c0ffe08080ffffffffffffffffff ,\r\n                                        0xfffffffffffffffffffffffffff8f0fff0f0f0fff0e8e0ffb05030ffe07880ff ,\r\n                                        0xa05050ff0000000000000000f0b0b0ffffc0c0fff08890ffffffffffffffffff ,\r\n                                        0xfffffffffffffffffffffffffffffffffff8f0fff0f0f0ffc05040ff603030ff ,\r\n                                        0xb05850ff0000000000000000f0b0b0ffffc0c0ffff9090ffffffffffffffffff ,\r\n                                        0xfffffffffffffffffffffffffffffffffffffffffff8f0ffc05850ffb05860ff ,\r\n                                        0xb05860ff0000000000000000f0b8b0fff0b8b0fff0b0b0fff0b0b0fff0a8b0ff ,\r\n                                        0xf0a0a0ffe098a0ffe09090ffe09090ffe08890ffe08080ffd07880ffd07870ff ,\r\n                                        0xd07070ff00000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =7440\r\n                                    LayoutCachedTop =4860\r\n                                    LayoutCachedWidth =9360\r\n                                    LayoutCachedHeight =5220\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =6960\r\n                                    Top =2280\r\n                                    Width =2400\r\n                                    Height =840\r\n                                    FontSize =10\r\n                                    ForeColor =5324600\r\n                                    Name =\"Label82\"\r\n                                    Caption =\"You may optionally include data from specific tables as part of the export proce\"\r\n                                        \"ss.\"\r\n                                    HorizontalAnchor =1\r\n                                    LayoutCachedLeft =6960\r\n                                    LayoutCachedTop =2280\r\n                                    LayoutCachedWidth =9360\r\n                                    LayoutCachedHeight =3120\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin Subform\r\n                                    OverlapFlags =247\r\n                                    Left =780\r\n                                    Top =2280\r\n                                    Width =6090\r\n                                    Height =3300\r\n                                    TabIndex =5\r\n                                    Name =\"sfrmTableData\"\r\n                                    SourceObject =\"Form.frmVCSTableData\"\r\n                                    HorizontalAnchor =2\r\n                                    VerticalAnchor =2\r\n\r\n                                    LayoutCachedLeft =780\r\n                                    LayoutCachedTop =2280\r\n                                    LayoutCachedWidth =6870\r\n                                    LayoutCachedHeight =5580\r\n                                End\r\n                                Begin ComboBox\r\n                                    LimitToList = NotDefault\r\n                                    RowSourceTypeInt =1\r\n                                    OverlapFlags =247\r\n                                    TextAlign =1\r\n                                    IMESentenceMode =3\r\n                                    ColumnCount =2\r\n                                    Left =6960\r\n                                    Top =4380\r\n                                    Width =2400\r\n                                    Height =360\r\n                                    TabIndex =6\r\n                                    Name =\"cboFormatTypeForOther\"\r\n                                    RowSourceType =\"Value List\"\r\n                                    ColumnWidths =\"0\"\r\n                                    BottomPadding =150\r\n                                    HorizontalAnchor =1\r\n                                    LeftMargin =44\r\n                                    TopMargin =22\r\n                                    RightMargin =44\r\n                                    BottomMargin =22\r\n\r\n                                    LayoutCachedLeft =6960\r\n                                    LayoutCachedTop =4380\r\n                                    LayoutCachedWidth =9360\r\n                                    LayoutCachedHeight =4740\r\n                                    ForeThemeColorIndex =0\r\n                                    ForeTint =75.0\r\n                                    ForeShade =100.0\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =255\r\n                                            TextAlign =1\r\n                                            Left =6960\r\n                                            Top =4020\r\n                                            Width =1830\r\n                                            Height =360\r\n                                            LeftMargin =44\r\n                                            TopMargin =22\r\n                                            RightMargin =44\r\n                                            BottomMargin =22\r\n                                            Name =\"Label15\"\r\n                                            Caption =\"Export As\"\r\n                                            BottomPadding =150\r\n                                            HorizontalAnchor =1\r\n                                            LayoutCachedLeft =6960\r\n                                            LayoutCachedTop =4020\r\n                                            LayoutCachedWidth =8790\r\n                                            LayoutCachedHeight =4380\r\n                                        End\r\n                                    End\r\n                                End\r\n                            End\r\n                        End\r\n                        Begin Page\r\n                            OverlapFlags =247\r\n                            Left =600\r\n                            Top =1980\r\n                            Width =8865\r\n                            Height =4290\r\n                            Name =\"pgeBuild\"\r\n                            Caption =\"Build\"\r\n                            LayoutCachedLeft =600\r\n                            LayoutCachedTop =1980\r\n                            LayoutCachedWidth =9465\r\n                            LayoutCachedHeight =6270\r\n                            Begin\r\n                                Begin CheckBox\r\n                                    OverlapFlags =247\r\n                                    Left =1140\r\n                                    Top =4555\r\n                                    TabIndex =1\r\n                                    Name =\"chkForceImportOriginalQuerySQL\"\r\n\r\n                                    LayoutCachedLeft =1140\r\n                                    LayoutCachedTop =4555\r\n                                    LayoutCachedWidth =1400\r\n                                    LayoutCachedHeight =4795\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1447\r\n                                            Top =4500\r\n                                            Width =3705\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"lblForceImportOriginalQuerySQL\"\r\n                                            Caption =\"Force import of original SQL for queries\"\r\n                                            LayoutCachedLeft =1447\r\n                                            LayoutCachedTop =4500\r\n                                            LayoutCachedWidth =5152\r\n                                            LayoutCachedHeight =4815\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin TextBox\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    Left =3420\r\n                                    Top =5040\r\n                                    Width =2640\r\n                                    Height =315\r\n                                    TabIndex =2\r\n                                    Name =\"txtRunBeforeBuild\"\r\n                                    ValidationRule =\"Like \\\"*?.?*\\\" Or \\\"\\\" Or Is Null\"\r\n                                    ValidationText =\"This value must include both module and sub names joined with a period. For exam\"\r\n                                        \"ple, use MyModule.Bootstrap to run the Bootstrap sub in MyModule.\"\r\n\r\n                                    LayoutCachedLeft =3420\r\n                                    LayoutCachedTop =5040\r\n                                    LayoutCachedWidth =6060\r\n                                    LayoutCachedHeight =5355\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1140\r\n                                            Top =5040\r\n                                            Width =2085\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label229\"\r\n                                            Caption =\"Run Sub Before Build:\"\r\n                                            LayoutCachedLeft =1140\r\n                                            LayoutCachedTop =5040\r\n                                            LayoutCachedWidth =3225\r\n                                            LayoutCachedHeight =5355\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin TextBox\r\n                                    OverlapFlags =247\r\n                                    IMESentenceMode =3\r\n                                    Left =3420\r\n                                    Top =5460\r\n                                    Width =2640\r\n                                    Height =315\r\n                                    Name =\"txtRunAfterBuild\"\r\n\r\n                                    LayoutCachedLeft =3420\r\n                                    LayoutCachedTop =5460\r\n                                    LayoutCachedWidth =6060\r\n                                    LayoutCachedHeight =5775\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =1140\r\n                                            Top =5460\r\n                                            Width =2055\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label104\"\r\n                                            Caption =\"Run Sub After Build:\"\r\n                                            LayoutCachedLeft =1140\r\n                                            LayoutCachedTop =5460\r\n                                            LayoutCachedWidth =3195\r\n                                            LayoutCachedHeight =5775\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =1140\r\n                                    Top =2100\r\n                                    Width =7860\r\n                                    Height =2388\r\n                                    ForeColor =5324600\r\n                                    Name =\"Label105\"\r\n                                    Caption =\"PLEASE NOTE:\\015\\012Building from source will completely rebuild your database p\"\r\n                                        \"roject from source files. It is very important that you test this carefully in y\"\r\n                                        \"our environment to make sure everything you need is being created in your databa\"\r\n                                        \"se during the build. (A backup copy of your current database will be made as a p\"\r\n                                        \"art of the build process.)\\015\\012\\015\\012Please see the online documentation fo\"\r\n                                        \"r additional details on the build process. If you encounter a problem, please fe\"\r\n                                        \"el free to open an issues on the GitHub project.\"\r\n                                    LayoutCachedLeft =1140\r\n                                    LayoutCachedTop =2100\r\n                                    LayoutCachedWidth =9000\r\n                                    LayoutCachedHeight =4488\r\n                                    ForeThemeColorIndex =-1\r\n                                    ForeTint =100.0\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =5197\r\n                                    Top =4544\r\n                                    Width =4170\r\n                                    Height =240\r\n                                    FontSize =10\r\n                                    Name =\"lblForceImportSQLNote\"\r\n                                    Caption =\"(\\\"Save Query SQL\\\" option needed when exporting)\"\r\n                                    LayoutCachedLeft =5197\r\n                                    LayoutCachedTop =4544\r\n                                    LayoutCachedWidth =9367\r\n                                    LayoutCachedHeight =4784\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =6240\r\n                                    Top =5100\r\n                                    Width =3120\r\n                                    Height =240\r\n                                    FontSize =10\r\n                                    Name =\"Label230\"\r\n                                    Caption =\"Use \\\"module.sub\\\" to specify module\"\r\n                                    LayoutCachedLeft =6240\r\n                                    LayoutCachedTop =5100\r\n                                    LayoutCachedWidth =9360\r\n                                    LayoutCachedHeight =5340\r\n                                End\r\n                                Begin Label\r\n                                    OverlapFlags =247\r\n                                    Left =6240\r\n                                    Top =5520\r\n                                    Width =2760\r\n                                    Height =240\r\n                                    FontSize =10\r\n                                    Name =\"Label231\"\r\n                                    Caption =\"(Module name optional)\"\r\n                                    LayoutCachedLeft =6240\r\n                                    LayoutCachedTop =5520\r\n                                    LayoutCachedWidth =9000\r\n                                    LayoutCachedHeight =5760\r\n                                End\r\n                            End\r\n                        End\r\n                        Begin Page\r\n                            OverlapFlags =247\r\n                            Left =600\r\n                            Top =1980\r\n                            Width =8865\r\n                            Height =4290\r\n                            Name =\"pgeSettings\"\r\n                            Caption =\"Settings\"\r\n                            LayoutCachedLeft =600\r\n                            LayoutCachedTop =1980\r\n                            LayoutCachedWidth =9465\r\n                            LayoutCachedHeight =6270\r\n                            Begin\r\n                                Begin OptionGroup\r\n                                    OverlapFlags =255\r\n                                    Left =960\r\n                                    Top =2280\r\n                                    Width =8160\r\n                                    Height =2460\r\n                                    Name =\"Frame62\"\r\n                                    HorizontalAnchor =2\r\n\r\n                                    LayoutCachedLeft =960\r\n                                    LayoutCachedTop =2280\r\n                                    LayoutCachedWidth =9120\r\n                                    LayoutCachedHeight =4740\r\n                                    Begin\r\n                                        Begin Label\r\n                                            BackStyle =1\r\n                                            OverlapFlags =247\r\n                                            Left =1080\r\n                                            Top =2100\r\n                                            Width =1620\r\n                                            Height =315\r\n                                            BackColor =15130848\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label63\"\r\n                                            Caption =\" System Defaults\"\r\n                                            LayoutCachedLeft =1080\r\n                                            LayoutCachedTop =2100\r\n                                            LayoutCachedWidth =2700\r\n                                            LayoutCachedHeight =2415\r\n                                            BackThemeColorIndex =-1\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =1260\r\n                                    Top =2520\r\n                                    Width =2160\r\n                                    Height =420\r\n                                    TabIndex =1\r\n                                    Name =\"cmdSaveAsDefault\"\r\n                                    Caption =\" Save as Default\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x000000000000000000000000d0687050c06860ffb05850ffa05050ffa05050ff ,\r\n                                        0xa05050ff904850ff904840ff904840ff804040ff803840ff803840ff703840ff ,\r\n                                        0x703830ff0000000000000000d06870fff09090ffe08080ffb04820ff403020ff ,\r\n                                        0xc0b8b0ffc0b8b0ffd0c0c0ffd0c8c0ff505050ffa04030ffa04030ffa03830ff ,\r\n                                        0x703840ff0000000000000000d07070ffff98a0fff08880ffe08080ff705850ff ,\r\n                                        0x404030ff907870fff0e0e0fff0e8e0ff908070ffa04030ffa04040ffa04030ff ,\r\n                                        0x803840ff0000000000000000d07870ffffa0a0fff09090fff08880ff705850ff ,\r\n                                        0x000000ff404030fff0d8d0fff0e0d0ff807860ffb04840ffb04840ffa04040ff ,\r\n                                        0x804040ff0000000000000000d07880ffffa8b0ffffa0a0fff09090ff705850ff ,\r\n                                        0x705850ff705850ff705850ff706050ff806860ffc05850ffb05050ffb04840ff ,\r\n                                        0x804040ff0000000000000000e08080ffffb0b0ffffb0b0ffffa0a0fff09090ff ,\r\n                                        0xf08880ffe08080ffe07880ffd07070ffd06870ffc06060ffc05850ffb05050ff ,\r\n                                        0x904840ff0000000000000000e08890ffffb8c0ffffb8b0ffd06060ffc06050ff ,\r\n                                        0xc05850ffc05040ffb05030ffb04830ffa04020ffa03810ffc06060ffc05850ff ,\r\n                                        0x904840ff0000000000000000e09090ffffc0c0ffd06860ffffffffffffffffff ,\r\n                                        0xfff8f0fff0f0f0fff0e8e0fff0d8d0ffe0d0c0ffe0c8c0ffa03810ffc06060ff ,\r\n                                        0x904850ff0000000000000000e098a0ffffc0c0ffd07070ffffffffffffffffff ,\r\n                                        0xfffffffffff8f0fff0f0f0fff0e8e0fff0d8d0ffe0d0c0ffa04020ffd06860ff ,\r\n                                        0xa05050ff0000000000000000f0a0a0ffffc0c0ffe07870ffffffffffffffffff ,\r\n                                        0xfffffffffffffffffff8f0fff0f0f0fff0e8e0fff0d8d0ffb04830ffd07070ff ,\r\n                                        0xa05050ff0000000000000000f0a8a0ffffc0c0ffe08080ffffffffffffffffff ,\r\n                                        0xfffffffffffffffffffffffffff8f0fff0f0f0fff0e8e0ffb05030ffe07880ff ,\r\n                                        0xa05050ff0000000000000000f0b0b0ffffc0c0fff08890ffffffffffffffffff ,\r\n                                        0xfffffffffffffffffffffffffffffffffff8f0fff0f0f0ffc05040ff603030ff ,\r\n                                        0xb05850ff0000000000000000f0b0b0ffffc0c0ffff9090ffffffffffffffffff ,\r\n                                        0xfffffffffffffffffffffffffffffffffffffffffff8f0ffc05850ffb05860ff ,\r\n                                        0xb05860ff0000000000000000f0b8b0fff0b8b0fff0b0b0fff0b0b0fff0a8b0ff ,\r\n                                        0xf0a0a0ffe098a0ffe09090ffe09090ffe08890ffe08080ffd07880ffd07870ff ,\r\n                                        0xd07070ff00000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =1260\r\n                                    LayoutCachedTop =2520\r\n                                    LayoutCachedWidth =3420\r\n                                    LayoutCachedHeight =2940\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =3900\r\n                                            Top =2580\r\n                                            Width =5040\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label53\"\r\n                                            Caption =\"Save these settings as default for new projects.\"\r\n                                            LayoutCachedLeft =3900\r\n                                            LayoutCachedTop =2580\r\n                                            LayoutCachedWidth =8940\r\n                                            LayoutCachedHeight =2895\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =1260\r\n                                    Top =3060\r\n                                    Width =2160\r\n                                    Height =420\r\n                                    TabIndex =2\r\n                                    Name =\"cmdRestoreDefaults\"\r\n                                    Caption =\" Restore Defaults\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000f0906060d0784080b0583010000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000e0785040f08850ffd07040ffa05830500000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000f0906020d0704060f08050ffd07050f0a050300000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x00000000000000000000000000000000c06840d0f08850ffc078508000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0xf0c0b01000000000000000000000000090482040e07840ffe08860ffe0a08000 ,\r\n                                        0x00000000000000000000000000000000d07040ffd07040ffc06840ffb06030ff ,\r\n                                        0xb05830ff905030ff0000000000000000b0603020c06840ffe08050ffd0886080 ,\r\n                                        0x00000000000000000000000000000000d07850ffe07030fff08050fff09870ff ,\r\n                                        0xe09060fff0a08040000000000000000080402000c06840ffe07840f0e09870c0 ,\r\n                                        0x00000000000000000000000000000000d08050ffe08050fff09060fff0a070ff ,\r\n                                        0x904830b0b0603040000000000000000080402000c06840ffd07040f0e09870d0 ,\r\n                                        0x00000000000000000000000000000000d08860ffe09060fff09870fff08850f0 ,\r\n                                        0xb06040ffb06040ffb060307000000000b0805020a05830f0d07840f0e09070d0 ,\r\n                                        0x000000000000000000000000e0b09010c08060ffd09870e0d0886090d09070ff ,\r\n                                        0xd08050ffc07040ffc06840ffb06030c0b07040e0a06040ffe08050ffd0a080e0 ,\r\n                                        0x00000000000000000000000000000000c08860ffd0a0804000000000d08860c0 ,\r\n                                        0xd08860ffd08050f0c06840ffb06840ffb06030f0e07840f0e0a080f0d09880e0 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0xf0a880c0e09880ffe09870f0e09070f0e09070e0e0a080f0e0a890f0f0b8a020 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x00000000f0b89060f0b090c0f0b8a0e0f0c0a0c0f0c0a090f0c0b02000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =1260\r\n                                    LayoutCachedTop =3060\r\n                                    LayoutCachedWidth =3420\r\n                                    LayoutCachedHeight =3480\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =3900\r\n                                            Top =3120\r\n                                            Width =5040\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label57\"\r\n                                            Caption =\"Apply system defaults to this project.\"\r\n                                            LayoutCachedLeft =3900\r\n                                            LayoutCachedTop =3120\r\n                                            LayoutCachedWidth =8940\r\n                                            LayoutCachedHeight =3435\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =1260\r\n                                    Top =3600\r\n                                    Width =2160\r\n                                    Height =420\r\n                                    TabIndex =3\r\n                                    Name =\"cmdClearDefaults\"\r\n                                    Caption =\" Clear Defaults\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000b0989070605040e0403020ff ,\r\n                                        0x403020ff403020ff503830ff503830ff504030e0604830806050401000000000 ,\r\n                                        0x0000000000000000000000000000000070585000a08880d0e0c8b0f0fff0d0ff ,\r\n                                        0xffffe0fffff0d0ffffe8c0ffffd8a0fff0d0b0ff604830e06048302000000000 ,\r\n                                        0x0000000000000000000000000000000090807040a08880fffffffffffff8ffff ,\r\n                                        0xfff8f0fffff0e0fffff0d0ffffe8c0ffffe0c0ff806850ff6048307000000000 ,\r\n                                        0x00000000000000000000000000000000a0888070b0a8a0f0f0f8e0ff70b860ff ,\r\n                                        0x006000ff004800ff004800ff309040fff0d8b0ffb09070ff604030b000000000 ,\r\n                                        0x00000000000000000000000000000000a08880b0e0d8d0f0d0f0d0ff209820ff ,\r\n                                        0xe0d0d0ffffffffffb0c090ff107020ffc0c890ffe0b8a0ff504030e060504000 ,\r\n                                        0x000000000000000000000000a0908010a08880f0f0f0f0fff0f8f0ff80d880ff ,\r\n                                        0x209820ffffffffff308030ff107010ffffe0c0fff0d0b0ff604830ff60484010 ,\r\n                                        0x000000000000000000000000a0908050b09890ffffffffffffffffffffffffff ,\r\n                                        0x20c020ff006800ff006010ffc0e0a0fffff0d0ffffe0b0ff706050ff60483040 ,\r\n                                        0x000000000000000000000000a08880b0c0b0b0ffffffffffffffffffffffffff ,\r\n                                        0xe0ffd0ff10b010ff90d890fffffff0fffff0d0ffffe8c0ffa08870ff504030a0 ,\r\n                                        0x000000000000000000000000a08880f0e0d0d0ffffffffffffffffffffffffff ,\r\n                                        0xfffffffffffffffffffffffffff8e0fffff0e0fffff0d0ffd0b090ff504030d0 ,\r\n                                        0x0000000000000000a0908000a08880fff0f0f0fffff8ffffffffffffffffffff ,\r\n                                        0xfffffffffffffffffffffffffff8f0fffff0e0fffff0e0fff0d0b0ff504030f0 ,\r\n                                        0x0000000000000000a0908010a08880f0a08880ffa09080ff908880ffa09080ff ,\r\n                                        0xa09080ffa09080ffa09080ffa09080ffa08880ffa08880ff706050ff504030ff ,\r\n                                        0x70584000000000009078600090786090806860e0807060ffb0a090ffe0d8d0ff ,\r\n                                        0xf0f0f0fffff8f0fffffffffffffffffff0f0f0ffd0c8c0ffe0d0d0ff504030c0 ,\r\n                                        0x50383000000000000000000090786010807060c0907860d0c0c0c0fff0f0f0ff ,\r\n                                        0xfff8ffffffffffffffffffffffffffffe0e0e0ffd0c0c0ff705840c050383010 ,\r\n                                        0x0000000000000000000000000000000090787010807860b0908070f0907870ff ,\r\n                                        0x908070ff907870ff907870ff807060ff705840ff504030d05038301000000000 ,\r\n                                        0x000000000000000000000000000000000000000090807000a0888000a0888010 ,\r\n                                        0xa088800000000000000000000000000000000000503830000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =1260\r\n                                    LayoutCachedTop =3600\r\n                                    LayoutCachedWidth =3420\r\n                                    LayoutCachedHeight =4020\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =3900\r\n                                            Top =3660\r\n                                            Width =5040\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label59\"\r\n                                            Caption =\"Reset all default settings to original values.\"\r\n                                            LayoutCachedLeft =3900\r\n                                            LayoutCachedTop =3660\r\n                                            LayoutCachedWidth =8940\r\n                                            LayoutCachedHeight =3975\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =1260\r\n                                    Top =4140\r\n                                    Width =2160\r\n                                    Height =420\r\n                                    TabIndex =4\r\n                                    Name =\"cmdOpenInstallFolder\"\r\n                                    Caption =\"Open Install Folder\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x000000000000000082c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2ea600000000082c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaa50000000082c2eaff9ed0efff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eae40000000082c2eaffd3e9f8ff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2ea2a82c2eafffcfeffff8ec8ecff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2ea6982c2eaffffffffffc1e1f5ff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaae82c2eafffffffffff3f9fdff85c3eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaea82c2eaffffffffffffffffffcde7f7ff85c3eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eab482c2eaffffffffffffffffffffffffffffffffffffffffff ,\r\n                                        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff82c2eaff ,\r\n                                        0x000000000000000082c2eaffd6ebf8ffffffffffffffffffffffffffffffffff ,\r\n                                        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff82c2eaff ,\r\n                                        0x000000000000000082c2ea6383c2eafb82c2eaff82c2eaff82c2eaff82c2eaff ,\r\n                                        0x82c2eaff82c2eaffa7d4f0fbfefeffffffffffffffffffffffffffff82c2eaff ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000082c2ea098bc7ecccb4daf2f9ffffffffffffffffffffffff82c2eaff ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x000000000000000082c2ea0f86c4ebd782c2eaff82c2eaff82c2eaff90c9ecb3 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =1260\r\n                                    LayoutCachedTop =4140\r\n                                    LayoutCachedWidth =3420\r\n                                    LayoutCachedHeight =4560\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =3900\r\n                                            Top =4200\r\n                                            Width =5040\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label241\"\r\n                                            Caption =\"Open VCS add-In Install Folder.\"\r\n                                            LayoutCachedLeft =3900\r\n                                            LayoutCachedTop =4200\r\n                                            LayoutCachedWidth =8940\r\n                                            LayoutCachedHeight =4515\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin OptionGroup\r\n                                    OverlapFlags =255\r\n                                    Left =960\r\n                                    Top =5100\r\n                                    Width =8160\r\n                                    Height =780\r\n                                    TabIndex =5\r\n                                    Name =\"Frame65\"\r\n                                    HorizontalAnchor =2\r\n\r\n                                    LayoutCachedLeft =960\r\n                                    LayoutCachedTop =5100\r\n                                    LayoutCachedWidth =9120\r\n                                    LayoutCachedHeight =5880\r\n                                    Begin\r\n                                        Begin Label\r\n                                            BackStyle =1\r\n                                            OverlapFlags =247\r\n                                            Left =1080\r\n                                            Top =4920\r\n                                            Width =1620\r\n                                            Height =315\r\n                                            BackColor =15130848\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label66\"\r\n                                            Caption =\" Remove Add-In\"\r\n                                            LayoutCachedLeft =1080\r\n                                            LayoutCachedTop =4920\r\n                                            LayoutCachedWidth =2700\r\n                                            LayoutCachedHeight =5235\r\n                                            BackThemeColorIndex =-1\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                                Begin CommandButton\r\n                                    OverlapFlags =247\r\n                                    Left =1260\r\n                                    Top =5340\r\n                                    Width =2160\r\n                                    Height =420\r\n                                    TabIndex =6\r\n                                    Name =\"cmdUninstall\"\r\n                                    Caption =\" Uninstall\"\r\n                                    OnClick =\"[Event Procedure]\"\r\n                                    LeftPadding =135\r\n                                    TopPadding =135\r\n                                    RightPadding =150\r\n                                    BottomPadding =150\r\n                                    ImageData = Begin\r\n                                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000003255d6273255d68d ,\r\n                                        0x3255d6cf3255d6ff3255d6ff3255d6cf3255d68d3255d6270000000000000000 ,\r\n                                        0x00000000000000000000000000000000000000003255d6723255d6f63255d6ff ,\r\n                                        0x3255d6ff3255d6ff3255d6ff3255d6ff3255d6ff3255d6f63255d67200000000 ,\r\n                                        0x0000000000000000000000003255d6063255d6b73255d6ff3255d6ff3255d6ff ,\r\n                                        0x3255d6ff3255d6ff3255d6ff3255d6ff3255d6ff3255d6ff3255d6ff3255d6b7 ,\r\n                                        0x3255d60600000000000000003255d6933255d6ff3255d6ff3759d7f94d6bdbe5 ,\r\n                                        0x3255d6ff3255d6ff3255d6ff3255d6ff4d6bdbe53759d7f93255d6ff3255d6ff ,\r\n                                        0x3255d690000000003255d62d3255d6fc3255d6ff3c5ed8f4eef1fccefcfcfee5 ,\r\n                                        0x4a69dbe73255d6ff3255d6ff4a69dbe7fcfcfee5eef1fcce3c5ed8f43255d6ff ,\r\n                                        0x3255d6fc3255d62d3255d6933255d6ff3255d6ff3457d6fce4e9fac8ffffffff ,\r\n                                        0xfafbfee04766dae94766dae9fafbfee0ffffffffe4e9fac83759d7f93255d6ff ,\r\n                                        0x3255d6ff3255d6903255d6db3255d6ff3255d6ff3255d6ff3759d7f9e8ecfaca ,\r\n                                        0xfffffffff9fafedcf8f9fedaffffffffe8ecfaca3759d7f93255d6ff3255d6ff ,\r\n                                        0x3255d6ff3255d6d53255d6f93255d6ff3255d6ff3255d6ff3255d6ff395bd7f6 ,\r\n                                        0xeceffbcdffffffffffffffffeceffbcd395bd7f63255d6ff3255d6ff3255d6ff ,\r\n                                        0x3255d6ff3255d6f33255d6f93255d6ff3255d6ff3255d6ff3255d6ff395bd7f6 ,\r\n                                        0xf2f4fcd3fffffffffffffffff2f4fcd33c5ed8f43255d6ff3255d6ff3255d6ff ,\r\n                                        0x3255d6ff3255d6f03255d6d83255d6ff3255d6ff3255d6ff395bd7f6eff2fcd0 ,\r\n                                        0xfffffffff5f6fdd4f5f6fdd4ffffffffeff2fcd0395bd7f63255d6ff3255d6ff ,\r\n                                        0x3255d6ff3255d6d53255d6903255d6ff3255d6ff3759d7f9ebeefbcbffffffff ,\r\n                                        0xf8f9feda4162d9ee4162d9eef8f9fedaffffffffebeefbcb3759d7f93255d6ff ,\r\n                                        0x3255d6ff3255d68d3255d62d3255d6fc3255d6ff395bd7f6ebeefbcbf9fafede ,\r\n                                        0x4464daec3255d6ff3255d6ff4464daecf9fafedeebeefbcb395bd7f63255d6ff ,\r\n                                        0x3255d6fc3255d62a000000003255d6903255d6ff3255d6ff3759d7f94766dae9 ,\r\n                                        0x3255d6ff3255d6ff3255d6ff3255d6ff4766dae93759d7f93255d6ff3255d6ff ,\r\n                                        0x3255d68d00000000000000003255d6063255d6b73255d6ff3255d6ff3255d6ff ,\r\n                                        0x3255d6ff3255d6ff3255d6ff3255d6ff3255d6ff3255d6ff3255d6ff3255d6b7 ,\r\n                                        0x3255d606000000000000000000000000000000003255d6723255d6f63255d6ff ,\r\n                                        0x3255d6ff3255d6ff3255d6ff3255d6ff3255d6ff3255d6f63255d67200000000 ,\r\n                                        0x0000000000000000000000000000000000000000000000003255d6273255d68d ,\r\n                                        0x3255d6cc3255d6fc3255d6fc3255d6cc3255d68d3255d6270000000000000000 ,\r\n                                        0x0000000000000000\r\n                                    End\r\n\r\n                                    LayoutCachedLeft =1260\r\n                                    LayoutCachedTop =5340\r\n                                    LayoutCachedWidth =3420\r\n                                    LayoutCachedHeight =5760\r\n                                    PictureCaptionArrangement =5\r\n                                    BackColor =14262935\r\n                                    BackThemeColorIndex =-1\r\n                                    BackTint =100.0\r\n                                    BorderColor =15321539\r\n                                    BorderThemeColorIndex =-1\r\n                                    BorderTint =100.0\r\n                                    HoverColor =15321539\r\n                                    HoverThemeColorIndex =-1\r\n                                    HoverTint =100.0\r\n                                    PressedColor =13072231\r\n                                    PressedThemeColorIndex =-1\r\n                                    PressedShade =100.0\r\n                                    Begin\r\n                                        Begin Label\r\n                                            OverlapFlags =247\r\n                                            Left =3900\r\n                                            Top =5400\r\n                                            Width =4500\r\n                                            Height =315\r\n                                            ForeColor =5324600\r\n                                            Name =\"Label108\"\r\n                                            Caption =\"Uninstall this add-in\"\r\n                                            LayoutCachedLeft =3900\r\n                                            LayoutCachedTop =5400\r\n                                            LayoutCachedWidth =8400\r\n                                            LayoutCachedHeight =5715\r\n                                            ForeThemeColorIndex =-1\r\n                                            ForeTint =100.0\r\n                                        End\r\n                                    End\r\n                                End\r\n                            End\r\n                        End\r\n                    End\r\n                End\r\n                Begin CommandButton\r\n                    FontUnderline = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =3720\r\n                    Top =6600\r\n                    Width =1560\r\n                    TabIndex =3\r\n                    Name =\"cmdSeeDocs\"\r\n                    Caption =\"See Docs...\"\r\n                    HyperlinkAddress =\"https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Documentation\"\r\n                    HorizontalAnchor =1\r\n                    VerticalAnchor =1\r\n                    ImageData = Begin\r\n                        0x2800000010000000100000000100200000000000000000000000000000000000 ,\r\n                        0x000000000000000000000000000000000000000000000000e0e8e000e0c8b000 ,\r\n                        0xe0d8d000e0d0c010e0d0c010d0d0c010d0d0c000d0d0d000e0e0e00000000000 ,\r\n                        0x0000000000000000000000000000000000000000f0e8e0009068303080582080 ,\r\n                        0x905010c0804820e0804820c0804810b06040108050381030d0c8c01000000000 ,\r\n                        0x000000000000000000000000e0780000e0a05010a0683070c08860f0e0c8b0ff ,\r\n                        0xf0f0f0fffffffffffffffffff0f0f0ffe0c8c0ffa07850c040301060d0c8c010 ,\r\n                        0xe0d8d0000000000000000000e0882000b0703070e0a880fffff0e0ffe0b8a0ff ,\r\n                        0xd08050ffc05820ffc05820ffd08050ffe0b8a0fff0e8e0ffb09070f050301060 ,\r\n                        0xd0c8c000e0e0e00000000000b0783030d09870f0fff0e0ffe0a890ffc05010ff ,\r\n                        0xc05010ffe0a890ffffffffffb04810ffb04810ffd0a080fff0f0e0ffa07050d0 ,\r\n                        0x50381030d0d0d000f0f0f000b0784080f0d8c0fff0c8b0ffe05820ffd05810ff ,\r\n                        0xd05010ffe08050ffe0a880ffc05010ffb04810ffb04810ffe0b8a0ffe0c8c0ff ,\r\n                        0x50401080d0d0d010f0f0f000d08040e0fff8f0fff09870fff06020ffe05820ff ,\r\n                        0xe05820fff0a890ffffffffffd05010ffc05010ffb05010ffc07850fff0f0f0ff ,\r\n                        0x804020c0e0d0c000f0f0f000d08040f0ffffffffff7840ffff6830fff06820ff ,\r\n                        0xf06020fff08850fffffffffff0c0b0ffc05820ffb05010ffb05820ffffffffff ,\r\n                        0x804820e0e0d0c010f0f0f000d08850f0ffffffffff8050ffff7030ffff6830ff ,\r\n                        0xff6830ffff6820fff09060fffff8f0fff0d8c0ffc05020ffc05820ffffffffff ,\r\n                        0x804820e0e0d8d010f0f0f000d08050c0fff8f0ffffa880ffff7040ffff8850ff ,\r\n                        0xffb090ffff7030fff06820fff09070fffffffffff08050ffd08860fffff0f0ff ,\r\n                        0x805820b0e0d8d010f0f0f000c0804070f0d8c0ffffd0c0ffff7840ffff9870ff ,\r\n                        0xffffffffffc8b0ffff9060ffffc8b0fffff8f0fff07840fff0c8b0ffe0c8b0ff ,\r\n                        0x90602070e0c8b00000000000c0884030e0a070f0fff8f0ffffc0a0ffff7840ff ,\r\n                        0xffb8a0fffff8f0fffffffffffff0e0ffff9870fff0b8a0fffff0e0ffc08850e0 ,\r\n                        0xa0682030f0e8e0000000000000000000c0884060e0b8a0f0fff8f0ffffd0c0ff ,\r\n                        0xffa880ffff8850ffff8850ffffa880fff0d0c0fffff0e0ffd0a880f0a0683060 ,\r\n                        0xe0c0a00000000000000000000000000000000000c0884060e0a070f0f0d8c0ff ,\r\n                        0xfff8f0fffffffffffffffffffff8f0fff0d8c0ffc09060e0a0703050f0b89000 ,\r\n                        0x0000000000000000000000000000000000000000f0f0f000c0884030c0804070 ,\r\n                        0xe0a070c0d09870e0d09860f0d09870d0b0784070b0784020f0e8f00000000000 ,\r\n                        0x0000000000000000000000000000000000000000000000000000000000000000 ,\r\n                        0xf0f0f000f0f0f000f0f0f000f0f0f000f0f0f00000000000f0f0f00000000000 ,\r\n                        0x0000000000000000\r\n                    End\r\n                    BackStyle =0\r\n\r\n                    LayoutCachedLeft =3720\r\n                    LayoutCachedTop =6600\r\n                    LayoutCachedWidth =5280\r\n                    LayoutCachedHeight =6960\r\n                    PictureCaptionArrangement =4\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =240\r\n                    Top =6660\r\n                    Width =3375\r\n                    Height =240\r\n                    FontSize =10\r\n                    Name =\"Label32\"\r\n                    Caption =\"joyfullservice/msaccess-vcs-addin\"\r\n                    VerticalAnchor =1\r\n                    LayoutCachedLeft =240\r\n                    LayoutCachedTop =6660\r\n                    LayoutCachedWidth =3615\r\n                    LayoutCachedHeight =6900\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                    ForeShade =50.0\r\n                End\r\n                Begin Line\r\n                    Visible = NotDefault\r\n                    LineSlant = NotDefault\r\n                    OverlapFlags =87\r\n                    Left =9540\r\n                    Top =6240\r\n                    Width =300\r\n                    Name =\"Line76\"\r\n                    LayoutCachedLeft =9540\r\n                    LayoutCachedTop =6240\r\n                    LayoutCachedWidth =9840\r\n                    LayoutCachedHeight =6240\r\n                End\r\n                Begin Line\r\n                    Visible = NotDefault\r\n                    LineSlant = NotDefault\r\n                    OverlapFlags =87\r\n                    Left =540\r\n                    Top =6240\r\n                    Width =0\r\n                    Height =240\r\n                    Name =\"Line77\"\r\n                    LayoutCachedLeft =540\r\n                    LayoutCachedTop =6240\r\n                    LayoutCachedWidth =540\r\n                    LayoutCachedHeight =6480\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =215\r\n                    TextAlign =2\r\n                    Left =7140\r\n                    Top =540\r\n                    Width =2040\r\n                    Height =240\r\n                    FontSize =10\r\n                    Name =\"lblVersion\"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =7140\r\n                    LayoutCachedTop =540\r\n                    LayoutCachedWidth =9180\r\n                    LayoutCachedHeight =780\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmVCSOptions.cls\"\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSOptions.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : Form_frmOptions\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : NOTE - Options are dynamically loaded to the controls, and then saved\r\n'           : back to the class (and subsequently project) when the user clicks the\r\n'           : save and close button. Most options will not require event coding since\r\n'           : they are mapped to their appropriate option using the control name.\r\n'           : I.e. chkUseFastSave = Options.UseFastSave\r\n'---------------------------------------------------------------------------------------\r\n\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate Enum eTableCol\r\n    etcName = 0\r\n    etcType = 1\r\n    etcHidden = 2\r\n    etcSystem = 3\r\n    etcOther = 4\r\n    etcLocal = 5\r\n    etcLinked = 6\r\nEnd Enum\r\n\r\nPrivate Enum eMapAction\r\n    emaClassToForm\r\n    emaFormToClass\r\nEnd Enum\r\n\r\n\r\n' Dictionary to stash database schemas while managing options.\r\nPublic DatabaseSchemas As Dictionary\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : chkTableShowHidden_Click\r\n' Author    : Adam Waller\r\n' Date      : 4/17/2020\r\n' Purpose   : Update the list of tables\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub chkTableShowHidden_Click()\r\n    RefreshTableDisplay\r\nEnd Sub\r\nPrivate Sub chkTableShowOther_Click()\r\n    RefreshTableDisplay\r\nEnd Sub\r\nPrivate Sub chkTableShowSystem_Click()\r\n    RefreshTableDisplay\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdAddDatabase_Click\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Add an external database connection\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdAddDatabase_Click()\r\n    DoCmd.OpenForm \"frmVCSDatabase\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : chkContributeTranslations_Click\r\n' Author    : Adam Waller\r\n' Date      : 9/2/2024\r\n' Purpose   : Set flag on whether to actively contribute to translation files.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub chkContributeTranslations_Click()\r\n    Translation.Contribute = True\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cboLanguage_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2021\r\n' Purpose   : Set language\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cboLanguage_Click()\r\n    With Translation\r\n        .Language = Nz2(cboLanguage, \"en_US\")\r\n        .SetLanguage .Language\r\n        MsgBox2 \"Language Set\", \"Changes to language settings will take effect \" & _\r\n            \"the next time you open this form.\", , vbInformation\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdBrowseTranslationsPath_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2021\r\n' Purpose   : Set path for translation files\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdBrowseTranslationsPath_Click()\r\n\r\n    Dim strFolder As String\r\n\r\n    ' Show a folder picker to select the file with source code.\r\n    With Application.FileDialog(msoFileDialogFolderPicker)\r\n        .AllowMultiSelect = False\r\n        .ButtonName = \"Select Translations Folder\"\r\n        .InitialFileName = StripSlash(txtTranslationsPath) & PathSep\r\n        .Title = \"Select Translations Files Folder\"\r\n        .Show\r\n        If .SelectedItems.Count > 0 Then\r\n            ' Selected a folder\r\n            strFolder = .SelectedItems(1) & PathSep\r\n            If FSO.FileExists(strFolder & PROJECT_NAME & \".pot\") Then\r\n                ' Has translation files\r\n                txtTranslationsPath = strFolder\r\n                Translation.TranslationsPath = strFolder\r\n            Else\r\n                MsgBox2 \"Translation files not found\", \"Required translation files were not found in this folder.\", _\r\n                    \"You selected: \" & .SelectedItems(1), vbExclamation\r\n                DoCmd.Hourglass False\r\n                Exit Sub\r\n            End If\r\n        End If\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdEditDatabase_Click\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Edit an existing database connection\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdEditDatabase_Click()\r\n    If Len(Nz(lstDatabases)) > 0 Then\r\n        ' Open the form as hidden, then load the properties\r\n        DoCmd.OpenForm \"frmVCSDatabase\", , , , , acHidden\r\n        With Form_frmVCSDatabase\r\n            .LoadSchema lstDatabases, Me.DatabaseSchemas(Nz(lstDatabases))\r\n            .Visible = True\r\n        End With\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdSyncTranslations_Click\r\n' Author    : Adam Waller\r\n' Date      : 8/23/2024\r\n' Purpose   : Load the translation files (other languages, not the master file),\r\n'           : then export all the translations, including the master file.\r\n'           : Changes to the master file are loaded during a build from source.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdSyncTranslations_Click()\r\n\r\n    With Translation\r\n        DoCmd.Hourglass True\r\n\r\n        ' Only export strings from the installed add-in project. This is where the\r\n        ' majority of the run-time strings will be generated. We don't want to\r\n        ' export the template from the development project because it may not\r\n        ' include these additional strings.\r\n        If CodeProject.FullName = GetAddInFileName Then\r\n            ' Make sure we have strings in the current database before exporting the template.\r\n            If .StringCount > 200 Then .ExportTemplate\r\n        End If\r\n\r\n        ' Load template, then other translation files\r\n        .LoadTranslations\r\n\r\n        ' Export all translations to include new strings from master\r\n        .ExportTranslations\r\n\r\n        DoCmd.Hourglass False\r\n    End With\r\n\r\n    MsgBox2 T(\"Translations Updated\"), T(\"Translation files have been syncronized with the add-in.\"), , vbInformation\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdTranslations_Click\r\n' Author    : Adam Waller\r\n' Date      : 8/7/2024\r\n' Purpose   : Show the translations tab\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdTranslations_Click()\r\n    With pgeTranslations\r\n        .Visible = True\r\n        .SetFocus\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Open\r\n' Author    : Adam Waller\r\n' Date      : 10/31/2024\r\n' Purpose   : Check for scenario that may crash Access\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Open(Cancel As Integer)\r\n    If Not DatabaseFileOpen Then\r\n        ' The table data subform may crash Access when the options form is closed with no database open.\r\n        MsgBox2 T(\"No Database Open\"), T(\"Please open a database file before configuring VCS options.\"), , vbInformation\r\n        Cancel = True\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : lstDatabases_DblClick\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Shortcut to edit the selected database\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub lstDatabases_DblClick(Cancel As Integer)\r\n    cmdEditDatabase_Click\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdDeleteDatabase_Click\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Delete a database connection\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdDeleteDatabase_Click()\r\n    Dim strName As String\r\n    strName = Nz(lstDatabases)\r\n    If Len(strName) = 0 Then\r\n        MsgBox2 T(\"Select a connection to delete\"), , , vbExclamation\r\n    Else\r\n        With Me.DatabaseSchemas\r\n            If .Exists(strName) Then\r\n                If MsgBox2(T(\"Remove Connection?\"), T(\"Are you sure you want to delete '{0}'?\", var0:=strName), _\r\n                    T(\"Click YES to remove or NO to cancel.\"), vbQuestion + vbYesNo) = vbYes Then\r\n                    .Remove strName\r\n                    RefreshSchemaList\r\n                End If\r\n            End If\r\n        End With\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadSchemas\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Load (or reload) the schemas from a dictionary object\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub RefreshSchemaList()\r\n\r\n    Dim varKey As Variant\r\n\r\n    With lstDatabases\r\n        .RowSource = vbNullString\r\n        ' Add header row\r\n        .AddItem T(\"Name;Description\", \"lstDatabases.RefreshSchemaList\", , \"Do not remove the semicolon\")\r\n        ' Update list from dictionary\r\n        For Each varKey In Me.DatabaseSchemas.Keys\r\n            .AddItem CStr(varKey) & \";\" & Me.DatabaseSchemas(varKey)(\"Description\")\r\n        Next varKey\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdCancel_Click\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Cancel (close) the form.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdCancel_Click()\r\n    DoCmd.Close acForm, Me.Name\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdClearDefaults_Click\r\n' Author    : Adam Waller\r\n' Date      : 4/16/2020\r\n' Purpose   : Clear the default and project settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdClearDefaults_Click()\r\n    Dim cDefaults As clsOptions\r\n    Set cDefaults = New clsOptions\r\n    cDefaults.SaveOptionsAsDefault\r\n    If MsgBox2(T(\"Apply to this Project?\"), _\r\n        T(\"The default options have been reset. Would you like these applied to this project as well?\"), _\r\n        T(\"Click 'Yes' to apply the default options, or 'No' to leave the current options.\"), _\r\n        vbQuestion + vbYesNo, T(\"Version Control System\")) = VbMsgBoxResult.vbYes Then\r\n        cmdRestoreDefaults_Click\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadTableList\r\n' Author    : Adam Waller\r\n' Date      : 4/16/2020\r\n' Purpose   : Load the list of tables in the current database, and merge in the list\r\n'           : of tables where we are opting to save data.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub LoadTableList()\r\n\r\n    Dim varKey As Variant\r\n    Dim strFormat As String\r\n    Dim intFormat As eTableDataExportFormat\r\n    Dim strName As String\r\n    Dim dbs As DAO.Database\r\n    Dim rstTableData As DAO.Recordset\r\n    Dim rstSource As DAO.Recordset\r\n    Dim strSql As String\r\n    Dim lngFlags As Long\r\n    Dim lngType As Long\r\n\r\n    ' Reset list of tables\r\n    Set dbs = CodeDb\r\n    dbs.Execute \"DELETE FROM tblTableData;\", dbFailOnError\r\n\r\n    ' Open table to load records\r\n    Set rstTableData = dbs.OpenRecordset(\"SELECT * FROM tblTableData;\", dbOpenDynaset)\r\n\r\n    ' Get list of tables if we have a database file open.\r\n    If DatabaseFileOpen Then\r\n        ' ADP does not have local tables. Export data is probably best handled with SQL Server tools.\r\n        If Not (CurrentProject.ProjectType = acADP) Then\r\n            ' Note that Access SQL does not support bitwise \"and\" operator\r\n            ' (Also known as BAND in ADO) so we will check the bit flags in VBA instead.\r\n            strSql = _\r\n                \"SELECT o.Name, o.Type, o.Flags \" & _\r\n                \"FROM MSysObjects AS o \" & _\r\n                \"WHERE o.Type IN (1, 4, 6) \" & _\r\n                \"ORDER BY o.Name;\"\r\n\r\n            Set rstSource = CurrentDb.OpenRecordset(strSql, dbOpenSnapshot)\r\n            With rstSource\r\n                Do While Not .EOF\r\n                    ' Determine type of table\r\n                    lngFlags = Nz(!Flags, 0)\r\n                    lngType = Nz(!Type, 0)\r\n                    If (lngFlags < 0) Or BitSet(lngFlags, 1) Then\r\n                        ' Don't include read-only or deeply hidden system tables.\r\n                        ' https://isladogs.co.uk/purpose-of-system-tables-2/index.html#TFE\r\n                    Else\r\n                        rstTableData.AddNew\r\n                            rstTableData!TableName = Nz(!Name)\r\n                            rstTableData!Flags = Nz(!Flags)\r\n                            rstTableData!IsSystem = BitSet(lngFlags, 2)\r\n                            rstTableData!IsHidden = BitSet(lngFlags, 8)\r\n                            rstTableData!IsLocal = (lngType = 1)\r\n                            ' Determine table icon\r\n                            rstTableData!TableIcon = GetTableIcon(etcLinked)    ' Default to linked table if no match.\r\n                            If rstTableData!IsLocal Then rstTableData!TableIcon = GetTableIcon(etcLocal)\r\n                            If rstTableData!IsSystem Then rstTableData!TableIcon = GetTableIcon(etcSystem)\r\n                        rstTableData.Update\r\n                    End If\r\n                    .MoveNext\r\n                Loop\r\n                .Close\r\n            End With\r\n        End If\r\n    End If\r\n\r\n    ' Add in the list of saved tables, adding into the sorted location\r\n    If Not Options.TablesToExportData Is Nothing Then\r\n        ' Loop through each table in the saved table list\r\n        For Each varKey In Options.TablesToExportData.Keys\r\n            strName = CStr(varKey)\r\n            strFormat = Options.TablesToExportData.Item(varKey)(\"Format\")\r\n            intFormat = Options.GetTableExportFormat(strFormat)\r\n\r\n            With rstTableData\r\n                .FindFirst \"[TableName]='\" & Replace$(strName, \"'\", \"''\") & \"'\"\r\n                If .NoMatch Then\r\n                    .AddNew\r\n                    !TableName = strName\r\n                    !TableIcon = GetTableIcon(etcOther)\r\n                    !FormatType = intFormat\r\n                    !IsOther = True\r\n                    .Update\r\n                Else\r\n                    .Edit\r\n                    !FormatType = intFormat\r\n                    .Update\r\n                End If\r\n            End With\r\n        Next varKey\r\n    End If\r\n\r\n    ' Close recordset after adding records\r\n    rstTableData.Close\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveTableList\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Save the list of tables that should have its data exported to options file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SaveTableList()\r\n\r\n    Dim rstTableData As DAO.Recordset\r\n    Dim dTables As Dictionary\r\n    Dim dTable As Dictionary\r\n\r\n    ' Save list of tables to export data\r\n    Set dTables = New Dictionary\r\n    dTables.CompareMode = TextCompare\r\n\r\n    ' ADP project type shouldn't export data; set it as an empty list.\r\n    If Not (CurrentProject.ProjectType = acADP) Then\r\n        Set rstTableData = CodeDb.OpenRecordset( _\r\n            \"SELECT TableName, FormatType FROM tblTableData \" & _\r\n            \"WHERE FormatType <> 0 ORDER BY TableName;\", dbOpenForwardOnly)\r\n        With rstTableData\r\n            Do Until .EOF\r\n                Set dTable = New Dictionary\r\n                dTable.CompareMode = TextCompare\r\n                dTable(\"Format\") = Options.GetTableExportFormatName(Nz(!FormatType, 0))\r\n                dTables.Add Nz(!TableName), dTable\r\n                .MoveNext\r\n            Loop\r\n            .Close\r\n        End With\r\n    End If\r\n\r\n    Set Options.TablesToExportData = dTables\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddUpdateTableInList\r\n' Author    : Adam Waller\r\n' Date      : 4/21/2020\r\n' Purpose   : Updates the a table in the collection, adding it if it does not exist.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub AddUpdateTableInList(strName As String, lngFormatType As eTableDataExportFormat, blnHidden As Boolean, blnSystem As Boolean, blnOther As Boolean, blnLocal As Boolean)\r\n\r\n    Dim rstClone As DAO.Recordset\r\n    Dim rstActive As DAO.Recordset\r\n\r\n    Set rstClone = Me.sfrmTableData.Form.RecordsetClone\r\n    Set rstActive = Me.sfrmTableData.Form.Recordset\r\n\r\n    With rstActive\r\n\r\n        ' Look for matching table name\r\n        rstClone.FindFirst \"TableName='\" & Replace$(strName, \"'\", \"''\") & \"'\"\r\n        If rstClone.NoMatch Then\r\n            ' Add new table to this list\r\n            Me.sfrmTableData.Form.AllowAdditions = True\r\n            .AddNew\r\n            !TableName = strName\r\n            !TableIcon = GetTableIcon(etcOther)\r\n        Else\r\n            .Bookmark = rstClone.Bookmark\r\n            .Edit\r\n        End If\r\n\r\n        ' Update remaining fields\r\n        !FormatType = lngFormatType\r\n        !IsHidden = blnHidden\r\n        !IsSystem = blnSystem\r\n        !IsOther = blnOther\r\n        !IsLocal = blnLocal\r\n        .Update\r\n        Me.sfrmTableData.Form.AllowAdditions = False\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RefreshTableDisplay\r\n' Author    : Adam Waller\r\n' Date      : 4/17/2020\r\n' Purpose   : Update the subform's display\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RefreshTableDisplay()\r\n\r\n    Dim strFilter As String\r\n    Dim strOrderBy As String\r\n\r\n    If Me.chkTableShowOther Then\r\n        strOrderBy = strOrderBy & \", IIf([IsOther], 0, 1)\"\r\n    Else\r\n        strFilter = strFilter & \" AND [IsOther] = 0\"\r\n    End If\r\n\r\n    If Me.chkTableShowSystem Then\r\n        strOrderBy = strOrderBy & \", IIf([IsSystem], 0, 1)\"\r\n    Else\r\n        strFilter = strFilter & \" AND [IsSystem] = 0\"\r\n    End If\r\n\r\n    If Me.chkTableShowHidden Then\r\n        strOrderBy = strOrderBy & \", IIf([IsHidden], 0, 1)\"\r\n    Else\r\n        strFilter = strFilter & \" AND [IsHidden] = 0\"\r\n    End If\r\n\r\n    strOrderBy = strOrderBy & \", IIf([IsLocal], 0, 1), [TableName]\"\r\n\r\n    If Len(strFilter) Then\r\n        strFilter = \" WHERE \" & Mid$(strFilter, 6)\r\n    End If\r\n\r\n    If Len(strOrderBy) Then\r\n        strOrderBy = \" ORDER BY \" & Mid$(strOrderBy, 3)\r\n    End If\r\n\r\n    Dim strSql As String\r\n    strSql = _\r\n        \"SELECT d.TableIcon, d.TableName, d.FormatType, d.IsHidden, d.IsSystem, d.IsOther, d.IsLocal \" & _\r\n        \"FROM tblTableData AS d \" & _\r\n        strFilter & _\r\n        strOrderBy\r\n\r\n    Me.sfrmTableData.Form.RecordSource = strSql\r\n\r\n    ' Update captions with counts\r\n    Me.lblTableShowHidden.Caption = GetCaptionWithCount(T(\"Show Hidden\"), \"d.IsHidden = True AND d.IsSystem = \" & chkTableShowSystem)\r\n    Me.lblTableShowSystem.Caption = GetCaptionWithCount(T(\"Show System\"), \"d.IsSystem = True AND d.IsHidden = \" & chkTableShowHidden)\r\n    Me.lblTableShowOther.Caption = GetCaptionWithCount(T(\"Show Other  \"), \"d.IsOther  = True\")\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetCaptionWithCount\r\n' Author    : bclothier\r\n' Date      : 5/15/2023\r\n' Purpose   : Provides caption with a count appended if non-zero.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetCaptionWithCount(TemplateCaption As String, CountFilter As String) As String\r\n    Dim rs As DAO.Recordset\r\n    Set rs = CodeDb.OpenRecordset( _\r\n        \"SELECT COUNT(d.TableName) FROM tblTableData AS d WHERE \" & CountFilter, dbOpenSnapshot)\r\n    If rs.EOF = False Then\r\n        If Nz(rs.Fields(0).Value, 0) Then\r\n            GetCaptionWithCount = TemplateCaption & \" (\" & rs.Fields(0).Value & \")\"\r\n            Exit Function\r\n        End If\r\n    End If\r\n    GetCaptionWithCount = TemplateCaption\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdOpenInstallFolder_Click\r\n' Author    : Adam Waller\r\n' Date      : 7/6/2023\r\n' Purpose   : Open the installation folder\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdOpenInstallFolder_Click()\r\n    Application.FollowHyperlink modInstall.GetInstallSettings.strInstallFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdRestoreDefaults_Click\r\n' Author    : Adam Waller\r\n' Date      : 4/16/2020\r\n' Purpose   : Restore the default options to this project.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdRestoreDefaults_Click()\r\n    Options.LoadDefaultOptions\r\n    MapControlsToOptions emaClassToForm\r\n    MsgBox2 T(\"Default Options Restored\"), _\r\n        T(\"The system default options have been restored to this project.\"), _\r\n        T(\"Be sure to click Save and Close when you are finished making changes.\"), _\r\n        vbInformation, T(\"Version Control System\")\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdSaveAndClose_Click\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Save and close the options dialog\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdSaveAndClose_Click()\r\n\r\n    ' Make sure we actually have a file open\r\n    If Not DatabaseFileOpen Then\r\n        MsgBox2 T(\"No Database File Open\"), _\r\n            T(\"You must have a database file open to save VCS options to a source folder.\"), _\r\n            T(\"Please open a database file before saving options for a project.\"), vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Save options and close.\r\n    MapControlsToOptions emaFormToClass\r\n    Options.SaveOptionsForProject\r\n    DoCmd.Close acForm, Me.Name\r\n\r\n    ' Update main form if options changed.\r\n    If IsLoaded(acForm, \"frmVCSMain\", True) Then Form_frmVCSMain.Form_Load\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdSaveAsDefault_Click\r\n' Author    : Adam Waller\r\n' Date      : 4/16/2020\r\n' Purpose   : Save current options as default for projects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdSaveAsDefault_Click()\r\n\r\n    Dim strPath As String\r\n\r\n    ' Note that we can't save an absolute path as default, or we will potentially\r\n    ' create some major issues with source files being overwritten and lost.\r\n    strPath = Nz(txtExportFolder)\r\n    If strPath <> vbNullString Then\r\n        If InStr(1, strPath, \"%dbName%\", vbTextCompare) < 1 Then\r\n            MsgBox2 T(\"Invalid Export Path for Default\"), _\r\n                T(\"If you specify an absolute or relative Export Path as a default option,\" & vbCrLf & _\r\n                \"you must include the %dbName% placeholder to keep the paths unique.\"), _\r\n                T(\"Please update the Export Path and try again.\"), vbExclamation\r\n            Exit Sub\r\n        End If\r\n    End If\r\n\r\n    ' Load the options from the form and save as default\r\n    MapControlsToOptions emaFormToClass\r\n    Options.SaveOptionsAsDefault\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Load\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Load options for this project\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Load()\r\n\r\n    Dim intFormat As eTableDataExportFormat\r\n    Dim intSanitizeLevel As eSanitizeLevel\r\n\r\n    ' Set version\r\n    lblVersion.Caption = GetVCSVersion\r\n\r\n    ' Apply translation\r\n    cboLanguage = Translation.Language\r\n    Translation.ApplyTo Me\r\n\r\n    MapControlsToOptions emaClassToForm\r\n    RefreshTableDisplay\r\n    RefreshSchemaList\r\n\r\n    ' Load list of table data export formats\r\n    Dim frmTableData As Form_frmVCSTableData\r\n    Set frmTableData = Me.sfrmTableData.Form\r\n    With frmTableData.cboFormatType\r\n        .RowSource = vbNullString\r\n        For intFormat = eTableDataExportFormat.etdNoData To eTableDataExportFormat.[_Last] - 1\r\n            .AddItem intFormat & \";\"\"\" & Options.GetTableExportFormatName(intFormat) & \"\"\"\", intFormat\r\n        Next intFormat\r\n        Me.cboFormatTypeForOther.RowSource = .RowSource\r\n        Me.cboFormatTypeForOther.RemoveItem etdNoData\r\n    End With\r\n\r\n    ' Load general sanitize options\r\n    With Me.cboSanitizeLevel\r\n        .RowSource = vbNullString\r\n        For intSanitizeLevel = eslMinimal To (eSanitizeLevel.[_Last] - 1)\r\n            .AddItem intSanitizeLevel & \";\" & Options.GetSanitizeLevelName(intSanitizeLevel)\r\n        Next intSanitizeLevel\r\n    End With\r\n\r\n    ' Load color sanitize options\r\n    With Me.cboSanitizeColors\r\n        .RowSource = vbNullString\r\n        For intSanitizeLevel = eslNone To (eSanitizeLevel.[_Last] - 1)\r\n            .AddItem intSanitizeLevel & \";\" & Options.GetSanitizeLevelName(intSanitizeLevel)\r\n        Next intSanitizeLevel\r\n    End With\r\n\r\n    ' Load language options\r\n    With Translation\r\n        cboLanguage = .Language\r\n        chkContributeTranslations = .Contribute\r\n        txtTranslationsPath = .TranslationsPath\r\n    End With\r\n\r\n    ' Make form resizable (helpful with table selection when many tables are listed)\r\n    MakeDialogResizable Me\r\n\r\n    ' Set inital column sizing for table data\r\n    DoEvents\r\n    Form_frmVCSTableData.Form_Resize\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MapControlsToOptions\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Map the form controls to the options, performing the specified action.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub MapControlsToOptions(eAction As eMapAction)\r\n\r\n    Dim pge As Access.Page\r\n    Dim ctl As Access.Control\r\n    Dim strKey As String\r\n    Dim dSettings As Dictionary\r\n\r\n    ' Loop through each page\r\n    For Each pge In tabOptions.Pages\r\n        For Each ctl In pge.Controls\r\n            Select Case ctl.ControlType\r\n                Case Access.acCheckBox, Access.acTextBox, Access.acComboBox\r\n                    strKey = Mid$(ctl.Name, 4)\r\n                    If pge Is pgePrinterSettings Then\r\n                        ' Export print options\r\n                        If eAction = emaClassToForm Then\r\n                            ctl = Options.ExportPrintSettings(strKey)\r\n                        ElseIf eAction = emaFormToClass Then\r\n                            Set dSettings = Options.ExportPrintSettings\r\n                            dSettings(strKey) = CBool(ctl)\r\n                        End If\r\n                    Else\r\n                        Select Case ctl.Name\r\n                            Case Me.chkTableShowHidden.Name, Me.chkTableShowSystem.Name, Me.chkTableShowOther.Name, _\r\n                                Me.cboFormatTypeForOther.Name, Me.txtOtherTableName.Name, _\r\n                                Me.cboLanguage.Name, Me.chkContributeTranslations.Name, Me.txtTranslationsPath.Name\r\n                                ' Skip these exceptions.\r\n                            Case Me.cboDiffTool.Name\r\n                                ' This is saved on the computer profile (registry)\r\n                                If eAction = emaClassToForm Then\r\n                                    ctl = Diff.ToolName\r\n                                Else\r\n                                    Diff.ToolName = Nz(ctl)\r\n                                End If\r\n                            Case Me.cboOpenRepository.Name\r\n                                ' Also saved to registry\r\n                                If eAction = emaClassToForm Then\r\n                                    ctl = Nz2(GetSetting(PROJECT_NAME, \"Options\", \"Open Repository Tool\"), 0)\r\n                                Else\r\n                                    SaveSetting PROJECT_NAME, \"Options\", \"Open Repository Tool\", CStr(Nz(cboOpenRepository, 0))\r\n                                End If\r\n                            Case Else\r\n                                ' Get option name from control name following prefix.\r\n                                If eAction = emaClassToForm Then\r\n                                    ctl = CallByName(Options, strKey, VbGet)\r\n                                ElseIf eAction = emaFormToClass Then\r\n                                    ' Check for any hooks on option change\r\n                                    OnOptionChange strKey, Nz(ctl.Value)\r\n                                    ' Set the option value\r\n                                    CallByName Options, strKey, VbLet, Nz(ctl.Value)\r\n                                End If\r\n                        End Select\r\n                    End If\r\n                Case Access.acListBox\r\n                    ' Special handling for lists\r\n                Case Else\r\n                    ' Ignore other controls\r\n            End Select\r\n        Next ctl\r\n    Next\r\n\r\n    ' Table list for selecting tables to save data.\r\n    If eAction = emaClassToForm Then\r\n        LoadTableList\r\n    ElseIf eAction = emaFormToClass Then\r\n        SaveTableList\r\n    End If\r\n\r\n    ' Database schemas\r\n    If eAction = emaClassToForm Then\r\n        Set DatabaseSchemas = CloneDictionary(Options.SchemaExports)\r\n    ElseIf eAction = emaFormToClass Then\r\n        Set Options.SchemaExports = CloneDictionary(DatabaseSchemas)\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OnOptionChange\r\n' Author    : Adam Waller\r\n' Date      : 11/9/2023\r\n' Purpose   : A hook to run special code or processing when specific options are changed\r\n'           : from their existing values. Add any specific rules here.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub OnOptionChange(strName As String, varNewValue As Variant)\r\n\r\n    Dim blnChanged As Boolean\r\n\r\n    ' Determine if the option was changed\r\n    blnChanged = Not (CVar(CallByName(Options, strName, VbGet)) = varNewValue)\r\n    If Not blnChanged Then Exit Sub\r\n\r\n    ' Define actual rules here\r\n    Select Case strName\r\n\r\n        ' If a user turns on the option to split files\r\n        Case \"SplitLayoutFromVBA\"\r\n            If varNewValue = True Then\r\n                If Git.Installed Then\r\n                    If Git.IsInsideRepository Then\r\n                        ' Prompt user with suggestion\r\n                        If MsgBox2(T(\"May I make a Suggestion?\"), _\r\n                            T(\"This project appears to be within a Git repository. This add-in includes a special utility \" & _\r\n                            \"that can split the files (layout and VBA) while preserving this history of previous changes in BOTH files.\"), _\r\n                            T(\"Would you like to see additional information on this from the wiki?\"), vbQuestion + vbYesNo) = vbYes Then\r\n                            FollowHyperlink \"https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Split-Files\"\r\n                        End If\r\n                    End If\r\n                End If\r\n            End If\r\n\r\n    End Select\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdAddOtherTableData_Click\r\n' Author    : Adam Waller\r\n' Date      : 4/16/2020\r\n' Purpose   : Select another table not used in the current database\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdAddOtherTableData_Click()\r\n    Dim strTable As String\r\n    If Nz(Me.cboFormatTypeForOther.Value, \"No Data\") = T(\"No Data\") Then\r\n        MsgBox2 T(\"Please select format\"), T(\"Select the format to save table data before adding the table to the list.\"), , vbInformation\r\n    Else\r\n        strTable = Nz(Me.txtOtherTableName.Value, vbNullString)\r\n        If strTable <> vbNullString Then\r\n            AddUpdateTableInList strTable, Nz(Me.cboFormatTypeForOther.Value), False, False, True, False\r\n            chkTableShowOther = True\r\n            RefreshTableDisplay\r\n        End If\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdPrintSettingsOptions_Click\r\n' Author    : Adam Waller\r\n' Date      : 11/9/2020\r\n' Purpose   : View advanced options for saving print settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdPrintSettingsOptions_Click()\r\n    With pgePrinterSettings\r\n        .Visible = True\r\n        .SetFocus\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdUninstall_Click\r\n' Author    : Adam Kauffman\r\n' Date      : 5/25/2020\r\n' Purpose   : Remove the add-in.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdUninstall_Click()\r\n    UninstallVCSAddin\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : txtExportFolder_BeforeUpdate\r\n' Author    : Adam Waller\r\n' Date      : 5/6/2021\r\n' Purpose   : Make sure we have a valid entry, blank, absolute path, or relative path.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub txtExportFolder_BeforeUpdate(Cancel As Integer)\r\n\r\n    Dim strPath As String\r\n\r\n    strPath = Nz(txtExportFolder)\r\n    If strPath <> vbNullString Then\r\n        If (Left(strPath, 1) = PathSep) Or _\r\n            (InStr(2, strPath, \":\" & PathSep) > 0) Then\r\n            ' Looks like a valid path\r\n        Else\r\n            MsgBox2 T(\"Invalid Export Folder\"), _\r\n                T(\"This does not appear to be a valid relative or absolute path.\"), _\r\n                T(\"Please see the wiki documentation for more detail and examples.\"), vbExclamation\r\n            Cancel = True\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTableIcon\r\n' Author    : Adam Waller & Indigo744\r\n' Date      : 11/11/2020\r\n' Purpose   : Get an icon depending on the type of table\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetTableIcon(ByRef lngColumn As eTableCol) As String\r\n    Select Case lngColumn\r\n        Case etcSystem\r\n            ' System tables\r\n            ' Uses symbol DOTTED SQUARE\r\n            ' http://www.fileformat.info/info/unicode/char/2b1a/index.htm\r\n            GetTableIcon = ChrW$(11034)\r\n        Case etcLocal\r\n            ' Local tables\r\n            ' Uses symbol SQUARED PLUS\r\n            ' http://www.fileformat.info/info/unicode/char/229e/index.htm\r\n            GetTableIcon = ChrW$(8862)\r\n        Case etcOther\r\n            ' \"Other\" tables\r\n            ' Uses symbol RIGHTWARDS ARROW WITH HOOK\r\n            ' http://www.fileformat.info/info/unicode/char/21aa/index.htm\r\n            GetTableIcon = ChrW$(8618)\r\n        Case Else\r\n            ' Anything else would be a linked table\r\n            ' Uses symbol EARTH GLOBE AMERICAS\r\n            ' https://www.fileformat.info/info/unicode/char/1f30e/index.htm\r\n            GetTableIcon = ChrW$(55356) & ChrW$(57102)\r\n    End Select\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSSplitFiles.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    PopUp = NotDefault\r\n    RecordSelectors = NotDefault\r\n    AutoCenter = NotDefault\r\n    NavigationButtons = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =0\r\n    ScrollBars =0\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =10800\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =245\r\n    Left =3225\r\n    Top =2430\r\n    Right =28545\r\n    Bottom =14895\r\n    RecSrcDt = Begin\r\n        0x79e78b777268e540\r\n    End\r\n    Caption =\"Version Control System\"\r\n    DatasheetFontName =\"Calibri\"\r\n    OnLoad =\"[Event Procedure]\"\r\n    AllowDatasheetView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Rectangle\r\n            SpecialEffect =3\r\n            BackStyle =0\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Line\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Image\r\n            BackStyle =0\r\n            OldBorderStyle =0\r\n            BorderLineStyle =0\r\n            SizeMode =3\r\n            PictureAlignment =2\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin CommandButton\r\n            FontSize =11\r\n            FontWeight =400\r\n            FontName =\"Calibri\"\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =1\r\n            Gradient =12\r\n            BackThemeColorIndex =4\r\n            BackTint =60.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =4\r\n            BorderTint =60.0\r\n            ThemeFontIndex =1\r\n            HoverThemeColorIndex =4\r\n            HoverTint =40.0\r\n            PressedThemeColorIndex =4\r\n            PressedShade =75.0\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n        End\r\n        Begin CheckBox\r\n            BorderLineStyle =0\r\n            LabelX =230\r\n            LabelY =-30\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin OptionGroup\r\n            SpecialEffect =3\r\n            BorderLineStyle =0\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin ListBox\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AllowValueListEdits =1\r\n            InheritValueList =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin ComboBox\r\n            AddColon = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AllowValueListEdits =1\r\n            InheritValueList =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =2\r\n            ForeShade =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Tab\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n            UseTheme =1\r\n            Shape =3\r\n            BackThemeColorIndex =1\r\n            BackShade =85.0\r\n            BorderLineStyle =0\r\n            BorderThemeColorIndex =2\r\n            BorderTint =60.0\r\n            HoverThemeColorIndex =1\r\n            PressedThemeColorIndex =1\r\n            HoverForeThemeColorIndex =0\r\n            HoverForeTint =75.0\r\n            PressedForeThemeColorIndex =0\r\n            PressedForeTint =75.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n        End\r\n        Begin Page\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =7200\r\n            BackColor =15130848\r\n            Name =\"Detail\"\r\n            AlternateBackColor =15130848\r\n            Begin\r\n                Begin Line\r\n                    BorderWidth =5\r\n                    OverlapFlags =85\r\n                    Top =1080\r\n                    Width =10800\r\n                    BorderColor =15321539\r\n                    Name =\"Line10\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedTop =1080\r\n                    LayoutCachedWidth =10800\r\n                    LayoutCachedHeight =1080\r\n                    BorderThemeColorIndex =-1\r\n                End\r\n                Begin Rectangle\r\n                    SpecialEffect =0\r\n                    BackStyle =1\r\n                    OldBorderStyle =0\r\n                    OverlapFlags =93\r\n                    Width =10800\r\n                    Height =1020\r\n                    BackColor =5324600\r\n                    Name =\"Box1\"\r\n                    HorizontalAnchor =2\r\n                    LayoutCachedWidth =10800\r\n                    LayoutCachedHeight =1020\r\n                    BackThemeColorIndex =-1\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =215\r\n                    Left =540\r\n                    Top =240\r\n                    Width =6060\r\n                    Height =540\r\n                    FontSize =18\r\n                    FontWeight =700\r\n                    Name =\"lblOptions\"\r\n                    Caption =\"Split Files and Preserve History\"\r\n                    LayoutCachedLeft =540\r\n                    LayoutCachedTop =240\r\n                    LayoutCachedWidth =6600\r\n                    LayoutCachedHeight =780\r\n                    ForeThemeColorIndex =1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin TextBox\r\n                    EnterKeyBehavior = NotDefault\r\n                    OverlapFlags =85\r\n                    IMESentenceMode =3\r\n                    Left =720\r\n                    Top =1800\r\n                    Width =6120\r\n                    Height =3960\r\n                    TabIndex =1\r\n                    Name =\"txtFileList\"\r\n                    DefaultValue =\"=\\\"c:\\\\example\\\\original.txt|c:\\\\example\\\\copy.txt\\\" & Chr(13) & Chr(10) & \\\"c:\\\\\"\r\n                        \"example\\\\original2.txt|c:\\\\example\\\\copy2.txt\\\"\"\r\n                    HorizontalAnchor =2\r\n                    VerticalAnchor =2\r\n\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =1800\r\n                    LayoutCachedWidth =6840\r\n                    LayoutCachedHeight =5760\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            Left =720\r\n                            Top =1440\r\n                            Width =2280\r\n                            Height =315\r\n                            Name =\"Label241\"\r\n                            Caption =\"List of files to split:\"\r\n                            LayoutCachedLeft =720\r\n                            LayoutCachedTop =1440\r\n                            LayoutCachedWidth =3000\r\n                            LayoutCachedHeight =1755\r\n                        End\r\n                    End\r\n                End\r\n                Begin CommandButton\r\n                    OverlapFlags =85\r\n                    Left =7560\r\n                    Top =3960\r\n                    Width =2400\r\n                    Height =780\r\n                    Name =\"cmdSplitFiles\"\r\n                    Caption =\"   Split Files\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    Picture =\"Export.png\"\r\n                    HorizontalAnchor =1\r\n                    ImageData = Begin\r\n                        0x89504e470d0a1a0a0000000d494844520000001e0000001e08060000003b30ae ,\r\n                        0xa2000031697a5458745261772070726f66696c65207479706520657869660000 ,\r\n                        0x78daad9c698e2639cea4ffeb147d04edcb71b402738339fe3c468fccaaaceefe ,\r\n                        0x80194c052a33f25ddc2591349a5194bbfbbfffd773fffad7bf42c835bb5c5aaf ,\r\n                        0xa356cf7f79e41127bf74fffdf7fd1d7cb63fbf7fdc9ff7c29fafbbdf6f445e4a ,\r\n                        0xfc9dbe7fd69fd7c3e4f5f2d7175afe797dfdf9ba6bfbe73afde742e1f785edbf ,\r\n                        0xa43bebf79fcff59f0ba5f8bd1e7efeedc6cff766fedb747efeaffef734be51fe ,\r\n                        0xe3dfb9b118a770bd145dbc2924cf9f5d7749dfff93ff037f8654f8904f8ddf0b ,\r\n                        0x3f7a25ffe7b573bf7ffdc7e2fdfeed1f6be7e7cfebe9cfa570bfc75bffb1463f ,\r\n                        0xaf87f28fd7d3efdbc43fadf6d79dff786385b8fcdffffbdbdabd77fa7bf79bdd ,\r\n                        0xc48946aeee6752bfa662bff1412e92937dadf2d3f8bff07bb39fc14f678a1b8b ,\r\n                        0x1dacb9f8d92e8c1059ed177238618617aefdbdc3668839ded8f83bc61d93bdd6 ,\r\n                        0x538b236e3346d64f78b1a5918e4b1d3b6dac967839fe1e4bb0fb0ebbdf0e9d3b ,\r\n                        0x9fc02763e062816ffcdb8ffb4f2ffebffcfcbed07b72dd107cffbd568c2bcaa7 ,\r\n                        0x19862ca73ff9140609ef674d8badaffdb8bff98dff9b6113162cb6cc9d094ebf ,\r\n                        0xbe4bac12fef2ad64764e7caef8ecfc171aa19d9f0bb044dcbb309890b080afb8 ,\r\n                        0x77a8c1b7181b809162c73e9391c794e3c202a19478827bd826a58a717ad4bdf9 ,\r\n                        0x4e0bf6d958e2f732d082214aaa844ac7421363e55cf09f963b3e34899eec4a29 ,\r\n                        0xb5b4d2cb28b3a69a6ba9b5b62a8c9a2db5dc4aabadb5de469b3df5dc4bafbdf5 ,\r\n                        0xde479f238e048495514773a38f31e6e4a6934b4fbe3df9c49c2baeb4f22aabae ,\r\n                        0xb6fa1a6b6edc67e75d76dd6df73df63cf1a443f89f7a9a3bfd8c336fb8b8d2cd ,\r\n                        0xb7dc7adbed77dcf9f0b5975e7ee5d5d75e7fe3cddf56fbb1ea9f560bffb0dcff ,\r\n                        0x6cb5f06335592cdbe7da5f56e3e5d67e5d22084e8a6c86c5620e58bcc9023874 ,\r\n                        0x94cd7c0f3947594e36f32312142562b550649c1364312c986f88e585dfb6fbcb ,\r\n                        0x72ffa3dd5cc9ff57768bffcd724ea6fbff613927d3fd58eedfedf61fac76a665 ,\r\n                        0x94640652146a4d7d7a001b93676af7fa3799670fb7c7952b8bf018f52de5ad99 ,\r\n                        0x16d38b9820a453d7f169a7f65e94151f01dc09662e747badab8f9acbd9ed96c0 ,\r\n                        0x08e33a25edc048c820bdd4dd574e6160da174b6eaf9dd9c7e2264d9e70e2ccd9 ,\r\n                        0xbdb55e3f2dc7c824f1c13b63d45dd6f585dbfffd45bdd4d29e7c3d8d9df28c8c ,\r\n                        0xf5ad54e46498bfaf771fbed1ce98c0f5b881911e8279e18a61efbcce9cb8ca29 ,\r\n                        0x7ca29efd4ac7a19e61927d2b1eaee0dab9bf47201c18ccadced2d33b3857cb25 ,\r\n                        0x9679562f37f2c1b0dfc38f733e4c7a62fa183a2b50fc8c6ee10f61cfd72797cd ,\r\n                        0x6532532d9f6f8fb55b98b0c5f6e66c7ab5e5d3265f0083324b371f1e45ced983 ,\r\n                        0x21b9b378211e4659f5fd8c5d987a8453ecc3a48faeb336769c24b05ea3ef3bf3 ,\r\n                        0x0b96223c70bd55f3c3e7965bbee3247330d9135e9e0ce19ec412ec574762f5f0 ,\r\n                        0x8e3ceeab77e7be7d39797bbecbf836962cfbe896bce75e21baf01566d15aac33 ,\r\n                        0xb51d6555be992e067b04ca7917b80d07dff49a737af3f2d7bb0b3f3db51e06e3 ,\r\n                        0x18724d8fe9e196384ff1dbeed6de39fcff6e7d2fcc7e5e612ccd6cc5c51b7ed8 ,\r\n                        0xde586f5792ae9cffba91f76838ee78d81617d90760386d359fefbd2fda4d6184 ,\r\n                        0x7786b6b8feac4577793b69981907397797c385f6637507899435bfcc3ee1b7c4 ,\r\n                        0x25f1b7fad997a5c4d687907f31df1dce19bd9edbf3258e27810acf7ab31537db ,\r\n                        0xc135b1410bf9f0d184090022ecd8083da14d0b7b608c97c5b1f67df932d57e43 ,\r\n                        0x660473dda7d54acd5dd825c4a1acdca69f27f7c1f4f8da67c633a11adb56b6dd ,\r\n                        0x511879888c27ca27f0593e3be5fe6325070aa44c9a4ecc83f5c3e34e03be887b ,\r\n                        0x0852b8bf0c4dd0847ecac5cfd6226623467edd0c93e6ab9ee827b79eebef02b6 ,\r\n                        0xa706f91e18526a638cbdb37227a6459a15eeb0ec89abf23bebb85e62b8987410 ,\r\n                        0xc840edc0574faf998bccba3c2e0087ca27e5351ee1f92c34b85e0d63168574d7 ,\r\n                        0x2bafd92dde82383502e84e87fbde0d0f7b38096eb41fa83708dfc7bd2baef905 ,\r\n                        0x341e90f94452f0f28d6997c717f7008e1b7093dce77e8c39dff1eac4e9827c97 ,\r\n                        0xf5da1578df7b74681bd920322abc6fb7a699cfd86a1dd94702e76522c21d7ca4 ,\r\n                        0x1e3201d3605ce732a94bae192fc57915887807082cd3ed4a367c3bc661beb99f ,\r\n                        0x793a64aeae4d16610255f4925cc8eac056b0f35d832b06ae3d0a8e72d6f795d0 ,\r\n                        0x09497ccbd712671b3e570f6491468a7729415f44392f34f544b2eccb6713a895 ,\r\n                        0x3053d4ee0b1891538157ec16d3d927dcc212b70b021d390ae0bf9c709241fe06 ,\r\n                        0x5c160af049ac62e9f2c3d3279174f852ec17e0279e3a59a2969c088c3e62b6f5 ,\r\n                        0x7e80ffcb5c238025c01029288d151631fff33a1e8572da8321cd51234e8ad5f3 ,\r\n                        0xfbc108c12411ccfc01ff99df256326be06161d0d03e87dbde40d627920a6b1ca ,\r\n                        0x2b5afce21c596e36b934c981d47e513a073692c928a04764718901260a285c92 ,\r\n                        0xd32e84c26dac269eb9160638f8b448e308650f9c5cc0cfcc5b349477494391d3 ,\r\n                        0x814901fc6860234c84051863e12abef232e65b78df1d033691c060a64e78939e ,\r\n                        0xc9792991f62617f2508323cb64be1beedde4ae463a237cf3987c88d154b2f719 ,\r\n                        0xfe9153b9760c950b6f9c66231f1e14aa3e470c1d793d7e18cf4971439c403480 ,\r\n                        0x2b4052ee8dab9231ba7cab01371788c66bc95cfe40391a6883addbc94e300b9b ,\r\n                        0x0c0ab6046a0ac662bd99a4dc611e8a8741da9f5808de958dda5df920d28ab892 ,\r\n                        0x7ba162f370c63e8688f66b5c0db51e78ab295decbb0477a5e5201af16ae9ab23 ,\r\n                        0xd370d28905363e0b3c25af1c8b5e03a1ed9d93fa047448347d4d12c4818bdc56 ,\r\n                        0xee4e24739963845405f95acfdb71fe178075dc6e00a8d37169fb78f96f1fdf2b ,\r\n                        0xf805ea9228b124c440c586a75ccae858d4f994a0b263c589003cbd8108967f4b ,\r\n                        0x6a5c377003a6bb374c260d98095f196b6127d11c56035c2067e17cc302dae124 ,\r\n                        0x24d6d377c1efccf161983561b6dcc223775f628fac82764997a4c9b7703d4b94 ,\r\n                        0x0727271541f5742103f60d7d81f4110b05db6a1c18b5a4d1f283bafebe817122 ,\r\n                        0x90c64ff4d02bbb178d077fe6eb8ea821a3363c0f941fe004947840b1e5850a2a ,\r\n                        0x38fb7a6de6bb082a406d284cfa3c704458701ab5e0ef085e57269882df47300f ,\r\n                        0x2f793dd5b73bc3255f6b04b912c778545a72a300b3e44a7c633745b17d9a2578 ,\r\n                        0xd1edbf7d9c35de7b8baccd00a86442964f4fd3709194045a8532062031e4dd40 ,\r\n                        0x5321da8853d0db3d243666d012b1148ba8a941b6ade0208c7e754617e0eeaf2e ,\r\n                        0xc2cd5c768a5a6db020a9cef07ceeb73707a28bd4870e6742d8e31c9325042970 ,\r\n                        0x37c0866bf9983a76d6a7f93084008e798b92f7088d70d38b61b34644162ee803 ,\r\n                        0xa1ca4af30dd0a0e9c3c4db636d59b2273e45e02ecc483ac22eb94bef72990d92 ,\r\n                        0xe206db05011024332e80050f3c48b3cfadf7629d40571245922e0cbcb1dbb880 ,\r\n                        0x3c6f4300e7947e63589d24efa02d38f6dae0d74aa2bf90fef9a02bc0da688524 ,\r\n                        0x3950c66be48478ae17172134218f10ff10f90a5fee17d66e99b68a262e187624 ,\r\n                        0x51b1c6902f6970251b600413ec7ca033782ca2a8219f4038e06430928d1c3aa4 ,\r\n                        0x780f63ab81f5875c15c606a6f20d980d73c1b8a4a2051442dec82ec40030015a ,\r\n                        0xc0c533cab0f655cf1c64fab013fc684f09b3debeba0e13002013698cc4b22f5a ,\r\n                        0xe7b038abc152e6c69da5db482aa78cde310d783e9bca1ecd914cee53ae25bfb2 ,\r\n                        0xfe283d40e6276062de38cc65bec05b9dababf8034bf6d04dc416172c533c53de ,\r\n                        0xe76076093f28dc344b50418bc5eb10f37b1387658b2b91556206e30ff02170dd ,\r\n                        0x418c7a2c2fc28e0e251c1c266cf24db40fd412179ab8e31ed743cf4163ec85ef ,\r\n                        0xd7ad61eb8315360520951f0ac9b20f7905ea0867f108ae54179151f287ba08a9 ,\r\n                        0x406a52154581b585241fc19498419c0fecb1a1490339c2044b83d57ac06f90f8 ,\r\n                        0x898d036dcf30506895a86697de1aad9bba43cfc72d8f44ac20a79fc07f4b3a01 ,\r\n                        0x731aae2b49a21e8213e044b5a05e375183b8917bc1ae02799e98476f0f3e0a12 ,\r\n                        0x8983a62780ada4b24c7659f8547547bcab02c59f22aba4cbb6f3b5a967ff240e ,\r\n                        0x353c2575af8b10b313188085c14bbbac4180a5555d4f92452c8dbedcbe2feb6b ,\r\n                        0x7ffb3a2813994b33a86528f51337d2f190088852c7291c3e078d26abd58c3627 ,\r\n                        0xe45bd99714f984c5467f483ee54b554889c73245f2f42e1eb210ce506d73919e ,\r\n                        0x911024a7610c714431745139df9a0813ce87806df8a58a2930ad6b492adea3bf ,\r\n                        0xf7fc2eae22e9ae6eea9b10d02ad5089efffdcdd54aaed8138ef4027a8ce5eb25 ,\r\n                        0x323e163dad240683574308fb04fc711a323d24187641082a931d1413e10ac5f4 ,\r\n                        0x6da8c49158af84ef27342ff86284805c0decdc6a62795e871e1e11c87f9e8caf ,\r\n                        0x5a2097e75fbadf5cc846185ec510625bb025e115cbc53d2641d4f37e30f70b89 ,\r\n                        0x7b6e8ae0152582e2530a35be5833805482986824a12cade791d438b696d9d225 ,\r\n                        0x76092b730be5f4198e5ba0d91b23b574af2540dc186e23c1064726514e51a9a2 ,\r\n                        0x845ecd24ccfd48412cc8ecb6ec5766d9f06c44513d9a264454a28dc523b69a0d ,\r\n                        0x08e201deaf9053c8c8083c115a5aaf9cb403630dcd1361bea05e72025cf31526 ,\r\n                        0x0d344ed861dd156f5d2be1e82cfe106d00bf414c120de4307aa413bc85acbe49 ,\r\n                        0xabe03d6beac24d4b0ade0802ab8f2042d4884281c517db18afb9704b04b85679 ,\r\n                        0x836f1217f27ee5a50b77a81deac72b9d487e75db22f251fd99207c25c5ee3fd8 ,\r\n                        0x7dad9bf26a7601b0edbd2f631cae4f1248d58950ec569859aac8fcb0da0cb9e2 ,\r\n                        0x748091273d1dfb6a203a81be7c7b85c31ce008006e8c00715158a11dc4211326 ,\r\n                        0x46722ce22b2852fa95269c42502201f0dc4821438f40f643c2ce0c147662eb80 ,\r\n                        0x23a2e7c43dd11fb80a89690ebe009e004490f80156799c1861717264346053aa ,\r\n                        0x4844d85d9a20378aa8554c7b441de7f10ec4c1af182b11a0d504019575bb3207 ,\r\n                        0x790ebb1010b66eb581628dc5ec30c28b34c0b2a9d644aac2f155d250b5ad10bc ,\r\n                        0xd23990de26a50054749244f71f0759e853e4d5ed569a9aaa97e0e84875b25d78 ,\r\n                        0x78fc72a80ec13c61a9f947c9ed4a767d5f61d0824adb1609ca8be73115b90f53 ,\r\n                        0x219d6d15d9cf180736511da141da3c640992c78355d5da018410e00c613ee8f7 ,\r\n                        0x2a52b7e7c9bef00240afcaf82400b15450160e7a1891854e79f190ce6a2e524f ,\r\n                        0x912c03c66187a84259666604340aa2291c062b054562d4a402d80a8e4a527780 ,\r\n                        0x0ce104fa6ca84b5dcae42c1f5c10aa2631e211f1e429d57156272331f11755a1 ,\r\n                        0x628a193a8e34031b2e0e8909a5aed7edb0ef51f09e71d7f692c9e40b82f07a10 ,\r\n                        0x413c1352f424c671982cbac51ac6f325021f8c1f5d18019188c3b1c88c1bccab ,\r\n                        0x09306a983a0b9370ea9b200f2a93d53d085ca80d6999bc971049c7f7a9527d6a ,\r\n                        0x43c3251fa1ab2b9ec27d41eca68228fa9414009d824235aebd9302237612ea12 ,\r\n                        0xd35cb508642609b2492fbf26300578bade4730e2edb05b15247d2910e6015a6f ,\r\n                        0xd6f6b28480cc43fd0e0b723fc4ad0779ad98ee5572ae5a899c937804149ffc27 ,\r\n                        0xae88197141857efcb271f18c797f05a183ab609ade83eb358a23a42f0c90e15f ,\r\n                        0x221a1532889fa1bdc89911ae821d81523c0e99a192a50402fc111078a7593dfb ,\r\n                        0x215be7a8622a0756385468571a61b6901480b97a565a10a10261422bcfc48a79 ,\r\n                        0xe07449072284f97178d108aab3c0659548a48d1b97bc56fc5b155ee0cf7cda83 ,\r\n                        0xc1fa78e4510db28305616b1d01350cc4d4c81f156e0e31e8385b10d55bcfe842 ,\r\n                        0x06807ae08679113e3f77ca658b1309ef2cabc7079d1aa82be751773d6af94f47 ,\r\n                        0x8335382601cb8dee005532866d402e04034b83645d0015d1c3307178218b0035 ,\r\n                        0x6414cb8140aaf8aea07a10d0024d05a00e42917c4396bd617bb166184b60102c ,\r\n                        0xcce62e90cd66548be85419b65e6416c2fa8898c1301027b1a82c8a0e811ed7a5 ,\r\n                        0x2d1b837b5583548b140a61040802026073cff6c41c5776a3c134f09adbb395ec ,\r\n                        0x6b812a2837f76fa5ae143b8b4ac49d0caa6f9574db12234f8033b395eadecf09 ,\r\n                        0xb05554315842cac1db21a0e559de54fe80c30a35912ae4741c9b2901c47c97f4 ,\r\n                        0x95faef1b3add71e287bd10a1781d815ab80d37323a65044ce436c119ee0aa008 ,\r\n                        0x291d2ba9686f095cee7e637543c933e28ca0b8871901c8c2fdfc85c0ce050294 ,\r\n                        0xc1267c6229e711515e24eae5f3714c74ea4003390224618ad281e4d1cfed6468 ,\r\n                        0x6e93b73429a1f23a68b77bf86aeba877d5d6ed5f5064812e89a99c5adc92f3f0 ,\r\n                        0xfe504a566c6a81d16615051243b5fc8394eff192f5996f26bbc4cad4e1064b04 ,\r\n                        0x0c49a97d0be57eb82b29a3f8fa0773351b5e1c92e4c57cc99107b549a89518fa ,\r\n                        0x906442e6dddc8974d67c38e938338258f6c24f875618f637b5bfd7150853f577 ,\r\n                        0xa80974e2a93c1ce7d64ed7579953257f43131cfa2d292aa6d5cb57503a52a578 ,\r\n                        0xc01bfaf6bd5e392c627fb3e8bde648cee693712f34554552a18d4909cb1dd205 ,\r\n                        0xb72e2ad692b24e41e3a82602fe4b67024c01fa97aa70c4f6981b0e3aef565247 ,\r\n                        0x9d4002b80f599d0bddfe6c2962478369dd55d36cd3b2c3c31f4939004ab7b209 ,\r\n                        0xd905e8fc5d872fbaf12e6b62fede208916ab9a0ed2df0f4f5a22dfa83c0eee90 ,\r\n                        0x2cb8a56d76a0de1b4111592488c45d3835df247c414b776235fa83a0fd65fad1 ,\r\n                        0xc7f3842ddc33f535e580a7fe7a379233e52a68e63e1095687206d0b7c3797e8a ,\r\n                        0x5bf8d7013fc07c2870e196981217039ff505a6cb20711bb071aa54583b041305 ,\r\n                        0x806c6fb02927cfaca7462561f2570ff89a72fecf0ae5970509537b818436f1ac ,\r\n                        0x72e0f9c20769dacb162d38c7e90e5c796ef8762091413c3c24ba11427713ea81 ,\r\n                        0x7806a409c20b114a3000fe13fde92a6a21a403b7851ab890ce6611bab6ca10a1 ,\r\n                        0x6327bfb173953f04092e6d756ab34c8a181aa584a0cd5548747c160b15118b5c ,\r\n                        0xdfdab2d486d820794d1886b60031d6a8fcd204fbabb344fd443825b92954651a ,\r\n                        0x8f9a11b413c5f7aa965adc04d50944a478e91d172310a0c9acf39877a90a9b89 ,\r\n                        0x8097bd76098af61355b801b3060ce888b9171427da11bd460ada409bded37e01 ,\r\n                        0x9c76117f43757e2840cf27707fedb480306faa77e0fd73d14b6ace362cc85529 ,\r\n                        0x0651db08a4a55e35f9297e02eb0f52cbdafc80307a79e2ee971ba17822c402c5 ,\r\n                        0x60a903ab911e2044a8a9016bf3c03af043cac73f8e76e1d3c41903b965a110cb ,\r\n                        0x530ad0b63777ed0b24220a206f974c5bb94b8a843d28cbfbca5d80443c630d6d ,\r\n                        0xce934c4b065fd61186b1fa4736092603b4ab3c8e716def00f20c79005355ef2d ,\r\n                        0xc776ef557f0220710f322440753ba84ebc679596587c3e8ecb41d8228ed12d24 ,\r\n                        0x1ddac26bcf05ce06c06a186faa625137249dabf528f7acda46583df6e9a18512 ,\r\n                        0x585fc51c26f67db638441cc4aa0b4b994a5a3045400880470324befb46cc2a2d ,\r\n                        0xa60e5412cb2871a3fe18f6ca5a4ab0509be746fe82e4f674b4f5348bc75853db ,\r\n                        0x2844520e04096469554060abc381fc02afc81eb6c4b28a8a32913201b6a10ac7 ,\r\n                        0x845640ad065a1e57058e9800cc0168e4ce952c8ca8256f91ed1489da2e67cafe ,\r\n                        0x42a6470076964a636a840a558953dd53109aaa024f81f1495bc16bf712280c20 ,\r\n                        0xe8242966e4048c90953ee2dd1921018ab3d80891719ed09cb44f6056d82401a3 ,\r\n                        0xdd9784f1b833a8403ac0d04b0d5e7064b2c84515f529a8cb8fbba66fbf1f91c8 ,\r\n                        0x32566519fcb025353e912cda5618b0de688d2726711f40a50dff42c8005780eb ,\r\n                        0x2486b59f46ace1f2100c6d8bb447dc2b51a095551c929e99b1426db32a65d000 ,\r\n                        0xd22376d0b6f0ccda1ff45214bd04cce4783177f85e02d554b958093ce0530d2d ,\r\n                        0x2d27552b06928860046dd6b77981f0823a0edc669edd05cecfbb92080b6dd50e ,\r\n                        0x6f59e867370410b8da71e212e46782ae13ae2bc4fe6dd03104b4346ac05b5708 ,\r\n                        0x69db448deaadf87350ed1347653c90386d0d4b957408176452aa182f20e51c72 ,\r\n                        0x0979fa6005c4ba57e9fee4e20c465304818f4a3a492aae2971f4ab4dc179936d ,\r\n                        0xc5a2c354ccd2d68a8d257edc3224af1d73d4a4935a2b28aa9623699aa8243925 ,\r\n                        0x220eb6462a65148867d0a52320b62ace38234e3d14af2c1c5441c04a50382833 ,\r\n                        0xb1d951c6cc87c867c148c3159bc2a72c2966081cd6e752247d20c6ab6106e82f ,\r\n                        0x1bb90a7b5ecd938a6d8da65a40940fc93ae0d3cdd78321aa5b4f2210a4455290 ,\r\n                        0xfd53455d43ba878a94785c52cbc65559821192693fc5b5072c0d6428a3fb859e ,\r\n                        0x44f24296540801179236cadac8beaa65361dc5fe22c5f92d1fd15bde79e29d78 ,\r\n                        0xc1eda1f0e52ff27498c68e2c035197ea50600167e262229d72908befc2ceb555 ,\r\n                        0xcf951d6180469f91bcb9bd585b0970d06d2d986d6a5be265bc674910aa2095d4 ,\r\n                        0xf5433e19954909ea2baba4e60a1825b939c6abe64e70d4d43497541d164c671d ,\r\n                        0x94303ace874e423a8089284e7ff6fe5838e94c6e925d24be713f2893eab7f85d ,\r\n                        0x52ff655e64ed833d6e278250eb5dfba333ca0b2e8908444395217f419d837c16 ,\r\n                        0x666379b807bc63a538861a942a6ccb834ce0dcd194ab40bb10a7e54e546815b1 ,\r\n                        0x082a39552450ef2bd79cba83306b8700a2303cd6042293c43ab3bac513efdacb ,\r\n                        0x10364da8e601044990f00f820acd8d7392d353031461fe2bada2620140688d1a ,\r\n                        0xa61d498c45729280c36d4fc66ea21e18beaa8086b3431c36bfb2c00b96500136 ,\r\n                        0x00a1940539eee20fd2478da10b50b6409a9861b1f695dd56d056394292512105 ,\r\n                        0xb86b662a6568e7c4494ea3b93392547b9c2cab5a2dbd1a67c437990db90e2578 ,\r\n                        0x6a203d1fdb181fac63a8a9374bfec776c65d51f755b82aa27ba23608efc847e5 ,\r\n                        0xabf55ded6c3388733e511342cb425674d7bd181a92e0b5fd03abd5ab0a2d043f ,\r\n                        0xec3fb57c5785c1ab55648ce4b55f9986b827575b38523b8a5f12052eb6a31a04 ,\r\n                        0x81b3d6c9fd89779e80566d0c84cb7e0d324f5c471821244f8d5eda10b132cf54 ,\r\n                        0x95cbb78f3a910b6e0cdf9e6d760778310fbd2d0966016638a536d4d1bd28a0fd ,\r\n                        0x1ed08d13437ae1ac2c34e9af54e488b57bc10d483704968314aa2c12614566fa ,\r\n                        0xa992dc963176cd24635503d42806ebebda786284acd3d4b8efd65a11d340d676 ,\r\n                        0x6a09b0be0014a88d2c5a2122cd58922a9b90ae8074603ebd5ced4f54d87f177b ,\r\n                        0x5431aae32c52da73412210968819394588d24604c8aad674cbc8bcaa5056c41b ,\r\n                        0xf5b3e41db2b6061dd567b7d54982027049bb5cf2eed583ff4af15015354c29d7 ,\r\n                        0xd6062ac805e22325a0bb14f8e0a07c32df4f90e2e16a1b228aa0d14ffb41e459 ,\r\n                        0xc8331f1bbae589084f0674e457db307c6a03e85d5bcb8ed08321b15a65fae2a3 ,\r\n                        0x83e342b2996551010b4fd4265c9d8843a94a8cac6e09d03749890a541738176a ,\r\n                        0x1c37da2ef154fd17a6e102441f1bab1cc84783b505107b92c85fd38bb95161bd ,\r\n                        0x6033da717c5a155547f6971cd6d7bbec90ad79a96974f70207de68316f7eac96 ,\r\n                        0x91a37dee033f87199180161c10966f9de5380ab86cc9416d30dd5927b4c20989 ,\r\n                        0x3f4a9171e3d606a976af19cf4e2ca26555b19ea96ec7821080478ea8ed95c4e4 ,\r\n                        0xb4808e6933957226f416fc384cb140c0e1e313dda506b0aeae82aa1309601caa ,\r\n                        0x9ec10deb4b1ca9547caa4a6d79c778d451a5f63aaf0a9cba7b02c8ac9d92c418 ,\r\n                        0x36ec480c19334007bcd6c0c68772549b924d12d81b4e7df20206f1f5a87e972e ,\r\n                        0x0fc8ad1ee5de6f9347f57008bb7ab864801ee59b24b6a67c5b54ab1ec9bd066b ,\r\n                        0x220e1b860aaded3cb5a783768613ab6471a51954a3520148484cfe441bd4a226 ,\r\n                        0x0ca83e3349479d2c7ba36899238c0adfdf1fe16bb21c64089a03a999442e10ab ,\r\n                        0xe302b6d18af6c23709ede5bff6dc06743b2b1f20c47093f095940bc4d8ac14a4 ,\r\n                        0xa1b16ed546480eca45da73a848c65ba568bf96d8a49c7e9c5a3c4837f9a72516 ,\r\n                        0x9da8506caa3703d18403a18812405d75b9d33cb77601736de4a2cff3a088feba ,\r\n                        0xae8a957a1dd54bd193daa0e1fe65aaff03a16063c35648102bd090d6501f7009 ,\r\n                        0x0160108f9205914404ed9b5e3da0d6a0d352b1b18129f2dbec5361e26817b14b ,\r\n                        0x35bfa68fb1142b4183b5934156653227e2a2b2fc2c2ca5e84ad56ad429ae8f4a ,\r\n                        0xbba3a34776bbb9880d83713eec6ec7868e644654239f9a039d7a26bce2545d41 ,\r\n                        0x685fb92cbc26239248a8aa264d0b22b205495f05117fc969b984bba73535a3a8 ,\r\n                        0xf8db756bfd628451ec5e8b3c18d3929fab35eb1450132643ac5e75bf77213901 ,\r\n                        0x8d1e28d6ddac2dbcaf6de8a92ca2645923f10ccb1edafe500d4f0de34bdbaaac ,\r\n                        0x4f569556cd800bcdd119db56e59fb1abf76c908add4006ea636aacd43d354fed ,\r\n                        0xa30177f56615785003997c866e458d608f89abc28074777e0595b5ed585d9f33 ,\r\n                        0xaa7f59e5d18d47a8d119d752238679165f568d34b06884bcee0a72a2582e134e ,\r\n                        0x3addc53a260ce1e618aa900b52fa57856c5bacafa4d5c70099b5410be5573e06 ,\r\n                        0x38824a56f1f74dd068522590643744171bb627f633c43cf5837602a1328ab0eb ,\r\n                        0x8c8ab6ee6056deda9859eba6de1ff594a99b1a46a015698fc556d318cb087891 ,\r\n                        0x43fa4fb5a3bef55541921a7444f0484155cd15227deaeef5f9ab8301e840e045 ,\r\n                        0xd484ae843a600c9f77837116d5ab325361945adb8a7a0ad4d0f3541ee6757505 ,\r\n                        0x3f357505759deee6a00e0347279daa9a46aa948e391daea53c3034adbc175ae0 ,\r\n                        0x08528fd2e8541f235002c9e45a2fc0514b74fde007aadbe53e968682222f16ff ,\r\n                        0x3ac41022392eccdc5a5213a5bc265ff8a4ed9e726f65d51333c88784c0cb0924 ,\r\n                        0x24de17e9517b830bb5ac68c975a8e31723342bfd4499041c1c564a86e1d79408 ,\r\n                        0xd19eae1b45d9232103f0fd58978a5a074a9bd4068df426d0ac5063b352391772 ,\r\n                        0xd451abda92239c2f014498b6ab9ed167ed2498f54aa69137e7b76de0cb8f5d94 ,\r\n                        0x11d183a88500a1603212abda1dd47edb4bb6f5e318cdedda983c79911fd5915f ,\r\n                        0xe7d791bf09ba00f4ab15c1202d9f25bd53d4292f7ebae10c8154eb21a68e0c40 ,\r\n                        0xb6de2a75a0ba805a263202543e831472f88b9f4f75b24098fa47e8017872ce9f ,\r\n                        0xdcd2895c0ea16412bf2327cae3dad64aa77d45ce2fa9f2a9c139bea64d98ad2d ,\r\n                        0x35d690f0570d0c6f1546ba934de832a9aae62fe55ae2f70ae1042b082c321d3e ,\r\n                        0xcfa02a9c3adb41475fc9d492f93a57837299373be117a88112550743d1ce90bf ,\r\n                        0xcc9788323cc0cf082d35baab5f8d35d3d1ab52481c04368e7db1122ad1bbdc7f ,\r\n                        0x5a19563ce29cf8205fcd048e7a544de52825b2c855fbd38a4f64e1b739a4a173 ,\r\n                        0x71b5fb37743f7021671b9568470e89e88d42fe80880ac98b1a2452e5cf68c525 ,\r\n                        0xed6a06092ff57de257d1db26976bea5645c7a114ae04dc862a1f72c7d11e1349 ,\r\n                        0x43eb0bd131d62191a5feb9667d9a38b64468d561104fa665d96f350f8adefaa0 ,\r\n                        0x7ea8264803e5caea1353537db346fa5d3b5ea0b617b58f7ee470cb039a23adab ,\r\n                        0x8437b565d6588daf8b5ec576f55f4fb5190d73c7af16322b4b3b7f3a94242c12 ,\r\n                        0xd786120ec7d8e60767e3fe358115675569269279f220bc23e8c49af4a5d38919 ,\r\n                        0x370cb0173bf170b44d7e862b224de943ebf073a8037547588be37515c96c4a0b ,\r\n                        0x12a1f35c24061673cebe8276c926b23e94004282606a20d0495b865f8e0e61a9 ,\r\n                        0xef1b01873fe172b0a182705479315765127503cffa548122d3adb610d0afaac4 ,\r\n                        0xca98b569cfe2698f565c0625b833432b47c5ca8318411556b5b814f5c801a47c ,\r\n                        0xb557eb644712f7908ed37999423e8c76368c68d4513e32363a40cdcfb821f00e ,\r\n                        0x7ef27d5890b7966724ee4065463089448fcbac86a6ad8d245d13509e77af92e2 ,\r\n                        0xeab780b5a9e75c5bc47e288cac5916aa3dc0beedc113404515e09ba238fb7611 ,\r\n                        0x220cad4ab892d7c612790c90d76e18ec8189317c1517c57448355ddabaaa6db6 ,\r\n                        0x82e2eaabc2eb6350ebf99a2ac6da6679094a0bd019042936ee60fbf14c9440c3 ,\r\n                        0x296ab9c2dca86d465c02727dacf69135dfe49dd8efb844ac0adb009e0eb291cf ,\r\n                        0x77a9197abab3ba30b65aa1ad7b5cf56d2e25a91b9ada9f205ebb89b7bb5beb45 ,\r\n                        0xb6f2efd990daea35689d9864bd598aaa7f91a60044b162581c708bc04baa7a8c ,\r\n                        0xc854f91eab31aff3da28b30e4502927c08603e7e993dabd8a402a74ea834062a ,\r\n                        0xbad39b3535202bc140943f23c838d9ecc35549caa0c205a44a552211e4057ad5 ,\r\n                        0xbcd5001e59cf52bf9e88e0b56ac4e1a9da3e6f649da3aeab1e03504b0a28c8c6 ,\r\n                        0xa07340f8512a5081ac9a505deab7dedaa1f22aa3a93c0b6cbfa7b31d883b421c ,\r\n                        0x928430dc24a4e1747a8aeca8b0b732838e7228bda96fab44357d049829f93fde ,\r\n                        0xef24198102b81ff80f9069b7d7f9bf09f32773a8150f27ac980f59b200366803 ,\r\n                        0x4befd5e679216f7d7b6d429ff8f068de6e0166190a5402e2be4249dd89f4964e ,\r\n                        0xdaa8631755a774364ded346091423d2c61e633390b586a17fb036b58048c068b ,\r\n                        0x106e44ad8bfe536444f9c4b152c781de0a358b30ed3b7edad2d41da5f79421d4 ,\r\n                        0x8905c557414c21ae5378b53b5e544fd219d050c8a30e650ed5e0750c147a5eb5 ,\r\n                        0x1b1c200e857ccca850ac45a7e1a024704d24300a8a85ddc76937f56bacc40b96 ,\r\n                        0x0e01f09de6b71a01adfb5dc702c612fc2cb5b62367ec60cb842caa8906a29130 ,\r\n                        0xe9d710f7b18f2536a73d7a5cac19fa3266f5411336da396950c1abde4ff0f5bb ,\r\n                        0xc36ae74b00e858673dfe2c3029e3e8ecda2dbfbafc7fda020ce8d50d24d20313 ,\r\n                        0xd77106a9ec23e4fa2ec3e7af4bdd8ee68c677b8f62d17af97bb54cf59622a755 ,\r\n                        0x388cd5aab4768f0e1e235f60c42bfe1cba72f64e5ee083c2a03688a80e465c70 ,\r\n                        0xda7f9cf120e2a63ae988546d3c4575cf4397cf43eb00c368cae437320b04d58e ,\r\n                        0x406965470517208c42da51dc265d96eda1399734db435a0313e7583733f98e0f ,\r\n                        0x0206a3032f2c3679ade1aa222fa4fa0e580868a1250bd7af00981ec090ccc719 ,\r\n                        0x9e58ac4e45c2da2499210e92972dba89d9c16418868e1591eb51fcd0373556e2 ,\r\n                        0x7b040d97bd5bbbccb80d836e10286b9aebb05a73e65dec2414d9137a4392d571 ,\r\n                        0x89900231a009aa635b35d762671910db034b759d2726b9699f39ecbd0b010405 ,\r\n                        0xe07e77381d28843856b57e933d81963d51af6830aff298205b12492d3a5a356d ,\r\n                        0x37be23b20efee16e0f1caf25e9ac2872980ff0ed1b81b63596b7f2edd0de286e ,\r\n                        0xa5e61932c350431a2acf645c8f3fcdf78cfd2b9431a2c4b0201350aa7bab9e4e ,\r\n                        0x40e8c7a55d0c06b259becca0b6aad08a95d074a6418d23f250342509b2a9b3f4 ,\r\n                        0xb8edbf93b2455d3341878c85d899b41b0b86dbac2b90eb478e7857812dad9101 ,\r\n                        0x12951bd4681fa0cd3784d65c6859054f48af9f6aab569aa8da6a6de08cb625a1 ,\r\n                        0x83ea8e085b12c47af3c9674d872736a61f3fc3a80e5798304694ac76d1ca4fab ,\r\n                        0xb01f788bd7fd59aa2ff6da5871a84577020c1878a9fb87c14b74e2230e4c0c3a ,\r\n                        0x13c06a284299319247471a7c5597950e2814a1fccf011acbf5a42255a3796f80 ,\r\n                        0xb724163ee3d459287130a69a10d53fa79ca0665c84d7b22603cb1aeaa4d7fee2 ,\r\n                        0x82c023ed8f7a58499e2aa658fdd61d554d8e4448cf370eebd4420a8c5e7c5547 ,\r\n                        0x3a5cbe684f17961efa2c5a633daba4e8dce52c88463ba854b6e365958907804a ,\r\n                        0x1a7d151aa966231d3bc9eae0d09eb3295955c0c5e6f3d35e12ae075692c8a067 ,\r\n                        0x03e0538bde0189cb55c577a357d473c805e549da510640ca0afe046d9fa8724f ,\r\n                        0xb62bf1926975c45e4ddd244f75233bb516eda29e26b5389345542724c50270ea ,\r\n                        0xaec82420ed1a273510553b3d072995f571469d9147cf93b45e7053b5047556c0 ,\r\n                        0x6181038ca95d28b17eb5662c1c7aabbfb0ff747212c02ad15bb97a7cc544a9dd ,\r\n                        0x36dd54f93d2ed5aeef6a9e80fc750fd562575335680dffbe734103424a9a1e7c ,\r\n                        0x9b3cd4742cb67d8545a7336f76ec5d3a505ac86e613547989bee0142dad96ab5 ,\r\n                        0xadfd8cd80633747e842caa031ff7c98f984a94b730c4af1a71e5f95c49d28104 ,\r\n                        0x054d470aab173c7f07b05481d3b9b480358ed832d8e6546db02e65a632740a88 ,\r\n                        0x54802572b1f9295b0f5fbbaa5bb03550f4cdfa6bc4a815754aa92f28e9882723 ,\r\n                        0x611509d39dc13230c8936c41d40049271796ea25a4484a3a63050ec1e7217f5d ,\r\n                        0x4f76d8c065ce5bcac1214ebe533dea0719434f3f0003dfcfb18fa946009d5b19 ,\r\n                        0x5dac9b240ffc6a7d0b7e9aad7b4c4df060aa8b4221b401c10ce869ffbdf7bc59 ,\r\n                        0x8cbd6024806655975a269b75e023a874af3db29624846a55f3fdd61929d79aa8 ,\r\n                        0xb70e02abb8a706487580e25445c753b4d90ebb4264a9cdabb26e7a64829ad355 ,\r\n                        0x5a2b8aee78745ef1bafdb5623386a5bec454c5045513d157a0b2a25c85145742 ,\r\n                        0x537d9921415f032b1188ff226d0a06eac8b9b60293c72c1b013fb4bf5dd7546f ,\r\n                        0x80dadbc8111d11c32f914054f763229cd06aaa9bca55a192ad5adf3df43874b2 ,\r\n                        0xb1ce9c5675c76bbf4acddb2a072d3bf7d0c90cd8117c4677b1864883a23845d0 ,\r\n                        0x879fb36d1054870ac4af7414f48bb24a30a1a8ec01062650fd77c00167ac4ca7 ,\r\n                        0x32bea7b7d5df69d65b706dd2b563b196941a8404c2b511988c051e7a559638ac ,\r\n                        0xf05233959718f73a19473a044f00706d1941518eb6ec660621a1c64967e4b4b9 ,\r\n                        0x13a0a805a7fca1e952dee97c85e7df20ba744ce041027e6096c06d5689b0d600 ,\r\n                        0x95ad7530161e3388d68e27e8d94bf65bf7fffc3b8199a2f1578f22b0830169bb ,\r\n                        0xadc28ee8139918b582c7a24a8f8e01212b4efa40d2dace83ace8e1ffe88a8f37 ,\r\n                        0xc39af51880811b145746d6231b4e0edf04f43407b52aa515b58f2dad9954b0b1 ,\r\n                        0xc6d6a1b4ab266c0c1dd43c35d40ddf59c2eb54eaaa6b2f2b1300ad30b7d8f448 ,\r\n                        0x8875c2a759e17b5063f2ead6aedb5288ebd912b8187a53ccd976707f9ae1f5ce ,\r\n                        0x08c47dd2c356d49dbed56e26604d4dcdff0fe5aa87a228f18daedea1ae22b375 ,\r\n                        0x950c25183d01450b64fdc5d9ec93efaf37edbd924561afdcaca9dbe5a8df716b ,\r\n                        0x47c2f627e095aae966d7bfe76588228b4e5b7bbb61e5c77b88054d25436db47f ,\r\n                        0x6337face6959d5a9ce5f1b1eeed715ff7641916bc03ad97cdeb649eaa0918057 ,\r\n                        0xfc7b7c3dbe7ac68300807c76cfd65908f5d1c9dd7fa6f2317ecb1e48e92b3888 ,\r\n                        0x3397e351d12aa6d69cbe9e59550d3d324c60d0a1c72bfe3189a55ee056279905 ,\r\n                        0x59fbfe3a63ac9b0c6b8db4ba5efa04dabad6e29b9c766feec5b1508b8a4d6f65 ,\r\n                        0x78e692a28eae9354a1955b2cbf02de07b061aa19176ada5444fb2f98d0d561c1 ,\r\n                        0xaf82b70c4c245189a4a3ee8f8c8ed0596fb5edd9342258330955fc528f22f91c ,\r\n                        0xc447b97fefcb5d83a5f9e7f454f559726b1c0ec756fdf65bd8de72565bcfbd2c ,\r\n                        0x4dd2c1ad6ffce0d15343c3d6a385a2a2a49d6fd76c9267367018aea0a70ae915 ,\r\n                        0xf426bdd54a9df5c656172568b2587cf36cb98392fa58075c48f13b0bf2cbf3f4 ,\r\n                        0x9c87a57ad9539bbe1a49f3e7f5bea8654d1c13a6ebc84c6a96055e97182380cc ,\r\n                        0x5a44c9f1a9d316046c1d3a2d4a3abc7a8c854e2d6ad7409b3f59c93133151d16 ,\r\n                        0x04b08abac8c723c14eb9e5f813cad4c508a2def16bb34374a0a4ef589815274c ,\r\n                        0xa53beddc2945684b3c773d71a80abce6eec71b8c4dff6f7f5f6ddfaa99f3583d ,\r\n                        0x20fbdd1035ea7d0621b31e3353f7c312f0693dd6e348c0372bf9420d9387b647 ,\r\n                        0x55049e8e4a9177181e7e3bf4ac881ba36b275fb179681ccaece78b104494a91e ,\r\n                        0x688024db3a080395b6424582b46615c7f46024af2744a81f0e790e3ff22fe157 ,\r\n                        0x53872e407c1244d5ce8ab61aba6a7b6842925799ea1d8495d67b06b756897ed8 ,\r\n                        0x611d960b19909dba5d3ff5fedaee3f67d58e9ac6f5e81252f0a7368e113fedc6 ,\r\n                        0xd499be2d33a04227eb7efa11f49c08347f9058530bd3538959dbebdda8976861 ,\r\n                        0xf4df17877596a86fc4969a24041552ca7b6453a0f6fceaa3193a191b9f1d2994 ,\r\n                        0xf4d7e1c2a4366192849d7933ea99dad5f11dc695a6fa8775c80fd61add77c2e9 ,\r\n                        0x10544d65e5dfe55fbd9e6fd0334284f9f9e3965bd7e83e6abf64ea7126a83aa3 ,\r\n                        0xafda85e8eb6af4a5a8cafdb7c1276d8c73ff9bbf54d5b51cf6f016c8bbca2df3 ,\r\n                        0xab2ddbd35bbc5377a8687926eda9a9f63e6b302952633a8f870ad34dbfe3543a ,\r\n                        0x610d35e9809736f09eff16a54164d06b304409f85f8ba22afad0b11e35f49699 ,\r\n                        0xf4448da5da8c9dcb5af60eb2923869c1b64fb6cae8c569933263e82dd859dfb5 ,\r\n                        0x729b489a75c9abd2210ffdf2730bf2f6d281c4b1b50ae9da9aa9033b3a92730e ,\r\n                        0xc30ee4f7f8cb8f46d5538c8e94a83729b27e2cdbc1967a321990805aea4fd0d3 ,\r\n                        0x79b44a0e6e515546ad0002601390634b0f3090cf563d010b9c20c0d250fafd59 ,\r\n                        0xa9da75dc388fa293cc92118cde19d34853099bec8e2ac49d75bcf1b310d11c8f ,\r\n                        0x3570219c8bb6e292f6c09b965cfdb4a845750d835a4e47e8b2ce6ce971342a4f ,\r\n                        0xb560f0a6ce7b9d69d70337b958fb3c49c786be64f0b323f6f55ac11b9df6784a ,\r\n                        0xd3aef236aceae4a6f655ced6df3efa75b4ab9313fa24590bd92c5d7f9bc20dd9 ,\r\n                        0xfdfa457f077cb4df2aaead0718c9624aa92b2a89f4acd2af0a223a0aa41d83ef ,\r\n                        0x28ec89da14ec0e9ff056c0ecaa987dd4414f5c1060e9c00759a37c426ffca172 ,\r\n                        0x193fccbde2b323e86089ce42a041c6d673385853e56fbc2cc3f443981841c54a ,\r\n                        0xd48b9eaaf28cba45f5cde9b8dfd0497742454fb9d07346970a78a4679b99ba7b ,\r\n                        0x95d25a872fb37acd67bd57f53c90881a567339b2da23a8c915e093a44fd173e7 ,\r\n                        0xf40c4d309f94b348f3cad25cd06bf73c65ecdfd58ea2a7847045188ae75bd133 ,\r\n                        0x9d264652d49ed32ae9ada5e3e65415dc0b66f4ccba147584353cf2223f627eb6 ,\r\n                        0x4362dd1e475b5b5c51f5b6c67041633d4f485b2ce1b8887f4ad2a88d30a21a47 ,\r\n                        0x1e3ae9c182303406a063b35b8ccfebc0fd282c9ceae17aaa5689ac365c570d2c ,\r\n                        0x7a6859d383279ab29a76b04653c69c7b6a2513d9b49040c8664bddfce15afd23 ,\r\n                        0xc0d172fd49960df427adbaf2475e85c4d9939db4b5fcd3cba3bc9a4df5ffdd97 ,\r\n                        0xa76516530ad5f48003f2fb7fcfa820af975857f9b97e679fb4f1ff7a8d4d9d9b ,\r\n                        0x3801d354ea7460d1e01eacb72a4da4d604eb846e5bded0d356feba40b3f2cb9d ,\r\n                        0x5b4f23b1c31f5947b61060701807bac89fe0d05fea4256a0dd337456c7ca742e ,\r\n                        0x04f216ed400cae7de4aec1ba6654ab42b743be3afc29bbd3164ea2137c651a32 ,\r\n                        0xcf27fea373c9e2ab10be9fc7dda4c62f764069dbe1653d7902fea767d0e1277a ,\r\n                        0xa6efcf06810a0e46be73685ae7ed875db03523681f393c5e4f0dfa3a158ac1f5 ,\r\n                        0x0e7ab2630f70487d5b57b19eebf6d9eabb8aa5cd039fd4835bc409f5b0b57f0c ,\r\n                        0x4bc821ac73cd5a03d50a3f42ef9a9fdaec6157df78be03412a8b8a1d64b0ff1f ,\r\n                        0x377a5edd191a11c321419e903f02de879e93a315fe8e7123390960f5adf9fdd9 ,\r\n                        0xefc15bd5e8a8a391590f5c4290bde034278dc0fff17d3514da3075ce9f51dab3 ,\r\n                        0x707f2ff211085e1de55d6792ca748ccc2d3d06a93ebb070166836b96a2e39f8b ,\r\n                        0xae460582edeb28337fc9ea982255f2ef75dc773071b55f732e833133d61a7e26 ,\r\n                        0x5aecf1bdf96734e9a3ceac248066cf8c53191e12e72079b6d6be9a55fecd6a6d ,\r\n                        0xd87d55fffabce9cf5192894855c486d39978d0bbd8f33fd4634b1ec0e67adc34 ,\r\n                        0x77cf082bb2a07a1fb34e7bab7f28ebb94424382900eb7021e31e622dfc6a4fb6 ,\r\n                        0x29aae487ca3e0a55381358fc7f0089a10a8f8e746eba00000006624b474400ff ,\r\n                        0x00ff00ffa0bda793000000097048597300000ec400000ec401952b0e1b000000 ,\r\n                        0x0774494d4507e4010f0d371648094ce4000009644944415448c785975b8c2547 ,\r\n                        0x79c77fd5ddd5dda74f9feb9c99d9d9bbbdbbdcb2f6ceda6b0b13222b262bcc45 ,\r\n                        0x58044c804896a52084bd8ae3585a8cc44572501240414858e22136c6d8eb8090 ,\r\n                        0x6563ae1171b4a0c4582678cdda8e19f0ac27cc7abd6766e7ccb9749fbe57150f ,\r\n                        0xb3c47989f6ff540f55fad5f77d555ffd4bdc75c73f9aa0d64429c3cd1f7a3f96 ,\r\n                        0x93e27a1a236284a5c8d302a35d2ceab49b0b7cfdfe130c36c6f4e666f8cc673f ,\r\n                        0x21a4076565705c0340515638b64b961a824050161ae90a84100028a5b02c0b47 ,\r\n                        0x3a355acd1e795e72c5c1ed081b6c09466440854082f128a690252090ecde7519 ,\r\n                        0xa3c990a5a5be198e5f6363708e52a75455415e1a16b6ed244d348b575ec1e597 ,\r\n                        0xcd1d2fabf25ea554eefb3ec618269389703c3720a835196ebe86563099689419 ,\r\n                        0x51e911da1454a922cfa0d5d84ebbd10163b3b4b44cb3dde08a83dbc0da46a9ae ,\r\n                        0xc27101341a8b382e79e4c4a39cd2a789a31d775e71e59bcf0b211ecff33cf13c ,\r\n                        0x8f56ab659c289a32370b795ee2ba5037165ed0c5b21b4001468276d105a812b4 ,\r\n                        0xb6d8bd6b0fa52ac932f003b06d284b4559e578b580309474da331c39722d4f3c ,\r\n                        0xf6c80e6d8a138b8b8bff668c39ae947adeb66d6349a786ef35c13860603c2ee9 ,\r\n                        0xf707bc7aee1c675656585959e177abab2cbdfc3b640d9224c1f5ea8c4611d285 ,\r\n                        0x340363812d6d842dd10a540528c1f6b916ef7edffbb9ef1b0ff1e2d2cb472d29 ,\r\n                        0xef1e4fe283066c476bc334be1819303f2f11ce0c956aa1748e10209d80221324 ,\r\n                        0x2918c0f33cd252d3bf00fdc10671ba41bd61916653ae3974185340209b980a1a ,\r\n                        0xad056ef9d831be76df37f8e8473ffc91ab0f1fec29b8dd914e0dcf0da9075d9e ,\r\n                        0xfd65c655d7fa4c63f06b0e9e7430c078a410da261a83e7058cc6535acd0ebd39 ,\r\n                        0xe86ceba1ec1e690ea10fe9149a01543a41ba202af0c3161ff8d05ff2d0c30fe3 ,\r\n                        0x38b71cddbb67c731274d4b2ce1e3b921ff72e2311efbee94bcdcc4f324519422 ,\r\n                        0x748399ee0eb269419615341a6d94ae180c37d9d884c240654196959c89363874 ,\r\n                        0x7081f5cd18e58ea81c58db788d42419665dc70c30d3cf8e083dcf1d7b7dd293e ,\r\n                        0x77fc5153731710c2a63b536765f539ea0df03c89257cf254321916d48326aeeb ,\r\n                        0x321a0ff06a36abeb2becd9bf93cd7842a3dbe6bab7bd9d438b3dc2001c013ffa ,\r\n                        0xd1f7387dfa34935870eedc0633dd36c2941479c2e6600df1a93bbe6376ef3c48 ,\r\n                        0x9e554ca2011bc365eaa1c37832442b68865d5c19924e0c0b0b3bc8f394caca78 ,\r\n                        0xe9955ff196c36fa2b26d5cbfc164aae9f7d739b4f826def7deab989d0307e8af ,\r\n                        0x256c9f0fc8f2ad5bd10ae1aebb3e855394194551b0b6b64e964ff8ea573f0602 ,\r\n                        0x8643e874606d0d5c0b8e7dfccbac2caf72c39f1d254a07485b72f7dd7fce734b ,\r\n                        0x053f39f914db765c8ef4e7d91c1bbe76df937cf0e67770d91ec1b6f9804a8310 ,\r\n                        0x5b5d0b6c0683015618fa08aba43b13e2f91669067ff1e1cff0c9bbbfc43882ce ,\r\n                        0x2c6c4e064cd30d5aad16d2aa114d4af2ace4ec59989d73b9eeedd7f2cad915e6 ,\r\n                        0x77efc5f67ac4798387bf7592248317fe7bc2c993a7585a5aa5d3b289a69a66b3 ,\r\n                        0x89334d46b49a31ca94c4c905ca0aba3335b66f9f279ae60cc67d842df16a9234 ,\r\n                        0x4d91d2432b43abd160fb0e282584b3757e7e6a86c178406366075ea3c5d973bf ,\r\n                        0xe53f9f19f3eeeb5b1cfaa3ab186c6a342084a0280a2ce9555c18fc0fd2cb71bc ,\r\n                        0x84242f31d698520f3164d8d2a2bfbe4e599634da21e3f190b05123cd26bc76be ,\r\n                        0xe2e5331778e9371bbcf3c6455efcf569c29644db8a5ad3e7bb3f7c9c53a7d779 ,\r\n                        0xf6b9555e39b3425980aa3460e15cffa76fe527fffa33fa1746cc6dabf1ef3f7d ,\r\n                        0x82c3470ed06ecdf2f813dfa65031596ca1448af40a7ebdfc0bfa1b6769cd29e6 ,\r\n                        0xe71d4266b13c2880689ae0b8506a455ee54469c2d5d7cc6152701d500a82c0de ,\r\n                        0x4af58def3acce2e2412a95e3f9b03e38c3cc4c9ba0d662348948f28403fbde88 ,\r\n                        0xc960a30f45a6f0eb3617c6ab9c3d771e19b6d9886d66b6b9a8dc6138326c6e4e ,\r\n                        0x280acde6e688e110ac5ce14a9bc968c06caf491cc7085d190490e5906423ba5d ,\r\n                        0x49924d49134dbbd3e5c7279f34d7bdf54f68780dd289c2776d6c072a4a0a6171 ,\r\n                        0xeccecf32ceead8b57d5c77fd4d2cbfd2c7d88620140cfa4b9c5ffa0fac62401c ,\r\n                        0x8d39f9e4b7d11a6ebfed6f708405d318a40b611800259ee751f36b141a26f184 ,\r\n                        0x7ae063a179e9b7cf60b3f5a0db8e83b27c6ebde556eefde747b9e93d37f2d47f ,\r\n                        0xbd48b7b7974992432599ebec616131e3d9a77fc8030f3cc0348134491142e018 ,\r\n                        0x03f53a2000e1324d2704351f8183635968e190a3899294bd97ef6761768e681c ,\r\n                        0x931715edd9369981d6ec713e7dcf57b8e9e6bf62b059d1e9cdd15f1b226d8fa7 ,\r\n                        0x9f7986871eb89fb00e5a81ebbab45a2d1c2e2a8e14ae6f706c178147966a846b ,\r\n                        0x311c965878d43c8fe75f5867f9c50d3cd70521885e58a5b4353b0fece7d81d1f ,\r\n                        0xe0fe6fdecfe12347c9d308cfadf1e3efff807bbff07956575fa52a7316afdcc7 ,\r\n                        0xe62866349ae008017906b560ab7610a0b4c2f73d14d0ebce5156a052387cf57e ,\r\n                        0xac028afca23d7240b8d01f27fcf1917d74bab772cfdf7d8523d71ce5a99f3dcf ,\r\n                        0xbd5ffe5bae7c03a4d14e00aa0a3a9dd696e74280e75f4c350016b6655d1c8155 ,\r\n                        0x95f836c446f1eca9df50a4056559e2f992522994ae700317c77170fd3a9f3f7e ,\r\n                        0x3b7fff0fffc4f1db6fa3614df8c5cf5fc5161acb86bdbbf710f8de4530ff17fa ,\r\n                        0x07dceb1a0f07a812da4d9b37bc711f524ab4d6b8ae439e97f8be4429c8f31c63 ,\r\n                        0x0c9db6cf17eff924fbf6ed248e15fbf7be85ac80248e68b51aa84aa175f57a8d ,\r\n                        0xff3f753a1d6c1bc693823367ce906519699ae2380e555561dbf6d6762d8b344d ,\r\n                        0x715d975eafc7d34f9f623a4d992629be1f50afd7d8bf7f3ff5a086ebba97068f ,\r\n                        0x46238ac2d06cba1c387000cf93680d524259be1e69bdee53969a2449683643c6 ,\r\n                        0xe388306c60d95bf3a228a21e0694794196659706cfcecfe3b882b50b31cbcbcb ,\r\n                        0x1445419ee70441801002cbb2288a02ad3542086cdb26cb32ac8be7643a991286 ,\r\n                        0x216118b2f7b23d049e4fe0f997069f3fbf469a2a7abd904663919abfd5e5008a ,\r\n                        0xa2c2711ca484a23064594610d488a298999990f138a319fa680d5194e0ba0e45 ,\r\n                        0x9a91e5c9a5c1dd6e977add4608288a122124455121a5f3bfd119e321c4d63725 ,\r\n                        0xcf0b94526499c1b22cfafd0181e762b4c60b6ad83eb88ebc34388ac62489c6f3 ,\r\n                        0x2c6cdb464ad0da410830c6d06efb0c875b6dd0f77d2c0ba4944ca75302dfa3dd ,\r\n                        0x08b16d9b344d298b2db75355c5a5c19e947ceb91130c0603c6e331799ea394c2 ,\r\n                        0x1883d6fa52cb29b39c76ab411cc7a02b8410ecdab58bdf030d51933dc28c84c4 ,\r\n                        0x0000000049454e44ae426082\r\n                    End\r\n\r\n                    LayoutCachedLeft =7560\r\n                    LayoutCachedTop =3960\r\n                    LayoutCachedWidth =9960\r\n                    LayoutCachedHeight =4740\r\n                    PictureCaptionArrangement =5\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =7560\r\n                    Top =1800\r\n                    Width =2640\r\n                    Height =1905\r\n                    ForeColor =5324600\r\n                    Name =\"Label227\"\r\n                    Caption =\"Use this tool to duplicate files while preserving commit history in Git.\\015\\012\"\r\n                        \"\\015\\012Paste in a list of Pipe delimited paths and click the button below.\"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =7560\r\n                    LayoutCachedTop =1800\r\n                    LayoutCachedWidth =10200\r\n                    LayoutCachedHeight =3705\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin Label\r\n                    OverlapFlags =85\r\n                    Left =7560\r\n                    Top =5040\r\n                    Width =2640\r\n                    Height =1560\r\n                    FontSize =9\r\n                    ForeColor =5324600\r\n                    Name =\"Label242\"\r\n                    Caption =\"Use full file paths with a pipe character (|) between the original and new file \"\r\n                        \"paths.\\015\\012\\015\\012Each entry should be on a new line. The list will be valid\"\r\n                        \"ated before running the operation.\"\r\n                    HorizontalAnchor =1\r\n                    LayoutCachedLeft =7560\r\n                    LayoutCachedTop =5040\r\n                    LayoutCachedWidth =10200\r\n                    LayoutCachedHeight =6600\r\n                    ForeThemeColorIndex =-1\r\n                    ForeTint =100.0\r\n                End\r\n                Begin TextBox\r\n                    OverlapFlags =85\r\n                    IMESentenceMode =3\r\n                    Left =720\r\n                    Top =6300\r\n                    Width =6120\r\n                    Height =315\r\n                    TabIndex =2\r\n                    Name =\"txtCommitMessage\"\r\n                    DefaultValue =\"=\\\"Split files while preserving history\\\"\"\r\n                    HorizontalAnchor =2\r\n                    VerticalAnchor =1\r\n\r\n                    LayoutCachedLeft =720\r\n                    LayoutCachedTop =6300\r\n                    LayoutCachedWidth =6840\r\n                    LayoutCachedHeight =6615\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            Left =720\r\n                            Top =5940\r\n                            Width =4620\r\n                            Height =315\r\n                            Name =\"Label244\"\r\n                            Caption =\"Final Commit Message:\"\r\n                            VerticalAnchor =1\r\n                            LayoutCachedLeft =720\r\n                            LayoutCachedTop =5940\r\n                            LayoutCachedWidth =5340\r\n                            LayoutCachedHeight =6255\r\n                        End\r\n                    End\r\n                End\r\n                Begin CommandButton\r\n                    FontUnderline = NotDefault\r\n                    OverlapFlags =85\r\n                    Left =4020\r\n                    Top =1440\r\n                    Width =2820\r\n                    TabIndex =3\r\n                    Name =\"cmdAddFormsAndReports\"\r\n                    Caption =\"Add Forms and Reports...\"\r\n                    OnClick =\"[Event Procedure]\"\r\n                    HorizontalAnchor =1\r\n                    BackStyle =0\r\n\r\n                    CursorOnHover =1\r\n                    LayoutCachedLeft =4020\r\n                    LayoutCachedTop =1440\r\n                    LayoutCachedWidth =6840\r\n                    LayoutCachedHeight =1800\r\n                    Alignment =3\r\n                    ForeThemeColorIndex =10\r\n                    ForeTint =100.0\r\n                    Gradient =0\r\n                    BackColor =14262935\r\n                    BackThemeColorIndex =-1\r\n                    BackTint =100.0\r\n                    OldBorderStyle =0\r\n                    BorderColor =15321539\r\n                    BorderThemeColorIndex =-1\r\n                    BorderTint =100.0\r\n                    HoverColor =15321539\r\n                    HoverThemeColorIndex =-1\r\n                    HoverTint =100.0\r\n                    PressedColor =13072231\r\n                    PressedThemeColorIndex =-1\r\n                    PressedShade =100.0\r\n                    Overlaps =1\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmVCSSplitFiles.cls\"\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSSplitFiles.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdAddFormsAndReports_Click\r\n' Author    : Adam Waller\r\n' Date      : 11/9/2023\r\n' Purpose   : Add the forms and reports source files for the project. Doing this\r\n'           : intelligently by only adding items that have a VBA code module.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdAddFormsAndReports_Click()\r\n\r\n    Dim intType As AcObjectType\r\n    Dim cComponent As IDbComponent\r\n    Dim varKey As Variant\r\n    Dim strFile As String\r\n    Dim strPrefix As String\r\n    Dim cList As clsConcat\r\n\r\n    ' Prepare class for new list\r\n    Set cList = New clsConcat\r\n    cList.AppendOnAdd = vbCrLf\r\n\r\n    ' Process for forms and reports (2 to 3)\r\n    DoCmd.Hourglass True\r\n    For intType = acForm To acReport\r\n\r\n        ' Get component type\r\n        If intType = acForm Then\r\n            Set cComponent = New clsDbForm\r\n            strPrefix = \"Form_\"\r\n        ElseIf intType = acReport Then\r\n            Set cComponent = New clsDbReport\r\n            strPrefix = \"Report_\"\r\n        End If\r\n\r\n        ' Loop through files\r\n        For Each varKey In cComponent.GetFileList.Keys\r\n            strFile = SwapExtension(CStr(varKey), \"cls\")\r\n            ' Skip files that already exist\r\n            If Not FSO.FileExists(strFile) Then\r\n                ' Check for code module marker in source file\r\n                If InStr(1, ReadFile(CStr(varKey)), \"CodeBehindForm\") > 0 Then\r\n                    ' Add to list of files to split\r\n                    cList.Add CStr(varKey), \"|\", strFile\r\n                End If\r\n            End If\r\n        Next varKey\r\n    Next intType\r\n    DoCmd.Hourglass False\r\n    cmdSplitFiles.SetFocus\r\n\r\n    ' See if we found any files to split.\r\n    If cList.Length > 0 Then\r\n        ' Replace existing content.\r\n        txtFileList = cList.GetStr\r\n    Else\r\n        MsgBox2 \"No Relevant Files Found\", _\r\n            \"Could not find any combined form or report source files that contained VBA modules\", _\r\n            , vbInformation\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : cmdSplitFiles_Click\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2023\r\n' Purpose   : Start the action to split the files.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub cmdSplitFiles_Click()\r\n\r\n    Dim varEntries As Variant\r\n    Dim varPaths As Variant\r\n    Dim strPaths() As String\r\n    Dim strNew() As String\r\n    Dim lngLine As Long\r\n    Dim strError As String\r\n    Dim strWorkingFolderBackup As String\r\n\r\n    ' Get an array of entries\r\n    varEntries = Split(Nz(txtFileList), vbCrLf)\r\n\r\n    ' Loop through lines, building arrays and validating each entry.\r\n    For lngLine = 0 To UBound(varEntries)\r\n        varPaths = Split(varEntries(lngLine), \"|\")\r\n        If UBound(varPaths) = 1 Then\r\n            ' Perform some validation on the entries\r\n            If Not FSO.FileExists(varPaths(0)) Then strError = \"File not found: \" & varPaths(0)\r\n            If FSO.FileExists(varPaths(1)) Then strError = \"File already exists: \" & varPaths(1)\r\n            If varPaths(0) = \"c:\\example\\original.txt\" Then strError = \"Please use your own file list, not the example.\"\r\n            If varPaths(0) = varPaths(1) Then strError = \"Cannot split to the same file name: \" & varPaths(0)\r\n            ' Add to arrays of file paths\r\n            AddToArray strPaths, varPaths(0)\r\n            AddToArray strNew, varPaths(1)\r\n        Else\r\n            If Len(Trim(varEntries(lngLine))) = 0 Then\r\n                ' Ignore blank lines\r\n            Else\r\n                strError = \"Expecting two file paths, separated by | character. See line: '\" & varPaths(0) & \"'\"\r\n            End If\r\n        End If\r\n        If Len(strError) Then Exit For\r\n    Next lngLine\r\n\r\n    ' Show validation error\r\n    If Len(strError) Then\r\n        MsgBox2 \"Validation Failed\", strError, \"Please correct the problem to continue.\", vbExclamation\r\n    Else\r\n        ' Proceed with the split after some validation\r\n\r\n        ' Get folder from first file (just in case they are from a different location)\r\n        strWorkingFolderBackup = Git.WorkingFolder\r\n        Git.WorkingFolder = FSO.GetParentFolderName(strPaths(0))\r\n\r\n        ' Require clean branch with git installation\r\n        If Not Git.IsCleanBranch Then strError = \"Cannot split files in Git when changes are present in the branch\"\r\n        If Not Git.Installed Then strError = \"Git must be installed to use this tool.\"\r\n\r\n        ' Make sure we don't have any errors with the Git commands\r\n        If Len(strError) Then\r\n            MsgBox2 \"Validation Failed\", strError, \"Please correct the problem to continue.\", vbExclamation\r\n        Else\r\n            ' Split the files using git commands\r\n            DoCmd.Hourglass True\r\n            Git.SplitFilesWithHistory strPaths, strNew, txtCommitMessage\r\n            DoCmd.Hourglass False\r\n\r\n            ' Show success message\r\n            MsgBox2 \"Finished\", \"The operation is complete.\", _\r\n                \"For additional details, please see `git.log` in the source folder.\", vbInformation\r\n\r\n            ' Clear existing list\r\n            txtFileList = vbNullString\r\n        End If\r\n\r\n        ' Restore original working folder\r\n        Git.WorkingFolder = strWorkingFolderBackup\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Load\r\n' Author    : Adam Waller\r\n' Date      : 3/22/2024\r\n' Purpose   :\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Load()\r\n    Translation.ApplyTo Me\r\n    MakeDialogResizable Me\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSTableData.bas",
    "content": "﻿Version =20\r\nVersionRequired =20\r\nBegin Form\r\n    RecordSelectors = NotDefault\r\n    NavigationButtons = NotDefault\r\n    AllowDeletions = NotDefault\r\n    DividingLines = NotDefault\r\n    AllowAdditions = NotDefault\r\n    AllowDesignChanges = NotDefault\r\n    DefaultView =2\r\n    ViewsAllowed =2\r\n    PictureAlignment =2\r\n    DatasheetGridlinesBehavior =3\r\n    GridX =24\r\n    GridY =24\r\n    Width =5040\r\n    DatasheetFontHeight =11\r\n    ItemSuffix =49\r\n    Left =6915\r\n    Top =2850\r\n    Right =17865\r\n    Bottom =9675\r\n    RecSrcDt = Begin\r\n        0xb0f4ef174201e640\r\n    End\r\n    RecordSource =\"SELECT d.TableIcon, d.TableName, d.FormatType, d.IsHidden, d.IsSystem, d.IsOther\"\r\n        \", d.IsLocal FROM tblTableData AS d WHERE [IsOther] = 0 AND [IsSystem] = 0 AND [I\"\r\n        \"sHidden] = 0 ORDER BY IIf([IsLocal], 0, 1), [TableName]; \"\r\n    Caption =\"Table Data\"\r\n    DatasheetFontName =\"Calibri\"\r\n    OnResize =\"[Event Procedure]\"\r\n    OnLoad =\"[Event Procedure]\"\r\n    AllowFormView =0\r\n    FilterOnLoad =0\r\n    ShowPageMargins =0\r\n    DisplayOnSharePointSite =1\r\n    DatasheetAlternateBackColor =15921906\r\n    DatasheetGridlinesColor12 =0\r\n    FitToScreen =1\r\n    DatasheetBackThemeColorIndex =1\r\n    BorderThemeColorIndex =3\r\n    ThemeFontIndex =1\r\n    ForeThemeColorIndex =0\r\n    AlternateBackThemeColorIndex =1\r\n    AlternateBackShade =95.0\r\n    Begin\r\n        Begin Label\r\n            BackStyle =0\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =0\r\n            BorderTint =50.0\r\n            ForeThemeColorIndex =0\r\n            ForeTint =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin TextBox\r\n            AddColon = NotDefault\r\n            FELineBreak = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AsianLineBreak =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ThemeFontIndex =1\r\n            ForeThemeColorIndex =0\r\n            ForeTint =75.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin ComboBox\r\n            AddColon = NotDefault\r\n            BorderLineStyle =0\r\n            LabelX =-1800\r\n            FontSize =11\r\n            FontName =\"Calibri\"\r\n            AllowValueListEdits =1\r\n            InheritValueList =1\r\n            ThemeFontIndex =1\r\n            BackThemeColorIndex =1\r\n            BorderThemeColorIndex =1\r\n            BorderShade =65.0\r\n            ForeThemeColorIndex =2\r\n            ForeShade =50.0\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin EmptyCell\r\n            Height =240\r\n            GridlineThemeColorIndex =1\r\n            GridlineShade =65.0\r\n        End\r\n        Begin Section\r\n            Height =1950\r\n            Name =\"Detail\"\r\n            AlternateBackThemeColorIndex =1\r\n            AlternateBackShade =95.0\r\n            BackThemeColorIndex =1\r\n            Begin\r\n                Begin TextBox\r\n                    Locked = NotDefault\r\n                    OverlapFlags =85\r\n                    TextAlign =1\r\n                    IMESentenceMode =3\r\n                    Left =1695\r\n                    Top =900\r\n                    Width =2625\r\n                    Height =360\r\n                    ColumnWidth =8370\r\n                    ColumnOrder =1\r\n                    TabIndex =1\r\n                    LeftMargin =44\r\n                    TopMargin =22\r\n                    RightMargin =44\r\n                    BottomMargin =22\r\n                    Name =\"txtTableName\"\r\n                    ControlSource =\"TableName\"\r\n                    GroupTable =1\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =2\r\n\r\n                    LayoutCachedLeft =1695\r\n                    LayoutCachedTop =900\r\n                    LayoutCachedWidth =4320\r\n                    LayoutCachedHeight =1260\r\n                    RowStart =1\r\n                    RowEnd =1\r\n                    ColumnStart =1\r\n                    ColumnEnd =1\r\n                    LayoutGroup =1\r\n                    GroupTable =1\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextAlign =1\r\n                            Left =360\r\n                            Top =900\r\n                            Width =1275\r\n                            Height =360\r\n                            LeftMargin =44\r\n                            TopMargin =22\r\n                            RightMargin =44\r\n                            BottomMargin =22\r\n                            Name =\"Label3\"\r\n                            Caption =\"Table Name\"\r\n                            GroupTable =1\r\n                            BottomPadding =150\r\n                            LayoutCachedLeft =360\r\n                            LayoutCachedTop =900\r\n                            LayoutCachedWidth =1635\r\n                            LayoutCachedHeight =1260\r\n                            RowStart =1\r\n                            RowEnd =1\r\n                            LayoutGroup =1\r\n                            GroupTable =1\r\n                        End\r\n                    End\r\n                End\r\n                Begin ComboBox\r\n                    RowSourceTypeInt =1\r\n                    OverlapFlags =85\r\n                    TextAlign =1\r\n                    IMESentenceMode =3\r\n                    ColumnCount =2\r\n                    ListWidth =2160\r\n                    Left =1695\r\n                    Top =1440\r\n                    Width =2625\r\n                    Height =360\r\n                    ColumnWidth =1815\r\n                    ColumnOrder =2\r\n                    TabIndex =2\r\n                    Name =\"cboFormatType\"\r\n                    ControlSource =\"FormatType\"\r\n                    RowSourceType =\"Value List\"\r\n                    RowSource =\"0;\\\"\\\";1;\\\"Tab Delimited\\\";2;\\\"XML Format\\\"\"\r\n                    ColumnWidths =\"0\"\r\n                    GroupTable =1\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =2\r\n                    LeftMargin =44\r\n                    TopMargin =22\r\n                    RightMargin =44\r\n                    BottomMargin =22\r\n\r\n                    LayoutCachedLeft =1695\r\n                    LayoutCachedTop =1440\r\n                    LayoutCachedWidth =4320\r\n                    LayoutCachedHeight =1800\r\n                    RowStart =2\r\n                    RowEnd =2\r\n                    ColumnStart =1\r\n                    ColumnEnd =1\r\n                    LayoutGroup =1\r\n                    ForeThemeColorIndex =0\r\n                    ForeTint =75.0\r\n                    ForeShade =100.0\r\n                    GroupTable =1\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextAlign =1\r\n                            Left =360\r\n                            Top =1440\r\n                            Width =1275\r\n                            Height =360\r\n                            LeftMargin =44\r\n                            TopMargin =22\r\n                            RightMargin =44\r\n                            BottomMargin =22\r\n                            Name =\"Label15\"\r\n                            Caption =\"Export As\"\r\n                            GroupTable =1\r\n                            BottomPadding =150\r\n                            LayoutCachedLeft =360\r\n                            LayoutCachedTop =1440\r\n                            LayoutCachedWidth =1635\r\n                            LayoutCachedHeight =1800\r\n                            RowStart =2\r\n                            RowEnd =2\r\n                            LayoutGroup =1\r\n                            GroupTable =1\r\n                        End\r\n                    End\r\n                End\r\n                Begin TextBox\r\n                    Locked = NotDefault\r\n                    OverlapFlags =85\r\n                    TextAlign =1\r\n                    IMESentenceMode =3\r\n                    Left =1695\r\n                    Top =360\r\n                    Width =2625\r\n                    Height =360\r\n                    ColumnWidth =-2\r\n                    ColumnOrder =0\r\n                    LeftMargin =44\r\n                    TopMargin =22\r\n                    RightMargin =44\r\n                    BottomMargin =22\r\n                    Name =\"txtTableIcon\"\r\n                    ControlSource =\"TableIcon\"\r\n                    GroupTable =1\r\n                    BottomPadding =150\r\n                    HorizontalAnchor =2\r\n\r\n                    LayoutCachedLeft =1695\r\n                    LayoutCachedTop =360\r\n                    LayoutCachedWidth =4320\r\n                    LayoutCachedHeight =720\r\n                    ColumnStart =1\r\n                    ColumnEnd =1\r\n                    LayoutGroup =1\r\n                    GroupTable =1\r\n                    Begin\r\n                        Begin Label\r\n                            OverlapFlags =85\r\n                            TextAlign =1\r\n                            Left =360\r\n                            Top =360\r\n                            Width =1275\r\n                            Height =360\r\n                            LeftMargin =44\r\n                            TopMargin =22\r\n                            RightMargin =44\r\n                            BottomMargin =22\r\n                            Name =\"Label40\"\r\n                            Caption =\" \"\r\n                            GroupTable =1\r\n                            BottomPadding =150\r\n                            LayoutCachedLeft =360\r\n                            LayoutCachedTop =360\r\n                            LayoutCachedWidth =1635\r\n                            LayoutCachedHeight =720\r\n                            LayoutGroup =1\r\n                            GroupTable =1\r\n                        End\r\n                    End\r\n                End\r\n            End\r\n        End\r\n    End\r\nEnd\r\nCodeBehindForm\r\n' See \"frmVCSTableData.cls\"\r\n"
  },
  {
    "path": "Version Control.accda.src/forms/frmVCSTableData.cls",
    "content": "﻿Attribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = True\r\nAttribute VB_PredeclaredId = True\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Load\r\n' Author    : Adam Waller\r\n' Date      : 3/22/2024\r\n' Purpose   : Load any initial settings\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Form_Load()\r\n    Translation.ApplyTo Me\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Form_Resize\r\n' Author    : Adam Waller\r\n' Date      : 3/22/2024\r\n' Purpose   : Scale column sizes\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Form_Resize()\r\n    ScaleColumns Me, , Array(Me.txtTableIcon.Name, Me.cboFormatType.Name)\r\n\r\n    ' Size to fit; don't rely on Access' saved settings to get this right.\r\n    Me.txtTableIcon.ColumnWidth = -2\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/macros/autoexec.bas",
    "content": "﻿Version =196611\r\nColumnsShown =0\r\nBegin\r\n    Action =\"SetLocalVar\"\r\n    Argument =\"__*L0_\"\r\n    Argument =\"[CurrentProject].[IsTrusted]\"\r\nEnd\r\nBegin\r\n    Action =\"SetLocalVar\"\r\n    Argument =\"__*L0C_\"\r\n    Argument =\"[LocalVars]![__*L0_]\"\r\nEnd\r\nBegin\r\n    Condition =\"[LocalVars]![__*L0C_]\"\r\n    Action =\"RunCode\"\r\n    Argument =\"AutoRun()\"\r\nEnd\r\nBegin\r\n    Action =\"SetLocalVar\"\r\n    Argument =\"__*L0C_\"\r\n    Argument =\"(Not [LocalVars]![__*L0_])\"\r\nEnd\r\nBegin\r\n    Condition =\"[LocalVars]![__*L0C_]\"\r\n    Action =\"MsgBox\"\r\n    Argument =\"This add-In file is not currently trusted. Please click \\\"Enable Content\\\" to tr\"\r\n        \"ust the add-in and run the startup code normally.\"\r\n    Argument =\"-1\"\r\n    Argument =\"2\"\r\n    Argument =\"Add-in is not trusted\"\r\nEnd\r\nBegin\r\n    Action =\"SetLocalVar\"\r\n    Argument =\"__*L0C_\"\r\n    Argument =\"False\"\r\nEnd\r\nBegin\r\n    Comment =\"_AXL:<?xml version=\\\"1.0\\\" encoding=\\\"UTF-16\\\" standalone=\\\"no\\\"?>\\015\\012<UserI\"\r\n        \"nterfaceMacro MinimumClientDesignVersion=\\\"14.0.0000.0000\\\" xmlns=\\\"http://schem\"\r\n        \"as.microsoft.com/office/accessservices/2009/11/application\\\" xmlns:a=\\\"http://sc\"\r\n        \"hemas.microsoft.com/office/acc\"\r\nEnd\r\nBegin\r\n    Comment =\"_AXL:essservices/2009/11/forms\\\"><Statements><ConditionalBlock><If><Condition>[C\"\r\n        \"urrentProject].[IsTrusted]</Condition><Statements><Action Name=\\\"RunCode\\\"><Argu\"\r\n        \"ment Name=\\\"FunctionName\\\">AutoRun()</Argument></Action></Statements></If><Else>\"\r\n        \"<Statements><Actio\"\r\nEnd\r\nBegin\r\n    Comment =\"_AXL:n Name=\\\"MessageBox\\\"><Argument Name=\\\"Message\\\">This add-In file is not cu\"\r\n        \"rrently trusted. Please click \\\"Enable Content\\\" to trust the add-in and run the\"\r\n        \" startup code normally.</Argument><Argument Name=\\\"Type\\\">Warning?</Argument><Ar\"\r\n        \"gument Name=\\\"Title\\\">A\"\r\nEnd\r\nBegin\r\n    Comment =\"_AXL:dd-in is not trusted</Argument></Action></Statements></Else></ConditionalBl\"\r\n        \"ock></Statements></UserInterfaceMacro>\"\r\nEnd\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/IDbComponent.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"IDbComponent\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : IDbComponent (Abstract Class)\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class interface defines the standard properties for exporting and\r\n'           : importing database objects for version control. This class should be\r\n'           : implemented into the classes defined for each object type to be exported\r\n'           : and imported as a part of the database source code.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic DbObject As Object\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Export(Optional strAlternatePath As String)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Import(strFile As String)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Merge(strFile As String)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the source file(s) associated with this component from one folder\r\n'           : to another. This is especially important for components that produce more\r\n'           : than one source file, such as VBE Forms, themes, and queries.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MoveSource(strFromFolder As String, strToFolder As String)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'           : If blnModifiedOnly is set to True, then only objects that have been\r\n'           : modified will be returned.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IsModified() As Boolean\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DateModified() As Date\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPublic Property Get BaseFolder() As String\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get SourceFile() As String\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get FileExtensions() As Collection\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'           : Based on the export folder defined in options.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetFileList() As Dictionary\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get ComponentType() As eDatabaseComponentType\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Name() As String\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Category() As String\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get SingleFile() As Boolean\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Count(Optional blnModifiedOnly As Boolean = False) As Long\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get QuickCount() As Long\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/IDbSchema.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"IDbSchema\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : IDbSchema (Abstract Class)\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : This class interface defines the standard functions for exporting and\r\n'           : importing database server objects for version control. This class should\r\n'           : be implemented into the classes defined for each server type used when\r\n'           : exporting object definitions for server-side objects.\r\n'           : NOTE: This is not intended to write to an external database server, but\r\n'           : only to download object definitions that may be related to development\r\n'           : in Microsoft Access databases.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Initialize\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : Initialize the instance of the server connection\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Initialize(dInstance As Dictionary)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ObjectCount\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Return a count of total objects, or how many changes were found between\r\n'           : the database server objects and the current index file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ObjectCount(blnModifiedOnly As Boolean) As Long\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : Export the database server objects (schema) to source files\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Export(blnFullExport As Boolean, Optional strAlternatePath As String)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : Base folder for export, (available after initializing)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get BaseFolder() As String\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ServerType\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : The type of database server represented by this class\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get ServerType() As eDatabaseServerType\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TypeDescription\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : Return description for this server type (i.e. Microsoft SQL Server)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get TypeDescription()\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Name() As String\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsAdpFunction.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsAdpFunction\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Function As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSQLObjectDefinitionForADP(m_Function.Name)\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim fn As AccessObject\r\n    Dim cFn As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each fn In CurrentData.AllFunctions\r\n            Set cFn = New clsAdpFunction\r\n            Set cFn.DbObject = fn\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cFn.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cFn.SourceFile, cFn\r\n        Next fn\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.sql\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = (GetSQLObjectModifiedDate(m_Function.Name, estOther) > VCSIndex.Item(Me).ExportDate)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_Function Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = GetSQLObjectModifiedDate(m_Function.Name, estOther)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Functions\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"functions\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Function Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Function.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Function Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(StripDboPrefix(m_Function.Name)) & \".sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentData.AllFunctions.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbAdpFunction\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Function\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Function = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Helps us know whether we have already counted the objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    'm_Count = -1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsAdpProcedure.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsAdpProcedure\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_SProc As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSQLObjectDefinitionForADP(m_SProc.Name)\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim sproc As AccessObject\r\n    Dim cSproc As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each sproc In CurrentData.AllStoredProcedures\r\n            Set cSproc = New clsAdpProcedure\r\n            Set cSproc.DbObject = sproc\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cSproc.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cSproc.SourceFile, cSproc\r\n        Next sproc\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.sql\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = (GetSQLObjectModifiedDate(m_SProc.Name, estStoredProcedure) > VCSIndex.Item(Me).ExportDate)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_SProc Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = GetSQLObjectModifiedDate(m_SProc.Name, estStoredProcedure)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Procedures\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"procedures\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_SProc Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_SProc.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_SProc Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(StripDboPrefix(m_SProc.Name)) & \".sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentData.AllStoredProcedures.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbAdpStoredProcedure\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_SProc\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_SProc = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Helps us know whether we have already counted the objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    'm_Count = -1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsAdpServerView.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsAdpServerView\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_View As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSQLObjectDefinitionForADP(m_View.Name)\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim view As AccessObject\r\n    Dim cView As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each view In CurrentData.AllViews\r\n            Set cView = New clsAdpServerView\r\n            Set cView.DbObject = view\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cView.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cView.SourceFile, cView\r\n        Next view\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.sql\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = (GetSQLObjectModifiedDate(m_View.Name, estView) > VCSIndex.Item(Me).ExportDate)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_View Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = GetSQLObjectModifiedDate(m_View.Name, estView)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Views\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"views\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_View Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_View.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_View Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(StripDboPrefix(m_View.Name)) & \".sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentData.AllViews.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbAdpServerView\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_View\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_View = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Helps us know whether we have already counted the objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    'm_Count = -1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsAdpTable.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsAdpTable\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Table As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim tbl As AccessObject\r\n    Dim cTable As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each tbl In CurrentData.AllTables\r\n            Set cTable = New clsAdpTable\r\n            Set cTable.DbObject = tbl\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cTable.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cTable.SourceFile, cTable\r\n        Next tbl\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 9/1/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n\r\n    Dim strSql As String\r\n    Dim rst As ADODB.Recordset\r\n    Dim intRst As Integer\r\n    Dim fld As ADODB.Field\r\n    Dim colText As New clsConcat\r\n\r\n    ' Initialize counter\r\n    intRst = 2\r\n\r\n    ' Get initial table information\r\n    strSql = \"exec sp_help N'\" & m_Table.Name & \"'\"\r\n    '@Ignore SetAssignmentWithIncompatibleObjectType\r\n    Set rst = CurrentProject.Connection.Execute(strSql)\r\n    colText.Add \"-- sp_help Recordset 1\" & vbCrLf & vbCrLf\r\n    For Each fld In rst.Fields\r\n        colText.Add fld.Name\r\n        colText.Add vbTab\r\n    Next fld\r\n    colText.Add vbCrLf\r\n    colText.Add rst.GetString(, , vbTab, vbCrLf)\r\n\r\n    ' Loop through additional recordsets for columns, keys and other data\r\n    Do\r\n        Set rst = rst.NextRecordset\r\n        If rst Is Nothing Then Exit Do\r\n        If rst.State = adStateClosed Then Exit Do\r\n\r\n        colText.Add vbCrLf & vbCrLf & \"-- sp_help Recordset \" & intRst & vbCrLf & vbCrLf\r\n        For Each fld In rst.Fields\r\n            colText.Add fld.Name\r\n            colText.Add vbTab\r\n        Next fld\r\n        If Not rst.EOF Then\r\n            colText.Add vbCrLf\r\n            colText.Add rst.GetString(, , vbTab, vbCrLf)\r\n        End If\r\n\r\n        intRst = intRst + 1\r\n    Loop\r\n\r\n    ' Clear references\r\n    Set fld = Nothing\r\n    Set rst = Nothing\r\n\r\n    ' Return SQL content\r\n    GetSource = colText.GetStr\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.txt\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = (GetSQLObjectModifiedDate(m_Table.Name, estTable) > VCSIndex.Item(Me).ExportDate)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_Table Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = GetSQLObjectModifiedDate(m_Table.Name, estTable)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"SQL Tables\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"sqltables\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"txt\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Table Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Table.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Table Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(StripDboPrefix(m_Table.Name)) & \".txt\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentData.AllTables.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbAdpTable\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Table\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Table = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsAdpTrigger.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsAdpTrigger\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' Values from sys.objects SQL table\r\nPublic TriggerName As String\r\nPublic TableName As String\r\nPublic SchemaName As String\r\nPublic SqlModifyDate As Date\r\n\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSQLObjectDefinitionForADP(Me.TriggerName)\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Not supported\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cTrigger As clsAdpTrigger\r\n    Dim cComponent As IDbComponent\r\n    Dim rst As ADODB.Recordset\r\n    Dim strSql As String\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n\r\n        ' Use parameter options if provided.\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n\r\n        ' Build list of triggers in database (from sysobjects)\r\n        strSql = \"SELECT [name],object_name(parent_object_id) AS parent_name, schema_name([schema_id]) AS [schema_name], modify_date FROM sys.objects WHERE type='TR'\"\r\n        Set rst = New ADODB.Recordset\r\n        With rst\r\n            .Open strSql, CurrentProject.Connection, adOpenForwardOnly, adLockReadOnly\r\n            Do While Not .EOF\r\n                Set cTrigger = New clsAdpTrigger\r\n                With cTrigger\r\n                    .TriggerName = Nz(rst!Name)\r\n                    .TableName = Nz(rst!parent_name)\r\n                    .SchemaName = Nz(rst!schema_name)\r\n                    .SqlModifyDate = Nz(rst!modify_date)\r\n                End With\r\n                Set cComponent = cTrigger\r\n                ' Compare modified date with index\r\n                blnAdd = True\r\n                If blnModifiedOnly Then blnAdd = cComponent.IsModified\r\n                If blnAdd Then m_Items(blnModifiedOnly).Add cComponent.SourceFile, cComponent\r\n                .MoveNext\r\n            Loop\r\n            .Close\r\n        End With\r\n        Set rst = Nothing\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.sql\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = (Me.SqlModifyDate > VCSIndex.Item(Me).ExportDate)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If Me.TriggerName = vbNullString Then Exit Function\r\n    IDbComponent_DateModified = GetSQLObjectModifiedDate(Me.TriggerName, estTrigger)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Triggers\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"triggers\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = Me.TriggerName\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If Me.TriggerName = vbNullString Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(Me.SchemaName & \"_\" & Me.TriggerName) & \".sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    ' Just return 1 since there is no easy way to count triggers without querying the database\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbAdpTrigger\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = Nothing\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsConcat.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsConcat\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' String concatenation (joining strings together) can have a significant\r\n' performance impact when you are using the ampersand character to join\r\n' strings together. While negligible in occasional use, if you start\r\n' running tens of thousands of these in a loop, it can really bog\r\n' down the processing due to the memory reallocations happening behind\r\n' the scenes. In those cases it is better to use the Mid$() function to\r\n' change an existing buffer to build the return string.\r\n\r\n' Special thanks to Nir Sofer - http://www.nirsoft.net/vb/strclass.html\r\n' and Chris Lucas - http://www.planetsourcecode.com/vb/scripts/ShowCode.asp?txtCodeId=37141&lngWId=1\r\n' for their inspiration with these concepts.\r\n\r\n' Set this to any character or string to add after each\r\n' call to `.Add()`. A common example would be vbCrLf.\r\nPublic AppendOnAdd As String\r\n\r\n' Set up an array of pages to hold strings\r\nPrivate astrPages() As String\r\nPrivate lngCurrentPage As Long\r\nPrivate lngCurrentPos As Long\r\nPrivate lngPageSize As Long\r\nPrivate lngInitialPages As Long\r\n\r\n' These defaults can be tweaked as needed\r\nConst clngPageSize As Long = 4096\r\nConst clngInitialPages As Long = 100\r\n\r\n\r\n\r\n' Prepares the initial buffer page\r\nPrivate Sub Class_Initialize()\r\n\r\n    If lngPageSize = 0 Then lngPageSize = clngPageSize\r\n    If lngInitialPages = 0 Then lngInitialPages = clngInitialPages\r\n\r\n    ' Set up the initial array of pages.\r\n    ReDim astrPages(0 To lngInitialPages - 1) As String\r\n\r\n    ' Prepare first page\r\n    astrPages(0) = Space$(lngPageSize)\r\n\r\nEnd Sub\r\n\r\n\r\n' Add 1 or more strings (avoiding the string conversion of paramarray)\r\nPublic Sub Add(str1 As String, Optional str2 As String, Optional str3 As String, Optional str4 As String, Optional str5 As String, _\r\n    Optional str6 As String, Optional str7 As String, Optional str8 As String, Optional str9 As String, Optional str10 As String)\r\n    If str1 <> vbNullString Then AddString str1\r\n    If str2 <> vbNullString Then AddString str2\r\n    If str3 <> vbNullString Then AddString str3\r\n    If str4 <> vbNullString Then AddString str4\r\n    If str5 <> vbNullString Then AddString str5\r\n    If str6 <> vbNullString Then AddString str6\r\n    If str7 <> vbNullString Then AddString str7\r\n    If str8 <> vbNullString Then AddString str8\r\n    If str9 <> vbNullString Then AddString str9\r\n    If str10 <> vbNullString Then AddString str10\r\n    AddString AppendOnAdd\r\nEnd Sub\r\n\r\n\r\n' Add to the string buffer\r\nPrivate Sub AddString(strAddString As String)\r\n\r\n    Dim lngLen          As Long\r\n    Dim lngRemaining    As Long\r\n    Dim lngAddStrPos    As Long\r\n    Dim lngAddLen       As Long\r\n\r\n    ' Get length of new string\r\n    lngLen = Len(strAddString)\r\n\r\n    ' No need to process a zero-length string\r\n    If lngLen > 0 Then\r\n        ' Set starting position for string we are adding\r\n        lngAddStrPos = 1\r\n\r\n        ' Continue filling pages till we reach the end of the new string\r\n        Do While lngAddStrPos <= lngLen\r\n\r\n            ' Check to see if we need a new page\r\n            If lngCurrentPos = lngPageSize Then\r\n                ' See if we already have a new page available in the array\r\n                If lngCurrentPage = UBound(astrPages) Then\r\n                    ' Need to add a page to the array.\r\n                    ReDim Preserve astrPages(0 To lngCurrentPage + 1)\r\n                End If\r\n                ' Prepare page as a buffer\r\n                lngCurrentPage = lngCurrentPage + 1\r\n                astrPages(lngCurrentPage) = Space$(lngPageSize)\r\n                lngCurrentPos = 0\r\n            End If\r\n\r\n            ' See if it fits on the current page\r\n            lngRemaining = lngPageSize - lngCurrentPos\r\n            If (lngLen - (lngAddStrPos - 1)) <= lngRemaining Then\r\n                ' Yes, add to current page.\r\n                lngAddLen = (lngLen - (lngAddStrPos - 1))\r\n                Mid$(astrPages(lngCurrentPage), lngCurrentPos + 1, lngAddLen) = Mid$(strAddString, lngAddStrPos)\r\n                lngAddStrPos = lngLen + 1\r\n                lngCurrentPos = lngCurrentPos + lngAddLen\r\n            Else\r\n                ' Fill remaining available space on current page.\r\n                Mid$(astrPages(lngCurrentPage), lngCurrentPos + 1, lngRemaining) = Mid$(strAddString, lngAddStrPos, lngRemaining)\r\n                ' Note position in new string\r\n                lngCurrentPos = lngPageSize\r\n                lngAddStrPos = lngAddStrPos + lngRemaining\r\n            End If\r\n\r\n        ' Move to next page, if needed\r\n        Loop\r\n\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n' Removes the specified number of chacters from the string.\r\n' (Technically just moves the position back)\r\nPublic Sub Remove(lngChars As Long)\r\n\r\n    Dim lngTotalLen As Long\r\n    Dim lngNewPosition As Long\r\n\r\n    ' Get total length of current string including all pages\r\n    lngTotalLen = lngCurrentPos + (lngCurrentPage * lngPageSize)\r\n\r\n    ' We can't remove more characters than we put in the string to start with.\r\n    If lngChars > lngTotalLen Then\r\n        ' Go to beginning\r\n        lngCurrentPage = 0\r\n        lngCurrentPos = 1\r\n    Else\r\n        ' Get new absolute position\r\n        lngNewPosition = lngTotalLen - lngChars\r\n        ' Calculate full pages\r\n        lngCurrentPage = (lngNewPosition \\ lngPageSize)\r\n        ' Set position on partial page\r\n        lngCurrentPos = lngNewPosition - (lngCurrentPage * lngPageSize)\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n' Returns the accumulated string\r\nPublic Function GetStr() As String\r\n\r\n    Dim lngCnt As Long\r\n\r\n    ' Prepare return string. This should be the filled pages plus the last\r\n    ' partial page, divided by 2 to get the string length instead of byte length.\r\n    GetStr = Space$((lngCurrentPage * lngPageSize) + lngCurrentPos)\r\n\r\n    ' Loop through filled pages, overlaying on return string.\r\n    ' (Last partial page is automatically trimmed based on returned string size.)\r\n    ' (If lngCurrentPos=0 then skip last page)\r\n    If Len(GetStr) > 0 Then\r\n        For lngCnt = 0 To lngCurrentPage - Abs(CBool(lngCurrentPos = 0))\r\n            Mid$(GetStr, (lngCnt * lngPageSize) + 1, lngPageSize) = astrPages(lngCnt)\r\n        Next lngCnt\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n' Return a partial string from a specified position\r\nPublic Function MidStr(lngStart As Long, Optional lngLength As Long = -1) As String\r\n\r\n    Dim lngPage As Long\r\n    Dim lngPos As Long\r\n    Dim lngStartPage As Long\r\n    Dim lngStartPos As Long\r\n\r\n    ' Prepare return string length.\r\n    If lngLength = -1 Then\r\n        ' Return remaining string after lngStart\r\n        lngLength = (Me.Length - lngStart) + 1\r\n        MidStr = Space$(lngLength)\r\n    Else\r\n        ' Return a specified number of characters\r\n        MidStr = Space$(lngLength)\r\n    End If\r\n\r\n    ' Determine start page and position for return string\r\n    lngStartPage = (lngStart - 1) \\ lngPageSize ' Zero based page\r\n    lngStartPos = lngStart - (lngStartPage * lngPageSize)\r\n\r\n    ' Loop through filled pages, overlaying on return string.\r\n    ' (Last partial page is automatically trimmed based on returned string size.)\r\n    If Len(MidStr) > 0 Then\r\n        For lngPage = lngStartPage To lngCurrentPage\r\n            ' Could start at any point on first page\r\n            If lngPage = lngStartPage Then\r\n                Mid$(MidStr, 1) = Mid$(astrPages(lngPage), lngStartPos)\r\n                ' lngPos is the current position in the new string\r\n                lngPos = lngPageSize - (lngStartPos - 2)\r\n            Else\r\n                ' Pull whole pages as needed\r\n                Mid$(MidStr, lngPos) = astrPages(lngPage)\r\n                lngPos = lngPos + lngPageSize\r\n            End If\r\n            ' Exit when we have filled the requested string.\r\n            If lngPos > lngLength Then Exit For\r\n        Next lngPage\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RTrim\r\n' Author    : Adam Waller\r\n' Date      : 8/14/2023\r\n' Purpose   : Trim trailing whitespace from content\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function RTrim(Optional strTrimChars As String = \" \")\r\n    Do\r\n        If Me.Length < Len(strTrimChars) Then Exit Do\r\n        If Me.RightStr(Len(strTrimChars)) = strTrimChars Then\r\n            Me.Remove Len(strTrimChars)\r\n        Else\r\n            Exit Do\r\n        End If\r\n    Loop\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Right\r\n' Author    : Adam Waller\r\n' Date      : 11/5/2020\r\n' Purpose   : Return the rightmost specified number of characters.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function RightStr(lngLength As Long) As String\r\n    If Me.Length > lngLength Then\r\n        RightStr = MidStr((Me.Length - lngLength) + 1)\r\n    Else\r\n        RightStr = GetStr\r\n    End If\r\nEnd Function\r\n\r\n\r\n' returns the length of the string, based on the current position\r\n' (Faster than building the string just to check the length)\r\nPublic Function Length() As Double\r\n    Length = (lngCurrentPage * lngPageSize) + lngCurrentPos\r\nEnd Function\r\n\r\n\r\n' Reset the buffer without changing the page size\r\nPublic Sub Clear()\r\n\r\n    Class_Initialize\r\n\r\n    ' Reset positions\r\n    lngCurrentPage = 0\r\n    lngCurrentPos = 0\r\n\r\nEnd Sub\r\n\r\n\r\n' Manually set page size if you want something different from the default.\r\nPublic Sub SetPageSize(lngNewPageSize As Long, Optional lngNewInitialPages As Long)\r\n    If lngCurrentPage > 0 Or lngCurrentPos > 1 Then\r\n        MsgBox \"Please set the page size before adding any data\", vbExclamation, \"Error in clsConcat\"\r\n    Else\r\n        lngPageSize = lngNewPageSize\r\n        If lngNewInitialPages > 0 Then lngInitialPages = lngNewInitialPages\r\n        ' Reinitialize with the updated sizes\r\n        Class_Initialize\r\n    End If\r\nEnd Sub\r\n\r\n\r\n' Test the class to make sure we are paging correctly.\r\nPublic Sub SelfTest()\r\n\r\n    SetPageSize 10, 5\r\n\r\n    Debug.Assert UBound(astrPages) = 4\r\n    Add \"abcdefghij\"\r\n    Add \"k\"\r\n    Debug.Assert Len(GetStr) = 11\r\n    Debug.Assert Length = 11\r\n    Remove 2\r\n    Debug.Assert Len(GetStr) = 9\r\n    Add \"jkl\"\r\n    Debug.Assert Len(GetStr) = 12\r\n    Debug.Assert GetStr = \"abcdefghijkl\"\r\n    Add \"m123456789\"\r\n    Remove 11\r\n    Debug.Assert GetStr = \"abcdefghijk\"\r\n    Debug.Assert MidStr(1, 1) = \"a\"\r\n    Debug.Assert MidStr(11, 1) = \"k\"\r\n    Debug.Assert MidStr(2, 3) = \"bcd\"\r\n    Debug.Assert MidStr(8) = \"hijk\"\r\n    Debug.Assert MidStr(10, 1) = \"j\"\r\n    Debug.Assert RightStr(1) = \"k\"\r\n    Debug.Assert RightStr(100) = \"abcdefghijk\"\r\n\r\n    ' Verify paging\r\n    With Me\r\n        .Clear\r\n        .SetPageSize 5, 2\r\n        .Add \"1234\"\r\n        Debug.Assert .GetStr = \"1234\"\r\n        .Add \"5\"\r\n        Debug.Assert .GetStr = \"12345\"\r\n        .Add \"6\"\r\n        Debug.Assert .GetStr = \"123456\"\r\n        .Add \"789\"\r\n        Debug.Assert .GetStr = \"123456789\"\r\n        .Add \"0\"\r\n        Debug.Assert .GetStr = \"1234567890\"\r\n        .Add \"A\"\r\n        Debug.Assert .GetStr = \"1234567890A\"\r\n        .Remove 1\r\n        Debug.Assert .GetStr = \"1234567890\"\r\n        .Remove 1\r\n        Debug.Assert .GetStr = \"123456789\"\r\n        .Add \"0A\"\r\n        Debug.Assert .GetStr = \"1234567890A\"\r\n        .Remove 2\r\n        Debug.Assert .GetStr = \"123456789\"\r\n        .Add \"0A\"\r\n        Debug.Assert .GetStr = \"1234567890A\"\r\n    End With\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsConflictItem.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsConflictItem\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPublic CategoryList As Dictionary\r\nPublic CategoryName As String\r\nPublic SingleFile As Boolean\r\nPublic ItemKey As String\r\nPublic FileName As String\r\nPublic ObjectDate As Date\r\nPublic IndexDate As Date\r\nPublic FileDate As Date\r\nPublic Operation As eIndexOperationType\r\nPublic ActionType As eResolveConflict\r\nPublic Resolution As eResolveConflict\r\nPublic Suggestion As eResolveConflict\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Resolve\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Resolve the conflict\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Resolve()\r\n\r\n    Select Case Me.Operation\r\n\r\n        ' Merge operation (import)\r\n        Case eatImport\r\n            Select Case Me.Resolution\r\n                Case ercDelete\r\n                    ' Delete orphaned object by merging it from a non-existent source\r\n                    ' file. (Object deletion is handled during the merge)\r\n                    ' Leave in list of objects to merge\r\n                    Log.Add \"  \" & FSO.GetFileName(FileName) & \" (Delete)\", False\r\n                Case ercOverwrite\r\n                    ' Leave in list of objects to overwrite\r\n                    Log.Add \"  \" & FSO.GetFileName(FileName) & \" (Overwrite)\", False\r\n                Case ercSkip\r\n                    ' Don't delete this item\r\n                    RemoveFromItemList\r\n                    Log.Add \"  \" & FSO.GetFileName(FileName) & \" (Skip)\", False\r\n            End Select\r\n\r\n        ' Export operation\r\n        Case eatExport, eatAltExport\r\n            Select Case Me.Resolution\r\n                Case ercOverwrite\r\n                    If Me.ActionType = ercDelete Then\r\n                        Log.Add \"  \" & FSO.GetFileName(FileName) & \" (Delete)\", False\r\n                        DeleteFile Me.FileName\r\n                    ElseIf Me.ActionType = ercOverwrite Then\r\n                        Log.Add \"  \" & FSO.GetFileName(FileName) & \" (Overwrite)\", False\r\n                        ' Leave in list of files to overwrite.\r\n                    End If\r\n                Case ercSkip\r\n                    RemoveFromItemList\r\n                    Log.Add \"  \" & FSO.GetFileName(FileName) & \" (Skip)\", False\r\n            End Select\r\n    End Select\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveFromCollection\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Remove this item from the parent collection of items to import/export\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RemoveFromItemList()\r\n\r\n    Dim lngCount As Long\r\n\r\n    If CategoryList.Exists(Me.CategoryName) Then\r\n        ' Check operation type to see if we are dealing with objects or source files\r\n        Select Case Operation\r\n\r\n            Case eatImport  ' Merge\r\n                If CategoryList(Me.CategoryName).Exists(\"Files\") Then\r\n                    With CategoryList(Me.CategoryName)(\"Files\")\r\n                        If .Exists(Me.FileName) Then\r\n                            ' Remove from list of files to import\r\n                            .Remove Me.FileName\r\n                        End If\r\n                        lngCount = .Count\r\n                    End With\r\n                End If\r\n\r\n            Case eatExport\r\n                If CategoryList(Me.CategoryName).Exists(\"Objects\") Then\r\n                    With CategoryList(Me.CategoryName)(\"Objects\")\r\n                        If .Exists(Me.ItemKey) Then\r\n                            ' Remove from object list\r\n                            .Remove Me.ItemKey\r\n                        End If\r\n                        lngCount = .Count\r\n                    End With\r\n                End If\r\n        End Select\r\n\r\n        ' Remove category if no more objects of this type,\r\n        ' or if this type exports a single file.\r\n        If lngCount = 0 Or CategoryList(Me.CategoryName)(\"Class\").SingleFile Then\r\n            CategoryList.Remove Me.CategoryName\r\n        End If\r\n    End If\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsConflicts.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsConflicts\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsConflicts\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : This class is used to manage the conflicts during import/export\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' Status to approve and continue.\r\nPublic ApproveResolutions As Boolean\r\n\r\nPrivate m_intOperationType As eIndexOperationType\r\nPrivate m_dCategories As Dictionary\r\nPrivate m_dItems As Dictionary\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ShowDialog\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Show the merge conflict dialog\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ShowDialog()\r\n    SaveToTable\r\n    With DoCmd\r\n        ' The following gives a smoother load when using the datasheet subform.\r\n        .Hourglass True\r\n        '.OpenForm \"frmVCSConflict\", , , , , acHidden\r\n        DoEvents\r\n        .Hourglass False\r\n        ' Open the form in dialog mode so that we return a status when it closes.\r\n        Perf.PauseTiming\r\n        .OpenForm \"frmVCSConflict\", , , , , acDialog\r\n        UpdateResolutionsFromTable\r\n        Perf.ResumeTiming\r\n    End With\r\n    ClearTable\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Resolve\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Resolve the conflicts, removing any objects that should be skipped in the\r\n'           : import/export process.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Resolve()\r\n    Dim cItem As clsConflictItem\r\n    Dim varKey As Variant\r\n    For Each varKey In m_dItems.Keys\r\n        Set cItem = m_dItems(varKey)\r\n        cItem.Resolve\r\n        ' Clear reference to categories dictionary\r\n        Set cItem.CategoryList = Nothing\r\n    Next varKey\r\n    ' Clear additional reference to list\r\n    Set m_dCategories = Nothing\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Add\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Add an item to the conflict list.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Add(cComponent As IDbComponent, strKey As String, dteIndexDate As Date, dteFileDate As Date, intActionType As eResolveConflict, _\r\n    Optional strSourceFile As String, Optional intSuggestedResolution As eResolveConflict)\r\n\r\n    Dim cItem As clsConflictItem\r\n\r\n    Set cItem = New clsConflictItem\r\n    With cItem\r\n        .Operation = m_intOperationType\r\n        .CategoryName = cComponent.Category\r\n        .ItemKey = strKey\r\n        .SingleFile = cComponent.SingleFile\r\n        .FileName = Nz2(strSourceFile, cComponent.SourceFile)\r\n        .ObjectDate = cComponent.DateModified\r\n        .IndexDate = dteIndexDate\r\n        .FileDate = dteFileDate\r\n        .ActionType = intActionType\r\n        .Suggestion = intSuggestedResolution\r\n        Set .CategoryList = m_dCategories\r\n        m_dItems.Add .FileName, cItem\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveToTable\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Save collection to table\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SaveToTable()\r\n\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n    Dim varKey As Variant\r\n    Dim cItem As clsConflictItem\r\n\r\n    ' Clear any existing records first\r\n    ClearTable\r\n    If m_dItems.Count = 0 Then Exit Sub\r\n\r\n    ' Open table to add items\r\n    Set dbs = CodeDb\r\n    Set rst = dbs.OpenRecordset(\"tblConflicts\")\r\n    With rst\r\n\r\n        ' Loop through items\r\n        For Each varKey In m_dItems.Keys\r\n            Set cItem = m_dItems(varKey)\r\n            .AddNew\r\n                !Component = cItem.CategoryName\r\n                !ItemKey = cItem.ItemKey\r\n                ' Save the file name, including object type folder\r\n                !FileName = Mid$(cItem.FileName, Len(Options.GetExportFolder) + 1)\r\n                !ObjectDate = ZN(cItem.ObjectDate)\r\n                !IndexDate = ZN(cItem.IndexDate)\r\n                !FileDate = ZN(cItem.FileDate)\r\n                !Suggestion = cItem.Suggestion\r\n                !Resolution = cItem.Resolution\r\n                ' Set default resolution, if provided\r\n                If cItem.Resolution = ercNone Then\r\n                    If cItem.Suggestion <> ercNone Then\r\n                        !Resolution = cItem.Suggestion\r\n                    ElseIf cItem.ActionType <> ercNone Then\r\n                        !Resolution = cItem.ActionType\r\n                    End If\r\n                End If\r\n            .Update\r\n        Next varKey\r\n        .Close\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UpdateResolutionsFromTable\r\n' Author    : Adam Waller\r\n' Date      : 11/1/2021\r\n' Purpose   : Update the class items with the specified resolution set by the user\r\n'           : in the work table.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub UpdateResolutionsFromTable()\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n\r\n    If m_dItems.Count = 0 Then Exit Sub\r\n\r\n    ' Open table to read records\r\n    Set dbs = CodeDb\r\n    Set rst = dbs.OpenRecordset(\"tblConflicts\")\r\n    With rst\r\n        Do While Not .EOF\r\n            ' Update matching class objects\r\n            If m_dItems.Exists(Nz(!ItemKey)) Then\r\n                m_dItems(Nz(!ItemKey)).Resolution = Nz(!Resolution, 0)\r\n            End If\r\n            .MoveNext\r\n        Loop\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ClearTable\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Clear the records from the conflicts table.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ClearTable()\r\n    Dim dbs As Database\r\n    Set dbs = CodeDb\r\n    dbs.Execute \"delete from tblConflicts\", dbFailOnError\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Return count of conflicts\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Count() As Long\r\n    Count = m_dItems.Count\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Reset\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Reset the class, clearing any existing conflicts\r\n'           : Preserve reference to categories dictionary so we can remove items later.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Initialize(ByRef dCategories As Dictionary, intOperationType As eIndexOperationType)\r\n    Set m_dCategories = dCategories\r\n    Set m_dItems = New Dictionary\r\n    m_intOperationType = intOperationType\r\n    Me.ApproveResolutions = False\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsConnectionODBC.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsConnectionODBC\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : This class provides the parsing of ODBC connection string into key-value\r\n'           : pairs, conforming to the grammar defined by MS-ODBCSTR open specification.\r\n'           : This class can be also used to aid in comparing whether 2 ODBC connection\r\n'           : strings are considered equivalent by Access via the property named\r\n'           : `SanitizedConnectionString`.\r\n'           :\r\n'           : For the complete speccification of MS-ODBCSTR, see:\r\n'           : https://learn.microsoft.com/en-us/openspecs/sql_server_protocols/ms-odbcstr/13b4e848-b36c-4b11-acce-d6bf199d5391\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate Type udtData\r\n    ' 2.2 Generic keys\r\n    Driver As String\r\n    DSN As String\r\n    FileDSN As String\r\n    UID As String\r\n    PWD As String\r\n    SaveFile As String\r\n\r\n    ' Those technically are not generic keys but they are very\r\n    ' common among a variety of ODBC drivers and Access do treat\r\n    ' two connection strings with a different values for those\r\n    ' keys differently.\r\n    Server As String\r\n    Port As String ' Should be either an integer between 0 to 65535 or empty\r\n    Database As String\r\n    Encrypt As String\r\n\r\n    ' The rest of members below are for class' use and is not related to\r\n    ' the ODBC connection string specifications.\r\n    OriginalConnectionString As String\r\n    SanitizedConnectionString As String\r\n\r\n    KeyValues As Dictionary\r\nEnd Type\r\nPrivate this As udtData\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Driver\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the value for the driver key in the connection string if present.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Driver() As String\r\n    Driver = this.Driver\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DSN\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the value for the DSN key in the connection string if present.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get DSN() As String\r\n    DSN = this.DSN\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileDSN\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the value for the FileDSN key in the connection string if present.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get FileDSN() As String\r\n    FileDSN = this.FileDSN\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UID\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the value for the UID key in the connection string if present.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get UID() As String\r\n    UID = this.UID\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PWD\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the value for the PWD key in the connection string if present.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get PWD() As String\r\n    PWD = this.PWD\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveFile\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the value for the SaveFile key in the connection string if present.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get SaveFile() As String\r\n    SaveFile = this.SaveFile\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Server\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the value for the Server key in the connection string if present.\r\n'           : This is not a generic key but Access does uses it for comparison.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Server() As String\r\n    Server = this.Server\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Port\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the value for the Port key in the connection string if present.\r\n'           : This is not a generic key but Access does uses it for comparison.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Port() As String\r\n    Port = this.Port\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Database\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the value for the Database key in the connection string if present.\r\n'           : This is not a generic key but Access does uses it for comparison.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Database() As String\r\n    Database = this.Database\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Encrypt\r\n' Author    : hecon5\r\n' Date      : 10/17/2024\r\n' Purpose   : Returns the value for the Encrypt key in the connection string if present.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Encrypt() As String\r\n    Encrypt = this.Encrypt\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OriginalConnectionString\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the original connection string that was used in the call to\r\n'           : ParseOdbcConnectionString.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Let OriginalConnectionString(ByRef NewValue As String)\r\n' Setting property triggers parsing as an overload of sorts.\r\n    ParseOdbcConnectionString NewValue\r\nEnd Property\r\n\r\nPublic Property Get OriginalConnectionString() As String\r\n    OriginalConnectionString = this.OriginalConnectionString\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SanitizedConnectionString\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns the sanitized connection string that was returned from the call to\r\n'           : ParseOdbcConnectionString. This is useful for comparing with other\r\n'           : connection strings to determine if they are equivalent.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get SanitizedConnectionString() As String\r\n    SanitizedConnectionString = this.SanitizedConnectionString\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetKeyValues\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Returns a dictionary of key/values. If a key is non-generic and has\r\n'           : multiple values, the value are split with vbNullChar character. The\r\n'           : generic key retains only the last value.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetKeyValues() As Dictionary\r\n\r\n    Set GetKeyValues = New Dictionary\r\n    GetKeyValues.CompareMode = TextCompare\r\n\r\n    Dim varKey As Variant\r\n\r\n    For Each varKey In this.KeyValues\r\n        GetKeyValues.Add varKey, this.KeyValues(varKey)\r\n    Next\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ParseOdbcConnectionString\r\n' Author    : bclothier, hecon5\r\n' Date      : 4/1/2023,\r\n' Purpose   : Parses the connection string objects into the elements and builds a\r\n'           : sanitized string for comparison.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ParseOdbcConnectionString(strConnect As String)\r\n\r\n    If StartsWith(strConnect, \"ODBC;\", vbTextCompare) = False Then\r\n        Exit Sub\r\n    End If\r\n\r\n    ResetData\r\n\r\n    ' 2.1.1 Common ABNF Rules\r\n    ' SC           = %x3B         ; Semicolon\r\n    ' LCB          = %x7B         ; Left curly brackets\r\n    ' RCB          = %x7D         ; Right curly brackets\r\n    ' EQ           = %x3D         ; Equal sign\r\n    ' ESCAPEDRCB   = 2RCB         ; Double right curly brackets\r\n    ' SpaceStr     = *(SP)        ; Any number of spaces (including 0 spaces)\r\n\r\n    ' 2.1.2 ODBC Connection String Format\r\n    ' ODBCConnectionString =  *(KeyValuePair SC) KeyValuePair [SC]\r\n    ' KeyValuePair = (Key EQ Value / SpaceStr)\r\n    ' Key = SpaceStr KeyName\r\n    ' KeyName = (nonSP-SC-EQ *nonEQ)\r\n    ' Value = (SpaceStr ValueFormat1 SpaceStr) / (ValueContent2)\r\n    ' ValueFormat1 = LCB ValueContent1 RCB\r\n    ' ValueContent1 = *(nonRCB / ESCAPEDRCB)\r\n    ' ValueContent2 = SpaceStr / SpaceStr (nonSP-LCB-SC) *nonSC\r\n    ' nonRCB = %x01-7C / %x7E-FFFF                                 ; not \"}\"\r\n    ' nonSP-LCB-SC = %x01-1F / %x21-3A / %x3C-7A / %x7C-FFFF       ; not space, \"{\" or \";\"\r\n    ' nonSP-SC-EQ = %x01-1F / %x21-3A / %x3C / %x3E-FFFF           ; not space, \";\" or \"=\"\r\n    ' nonEQ = %x01-3C / %x3E-FFFF                                  ; not \"=\"\r\n    ' nonSC = %x01-003A / %x3C-FFFF                                ; not \";\"\r\n\r\n    ' Use https://regex101.com/ to provide a detailed explanation of the pattern\r\n    ' It should conform to the rules defined by the MS-ODBCSTR and extract the\r\n    ' key as the first match and the value as either 2nd or 3rd match. The 2nd\r\n    ' match represents the ValueFormat1 specification and 3rd match, the\r\n    ' ValueContent2 specification.\r\n    Const RegExpPattern As String = \"\\s?([^ ;=][^=]*?)=(?:\\s?(\\{(?:[^}]|\\}\\})*?\\})\\s?|\\s?([^ ;{][^;]*)|\\s?)(?:;|$)\"\r\n\r\n    Dim objRegExp As VBScript_RegExp_55.RegExp\r\n    Dim objMatches As VBScript_RegExp_55.MatchCollection\r\n    Dim objMatch As VBScript_RegExp_55.Match\r\n    Dim strKey As String\r\n    Dim strValue As String\r\n\r\n    Set objRegExp = New VBScript_RegExp_55.RegExp\r\n    With objRegExp\r\n        .Global = True\r\n        .IgnoreCase = True\r\n        .Multiline = True\r\n        .Pattern = RegExpPattern\r\n        ' Test only the substring without the `ODBC;` prefix which technically is not\r\n        ' a part of the ODBC connection string but rather a protocol specifier used by\r\n        ' Access itself.\r\n        Set objMatches = .Execute(Mid$(strConnect, 6))\r\n    End With\r\n\r\n    this.OriginalConnectionString = strConnect\r\n    Set this.KeyValues = New Dictionary\r\n    this.KeyValues.CompareMode = TextCompare\r\n\r\n    For Each objMatch In objMatches\r\n        With objMatch\r\n            strKey = .SubMatches(0)\r\n            If Len(.SubMatches(1)) Then\r\n                strValue = .SubMatches(1) ' ValueFormat1\r\n            ElseIf Len(.SubMatches(2)) Then\r\n                strValue = .SubMatches(2) ' ValueContent2\r\n            Else\r\n                strValue = vbNullString ' No matches; assume empty string\r\n            End If\r\n\r\n            If this.KeyValues.Exists(strKey) Then\r\n                ' 2.2.3 indicates generic key takes the last value. Driver specific\r\n                ' key however is driver-defined. We'll use vbNullChar as delimiter\r\n                ' to support weird oddball drivers that likes having multiple values.\r\n                '\r\n                ' NOTE: according to 3.10, SQL Server drivers will take the first value\r\n                ' and ignore subsequent values.\r\n                If IsGenericKey(strKey) Then\r\n                    this.KeyValues(strKey) = strValue\r\n                Else\r\n                    this.KeyValues(strKey) = this.KeyValues(strKey) & vbNullChar & strValue\r\n                End If\r\n            Else\r\n                this.KeyValues.Add strKey, strValue\r\n            End If\r\n\r\n            Select Case strKey\r\n                Case \"Driver\"\r\n                    this.Driver = strValue\r\n                Case \"DSN\"\r\n                    this.DSN = strValue\r\n                Case \"FileDSN\"\r\n                    this.FileDSN = strValue\r\n                Case \"SaveFile\"\r\n                    this.SaveFile = strValue\r\n                Case \"UID\"\r\n                    this.UID = strValue\r\n                Case \"PWD\"\r\n                    this.PWD = strValue\r\n\r\n                ' The following keys are non-generic so take the first value\r\n                ' for the purpose of sanitized connection string comparison\r\n                Case \"Server\"\r\n                    If Len(this.Server) = 0 Then\r\n                        this.Server = strValue\r\n                    End If\r\n                Case \"Database\"\r\n                    If Len(this.Database) = 0 Then\r\n                        this.Database = strValue\r\n                    End If\r\n                Case \"Port\"\r\n                    If Len(this.Port) = 0 Then\r\n                        this.Port = strValue\r\n                    End If\r\n                Case \"Encrypt\"\r\n                    If Len(this.Encrypt) = 0 Then this.Encrypt = strValue\r\n\r\n            End Select\r\n        End With\r\n    Next\r\n\r\n    BuildSanitizedConnectionString\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildSanitizedConnectionString\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Build the sanitized connection string based on key parameters that Access will use\r\n'           : to differeniate one connection string from another. This is based on experience\r\n'           : because there is no formal documentation for how Access treats one connection string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub BuildSanitizedConnectionString()\r\n    '\r\n    ' WizHook provides IsMatchToDbcConnectString but that does not seem to suit our needs.\r\n    ' What is known:\r\n    '   Access pays attention to the following keywords: Driver, Server, Database and Port\r\n    '   Access does not consider UID or PWD\r\n    '   Other keys are ignored.\r\n    '   Order of keys appearing in the connection string seems to have a factor.\r\n    '\r\n    ' As a matter of best practice, it's best that we avoid having multiple variants of\r\n    ' essentially same connection string and user probably should consider multiple\r\n    ' occurrences of same connection strings differently formatted as a potential problem\r\n    ' to be fixed to avoid runtime problems such as Access prompting for credentials\r\n    ' in unexpected places. So for this purpose, we will allow multiple variants and leave it\r\n    ' up to the user to make corrections to their project to minimize the variants.\r\n    '\r\n    ' According to 2.3.2 & 3.8, generally the key value pairs in the connection string\r\n    ' takes precedence so if we find `Driver` key, we always use that, followed by `DSN`\r\n    ' then finally `FileDSN`. The specification also mentions FileDSN needing to precede\r\n    ' DSN but we don't handle this.\r\n\r\n    Dim objConcat As clsConcat\r\n    Set objConcat = New clsConcat\r\n\r\n    If Len(this.Driver) Then\r\n        objConcat.Add \";Driver=\", this.Driver\r\n    ElseIf Len(this.DSN) Then\r\n        objConcat.Add \";DSN=\", this.DSN\r\n    ElseIf Len(this.FileDSN) Then\r\n        objConcat.Add \";FileDSN=\", this.FileDSN\r\n    Else\r\n        ' Apparently this is an incomplete ODBC connection string. Access would always prompt to fill in.... Masochistic?\r\n    End If\r\n\r\n    If Len(this.SaveFile) Then\r\n        objConcat.Add \";SaveFile=\", this.SaveFile\r\n    End If\r\n\r\n    If Len(this.Server) Then\r\n        objConcat.Add \";SERVER=\", this.Server\r\n    End If\r\n\r\n    If Len(this.Port) Then\r\n        objConcat.Add \";PORT=\", this.Port\r\n    End If\r\n\r\n    If Len(this.Database) Then\r\n        objConcat.Add \";DATABASE=\", this.Database\r\n    End If\r\n\r\n    If Len(this.UID) Then\r\n        objConcat.Add \";UID=\", this.UID\r\n    End If\r\n\r\n    If Len(this.PWD) Then\r\n        objConcat.Add \";PWD=\", this.PWD\r\n    End If\r\n\r\n    this.SanitizedConnectionString = \"ODBC\" & objConcat.GetStr\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsGenericKey\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Indicates if a given key name is generic or not, as specified in\r\n'           : MS-ODBCSTR 2.2 Generic Keys\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IsGenericKey(strKey As String) As Boolean\r\n\r\n    Select Case strKey\r\n        Case \"Driver\", _\r\n             \"DSN\", _\r\n             \"FileDSN\", _\r\n             \"UID\", _\r\n             \"PWD\", _\r\n             \"SaveFile\"\r\n            IsGenericKey = True\r\n    End Select\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ResetData\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Resets the cached values\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ResetData()\r\n\r\n    Dim blank As udtData\r\n\r\n    ' Not necessary but best to ensure proper de-referencing\r\n    Set this.KeyValues = Nothing\r\n\r\n    LSet this = blank\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbCommandBar.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbCommandBar\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_TemplateCommandBar As CommandBar\r\nPrivate m_CommandBar As CommandBar\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_dItems As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\nPrivate Type udtThis\r\n    BarProperties As Collection\r\n    CtlReadProperties As Collection\r\n    CtlWriteProperties As Collection\r\n    dImages As Dictionary       ' Dictionary of image objects used with this CommandBar\r\nEnd Type\r\nPrivate this As udtThis\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    SaveImages\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveImages\r\n' Author    : Adam Waller\r\n' Date      : 1/12/2024\r\n' Purpose   : Saves the images for the menu bar\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SaveImages()\r\n\r\n    Dim varPic As Variant\r\n\r\n    ' Make sure we actually have images to save\r\n    If this.dImages Is Nothing Then Exit Sub\r\n    If this.dImages.Count = 0 Then Exit Sub\r\n\r\n    ' Verify the first path, to make sure the folder exists\r\n    VerifyPath CStr(this.dImages.Keys(0))\r\n\r\n    Perf.OperationStart \"Save CommandBar Images\"\r\n    For Each varPic In this.dImages.Keys\r\n        ' Save the item image to a file\r\n        stdole.SavePicture this.dImages(varPic).Picture, varPic & \"_Picture.bmp\"\r\n        ' Also save image mask\r\n        stdole.SavePicture this.dImages(varPic).Mask, varPic & \"_Mask.bmp\"\r\n    Next varPic\r\n    Perf.OperationEnd\r\n\r\n    ' After saving the images, we can clear the references\r\n    ' to the image objects\r\n    Set this.dImages = Nothing\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dBar As Dictionary\r\n    Dim strName As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n    Set dBar = ReadJsonFile(strFile)\r\n\r\n    Perf.OperationStart \"Building CommandBar\"\r\n    ' Delete any existing bar with this name.\r\n    strName = GetObjectNameFromFileName(strFile)\r\n    Set m_CommandBar = GetExisting(strName)\r\n    If Not m_CommandBar Is Nothing Then\r\n        If m_CommandBar.BuiltIn Then\r\n            Log.Error eelError, \"You cannot replace the existing built-in command bar: \" & m_CommandBar.Name, ModuleName(Me) & \".Import\"\r\n            Exit Sub\r\n        Else\r\n            ' Delete the command bar\r\n            m_CommandBar.Delete\r\n            Set m_CommandBar = Nothing\r\n        End If\r\n    End If\r\n\r\n    ' Now, create a new command bar, and add controls to it.\r\n    Dim IsMenu As Boolean\r\n    Dim Position As MsoBarPosition\r\n\r\n    Position = -1 'set to invalid value to simulate the \"missing\" value which cannot be directly set in VBA...\r\n    IsMenu = (dBar(\"Items\").Item(\"Type\") = msoBarTypeMenuBar)\r\n    If CatchAny(eelNoError, vbNullString) Then\r\n        IsMenu = False\r\n    End If\r\n    Position = dBar(\"Items\").Item(\"Position\")\r\n    If CatchAny(eelNoError, vbNullString) Then\r\n        Position = -1\r\n    End If\r\n\r\n    If Position = -1 Then\r\n        Set m_CommandBar = CommandBars.Add(strName, , IsMenu, False)\r\n    Else\r\n        Set m_CommandBar = CommandBars.Add(strName, Position, IsMenu, False)\r\n    End If\r\n    BuildControls dBar(\"Items\"), m_CommandBar\r\n    Perf.OperationEnd\r\n\r\n    ' Log any errors\r\n    CatchAny eelError, \"Importing CommandBar \" & strName, ModuleName(Me) & \".Import\"\r\n\r\n    ' Save to index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildControls\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2024\r\n' Purpose   : Recursive function to build the controls on the popup menu\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub BuildControls(dParent As Dictionary, objItem As Object)\r\n\r\n    Dim varProp As Variant\r\n    Dim varCtl As Variant\r\n    Dim dControl As Dictionary\r\n    Dim varValue As Variant\r\n    Dim varCtlProp As Variant\r\n    Dim colItems As Collection\r\n    Dim picItem As IPictureDisp\r\n    Dim strBasePath As String\r\n    Dim strPath As String\r\n    Dim blnCustomBuiltIn As Boolean\r\n\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    If TypeOf objItem Is CommandBar Then\r\n        ' Set command bar properties\r\n        For Each varProp In this.BarProperties\r\n            If varProp <> \"Type\" Then\r\n                If dParent.Exists(varProp) Then\r\n                    CallByName objItem, varProp, VbLet, dParent(varProp)\r\n                    If Catch(438, 440) Then\r\n                        ' No point logging those errors.\r\n                    ElseIf CatchAny(eelNoError, vbNullString) Then\r\n                        Debug.Print \"Could not set \" & varProp & \" on \" & objItem.Name\r\n                    End If\r\n                End If\r\n            End If\r\n        Next\r\n        ' Now add any nested controls\r\n        If dParent.Exists(\"Controls\") Then\r\n            For Each dControl In dParent(\"Controls\")\r\n                If dControl(\"BuiltIn\") Then\r\n                    ' We must first determine if it's an Access custom built-in control which requires special handling\r\n                    Select Case dControl(\"Id\")\r\n                        Case 1835, 1836, 1837, 1838, 1839, 3885, 3886, 3888\r\n                            blnCustomBuiltIn = True\r\n                        Case 3887 ' We don't have diagrams yet. :-(\r\n                            ' Throw error (it's more of a warning but a warning doesn't show up in build screen and can be overlooked)\r\n                            Log.Error eelError, \"Custom commandbar opening database diagrams are not supported. If you want to support diagrams, please submit a PR including a working command bar with the diagram button.\", ModuleName(Me) & \".BuildControls\"\r\n                        Case Else\r\n                            blnCustomBuiltIn = False\r\n                    End Select\r\n\r\n                    If blnCustomBuiltIn Then\r\n                        ' It's an Access \"custom\" built-in control which we cannot use .Add method.\r\n                        ' We must instead copy from our template command bar into the target command bar.\r\n\r\n                        ' Build the custom \"built-in\" control ourselves\r\n                        BuildControls dControl, m_TemplateCommandBar.FindControl(ID:=dControl(\"Id\")).Copy(objItem)\r\n                    Else\r\n                        ' Insert built-in control\r\n\r\n                        ' A built-in control may fail to add. In this case, we need to warn the users\r\n                        Dim objBuiltIn As CommandBarControl\r\n                        Set objBuiltIn = objItem.Controls.Add(dControl(\"Type\"), dControl(\"Id\"))\r\n                        If Not CatchAny(eelError, \"Unable to add a built-in control using Id \" & dControl(\"Id\"), blnIncludeErrorWithDescription:=True) Then\r\n                            objBuiltIn.Visible = dControl(\"Visible\")\r\n                        End If\r\n                    End If\r\n                Else\r\n                    ' Build the control ourselves\r\n                    BuildControls dControl, objItem.Controls.Add(dControl(\"Type\"))\r\n                End If\r\n            Next dControl\r\n        End If\r\n    ElseIf TypeOf objItem Is CommandBarControl Then\r\n        ' Set control properties\r\n        For Each varProp In this.CtlWriteProperties\r\n            If dParent.Exists(varProp) Then\r\n                CallByName objItem, varProp, VbLet, dParent(varProp)\r\n                If Catch(438, 440) Then\r\n                    ' No point logging those errors\r\n                ElseIf CatchAny(eelNoError, vbNullString) Then\r\n                    Debug.Print \"Could not set \" & varProp & \" on \" & objItem.Caption\r\n                End If\r\n            End If\r\n        Next varProp\r\n        If TypeOf objItem Is CommandBarPopup And dParent.Exists(\"Controls\") Then\r\n            ' Add nested controls\r\n            For Each dControl In dParent(\"Controls\")\r\n                BuildControls dControl, objItem.Controls.Add(dControl(\"Type\"))\r\n            Next dControl\r\n        ElseIf TypeOf objItem Is CommandBarComboBox And dParent.Exists(\"List\") Then\r\n            Dim varItem As Variant\r\n            Dim cbcItem As CommandBarComboBox\r\n            Set objItem = cbcItem\r\n            For Each varItem In dParent(\"List\")\r\n                cbcItem.AddItem varItem\r\n            Next\r\n        Else\r\n            ' Check for picture\r\n            If dParent.Exists(\"ImagePath\") Then\r\n                strBasePath = GetPathFromRelative(dParent(\"ImagePath\"), IDbComponent_BaseFolder)\r\n\r\n                ' Build out full path to main image (Picture)\r\n                strPath = strBasePath & \"_Picture.bmp\"\r\n                If FSO.FileExists(strPath) Then\r\n                    Set picItem = stdole.LoadPicture(strPath)\r\n                    objItem.Picture = picItem\r\n                End If\r\n                ' Build path to image mask (transparency)\r\n                strPath = strBasePath & \"_Mask.bmp\"\r\n                If FSO.FileExists(strPath) Then\r\n                    Set picItem = stdole.LoadPicture(strPath)\r\n                    objItem.Mask = picItem\r\n                End If\r\n            End If\r\n        End If\r\n    Else\r\n        ' Unsupported object type\r\n        Stop\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetExisting\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2024\r\n' Purpose   : Returns any existing command bar by that name\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetExisting(strName As String) As CommandBar\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    Set GetExisting = CommandBars(strName)\r\n    If Err Then Err.Clear\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"CommandBar\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a dictionary object of project properties.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary(Optional blnUseCache As Boolean = True) As Dictionary\r\n    Dim dParent As Dictionary\r\n    Set dParent = New Dictionary\r\n    Set this.dImages = New Dictionary\r\n    Set GetDictionary = BuildElementDictionary(dParent, m_CommandBar)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildElementDictionary\r\n' Author    : Adam Waller\r\n' Date      : 1/12/2024\r\n' Purpose   : A recursive function to build out the dictionary elements representing\r\n'           : a CommandBar popup menu.\r\n'           : objItem may represent a CommandBar, or a CommandBarControl.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function BuildElementDictionary(dParent As Dictionary, objItem As Object) As Dictionary\r\n\r\n    Dim varProp As Variant\r\n    Dim varCtl As Variant\r\n    Dim varValue As Variant\r\n    Dim varCtlProp As Variant\r\n    Dim colItems As Collection\r\n    Dim picItem As IPictureDisp\r\n    Dim strPath As String\r\n\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    If TypeOf objItem Is CommandBar Then\r\n        ' Add command bar properties\r\n        For Each varProp In this.BarProperties\r\n            varValue = CallByName(objItem, varProp, VbGet)\r\n            If Not CatchAny(eelNoError, vbNullString) Then\r\n                dParent.Add varProp, varValue\r\n            End If\r\n        Next varProp\r\n        ' Now add any nested controls\r\n        Set colItems = New Collection\r\n        dParent.Add \"Controls\", BuildControlCollection(objItem.Controls)\r\n    ElseIf TypeOf objItem Is CommandBarControl Then\r\n        ' Add control properties\r\n        For Each varProp In this.CtlReadProperties\r\n            varValue = Empty ' Clear the value from previous run\r\n            varValue = CallByName(objItem, varProp, VbGet)\r\n            ' No point in logging those 2 errors.\r\n            If Catch(438, 440) Then\r\n            ' Check for any other errors which should be logged just in case.\r\n            ElseIf Not CatchAny(eelNoError, vbNullString) Then\r\n                dParent.Add varProp, varValue\r\n            End If\r\n        Next varProp\r\n        ' Save the ID for built-in items\r\n        If objItem.BuiltIn Then\r\n            dParent.Add \"Id\", objItem.ID\r\n        End If\r\n        If TypeOf objItem Is CommandBarComboBox Then\r\n            If objItem.Type = msoControlDropdown Or objItem.Type = msoControlComboBox Then\r\n            ' Build List\r\n            Dim cbcItem As CommandBarComboBox\r\n            Dim colList As Collection\r\n            Dim i As Long\r\n            Set cbcItem = objItem\r\n\r\n            Set colList = New Collection\r\n            For i = 1 To cbcItem.ListCount\r\n                colList.Add cbcItem.List(i), CStr(i)\r\n            Next\r\n            dParent.Add \"List\", colList\r\n            End If\r\n        End If\r\n        If TypeOf objItem Is CommandBarPopup Then\r\n            ' Loop through nested controls\r\n            dParent.Add \"Controls\", BuildControlCollection(objItem.Controls)\r\n        Else\r\n            ' Check for picture\r\n            Set picItem = objItem.Picture\r\n            ' Not all command bar controls have picture\r\n            If Catch(438) Then\r\n                ' Don't log this error and skip\r\n            ElseIf CatchAny(eelNoError, vbNullString) Then\r\n                ' Log any other error and skip\r\n            Else\r\n                ' Proceed with picture processing\r\n                If Not picItem Is Nothing Then\r\n                    strPath = BuildPath2(IDbComponent_BaseFolder, GetImagePath(objItem))\r\n                    ' Check path for possible duplicates (menu items with the same name)\r\n                    If this.dImages.Exists(strPath) Then\r\n                        ' Add a number to make the path unique.\r\n                        strPath = strPath & \"_\" & this.dImages.Count\r\n                    End If\r\n                    ' Save reference to image objects to use when\r\n                    ' exporting images to files.\r\n                    this.dImages.Add strPath, objItem\r\n                    ' Save path to images in element dictionary\r\n                    dParent.Add \"ImagePath\", GetRelativePath(strPath, IDbComponent_BaseFolder)\r\n                End If\r\n            End If\r\n        End If\r\n    Else\r\n        ' Unsupported object type\r\n        Stop\r\n    End If\r\n\r\n    ' Return dictionary\r\n    Set BuildElementDictionary = dParent\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildControlCollection\r\n' Author    : Adam Waller\r\n' Date      : 1/12/2024\r\n' Purpose   : Return a collection of the command bar controls.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function BuildControlCollection(ctls As CommandBarControls) As Collection\r\n    Dim ctl As CommandBarControl\r\n    Dim dItem As Dictionary\r\n    Set BuildControlCollection = New Collection\r\n    For Each ctl In ctls\r\n        Set dItem = New Dictionary\r\n        BuildControlCollection.Add BuildElementDictionary(dItem, ctl)\r\n    Next ctl\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetImagePath\r\n' Author    : Adam Waller\r\n' Date      : 1/12/2024\r\n' Purpose   : Build a base path for the image files. (Image and mask)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetImagePath(ctl As CommandBarControl) As String\r\n\r\n    Dim objParent As Object\r\n    Dim ctlTest As Object\r\n    Dim strName As String\r\n    Dim strSegment As String\r\n\r\n    On Error GoTo 0\r\n    Set ctlTest = ctl\r\n    ' Walk up the parent objects to build a full path to the control.\r\n    ' (We are using a name path since the control IDs and indexes may change)\r\n    Do\r\n        If TypeOf ctlTest Is CommandBar Then\r\n            ' Don't include command bar name, since we are using a subfolder\r\n            ' to store images for each commmand bar.\r\n            strSegment = vbNullString\r\n        ElseIf TypeOf ctlTest Is CommandBarPopup Then\r\n            ' We already have the name for this item\r\n            strSegment = vbNullString\r\n        Else\r\n            ' Control item. (Use caption or ID)\r\n            strSegment = Nz2(MultiReplace(ctlTest.Caption, _\r\n                \"&\", vbNullString, _\r\n                \"...\", vbNullString) _\r\n                , ctlTest.ID)\r\n        End If\r\n        If Len(strSegment) Then\r\n            If Len(strName) Then\r\n                strName = strSegment & \"_\" & strName\r\n            Else\r\n                strName = strSegment\r\n            End If\r\n        End If\r\n        If ctlTest.Parent Is Application Then\r\n            ' This is the top level\r\n            Exit Do\r\n        Else\r\n            ' Move up to parent object\r\n            Set ctlTest = ctlTest.Parent\r\n        End If\r\n    Loop\r\n\r\n    ' Convert name to a filesafe name\r\n    GetImagePath = BuildPath2(GetSafeFileName(m_CommandBar.Name) & \"_Images\", GetSafeFileName(strName))\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Import if file exists\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\n    MoveFolderIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cCmdBar As IDbComponent\r\n    Dim bar As CommandBar\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        ' Loop through command bars, looking for any custom ones.\r\n\r\n        ' TODO: Figure out a way to determine whether a command bar belongs to the current\r\n        ' database. The command bars are associated with the application, not with the database\r\n        ' so the application could have additional command bars loaded via add-in or other processes\r\n        ' that are not actually a part of the database itself. The data is there in the internal\r\n        ' table but not human readable.\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each bar In Application.CommandBars\r\n            If bar.BuiltIn Then\r\n                ' Skip\r\n            ElseIf bar.Name = strTemplateCommandBarName Then\r\n                ' Skip our template command bar\r\n            Else\r\n                Set cCmdBar = New clsDbCommandBar\r\n                Set cCmdBar.DbObject = bar\r\n                blnAdd = True\r\n                If blnModifiedOnly Then blnAdd = cCmdBar.IsModified\r\n                If blnAdd Then m_Items(blnModifiedOnly).Add cCmdBar.SourceFile, cCmdBar\r\n            End If\r\n        Next bar\r\n\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.json\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"CommandBars\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"menus\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n'    If m_Project Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_CommandBar.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_CommandBar.Name) & \".json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    ' Probably 0 in most databases. We will have to iterate through the CommandBars\r\n    ' to check the .BuiltIn flag to get an actual count.\r\n    IDbComponent_QuickCount = 0\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbCommandBar\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_CommandBar\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_CommandBar = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Helps us know whether we have already counted the objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    'm_Count = -1\r\n\r\n    ' We assume the add-in file already has the template command bar included. If it doesn't, then\r\n    ' we will have problem importing Access' custom built-in buttons.\r\n    Set m_TemplateCommandBar = Application.CommandBars(strTemplateCommandBarName)\r\n\r\n    ' Set up property name lists\r\n\r\n    ' CommandBar Properties\r\n    Set this.BarProperties = New Collection\r\n    With this.BarProperties\r\n        .Add \"AdaptiveMenu\"\r\n        .Add \"Context\"\r\n        .Add \"Enabled\"\r\n        .Add \"Height\"\r\n        .Add \"Left\"\r\n        .Add \"NameLocal\"\r\n        .Add \"Position\"\r\n        .Add \"Protection\"\r\n        .Add \"RowIndex\"\r\n        .Add \"Top\"\r\n        .Add \"Type\"\r\n        .Add \"Visible\"\r\n        .Add \"Width\"\r\n    End With\r\n\r\n    ' CommmandBarControl Properties\r\n    ' This should include all properties from different types of CommandBarControl\r\n    ' even if they are not common among all types.\r\n\r\n    ' Build a list for read-write properties to be used in building controls.\r\n    Set this.CtlWriteProperties = New Collection\r\n    With this.CtlWriteProperties\r\n        .Add \"BeginGroup\"\r\n        .Add \"BuiltInFace\"\r\n        .Add \"Caption\"\r\n        .Add \"DescriptionText\"\r\n        .Add \"DropDownLines\"\r\n        .Add \"DropDownWidth\"\r\n        .Add \"Enabled\"\r\n        .Add \"FaceId\"\r\n        .Add \"Height\"\r\n        .Add \"HelpContextId\"\r\n        .Add \"HelpFile\"\r\n        .Add \"HyperlinkType\"\r\n        .Add \"ListHeaderCount\"\r\n        .Add \"ListIndex\"\r\n        .Add \"OLEMenuGroup\"\r\n        .Add \"OLEUsage\"\r\n        .Add \"OnAction\"\r\n        .Add \"Parameter\"\r\n        .Add \"Priority\"\r\n        .Add \"ShortcutText\"\r\n        .Add \"State\"\r\n        .Add \"Style\"\r\n        .Add \"Tag\"\r\n        .Add \"Text\"\r\n        .Add \"TooltipText\"\r\n        .Add \"Visible\"\r\n        .Add \"Width\"\r\n    End With\r\n\r\n    ' Build dictionary to be used for writing out to text files to include additional data about the\r\n    ' controls so any changes can be detected via source code control\r\n    Set this.CtlReadProperties = New Collection\r\n    With this.CtlReadProperties\r\n        .Add \"BeginGroup\"\r\n        .Add \"BuiltIn\"\r\n        .Add \"BuiltInFace\"\r\n        .Add \"Caption\"\r\n        .Add \"DescriptionText\"\r\n        .Add \"DropDownLines\"\r\n        .Add \"DropDownWidth\"\r\n        .Add \"Enabled\"\r\n        .Add \"FaceId\"\r\n        .Add \"Height\"\r\n        .Add \"HelpContextId\"\r\n        .Add \"HelpFile\"\r\n        .Add \"HyperlinkType\"\r\n        .Add \"Index\"\r\n        .Add \"IsPriorityDropped\"\r\n        .Add \"Left\"\r\n        .Add \"ListCount\"\r\n        .Add \"ListHeaderCount\"\r\n        .Add \"ListIndex\"\r\n        .Add \"OLEMenuGroup\"\r\n        .Add \"OLEUsage\"\r\n        .Add \"OnAction\"\r\n        .Add \"Parameter\"\r\n        .Add \"Priority\"\r\n        .Add \"ShortcutText\"\r\n        .Add \"State\"\r\n        .Add \"Style\"\r\n        .Add \"Tag\"\r\n        .Add \"Text\"\r\n        .Add \"TooltipText\"\r\n        .Add \"Top\"\r\n        .Add \"Type\"\r\n        .Add \"Visible\"\r\n        .Add \"Width\"\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Terminate\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2024\r\n' Purpose   : Clear any object references\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Terminate()\r\n    ' Clear the images dictionary, just in case it still has references\r\n    ' to the commandbar image objects.\r\n    Set this.dImages = Nothing\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbConnection.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbConnection\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'           :\r\n'           : This class actually doesn't import or export anything but provides\r\n'           : useful metadata about the connections that the database project has.\r\n'           : During \"export\", all possible connections are harvested while \"import\"\r\n'           : will prime Access' internal cache, which helps reduce the numbers of\r\n'           : ODBC login dialogs that may pop open during the imports of associated\r\n'           : database objects.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Items(True To False) As Dictionary   ' Instances of this class\r\nPrivate m_dItems As Dictionary  ' Connection items for JSON output in GetDictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dConnections As Dictionary\r\n    Dim varConnect As Variant\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n    Set dConnections = ReadJsonFile(strFile).Item(\"Items\")\r\n\r\n    ' Prime Access' internal cache. If the connection string is incomplete, it will\r\n    ' pop open a dialog from the driver for the user to then fill in.\r\n    For Each varConnect In dConnections\r\n        ' We only need to process the sanitized connection strings, rather than possible\r\n        ' connection strings which help reduce number of logins user may need to\r\n        ' complete.\r\n        Dim x As Variant\r\n        For Each x In dConnections(varConnect)\r\n            CacheConnection CStr(x)\r\n        Next\r\n    Next\r\n\r\n    CatchAny eelError, \"Importing Connection\", ModuleName(Me) & \".Import\"\r\n\r\n    ' Save to index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, _\r\n        \"Database connections used within the project used to prime Access' internal cache during import.\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a dictionary object of project connections.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetDictionary(Optional blnUseCache As Boolean = True) As Dictionary\r\n\r\n    ' Check cache first\r\n    If blnUseCache And Not m_dItems Is Nothing Then\r\n        Set GetDictionary = m_dItems\r\n        Exit Function\r\n    End If\r\n\r\n    ' Read project connections\r\n    Set GetDictionary = New Dictionary\r\n\r\n    Dim tdf As DAO.TableDef\r\n    Dim qdf As DAO.QueryDef\r\n    Dim cConnection As clsConnectionODBC\r\n\r\n    Set cConnection = New clsConnectionODBC\r\n    With CurrentDb\r\n        For Each tdf In .TableDefs\r\n            If StartsWith(tdf.Connect, \"ODBC;\", vbTextCompare) And (Len(tdf.Connect) > 5) Then\r\n                If StartsWith(tdf.Name, \"~\") Then\r\n                    ' Ignore temporary table objects\r\n                Else\r\n                    cConnection.ParseOdbcConnectionString tdf.Connect\r\n                    AddOdbcConnectionString GetDictionary, cConnection, tdf.Name\r\n                End If\r\n            End If\r\n        Next\r\n        For Each qdf In .QueryDefs\r\n            ' We have to handle two possible scenarios:\r\n            ' 1. Sometimes a certain querydef will throw an unrelated error when trying to load\r\n            '    querydef by accessing one of its properties for the first time. In this case,\r\n            '    we simply retry and we are usually able to read the properties of the querydef.\r\n            ' 2. Sometimes the querydef is simply unusable and will always throw errors when\r\n            '    trying to read the property. We should just skip over them and hope for the best.\r\n            Dim strQdfConnect As String\r\n            On Error Resume Next\r\n            strQdfConnect = qdf.Connect\r\n            If Err.Number Then\r\n                Err.Clear\r\n                strQdfConnect = qdf.Connect\r\n                If Err.Number Then\r\n                    ' Even though we cannot read the Connect property, Name property should be always available.\r\n                    CatchAny eelWarning, \"Unable to read the Connect property for query '\" & qdf.Name & \"'. Error \" & Err.Number & \": \" & Err.Description, ModuleName(Me) & \".GetDictionary\"\r\n                    strQdfConnect = vbNullString\r\n                End If\r\n            End If\r\n            On Error GoTo 0\r\n            If StartsWith(strQdfConnect, \"ODBC;\", vbTextCompare) And (Len(strQdfConnect) > 5) Then\r\n                If StartsWith(qdf.Name, \"~\") Then\r\n                    ' Ignore temporary queries\r\n                Else\r\n                    cConnection.ParseOdbcConnectionString strQdfConnect\r\n                    AddOdbcConnectionString GetDictionary, cConnection, qdf.Name\r\n                End If\r\n            End If\r\n        Next\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddOdbcConnectionString\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : The values of the GetDictionary are themselves a dictionary of the original\r\n'           : ODBC connection strings. This procedure helps handle creating a new\r\n'           : dictionary if there's not an entry for the sanitized connection string\r\n'           : already and then adds the original connection string under the key of\r\n'           : sanitized connection string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function AddOdbcConnectionString(dSanitizedConnections As Dictionary, cConnection As clsConnectionODBC, strObjectName As String)\r\n    Dim dConnections As Dictionary\r\n\r\n    If dSanitizedConnections.Exists(cConnection.SanitizedConnectionString) Then\r\n        Set dConnections = dSanitizedConnections(cConnection.SanitizedConnectionString)\r\n    Else\r\n        Set dConnections = New Dictionary\r\n        dConnections.CompareMode = TextCompare\r\n        dSanitizedConnections.Add cConnection.SanitizedConnectionString, dConnections\r\n    End If\r\n    If dConnections.Exists(cConnection.OriginalConnectionString) = False Then\r\n        dConnections.Add cConnection.OriginalConnectionString, strObjectName\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Import if file exists\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim varKey As Variant\r\n\r\n    ' Build a dictionary of component class objects if not already cached.\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        If Not blnModifiedOnly Or IDbComponent_IsModified Then\r\n            ' Return all the items since we are not concerned about which ones changed.\r\n            For Each varKey In GetDictionary.Keys\r\n                ' Since we are only processing one of these instances, we can\r\n                ' just reference the existing instance and avoid rescanning the\r\n                ' tables and queries.\r\n                m_Items(blnModifiedOnly).Add varKey, Me\r\n            Next varKey\r\n        End If\r\n    End If\r\n\r\n    ' Return cached dictionary\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    Set IDbComponent_GetFileList = New Dictionary\r\n    If FSO.FileExists(IDbComponent_SourceFile) Then IDbComponent_GetFileList.Add IDbComponent_SourceFile, vbNullString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"DB Connections\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = \"Database Connection\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & \"db-connection.json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbConnection\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    'Not applicable\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    'Not applicable\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = True\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Helps us know whether we have already counted the objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    'm_Count = -1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbDocument.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbDocument\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Items(True To False) As Dictionary\r\nPublic m_dItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n    Dim dItems As Dictionary\r\n    Dim dCont As Dictionary\r\n    Dim dDoc As Dictionary\r\n    Dim dbs As Database\r\n    Dim varCont As Variant\r\n    Dim varDoc As Variant\r\n    Dim varProp As Variant\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Trap error thrown if the object does not exist\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If Not dFile Is Nothing Then\r\n        ClearDatabaseSummaryProperties\r\n        Set dbs = CurrentDb\r\n        Set dItems = dFile(\"Items\")\r\n        For Each varCont In dItems.Keys\r\n            Set dCont = dItems(varCont)\r\n            For Each varDoc In dCont.Keys\r\n                Set dDoc = dCont(varDoc)\r\n                For Each varProp In dDoc.Keys\r\n                    ' Attempt to add or update the property value on the object.\r\n                    SetDAOProperty dbs.Containers(varCont).Documents(varDoc), dbText, CStr(varProp), dDoc(varProp)\r\n                    CatchAny eelError, \"Error setting document property \" & varCont & \".\" & varDoc & \".\" & varProp, ModuleName(Me)\r\n                Next varProp\r\n            Next varDoc\r\n        Next varCont\r\n    End If\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary(False))\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Remove any document properties that don't exist in the incoming file,\r\n    ' then import the file.\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If dFile Is Nothing Then Set dFile = New Dictionary\r\n    RemoveMissing dFile(\"Items\"), GetDictionary\r\n\r\n    ' Import if file exists\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ClearDatabaseSummaryProperties\r\n' Author    : Adam Waller\r\n' Date      : 5/13/2020\r\n' Purpose   : When creating a new database, some properties may be filled out by\r\n'           : default. Since the imported file only sets the ones that have values,\r\n'           : it won't clear existing values that don't exist in the import file.\r\n'           : I.e. `Company` may be already filled out as \"Microsoft\". This value would\r\n'           : not be changed if the imported file did not specify this field.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ClearDatabaseSummaryProperties()\r\n\r\n    Dim doc As DAO.Document\r\n    Dim prp As DAO.Property\r\n    Dim dbs As DAO.Database\r\n    Dim intProp As Integer\r\n\r\n    Set dbs = CurrentDb\r\n    Set doc = dbs.Containers(\"Databases\").Documents(\"SummaryInfo\")\r\n    ' Loop backwards through the collection since we may be removing items.\r\n    For intProp = doc.Properties.Count - 1 To 0 Step -1\r\n        Set prp = doc.Properties(intProp)\r\n        Select Case prp.Type\r\n            Case dbText, dbMemo\r\n                ' Text properties\r\n                Select Case prp.Name\r\n                    Case \"Name\", \"Owner\", \"UserName\", \"Container\" ' Leave these properties\r\n                    Case Else\r\n                        ' Remove other properties that might contain sensitive info.\r\n                        ' They will be recreated from source files if they were in use.\r\n                        doc.Properties.Delete prp.Name\r\n                End Select\r\n        End Select\r\n    Next intProp\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Database Documents Properties (DAO)\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Build a dictionary object of the document properties, just as you would\r\n'           : use for the export content.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary(Optional blnUseCache As Boolean = True) As Dictionary\r\n\r\n    Dim prp As DAO.Property\r\n    Dim dItems As Dictionary    ' All Items\r\n    Dim dCont As Dictionary     ' Container\r\n    Dim dDoc As Dictionary      ' Document\r\n    Dim cont As DAO.Container\r\n    Dim dbs As Database\r\n    Dim doc As DAO.Document\r\n    Dim blnSave As Boolean\r\n\r\n    ' Check cache parameter\r\n    If blnUseCache And Not m_dItems Is Nothing Then\r\n        ' Return cached dictionary\r\n        Set GetDictionary = m_dItems\r\n        Exit Function\r\n    End If\r\n\r\n    ' Create dictionary object to hold all the items\r\n    Set dItems = New Dictionary\r\n    Set dbs = CurrentDb\r\n\r\n    ' Loop through all the containers, documents, and properties.\r\n    ' Note, we don't want to collect everything here. We are taking\r\n    ' a whitelist approach to specify the ones we want to save and\r\n    ' write back to the database when importing.\r\n    For Each cont In dbs.Containers\r\n        Set dCont = New Dictionary\r\n        For Each doc In cont.Documents\r\n            Set dDoc = New Dictionary\r\n            For Each prp In doc.Properties\r\n                blnSave = False\r\n                If cont.Name = \"Databases\" And doc.Name = \"SummaryInfo\" Then\r\n                    ' Keep most of this information (Blacklist approach)\r\n                    Select Case prp.Name\r\n                        Case \"AllPermissions\", \"Container\", \"DateCreated\", \"LastUpdated\", _\r\n                            \"Name\", \"Owner\", \"GUID\", \"Permissions\", \"UserName\" ' Ignore these\r\n                        Case Else\r\n                            blnSave = True\r\n                    End Select\r\n                Else\r\n                    ' For other documents, use the whitelist approach, primarily\r\n                    ' gathering navigation pane item descriptions and hidden status.\r\n                    Select Case prp.Name\r\n                        Case \"Description\"\r\n                            blnSave = True\r\n                    End Select\r\n                End If\r\n                ' Don't save properties on temporary items\r\n                If Left(doc.Name, 1) = \"~\" Then blnSave = False\r\n                ' If save flag set, save the property\r\n                If blnSave Then dDoc.Add prp.Name, prp.Value\r\n            Next prp\r\n            If dDoc.Count > 0 Then dCont.Add doc.Name, SortDictionaryByKeys(dDoc)\r\n        Next doc\r\n        If dCont.Count > 0 Then dItems.Add cont.Name, SortDictionaryByKeys(dCont)\r\n    Next cont\r\n\r\n    ' Return assembled dictionary\r\n    Set GetDictionary = dItems\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveMissing\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Removes current document properties missing from the master dictionary.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RemoveMissing(dMaster As Dictionary, dTarget As Dictionary)\r\n\r\n    Dim dCont As Dictionary\r\n    Dim dDoc As Dictionary\r\n    Dim dbs As Database\r\n    Dim varCont As Variant\r\n    Dim varDoc As Variant\r\n    Dim varProp As Variant\r\n\r\n    ' Go through target dictionary, removing properties that don't exist\r\n    ' in the master dictionary. (Note that this is only checking the\r\n    ' properties we are actually interested in tracking.)\r\n    Set dbs = CurrentDb\r\n    For Each varCont In dTarget.Keys\r\n        Set dCont = dTarget(varCont)\r\n        For Each varDoc In dCont.Keys\r\n            Set dDoc = dCont(varDoc)\r\n            For Each varProp In dDoc.Keys\r\n                ' Check to see if this key exists in the master\r\n                If Not KeyExists(dMaster, varCont, varDoc, varProp) Then\r\n                    ' Remove the property from the current database\r\n                    dbs.Containers(varCont).Documents(varDoc).Properties.Delete CStr(varProp)\r\n                End If\r\n            Next varProp\r\n        Next varDoc\r\n    Next varCont\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cDoc As IDbComponent\r\n    Dim dCont As Dictionary\r\n    Dim dDoc As Dictionary\r\n    Dim dbs As Database\r\n    Dim varCont As Variant\r\n    Dim varDoc As Variant\r\n    Dim varProp As Variant\r\n    Dim strKey As String\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n\r\n        ' Check index hash of properties to determine if they have been changed.\r\n        If Not blnModifiedOnly Or IDbComponent_IsModified Then\r\n            Set m_dItems = GetDictionary\r\n            Set dbs = CurrentDb\r\n\r\n            ' Loop through all the containers, documents, and properties.\r\n            For Each varCont In m_dItems.Keys\r\n                Set dCont = m_dItems(varCont)\r\n                For Each varDoc In dCont.Keys\r\n                    Set dDoc = dCont(varDoc)\r\n                    For Each varProp In dDoc.Keys\r\n                        ' Add as class instance\r\n                        Set cDoc = New clsDbDocument\r\n                        Set cDoc.DbObject = dbs.Containers(varCont).Documents(varDoc).Properties(varProp)\r\n                        strKey = cDoc.SourceFile & \":\" & varCont & \":\" & varDoc & \":\" & varProp\r\n                        m_Items(blnModifiedOnly).Add strKey, cDoc\r\n                    Next varProp\r\n                Next varDoc\r\n                If m_dItems.Exists(varCont) Then\r\n                End If\r\n            Next varCont\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    Set IDbComponent_GetFileList = New Dictionary\r\n    If FSO.FileExists(IDbComponent_SourceFile) Then IDbComponent_GetFileList.Add IDbComponent_SourceFile, vbNullString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    ' Modified date unknown.\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Doc Properties\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = \"Database Documents\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & \"documents.json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    ' Skip the more complex iteration for fast single-file output\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbDocument\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = Nothing\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = True\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbForm.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbForm\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Form As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strHash As String\r\n    strHash = SaveComponentAsText(acForm, m_Form.Name, Nz2(strAlternatePath, IDbComponent_SourceFile), Me)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), _\r\n        strHash, GetCodeModuleHash(IDbComponent_ComponentType, m_Form.Name)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim strName As String\r\n    Dim blnStage As Boolean\r\n    Dim blnImportCheck As Boolean\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.bas\" Then Exit Sub\r\n\r\n    strName = GetObjectNameFromFileName(strFile)\r\n\r\n    ' We can't import a form with the same name as a running form, even though\r\n    ' it is technically in a different database.\r\n    If strName = \"frmVCSMain\" Then\r\n        If IsLoaded(acForm, \"frmVCSMain\") Then\r\n            StageMainForm\r\n            blnStage = True\r\n        End If\r\n    End If\r\n\r\n    ' Load the form from the source file\r\n    blnImportCheck = LoadComponentFromText(acForm, strName, strFile)\r\n    If Not blnImportCheck Or Log.ErrorLevel = eelCritical Then Exit Sub\r\n    Set m_Form = CurrentProject.AllForms(strName)\r\n    VCSIndex.Update Me, eatImport, GetCodeModuleHash(IDbComponent_ComponentType, strName)\r\n\r\n    ' Restore main form if staged\r\n    If blnStage Then RestoreMainForm\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    Dim strName As String\r\n\r\n    ' Get object name\r\n    strName = GetObjectNameFromFileName(strFile)\r\n\r\n    ' Don't attempt to merge forms with same names as add-in forms.\r\n    If ObjectExists(acForm, strName, True) Then\r\n        Log.Error eelError, \"Merging not supported for add-in forms. Use full build instead.\", _\r\n            ModuleName(Me) & \".Merge\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Import the form if the source file exists.\r\n    If FSO.FileExists(strFile) Then\r\n        ' Importing the form will replace the existing object\r\n        IDbComponent_Import strFile\r\n    Else\r\n        DeleteObjectIfExists acForm, strName\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\n    MoveFileIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile) & \".json\", strToFolder\r\n    MoveFileIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile) & \".cls\", strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim frm As AccessObject\r\n    Dim cForm As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each frm In CurrentProject.AllForms\r\n            Set cForm = New clsDbForm\r\n            Set cForm.DbObject = frm\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cForm.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cForm.SourceFile, cForm\r\n        Next frm\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.bas\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n\r\n    ' Item is considered modified unless proven otherwise.\r\n    IDbComponent_IsModified = True\r\n\r\n    ' Check the modified date first.\r\n    ' (This may not reflect some code changes)\r\n    If m_Form.DateModified <= VCSIndex.Item(Me).ExportDate Then\r\n\r\n        ' Date is okay, check hash\r\n        IDbComponent_IsModified = VCSIndex.Item(Me).OtherHash <> GetCodeModuleHash(IDbComponent_ComponentType, m_Form.Name)\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_Form Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = m_Form.DateModified\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Forms\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"forms\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"bas\"\r\n    IDbComponent_FileExtensions.Add \"cls\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Form Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Form.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Form Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Form.Name) & \".bas\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentProject.AllForms.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbForm\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Form\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Form = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbHiddenAttribute.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbHiddenAttribute\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_dItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n    Dim dItems As Dictionary\r\n    Dim dbs As Database\r\n    Dim varCont As Variant\r\n    Dim varDoc As Variant\r\n    Dim objType As AcObjectType\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If Not dFile Is Nothing Then\r\n        Set dbs = CurrentDb\r\n        Set dItems = dFile(\"Items\")\r\n        For Each varCont In dItems.Keys\r\n            objType = GetObjectTypeFromContainer(dbs.Containers(varCont))\r\n            If objType <> acDefault Then\r\n                For Each varDoc In dItems(varCont)\r\n                    ' Set object to hidden\r\n                    Application.SetHiddenAttribute objType, varDoc, True\r\n                    CatchAny eelError, \"Error setting hidden attribute for \" & varDoc, ModuleName(Me)\r\n                Next varDoc\r\n            End If\r\n        Next varCont\r\n    End If\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary(False))\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Remove any document properties that don't exist in the incoming file,\r\n    ' then import the file.\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If dFile Is Nothing Then Set dFile = New Dictionary\r\n    RemoveMissing dFile(\"Items\"), GetDictionary\r\n\r\n    ' Import if file exists\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cDoc As IDbComponent\r\n    Dim colCont As Collection\r\n    Dim dbs As Database\r\n    Dim varCont As Variant\r\n    Dim varDoc As Variant\r\n    Dim strKey As String\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        Set m_dItems = GetDictionary\r\n\r\n        ' Return no objects if we match the hash from the cache\r\n        If (Not blnModifiedOnly) Or IDbComponent_IsModified Then\r\n\r\n            ' Loop through all the containers, documents, and check hidden property\r\n            Set dbs = CurrentDb\r\n            For Each varCont In m_dItems.Keys\r\n                Set colCont = m_dItems(varCont)\r\n                For Each varDoc In colCont\r\n                    Set cDoc = New clsDbHiddenAttribute\r\n                    Set cDoc.DbObject = dbs.Containers(varCont).Documents(varDoc)\r\n                    strKey = cDoc.SourceFile & \":\" & varCont & \":\" & varDoc\r\n                    m_Items(blnModifiedOnly).Add strKey, cDoc\r\n                Next varDoc\r\n            Next varCont\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Database objects hidden attribute\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Return a dictionary object with the items\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetDictionary(Optional blnUseCache As Boolean = True) As Dictionary\r\n\r\n    Dim dItems As Dictionary\r\n    Dim dCont As Dictionary\r\n    Dim cont As DAO.Container\r\n    Dim doc As DAO.Document\r\n    Dim dbs As Database\r\n    Dim contType As AcObjectType\r\n    Dim colItems As Collection\r\n\r\n    ' Check cache parameter\r\n    If blnUseCache And Not m_dItems Is Nothing Then\r\n        ' Return cached dictionary\r\n        Set GetDictionary = m_dItems\r\n        Exit Function\r\n    End If\r\n\r\n    ' Create dictionary object to hold all the items\r\n    Set dItems = New Dictionary\r\n    Set dbs = CurrentDb\r\n\r\n    ' Errors should not typically occur\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Loop through all the containers, documents, and check hidden property\r\n    For Each cont In dbs.Containers\r\n        Set dCont = New Dictionary\r\n        Set dCont = New Dictionary\r\n        Set colItems = New Collection\r\n        contType = GetObjectTypeFromContainer(cont)\r\n        For Each doc In cont.Documents\r\n            If contType <> acDefault _\r\n                And Not (contType = acTable _\r\n                And (doc.Name Like \"MSys*\" Or doc.Name Like \"~*\")) Then\r\n                    ' Check Hidden Attribute property (only exposed here)\r\n                    If Application.GetHiddenAttribute(contType, doc.Name) Then\r\n                        ' Add to collection of hidden item item names of this type.\r\n                        colItems.Add doc.Name\r\n                    End If\r\n                    CatchAny eelError, T(\"Error accessing Hidden Attribute for {0}.\", , , , doc.Name), _\r\n                        ModuleName(Me) & \".GetDictionary\"\r\n            End If\r\n        Next doc\r\n        If colItems.Count > 0 Then dItems.Add cont.Name, SortCollectionByValue(colItems)\r\n    Next cont\r\n\r\n    ' Return assembled dictionary\r\n    Set GetDictionary = dItems\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveMissing\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Unsets the hidden flag for documents that should not have it.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RemoveMissing(dMaster As Dictionary, dTarget As Dictionary)\r\n\r\n    Dim dCont As Dictionary\r\n    Dim dbs As Database\r\n    Dim contType As AcObjectType\r\n    Dim varCont As Variant\r\n    Dim varDoc As Variant\r\n    Dim strDoc As String\r\n\r\n    ' Go through target dictionary, removing the flag that doesn't exist\r\n    ' in the master dictionary.\r\n    Set dbs = CurrentDb\r\n    For Each varCont In dTarget.Keys\r\n        Set dCont = dTarget(varCont)\r\n        contType = GetObjectTypeFromContainer(dbs.Containers(varCont))\r\n        For Each varDoc In dCont.Keys\r\n            strDoc = CStr(varDoc)\r\n            If contType <> acDefault _\r\n                And Not (contType = acTable _\r\n                And (strDoc Like \"MSys*\" Or strDoc Like \"~*\")) Then\r\n                    ' Unset the Hidden Attribute property (only exposed here)\r\n                    Application.SetHiddenAttribute contType, strDoc, False\r\n            End If\r\n        Next varDoc\r\n    Next varCont\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetObjectTypeFromContainer\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Get an object type from a DAO container\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetObjectTypeFromContainer(ByRef cont As DAO.Container) As AcObjectType\r\n    Select Case cont.Name\r\n        Case \"Tables\"\r\n            GetObjectTypeFromContainer = acTable\r\n        Case \"Forms\"\r\n            GetObjectTypeFromContainer = acForm\r\n        Case \"Scripts\"\r\n            GetObjectTypeFromContainer = acMacro\r\n        Case \"Queries\"\r\n            GetObjectTypeFromContainer = acQuery\r\n        Case \"Reports\"\r\n            GetObjectTypeFromContainer = acReport\r\n        Case \"Modules\"\r\n            GetObjectTypeFromContainer = acModule\r\n        Case Else\r\n            ' Unknown\r\n            GetObjectTypeFromContainer = acDefault\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    Set IDbComponent_GetFileList = New Dictionary\r\n    If FSO.FileExists(IDbComponent_SourceFile) Then IDbComponent_GetFileList.Add IDbComponent_SourceFile, vbNullString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    ' Modified date unknown.\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Hidden Attributes\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = \"Database hidden attributes\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & \"hidden-attributes.json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    ' Single file output\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbHiddenAttribute\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/14/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = Nothing\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = True\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbImexSpec.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbImexSpec\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPublic Name As String\r\nPublic ID As Long\r\n\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n    Dim dSpec As Dictionary\r\n    Dim dCol As Dictionary\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim fld As DAO.Field\r\n    Dim lngID As Long\r\n    Dim varKey As Variant\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Read data from JSON file\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If Not dFile Is Nothing Then\r\n\r\n        ' Create IMEX tables if needed\r\n        VerifyImexTables\r\n        Set dSpec = dFile(\"Items\")\r\n        Set dbs = CurrentDb\r\n\r\n        ' Add header record\r\n        Set rst = dbs.OpenRecordset(\"MSysIMEXSpecs\")\r\n        With rst\r\n            .AddNew\r\n                For Each fld In .Fields\r\n                    If dSpec.Exists(fld.Name) Then\r\n                        fld.Value = dSpec(fld.Name)\r\n                    End If\r\n                Next fld\r\n            .Update\r\n            ' Save ID from header so we can use it for columns\r\n            .Bookmark = .LastModified\r\n            lngID = !SpecID\r\n            ' Update class with current ID and name\r\n            Me.ID = lngID\r\n            Me.Name = Nz(!SpecName, \"Spec \" & lngID)\r\n        End With\r\n\r\n        ' Add columns records\r\n        Set rst = dbs.OpenRecordset(\"MSysIMEXColumns\")\r\n        With rst\r\n            For Each varKey In dSpec(\"Columns\").Keys\r\n                Set dCol = dSpec(\"Columns\")(varKey)\r\n                .AddNew\r\n                    !SpecID = lngID\r\n                    !FieldName = CStr(varKey)\r\n                    For Each fld In .Fields\r\n                        If dCol.Exists(fld.Name) Then\r\n                            fld.Value = dCol(fld.Name)\r\n                        End If\r\n                    Next fld\r\n                .Update\r\n            Next varKey\r\n        End With\r\n    End If\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetStringHash(GetSource, True)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    ' Remove any existing spec by this name\r\n    DeleteSpecByName GetObjectNameFromFileName(strFile)\r\n\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        ' Remove from index if the source file doesn't exist.\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cSpec As clsDbImexSpec\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim strName As String\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n\r\n        ' Set up new collection\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n\r\n        ' This table may not (yet) exist.\r\n        If TableExists(\"MSysIMEXSpecs\") Then\r\n            ' Look up specs from table\r\n            Set dbs = CurrentDb\r\n            Set rst = dbs.OpenRecordset(\"MSysIMEXSpecs\", dbOpenSnapshot, dbReadOnly)\r\n            With rst\r\n                Do While Not .EOF\r\n                    ' Keep in mind that the spec name may be blank\r\n                    strName = Nz(!SpecName)\r\n                    If strName = vbNullString Then strName = \"Spec \" & Nz(!SpecID, 0)\r\n                    ' Add spec name\r\n                    Set cSpec = New clsDbImexSpec\r\n                    cSpec.Name = strName\r\n                    cSpec.ID = Nz(!SpecID, 0)\r\n                    blnAdd = True\r\n                    If blnModifiedOnly Then blnAdd = cSpec.IDbComponent_IsModified\r\n                    If blnAdd Then m_Items(blnModifiedOnly).Add cSpec.Parent.SourceFile, cSpec\r\n                    .MoveNext\r\n                Loop\r\n                .Close\r\n            End With\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Import/Export Specification from MSysIMEXSpecs\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2023\r\n' Purpose   : Return a dictionary object with the specification definition.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary() As Dictionary\r\n\r\n    Dim dSpec As Dictionary\r\n    Dim dCol As Dictionary\r\n    Dim dCols As Dictionary\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim strSql As String\r\n    Dim fld As DAO.Field\r\n\r\n    Set dSpec = New Dictionary\r\n    Set dCols = New Dictionary\r\n    Set dbs = CurrentDb\r\n\r\n    ' Make sure we have an object specified\r\n    If Me.ID = 0 Then Exit Function\r\n\r\n    ' Build header info first\r\n    strSql = \"SELECT * FROM MSysIMEXSpecs WHERE SpecID=\" & Me.ID\r\n    Set rst = dbs.OpenRecordset(strSql, dbOpenSnapshot, dbReadOnly)\r\n    With rst\r\n        For Each fld In .Fields\r\n            If fld.Name <> \"SpecID\" Then\r\n                ' Add all columns except the primary key.\r\n                dSpec.Add CStr(fld.Name), fld.Value\r\n            End If\r\n        Next fld\r\n        .Close\r\n    End With\r\n\r\n    ' Build list of columns\r\n    strSql = \"SELECT * FROM MSysIMEXColumns WHERE SpecID=\" & Me.ID\r\n    Set rst = dbs.OpenRecordset(strSql, dbOpenSnapshot, dbReadOnly)\r\n    With rst\r\n        Do While Not .EOF\r\n            Set dCol = New Dictionary\r\n            For Each fld In .Fields\r\n                Select Case fld.Name\r\n                    Case \"SpecID\", \"FieldName\"\r\n                    Case Else\r\n                        dCol.Add CStr(fld.Name), fld.Value\r\n                End Select\r\n            Next fld\r\n            ' Add column to columns dictionary\r\n            dCols.Add Nz(!FieldName), dCol\r\n            .MoveNext\r\n        Loop\r\n        .Close\r\n    End With\r\n\r\n    ' Add columns to spec\r\n    dSpec.Add \"Columns\", dCols\r\n\r\n    ' Return dictionary\r\n    Set GetDictionary = dSpec\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DeleteSpecByName\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2023\r\n' Purpose   : Delete a saved import specification by name.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub DeleteSpecByName(strName As String)\r\n\r\n    Dim lngID As Long\r\n\r\n    ' Skip lookup if we don't even have the corresponding system table.\r\n    If Not TableExists(\"MSysIMEXSpecs\") Then Exit Sub\r\n\r\n    ' Attempt to look up spec by name\r\n    lngID = Nz(DLookup(\"SpecID\", \"MSysIMEXSpecs\", \"SpecName=\"\"\" & DblQ(strName) & \"\"\"\"))\r\n    If lngID > 0 Then\r\n        ' Remove related records\r\n        CurrentDb.Execute \"delete from MSysIMEXColumns where SpecID=\" & lngID, dbFailOnError\r\n        CurrentDb.Execute \"delete from MSysIMEXSpecs where SpecID=\" & lngID, dbFailOnError\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyImexTables\r\n' Author    : Adam Waller\r\n' Date      : 5/7/2020\r\n' Purpose   : If we have not done any import/export in this database, we may need\r\n'           : to create the system tables. For this we will use an undocumented SysCmd\r\n'           : call to create the tables. I have found this documented a few places\r\n'           : online, and used in the VTools project as well.\r\n'           : https://www.everythingaccess.com/tutorials.asp?ID=Undocumented-SysCmd-Functions\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub VerifyImexTables()\r\n    ' Check to see if the tables exists in the current database\r\n    If (Not TableExists(\"MSysIMEXSpecs\")) Or (Not TableExists(\"MSysIMEXColumns\")) Then\r\n        ' Use an undocumented SysCmd function to create the tables.\r\n        SysCmd 555\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.json\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    ' No modified date on a spec\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"IMEX Specs\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"imexspecs\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = Me.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(Me.Name) & \".json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    ' No simple way to get count without a database query\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbImexSpec\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = Nothing\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbMacro.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbMacro\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Macro As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strHash As String\r\n    strHash = SaveComponentAsText(acMacro, m_Macro.Name, Nz2(strAlternatePath, IDbComponent_SourceFile))\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), strHash\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim blnImportCheck As Boolean\r\n    Dim strName As String\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.bas\" Then Exit Sub\r\n\r\n    strName = GetObjectNameFromFileName(strFile)\r\n    blnImportCheck = LoadComponentFromText(acMacro, strName, strFile)\r\n    If Not blnImportCheck Or Log.ErrorLevel = eelCritical Then Exit Sub\r\n    Set m_Macro = CurrentProject.AllMacros(strName)\r\n    VCSIndex.Update Me, eatImport, GetFileHash(strFile)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    DeleteObjectIfExists acMacro, GetObjectNameFromFileName(strFile)\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim oMac As AccessObject\r\n    Dim cMac As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each oMac In CurrentProject.AllMacros\r\n            Set cMac = New clsDbMacro\r\n            Set cMac.DbObject = oMac\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cMac.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cMac.SourceFile, cMac\r\n        Next oMac\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.bas\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = (m_Macro.DateModified > VCSIndex.Item(Me).ExportDate)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_Macro Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = m_Macro.DateModified\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Macros\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"macros\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"bas\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Macro Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Macro.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Macro Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Macro.Name) & \".bas\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentProject.AllMacros.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbMacro\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Macro\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Macro = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbModule.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbModule\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Module As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\nPrivate Type udtVbaFileContent\r\n    strContent As String\r\n    blnIsClass As Boolean\r\n    blnPublicCreatable As Boolean\r\nEnd Type\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n\r\n    Dim strExt As String\r\n    Dim strAlternateFile As String\r\n\r\n    ' Export as sanitized UTF-8 file\r\n    ExportCodeModule m_Module.Name, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n\r\n    ' Remove any file with the same name but alternate extension\r\n    strExt = IIf(GetExtension = \".bas\", \".cls\", \".bas\")\r\n    strAlternateFile = IDbComponent_BaseFolder & GetSafeFileName(m_Module.Name) & strExt\r\n    If FSO.FileExists(strAlternateFile) Then DeleteFile strAlternateFile\r\n\r\n    ' Update the index with the current VBA hash. (Note, this will not show\r\n    ' changes to the hidden VBE properties that might have been added.)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), _\r\n        GetFileHash(IDbComponent_SourceFile), GetCodeModuleHash(edbModule, m_Module.Name)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Const cintPublicCreateable As Integer = 5   ' Instancing\r\n\r\n    Dim FunctionName As String\r\n\r\n    Dim strName As String\r\n    Dim strTempFile As String\r\n    Dim udtFile As udtVbaFileContent\r\n\r\n    FunctionName = ModuleName(Me) & \".Import\"\r\n\r\n    LogUnhandledErrors FunctionName\r\n    On Error Resume Next\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not (strFile Like \"*.bas\" Or strFile Like \"*.cls\") Then Exit Sub\r\n\r\n    ' Parse source file\r\n    strName = GetObjectNameFromFileName(strFile)\r\n    udtFile = ParseSourceFile(strFile, strName)\r\n\r\n    ' Write to a new file using system encoding (converting from UTF-8)\r\n    strTempFile = GetTempFile\r\n    WriteFile udtFile.strContent, strTempFile, GetSystemEncoding\r\n\r\n    ' Import the source code\r\n    LoadVbeModuleFromFile strTempFile, strName\r\n\r\n    ' Make sure the correct project is active before saving.\r\n    Set VBE.ActiveVBProject = CurrentVBProject\r\n    DoCmd.Save acModule, strName\r\n\r\n    ' Update instancing for public creatable classes\r\n    If udtFile.blnPublicCreatable Then\r\n        CurrentVBProject.VBComponents(strName).Properties(\"Instancing\") = cintPublicCreateable\r\n        DoCmd.Save acModule, strName\r\n    End If\r\n\r\n    ' Set reference to object\r\n    CatchAny eelError, T(\"Error with module: {0}\", var0:=strName), FunctionName\r\n\r\n    Set m_Module = CurrentProject.AllModules(strName)\r\n    If Catch(2467) Then Log.Error eelCritical, _\r\n        T(\"Imported module not found after import: {0}\" & vbNewLine & _\r\n            \"Ensure 'Attribute VB_Name = {0}' is found at the start of the file. \" & _\r\n            \"Filenames (before prefix) must match the 'Attribute VB_Name' value.\" _\r\n            , var0:=strName), FunctionName\r\n\r\n    ' Save hash, update the index, and remove the temp file\r\n    VCSIndex.Update Me, eatImport, GetCodeModuleHash(IDbComponent_ComponentType, strName)\r\n    DeleteFile strTempFile\r\n\r\n    CatchAny eelError, T(\"Error importing module: {0}\", var0:=strName), FunctionName\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ParseSourceFile\r\n' Author    : Adam Waller\r\n' Date      : 7/13/2021\r\n' Purpose   : Parse the source file to build VBE content\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ParseSourceFile(strFile As String, strName As String) As udtVbaFileContent\r\n\r\n    Dim strLines() As String\r\n    Dim lngLine As Long\r\n    Dim cData As clsConcat\r\n    Dim blnIsClass As Boolean\r\n    Dim blnHasHeader As Boolean\r\n    Dim blnCreatable As Boolean\r\n    Dim blnPublic As Boolean\r\n\r\n    Perf.OperationStart \"Parse VBA Module\"\r\n\r\n    ' Read file contents into array of lines\r\n    strLines = Split(ReadFile(strFile), vbCrLf)\r\n\r\n    ' Loop through first several lines to determine type and header\r\n    For lngLine = 0 To UBound(strLines)\r\n        If strLines(lngLine) = \"VERSION 1.0 CLASS\" Then\r\n            ' Class with VBE header\r\n            blnIsClass = True\r\n            blnHasHeader = True\r\n        ElseIf StartsWith(strLines(lngLine), \"Attribute VB_Name = \"\"\") Then\r\n            ' Module with VBE header\r\n            blnHasHeader = True\r\n        ElseIf StartsWith(strLines(lngLine), \"Attribute VB_GlobalNameSpace = \") Then\r\n            ' Class with no header\r\n            blnIsClass = True\r\n        ElseIf strLines(lngLine) = \"Attribute VB_Creatable = True\" Then\r\n            blnCreatable = True\r\n        ElseIf strLines(lngLine) = \"Attribute VB_Exposed = True\" Then\r\n            blnPublic = True\r\n        End If\r\n        ' Exit after 9 lines\r\n        If lngLine > 8 Then Exit For\r\n    Next lngLine\r\n\r\n    ' Use concatenation class to build file contents\r\n    Set cData = New clsConcat\r\n    With cData\r\n        .AppendOnAdd = vbCrLf\r\n\r\n        ' Build header, if needed\r\n        If Not blnHasHeader Then\r\n            If blnIsClass Then\r\n                .Add \"VERSION 1.0 CLASS\"\r\n                .Add \"BEGIN\"\r\n                .Add \"  MultiUse = -1  'True\"\r\n                .Add \"END\"\r\n            End If\r\n            .Add \"Attribute VB_Name = \"\"\", strName, \"\"\"\"\r\n        End If\r\n\r\n        ' Add in file contents\r\n        For lngLine = 0 To UBound(strLines)\r\n            .Add strLines(lngLine)\r\n        Next lngLine\r\n\r\n        ' Remove trailing vbCrLf\r\n        .Remove 2\r\n    End With\r\n\r\n    ' Return values\r\n    With ParseSourceFile\r\n        .blnIsClass = blnIsClass\r\n        .strContent = cData.GetStr\r\n        .blnPublicCreatable = (blnCreatable And blnPublic)\r\n    End With\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadVbeModuleFromFile\r\n' Author    : Adam Waller\r\n' Date      : 7/12/2021\r\n' Purpose   : Load the VBA standard/class module from a file through VBE. (This allows\r\n'           : us to preserve hidden attributes not recognized in then LoadFromText\r\n'           : import of code modules and classes.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub LoadVbeModuleFromFile(strFile As String, strName As String)\r\n\r\n    Dim proj As VBProject\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    Set proj = CurrentVBProject\r\n    Perf.OperationStart \"Import VBE Module\"\r\n    With proj.VBComponents\r\n\r\n        ' Remove any existing component (In most cases the module will exist)\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        .Remove .Item(strName)\r\n        If DebugMode(False) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n        ' Load from the file\r\n        .Import strFile\r\n    End With\r\n    Perf.OperationEnd\r\n\r\n    CatchAny eelError, \"Error importing VBA code for \" & strName, ModuleName(Me) & \".LoadVbeModuleFromFile\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    DeleteObjectIfExists acModule, GetObjectNameFromFileName(strFile)\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim oMod As AccessObject\r\n    Dim cModule As IDbComponent\r\n    Dim proj As VBProject\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        Set proj = CurrentVBProject\r\n        For Each oMod In CurrentProject.AllModules\r\n            Set cModule = New clsDbModule\r\n            Set cModule.DbObject = oMod\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cModule.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cModule.SourceFile, cModule\r\n        Next oMod\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then\r\n        Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.bas\")\r\n        MergeDictionary m_FileList, GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.cls\")\r\n    End If\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export/import of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n\r\n    ' The modified date for the object changes frequently with compile/save operations,\r\n    ' so use the hash instead to detect changes.\r\n\r\n    ' NOTE: This will not detect changes to hidden VBE attributes\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).OtherHash <> GetCodeModuleHash(IDbComponent_ComponentType, m_Module.Name)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_Module Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = CurrentProject.AllModules(m_Module.Name).DateModified\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Modules\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"modules\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"bas\"\r\n    IDbComponent_FileExtensions.Add \"cls\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Module Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Module.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Module Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Module.Name) & GetExtension\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetExtension\r\n' Author    : Adam Waller\r\n' Date      : 6/4/2021\r\n' Purpose   : Return the extension (\".cls\" or \".bas\") based on the component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetExtension() As String\r\n    If CurrentVBProject.VBComponents(m_Module.Name).Type = vbext_ct_StdModule Then\r\n        GetExtension = \".bas\"\r\n    Else\r\n        GetExtension = \".cls\"\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentProject.AllModules.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbModule\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Module\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Module = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbNavPaneGroup.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbNavPaneGroup\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_dItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'           : Here we are writing information directly to the system tables since\r\n'           : Microsoft Access does not provide a way to do this programatically.\r\n'           : Helpful links: https://stackoverflow.com/questions/26523619\r\n'           : and https://stackoverflow.com/questions/27366038\r\n'           : https://support.microsoft.com/en-us/office/customize-the-navigation-pane-ccfb0ee7-d72f-4923-b4fb-ed6c15484244\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n    Dim intGroup As Integer\r\n    Dim intCategory As Integer\r\n    Dim dGroup As Dictionary\r\n    Dim dCategory As Dictionary\r\n    Dim lngGroupID As Long\r\n    Dim lngCategoryID As Long\r\n    Dim intObject As Integer\r\n    Dim dObject As Dictionary\r\n    Dim lngObjectID As Long\r\n    Dim lngLinkID As Long\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If Not dFile Is Nothing Then\r\n\r\n        ' Upgrade from any previous formats\r\n        Set m_dItems = dFile\r\n\r\n        ' Remove any existing custom groups (Some may be automatically created with a new database)\r\n        ClearExistingNavGroups\r\n\r\n        ' Import custom navigation categories/groups\r\n        If m_dItems(\"Items\").Exists(\"Categories\") Then\r\n\r\n            ' Loop through custom categories\r\n            For intCategory = 1 To m_dItems(\"Items\")(\"Categories\").Count\r\n                Set dCategory = m_dItems(\"Items\")(\"Categories\")(intCategory)\r\n                ' Check for existing category with this name\r\n                lngCategoryID = Nz(DLookup(\"Id\", \"MSysNavPaneGroupCategories\", \"Type=4 and Name=\"\"\" & dCategory(\"Name\") & \"\"\"\"), 0)\r\n                If lngCategoryID = 0 Then\r\n                    ' Add additional field values and create record\r\n                    dCategory.Add \"Type\", 4\r\n                    lngCategoryID = LoadRecord(\"MSysNavPaneGroupCategories\", dCategory)\r\n                End If\r\n                ' Make sure we got a category record\r\n                If lngCategoryID = 0 Then\r\n                    Log.Error eelError, _\r\n                        \"Could not create custom category record for \" & dCategory(\"Name\") & \" in MSysNavPaneGroupCategories.\", _\r\n                        ModuleName(Me) & \".Import\"\r\n                    Exit Sub\r\n                End If\r\n\r\n                ' Loop through groups in category\r\n                For intGroup = 1 To m_dItems(\"Items\")(\"Categories\")(intCategory)(\"Groups\").Count\r\n                    Set dGroup = m_dItems(\"Items\")(\"Categories\")(intCategory)(\"Groups\")(intGroup)\r\n                    ' Add additional field values for new record\r\n                    dGroup.Add \"GroupCategoryID\", lngCategoryID\r\n                    dGroup.Add \"Object Type Group\", -1\r\n                    dGroup.Add \"ObjectID\", 0\r\n                    ' Check for existing group with this name. (Such as Unassigned Objects)\r\n                    lngGroupID = Nz(DLookup(\"Id\", \"MSysNavPaneGroups\", \"GroupCategoryID=\" & lngCategoryID & \" AND Name=\"\"\" & dGroup(\"Name\") & \"\"\"\"), 0)\r\n                    If lngGroupID = 0 Then lngGroupID = LoadRecord(\"MSysNavPaneGroups\", dGroup)\r\n                    For intObject = 1 To dGroup(\"Objects\").Count\r\n                        Set dObject = dGroup(\"Objects\")(intObject)\r\n                        lngObjectID = Nz(DLookup(\"Id\", \"MSysObjects\", \"Name=\"\"\" & dObject(\"Name\") & \"\"\" AND Type=\" & dObject(\"Type\")), 0)\r\n                        If lngObjectID <> 0 Then\r\n                            dObject.Add \"ObjectID\", lngObjectID\r\n                            dObject.Add \"GroupID\", lngGroupID\r\n                            ' Change name to the name defined in this group. (Could be different from the object name)\r\n                            dObject(\"Name\") = dObject(\"NameInGroup\")\r\n                            ' Should not already be a link, but just in case...\r\n                            lngLinkID = Nz(DLookup(\"Id\", \"MSysNavPaneGroupToObjects\", \"ObjectID=\" & lngObjectID & \" AND GroupID = \" & lngGroupID), 0)\r\n                            If lngLinkID = 0 Then lngLinkID = LoadRecord(\"MSysNavPaneGroupToObjects\", dObject)\r\n                        End If\r\n                    Next intObject\r\n                Next intGroup\r\n            Next intCategory\r\n        End If\r\n    End If\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary(False))\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ClearExistingNavGroups\r\n' Author    : Adam Waller\r\n' Date      : 2/22/2021\r\n' Purpose   : Clears existing custom groups/categories (Used before importing)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ClearExistingNavGroups()\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim strSql As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Get SQL for query of NavPaneGroup objects\r\n    Set dbs = CodeDb\r\n    strSql = dbs.QueryDefs(\"qryNavPaneGroups\").SQL\r\n\r\n    ' Look up list of custom categories\r\n    Set dbs = CurrentDb\r\n    Set rst = dbs.OpenRecordset(strSql, dbOpenSnapshot)\r\n\r\n    With rst\r\n        Do While Not .EOF\r\n            ' Remove records from three tables\r\n            If Nz(!LinkID, 0) <> 0 Then dbs.Execute \"delete from MSysNavPaneGroupToObjects where id=\" & Nz(!LinkID, 0), dbFailOnError\r\n            If Nz(!GroupID, 0) <> 0 Then dbs.Execute \"delete from MSysNavPaneGroups where id=\" & Nz(!GroupID, 0), dbFailOnError\r\n            If Nz(!CategoryID, 0) <> 0 Then dbs.Execute \"delete from MSysNavPaneGroupCategories where id=\" & Nz(!CategoryID, 0), dbFailOnError\r\n            .MoveNext\r\n        Loop\r\n        .Close\r\n    End With\r\n\r\n    CatchAny eelError, \"Error clearing existing navigation pane groups.\", ModuleName(Me) & \".ClearExisting\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'           : NOTE: This REPLACES any existing pane groups, it does not attempt to\r\n'           : merge changes between the source file and the existing groups.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    ' Clear any existing customizations\r\n    ClearExistingNavGroups\r\n\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import (strFile)\r\n    Else\r\n        ' Remove from index when source file is removed\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Navigation Pane Custom Groups\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Return a dictionary of the navigation pane groups\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary(Optional blnUseCache As Boolean) As Dictionary\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim strSql As String\r\n    Dim strCategory As String\r\n    Dim strGroup As String\r\n    Dim colCategories As Collection\r\n    Dim colGroups As Collection\r\n    Dim dCategory As Dictionary\r\n    Dim dGroup As Dictionary\r\n    Dim colObjects As Collection\r\n    Dim dObject As Dictionary\r\n    Dim dItems As Dictionary\r\n\r\n    ' Check cache parameter\r\n    If blnUseCache And Not m_dItems Is Nothing Then\r\n        ' Return cached dictionary\r\n        Set GetDictionary = m_dItems\r\n        Exit Function\r\n    End If\r\n\r\n    ' Load query SQL from saved query in add-in database\r\n    Set dbs = CodeDb\r\n    strSql = dbs.QueryDefs(\"qryNavPaneGroups\").SQL\r\n\r\n    ' Open query in the current db\r\n    Set dbs = CurrentDb\r\n    Set rst = dbs.OpenRecordset(strSql)\r\n    Set dItems = New Dictionary\r\n    Set colCategories = New Collection\r\n\r\n    ' Loop through records\r\n    With rst\r\n        Do While Not .EOF\r\n\r\n            ' Check for change in group name.\r\n            If Nz(!GroupName) <> strGroup Then\r\n                ' Finish recording any previous group\r\n                If strGroup <> vbNullString Then\r\n                    dGroup.Add \"Objects\", colObjects\r\n                    colGroups.Add dGroup\r\n                End If\r\n                ' Set up new group\r\n                Set colObjects = New Collection\r\n                Set dGroup = New Dictionary\r\n                strGroup = Nz(!GroupName)\r\n                dGroup.Add \"Name\", strGroup\r\n                dGroup.Add \"Flags\", Nz(!GroupFlags, 0)\r\n                dGroup.Add \"Position\", Nz(!GroupPosition, 0)\r\n            End If\r\n\r\n            ' Check for change in category name\r\n            If Nz(!CategoryName) <> strCategory Then\r\n                ' Finish recording any previous category\r\n                If strCategory <> vbNullString Then\r\n                    dCategory.Add \"Groups\", colGroups\r\n                    colCategories.Add dCategory\r\n                End If\r\n                ' Set up new category\r\n                'Set colCategories = New Collection\r\n                Set colGroups = New Collection\r\n                strCategory = Nz(!CategoryName)\r\n                Set dCategory = New Dictionary\r\n                dCategory.Add \"Name\", strCategory\r\n                dCategory.Add \"Flags\", Nz(!CategoryFlags, 0)\r\n                dCategory.Add \"Position\", Nz(!CategoryPosition, 0)\r\n            End If\r\n\r\n            ' Add any item listed in this group\r\n            If Nz(!ObjectName) = vbNullString Then\r\n                ' Saved group with no items.\r\n            ElseIf Left(Nz(!ObjectName), 1) = \"~\" Then\r\n                ' Skip temporary placeholder after deletion.\r\n            Else\r\n                Set dObject = New Dictionary\r\n                dObject.Add \"Name\", Nz(!ObjectName)\r\n                dObject.Add \"Type\", Nz(!ObjectType, 0)\r\n                dObject.Add \"Flags\", Nz(!ObjectFlags, 0)\r\n                dObject.Add \"Icon\", Nz(!ObjectIcon, 0)\r\n                dObject.Add \"Position\", Nz(!ObjectPosition, 0)\r\n                dObject.Add \"NameInGroup\", Nz(!NameInGroup)\r\n                colObjects.Add dObject\r\n            End If\r\n\r\n            ' Move to next record.\r\n            .MoveNext\r\n        Loop\r\n        .Close\r\n        ' Close out last group and category, and add items\r\n        ' to output dictionary\r\n        If strGroup <> vbNullString Then\r\n            dGroup.Add \"Objects\", colObjects\r\n            colGroups.Add dGroup\r\n        End If\r\n        If strCategory <> vbNullString Then\r\n            dCategory.Add \"Groups\", colGroups\r\n            colCategories.Add dCategory\r\n            dItems.Add \"Categories\", colCategories\r\n        End If\r\n    End With\r\n\r\n    ' Return dictionary\r\n    Set GetDictionary = dItems\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadRecord\r\n' Author    : Adam Waller\r\n' Date      : 5/12/2020\r\n' Purpose   : Loads a new record into the specified table and returns the ID\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function LoadRecord(strTable As String, dCols As Dictionary) As Long\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim fld As DAO.Field\r\n\r\n    Set dbs = CurrentDb\r\n    Set rst = dbs.OpenRecordset(strTable)\r\n    With rst\r\n        .AddNew\r\n            For Each fld In .Fields\r\n                ' Load field value in matching column\r\n                If dCols.Exists(fld.Name) Then fld.Value = dCols(fld.Name)\r\n            Next fld\r\n        .Update\r\n        .Bookmark = .LastModified\r\n        ' Return ID from new record.\r\n        LoadRecord = Nz(!ID, 0)\r\n        .Close\r\n    End With\r\n\r\n    Set rst = Nothing\r\n    Set dbs = Nothing\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cGroup As IDbComponent\r\n    Dim varCategory As Variant\r\n    Dim dCategory As Dictionary\r\n    Dim varGroup As Variant\r\n    Dim strKey As String\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        Set m_dItems = GetDictionary\r\n\r\n        ' Only need to iterate through the groups if we need to get a count.\r\n        If Not blnModifiedOnly Or IDbComponent_IsModified Then\r\n\r\n            ' Loop through dictionary to build count of custom groups\r\n            ' (Not counting individual objects within groups)\r\n            If m_dItems.Exists(\"Categories\") Then\r\n                For Each varCategory In m_dItems(\"Categories\")\r\n                    Set dCategory = varCategory\r\n                    If dCategory.Exists(\"Groups\") Then\r\n                        For Each varGroup In dCategory(\"Groups\")\r\n                            Set cGroup = New clsDbNavPaneGroup\r\n                            strKey = varCategory(\"Name\") & \":\" & varGroup(\"Name\")\r\n                            If Not m_Items(blnModifiedOnly).Exists(strKey) Then\r\n                                m_Items(blnModifiedOnly).Add strKey, cGroup\r\n                            End If\r\n                        Next varGroup\r\n                    End If\r\n                Next varCategory\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    Set IDbComponent_GetFileList = New Dictionary\r\n    If FSO.FileExists(IDbComponent_SourceFile) Then IDbComponent_GetFileList.Add IDbComponent_SourceFile, vbNullString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Nav Pane Groups\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = \"Groups\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & \"nav-pane-groups.json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    ' No simple way to pull this without queries\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbNavPaneGroup\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = Nothing\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = True\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbProjProperty.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbProjProperty\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Property As AccessObjectProperty\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_dItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dExisting As Dictionary\r\n    Dim prp As AccessObjectProperty\r\n    Dim dImport As Dictionary\r\n    Dim dItems As Dictionary\r\n    Dim projCurrent As CurrentProject\r\n    Dim varKey As Variant\r\n    Dim varValue As Variant\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Pull a list of the existing properties so we know whether\r\n    ' to add or update the existing property.\r\n    Set projCurrent = CurrentProject\r\n    Set dExisting = New Dictionary\r\n    For Each prp In projCurrent.Properties\r\n        Select Case prp.Name\r\n            Case \"Connection\"\r\n            Case Else\r\n                dExisting.Add prp.Name, prp.Value\r\n        End Select\r\n    Next prp\r\n\r\n    ' Read properties from source file\r\n    Set dImport = ReadJsonFile(strFile)\r\n    If Not dImport Is Nothing Then\r\n        Set dItems = dImport(\"Items\")\r\n        For Each varKey In dItems.Keys\r\n            Select Case varKey\r\n                Case \"Name\", \"Connection\"\r\n                    ' Skip these properties\r\n                Case Else\r\n                    varValue = dItems(varKey)\r\n                    If IsRelativePath(CStr(varValue)) Then varValue = GetPathFromRelative(CStr(varValue))\r\n                    If dExisting.Exists(varKey) Then\r\n                        If dItems(varKey) <> dExisting(varKey) Then\r\n                            ' Update value of existing property if different.\r\n                            projCurrent.Properties(varKey).Value = varValue\r\n                        End If\r\n                    Else\r\n                        ' Add properties that don't exist.\r\n                        projCurrent.Properties.Add varKey, varValue\r\n                    End If\r\n            End Select\r\n        Next varKey\r\n    End If\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary(False))\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 5/29/2021\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Remove any document properties that don't exist in the incoming file,\r\n    ' then import the file.\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If dFile Is Nothing Then Set dFile = New Dictionary\r\n    RemoveMissing dFile(\"Items\"), GetDictionary\r\n\r\n    ' Import if file exists\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Project Properties (Access)\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/29/2021\r\n' Purpose   : Return a dictionary of the ordered database properties\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary(Optional blnUseCache As Boolean) As Dictionary\r\n\r\n    Dim prp As AccessObjectProperty\r\n    Dim dCollection As Dictionary\r\n\r\n    ' Check cache parameter\r\n    If blnUseCache And Not m_dItems Is Nothing Then\r\n        ' Return cached dictionary\r\n        Set GetDictionary = m_dItems\r\n        Exit Function\r\n    End If\r\n\r\n    Set dCollection = New Dictionary\r\n\r\n    ' Loop through all properties\r\n    For Each prp In CurrentProject.Properties\r\n        Select Case prp.Name\r\n            Case \"Connection\"\r\n                ' Connection object for ODBCDirect workspaces. Not needed.\r\n            Case \"Last VCS Export\", \"Last VCS Version\"\r\n                ' Legacy properties no longer needed.\r\n            Case \"AppIcon\"\r\n                ' ADP projects may have this property\r\n                dCollection.Add prp.Name, GetRelativePath(CStr(prp.Value))\r\n            Case Else\r\n                dCollection.Add prp.Name, prp.Value\r\n        End Select\r\n    Next prp\r\n\r\n    ' Return sorted dictionary\r\n    Set GetDictionary = SortDictionaryByKeys(dCollection)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveMissing\r\n' Author    : Adam Waller\r\n' Date      : 5/29/2021\r\n' Purpose   : Removes current document properties missing from the master dictionary.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RemoveMissing(dMaster As Dictionary, dTarget As Dictionary)\r\n\r\n    Dim proj As CurrentProject\r\n    Dim varProp As Variant\r\n\r\n    ' Go through target dictionary, removing properties that don't exist\r\n    ' in the master dictionary. (Note that this is only checking the\r\n    ' properties we are actually interested in tracking.)\r\n    Set proj = CurrentProject\r\n    For Each varProp In dTarget.Keys\r\n        ' Check to see if this key exists in the master\r\n        If Not KeyExists(dMaster, varProp) Then\r\n            ' Remove the property from the current project\r\n            proj.Properties.Remove CStr(varProp)\r\n        End If\r\n    Next varProp\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim prp As AccessObjectProperty\r\n    Dim cProp As IDbComponent\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        If Not blnModifiedOnly Or IDbComponent_IsModified Then\r\n            ' Return all the properties, since we don't know which ones\r\n            ' were modified.\r\n            For Each prp In CurrentProject.Properties\r\n                Set cProp = New clsDbProjProperty\r\n                Set cProp.DbObject = prp\r\n                m_Items(blnModifiedOnly).Add cProp.SourceFile & \":\" & prp.Name, cProp\r\n            Next prp\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    Set IDbComponent_GetFileList = New Dictionary\r\n    If FSO.FileExists(IDbComponent_SourceFile) Then IDbComponent_GetFileList.Add IDbComponent_SourceFile, vbNullString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    ' Modified date unknown.\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Proj Properties\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = \"Current Project Properties (Access)\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & \"proj-properties.json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbProjectProperty\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Property\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Property = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = True\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbProject.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbProject\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Project As CurrentProject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_dItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dProject As Dictionary\r\n    Dim proj As CurrentProject\r\n    Dim strConnect As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n    Set dProject = ReadJsonFile(strFile)\r\n    Set proj = CurrentProject\r\n\r\n    ' Update project properties (Only a couple we can really change)\r\n    With proj\r\n        .RemovePersonalInformation = Nz2(dNZ(dProject, \"Items\\RemovePersonalInformation\"), False)\r\n        If .ProjectType = acADP Then\r\n            strConnect = dNZ(dProject, \"Items\\BaseConnectionString\")\r\n            ' Connect to SQL server\r\n            If strConnect <> vbNullString Then CurrentProject.OpenConnection strConnect\r\n        End If\r\n    End With\r\n\r\n    CatchAny eelError, \"Importing Project\", ModuleName(Me) & \".Import\"\r\n\r\n    ' Save to index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Project\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a dictionary object of project properties.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary(Optional blnUseCache As Boolean = True) As Dictionary\r\n\r\n    ' Check cache first\r\n    If blnUseCache And Not m_dItems Is Nothing Then\r\n        Set GetDictionary = m_dItems\r\n        Exit Function\r\n    End If\r\n\r\n    ' Make sure we have a reference to the VB project\r\n    If m_Project Is Nothing Then Set m_Project = CurrentProject\r\n\r\n    ' Read project properties\r\n    Set GetDictionary = New Dictionary\r\n    With GetDictionary\r\n        If m_Project.ProjectType = acADP Then\r\n            ' Only save the connection string for ADP projects\r\n            .Add \"BaseConnectionString\", m_Project.BaseConnectionString\r\n        End If\r\n        .Add \"FileFormat\", m_Project.FileFormat\r\n        .Add \"RemovePersonalInformation\", m_Project.RemovePersonalInformation\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Import if file exists\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cProj As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        ' Load class details\r\n        Set m_Project = CurrentProject\r\n        Set m_dItems = GetDictionary\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        Set cProj = New clsDbProject\r\n        Set cProj.DbObject = m_Project\r\n        blnAdd = True\r\n        If blnModifiedOnly Then blnAdd = cProj.IsModified\r\n        If blnAdd Then m_Items(blnModifiedOnly).Add cProj.SourceFile, cProj\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    Set IDbComponent_GetFileList = New Dictionary\r\n    If FSO.FileExists(IDbComponent_SourceFile) Then IDbComponent_GetFileList.Add IDbComponent_SourceFile, vbNullString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Project\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Project Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Project.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & \"project.json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbProject\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Project\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Project = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = True\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Helps us know whether we have already counted the objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    'm_Count = -1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbProperty.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbProperty\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Property As DAO.Property\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_dItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dExisting As Dictionary\r\n    Dim prp As DAO.Property\r\n    Dim dImport As Dictionary\r\n    Dim dItems As Dictionary\r\n    Dim dbs As DAO.Database\r\n    Dim varKey As Variant\r\n    Dim varValue As Variant\r\n    Dim blnAdd As Boolean\r\n    Dim varItem As Variant\r\n    Dim bArray() As Byte\r\n    Dim i As Long\r\n    Dim bUpdate As Boolean\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    Set dbs = CurrentDb\r\n\r\n    ' Pull a list of the existing properties so we know whether\r\n    ' to add or update the existing property.\r\n    Set dExisting = New Dictionary\r\n    For Each prp In dbs.Properties\r\n        Select Case prp.Name\r\n            Case \"Connection\"   ' This is an object.\r\n            Case Else\r\n                dExisting.Add prp.Name, Array(prp.Value, prp.Type)\r\n        End Select\r\n    Next prp\r\n\r\n    ' Read properties from source file\r\n    Set dImport = ReadJsonFile(strFile)\r\n    If Not dImport Is Nothing Then\r\n        Set dItems = dImport(\"Items\")\r\n        For Each varKey In dItems.Keys\r\n            Select Case varKey\r\n                Case \"Connection\", \"Name\", \"Version\", \"CollatingOrder\" ' Can't set these properties\r\n                Case Else\r\n                    blnAdd = False\r\n                    bUpdate = False\r\n                    ' Check if value is as Collection\r\n                    If Not TypeOf dItems(varKey)(\"Value\") Is Collection Then\r\n                        varValue = dItems(varKey)(\"Value\")\r\n                        ' Check for relative path\r\n                        If IsRelativePath(CStr(varValue)) Then varValue = GetPathFromRelative(CStr(varValue))\r\n                        ' Check for UTC date that might need to be converted back to local\r\n                        If dItems(varKey)(\"Type\") = dbDate Then\r\n                            If (Not IsDate(varValue)) And (Right(varValue, 1) = \"Z\") Then\r\n                                ' Convert UTC date to local date\r\n                                dItems(varKey)(\"Value\") = modUtcConverter.ParseIso(CStr(varValue))\r\n                                varValue = CDate(dItems(varKey)(\"Value\"))\r\n                            End If\r\n                        End If\r\n                    Else\r\n                        ReDim bArray(0 To dItems(varKey)(\"Value\").Count - 1)\r\n                        For Each varItem In dItems(varKey)(\"Value\")\r\n                            bArray(i) = CByte(varItem)\r\n                            i = i + 1\r\n                        Next\r\n                    End If\r\n                    ' Check for existing value\r\n                    If dExisting.Exists(varKey) Then\r\n                        If dItems(varKey)(\"Type\") <> dExisting(varKey)(1) Then\r\n                            ' Type is different. Need to remove and add as correct type.\r\n                            dbs.Properties.Delete varKey\r\n                            blnAdd = True\r\n                        Else\r\n                            ' Check if value is a Collection\r\n                            If Not TypeOf dItems(varKey)(\"Value\") Is Collection Then\r\n                                ' Check the value, and update if different\r\n                                If varValue <> dExisting(varKey)(0) Then\r\n                                    ' Update value of existing property if different.\r\n                                    dbs.Properties(varKey).Value = varValue\r\n                                End If\r\n                            Else\r\n                                ' Check the arrays, and update if different\r\n                                If (LBound(bArray) <> LBound(dExisting(varKey)(0))) Or (UBound(bArray) <> UBound(dExisting(varKey)(0))) Then\r\n                                    ' Different size\r\n                                    bUpdate = True\r\n                                Else\r\n                                    ' Same size\r\n                                    ' Check content\r\n                                    For i = LBound(bArray) To UBound(bArray)\r\n                                        If (bArray(i) <> dExisting(varKey)(0)(i)) Then\r\n                                            bUpdate = True\r\n                                            Exit For\r\n                                        End If\r\n                                    Next\r\n                                End If\r\n                                If bUpdate Then\r\n                                    ' Update value of existing property if different.\r\n                                    dbs.Properties(varKey).Value = bArray\r\n                                End If\r\n                            End If\r\n                        End If\r\n                    Else\r\n                        ' Add properties that don't exist.\r\n                        blnAdd = True\r\n                    End If\r\n\r\n                    ' Can't add a text property with a null value. See issue #126\r\n                    If dItems(varKey)(\"Type\") = dbText Then\r\n                        If varValue = vbNullChar Then blnAdd = False\r\n                    End If\r\n                    ' Add the property if the flag has been set.\r\n                    If blnAdd Then\r\n                        ' Check if value is a Collection\r\n                        If Not TypeOf dItems(varKey)(\"Value\") Is Collection Then\r\n                            ' Create property\r\n                            Set prp = dbs.CreateProperty(varKey, dItems(varKey)(\"Type\"), varValue)\r\n                        Else\r\n                            ' Create property from array\r\n                            Set prp = dbs.CreateProperty(varKey, dItems(varKey)(\"Type\"), bArray)\r\n                        End If\r\n\r\n                        ' Append property to collection\r\n                        dbs.Properties.Append prp\r\n                    End If\r\n            End Select\r\n        Next varKey\r\n    End If\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary(False))\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Remove any document properties that don't exist in the incoming file,\r\n    ' then import the file.\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If dFile Is Nothing Then Set dFile = New Dictionary\r\n    RemoveMissing dFile(\"Items\"), GetDictionary\r\n\r\n    ' Import if file exists\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Database Properties (DAO)\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Return a dictionary of the ordered, unique VBE references\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary(Optional blnUseCache As Boolean) As Dictionary\r\n\r\n    Dim prp As DAO.Property\r\n    Dim dCollection As Dictionary\r\n    Dim dItem As Dictionary\r\n    Dim varValue As Variant\r\n\r\n    ' Check cache parameter\r\n    If blnUseCache And Not m_dItems Is Nothing Then\r\n        ' Return cached dictionary\r\n        Set GetDictionary = m_dItems\r\n        Exit Function\r\n    End If\r\n\r\n    Set dCollection = New Dictionary\r\n\r\n    ' Loop through all properties\r\n    For Each prp In CurrentDb.Properties\r\n        Select Case prp.Name\r\n            Case \"Connection\"\r\n                ' Connection object for ODBCDirect workspaces. Not needed.\r\n            Case \"Last VCS Export\", \"Last VCS Version\"\r\n                ' Legacy properties no longer needed.\r\n            Case Else\r\n                varValue = prp.Value\r\n                If prp.Name = \"AppIcon\" Or prp.Name = \"Name\" Then\r\n                    If Len(varValue) > 0 Then\r\n                        ' Try to use a relative path\r\n                        varValue = GetRelativePath(CStr(varValue))\r\n                    End If\r\n                End If\r\n                ' Convert dates to UTC\r\n                If prp.Type = dbDate Then\r\n                    If IsDate(varValue) Then\r\n                        ' Store dates in JSON as UTC dates.\r\n                        varValue = modUtcConverter.ConvertToIsoTime(CDate(varValue))\r\n                    End If\r\n                End If\r\n                Set dItem = New Dictionary\r\n                dItem.Add \"Value\", varValue\r\n                dItem.Add \"Type\", prp.Type\r\n                dCollection.Add prp.Name, dItem\r\n        End Select\r\n    Next prp\r\n\r\n    ' Return sorted dictionary\r\n    Set GetDictionary = SortDictionaryByKeys(dCollection)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveMissing\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Removes current document properties missing from the master dictionary.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RemoveMissing(dMaster As Dictionary, dTarget As Dictionary)\r\n\r\n    Dim dbs As Database\r\n    Dim varProp As Variant\r\n\r\n    ' Go through target dictionary, removing properties that don't exist\r\n    ' in the master dictionary. (Note that this is only checking the\r\n    ' properties we are actually interested in tracking.)\r\n    Set dbs = CurrentDb\r\n    For Each varProp In dTarget.Keys\r\n        ' Check to see if this key exists in the master\r\n        If Not KeyExists(dMaster, varProp) Then\r\n            ' Remove the property from the current database\r\n            dbs.Properties.Delete CStr(varProp)\r\n        End If\r\n    Next varProp\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim prp As DAO.Property\r\n    Dim cProp As IDbComponent\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        If Not blnModifiedOnly Or IDbComponent_IsModified Then\r\n            ' Return all the properties, since we don't know which ones\r\n            ' were modified.\r\n            For Each prp In CurrentDb.Properties\r\n                Set cProp = New clsDbProperty\r\n                Set cProp.DbObject = prp\r\n                m_Items(blnModifiedOnly).Add cProp.SourceFile & \":\" & prp.Name, cProp\r\n            Next prp\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    Set IDbComponent_GetFileList = New Dictionary\r\n    If FSO.FileExists(IDbComponent_SourceFile) Then IDbComponent_GetFileList.Add IDbComponent_SourceFile, vbNullString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    ' Modified date unknown.\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"DB Properties\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = \"Database Properties (DAO)\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & \"dbs-properties.json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbDbsProperty\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Property\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Property = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = True\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Helps us know whether we have already counted the tables.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    'm_Count = -1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbQuery.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbQuery\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Query As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n\r\n    Dim strFile As String\r\n    Dim strHash As String\r\n    Dim strSql As String\r\n    Dim dbs As DAO.Database\r\n\r\n    ' Save and sanitize file\r\n    strHash = SaveComponentAsText(acQuery, m_Query.Name, Nz2(strAlternatePath, IDbComponent_SourceFile))\r\n\r\n    ' If saving in source folder, check SQL option\r\n    If strAlternatePath = vbNullString Then\r\n        ' Export as SQL (if using that option)\r\n        If Options.SaveQuerySQL Then\r\n            Perf.OperationStart \"Save Query SQL\"\r\n            Set dbs = CurrentDb\r\n            strFile = IDbComponent_BaseFolder & GetSafeFileName(m_Query.Name) & \".sql\"\r\n            On Error Resume Next\r\n            strSql = dbs.QueryDefs(m_Query.Name).SQL\r\n            If Catch(3258) Then\r\n                Log.Error eelWarning, \"Unable to access SQL property of query '\" & m_Query.Name & \"'. SQL view may not be available for this query.\", ModuleName(Me) & \".Export\"\r\n            ElseIf Err.Number Then\r\n                Log.Error eelError, \"Unexpected error accessing SQL property of query '\" & m_Query.Name & \"'. Error \" & Err.Number & \": \" & Err.Description, ModuleName(Me) & \".Export\"\r\n            End If\r\n            On Error GoTo 0\r\n            If strSql <> vbNullString Then\r\n                ' Pass-through queries should not be formatted, since they support formatting and comments.\r\n                Select Case dbs.QueryDefs(m_Query.Name).Type\r\n                    'Case dbQSetOperation - Go ahead and format Union queries\r\n                    Case dbQSQLPassThrough, dbQSPTBulk\r\n                        'Do not automatically format these query types\r\n                    Case Else\r\n                        With New clsSqlFormatter\r\n                            Perf.OperationStart \"Format SQL\"\r\n                            strSql = .FormatSQL(strSql, esdAccess)\r\n                            Perf.OperationEnd\r\n                        End With\r\n                End Select\r\n                ' Write SQL to file\r\n                WriteFile strSql, strFile\r\n            End If\r\n            Perf.OperationEnd\r\n            Log.Add \"  \" & m_Query.Name & \" (SQL)\", Options.ShowDebug\r\n        End If\r\n    End If\r\n\r\n    ' Update the index\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), strHash\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller / Indigo\r\n' Date      : 10/24/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim strQueryName As String\r\n    Dim strFileSql As String\r\n    Dim strSql As String\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.bas\" Then Exit Sub\r\n\r\n    ' Import query from file\r\n    strQueryName = GetObjectNameFromFileName(strFile)\r\n    LoadComponentFromText acQuery, strQueryName, strFile\r\n    Set m_Query = CurrentData.AllQueries(strQueryName)\r\n    VCSIndex.Update Me, eatImport, GetFileHash(strFile)\r\n\r\n    ' In some cases, such as when a query contains a subquery, AND has been modified in the\r\n    ' visual query designer, it may be imported incorrectly and unable to run. For these\r\n    ' cases we have added an option to overwrite the .SQL property with the SQL that we\r\n    ' saved separately during the export. See the following link for further details:\r\n    ' https://github.com/joyfullservice/msaccess-vcs-addin/issues/76\r\n\r\n    ' Check option to import exact query from SQL\r\n    If Options.ForceImportOriginalQuerySQL Then\r\n\r\n        ' Replace .bas extension with .sql to get file content\r\n        strFileSql = Left$(strFile, Len(strFile) - 4) & \".sql\"\r\n\r\n        ' Tries to get SQL content from the SQL file previously exported\r\n        strSql = ReadFile(strFileSql)\r\n\r\n        ' Update query def with saved SQL\r\n        If strSql <> vbNullString Then\r\n            Set dbs = CurrentDb\r\n            dbs.QueryDefs(strQueryName).SQL = strSql\r\n            Log.Add \"  Restored original SQL for \" & strQueryName, Options.ShowDebug\r\n        Else\r\n            Log.Add \"  Couldn't get original SQL query for \" & strQueryName\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    DeleteObjectIfExists acQuery, GetObjectNameFromFileName(strFile)\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\n    MoveFileIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile) & \".sql\", strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim qry As AccessObject\r\n    Dim cQuery As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each qry In CurrentData.AllQueries\r\n            Set cQuery = New clsDbQuery\r\n            Set cQuery.DbObject = qry\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cQuery.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cQuery.SourceFile, cQuery\r\n        Next qry\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.bas\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = (m_Query.DateModified > VCSIndex.Item(Me).ExportDate)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_Query Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = m_Query.DateModified\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Queries\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"queries\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"bas\"\r\n    IDbComponent_FileExtensions.Add \"sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Query Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Query.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Query Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Query.Name) & \".bas\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentData.AllQueries.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbQuery\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Query\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Query = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbRelation.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbRelation\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Relation As DAO.Relation\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\nPrivate m_Dbs As DAO.Database\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Terminate\r\n' Author    : Adam Waller\r\n' Date      : 4/30/2020\r\n' Purpose   : Release reference to current db\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Terminate()\r\n    Set m_Relation = Nothing\r\n    Set m_Dbs = Nothing\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dItem As Dictionary\r\n    Dim dFile As Dictionary\r\n    Dim dField As Dictionary\r\n    Dim fld As DAO.Field\r\n    Dim rel As DAO.Relation\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Parse json file\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If Not dFile Is Nothing Then\r\n\r\n        ' Create new relation\r\n        Set m_Dbs = CurrentDb\r\n        Set dItem = dFile(\"Items\")\r\n        Set rel = m_Dbs.CreateRelation(dItem(\"Name\"), dItem(\"Table\"), dItem(\"ForeignTable\"))\r\n        rel.Attributes = dItem(\"Attributes\")\r\n\r\n        ' Add fields, and append to relation\r\n        For Each dField In dItem(\"Fields\")\r\n            Set fld = rel.CreateField(dField(\"Name\"))\r\n            fld.ForeignName = dField(\"ForeignName\")\r\n            rel.Fields.Append fld\r\n        Next dField\r\n\r\n        ' Relationships create indexes, so we need to make sure an index\r\n        ' with this name doesn't already exist. (Also check to be sure that\r\n        ' we don't already have a relationship with this name.)\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        With m_Dbs\r\n            .TableDefs(rel.Table).Indexes.Delete rel.Name\r\n            .TableDefs(rel.ForeignTable).Indexes.Delete rel.Name\r\n            .Relations.Delete rel.Name\r\n        End With\r\n        CatchAny eelNoError, vbNullString, , False\r\n\r\n        ' Add relationship to database\r\n        m_Dbs.Relations.Append rel\r\n        Set m_Relation = rel\r\n    End If\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Import if file exists\r\n    If FSO.FileExists(strFile) Then\r\n        ' Existing relationships will be removed during import\r\n        IDbComponent_Import strFile\r\n    Else\r\n        ' If the source file was deleted, remove any existing relationship by this\r\n        ' name during a merge operation.\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        CurrentDb.Relations.Delete FSO.GetBaseName(strFile)\r\n        VCSIndex.Remove Me, strFile\r\n        If Err Then Err.Clear\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim rel As Relation\r\n    Dim cRelation As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n\r\n        ' Maintain persistent reference to database object so we don't\r\n        ' lose the reference to the relation object with this procedure\r\n        ' goes out of scope. (Make sure we release this on termination)\r\n        If m_Dbs Is Nothing Then Set m_Dbs = CurrentDb\r\n\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each rel In m_Dbs.Relations\r\n            ' Navigation pane groups are handled separately\r\n            If Not (rel.Name = \"MSysNavPaneGroupsMSysNavPaneGroupToObjects\" _\r\n                Or rel.Name = \"MSysNavPaneGroupCategoriesMSysNavPaneGroups\" _\r\n                Or IsInherited(rel)) Then\r\n                Set cRelation = New clsDbRelation\r\n                Set cRelation.DbObject = rel\r\n                blnAdd = True\r\n                If blnModifiedOnly Then blnAdd = cRelation.IsModified\r\n                If blnAdd Then m_Items(blnModifiedOnly).Add cRelation.SourceFile, cRelation\r\n            End If\r\n        Next rel\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsInherited\r\n' Author    : Adam Waller\r\n' Date      : 6/30/2020\r\n' Purpose   : Returns true if the relationship was inherited from tables in a linked\r\n'           : database. (We don't need to export or import these.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IsInherited(objRelation As Relation) As Boolean\r\n    IsInherited = ((objRelation.Attributes And dbRelationInherited) = dbRelationInherited)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetRelationFileName\r\n' Author    : Adam Waller\r\n' Date      : 6/4/2015\r\n' Purpose   : Build file name based on relation name, including support for linked\r\n'           : tables that would put a slash in the relation name.\r\n'           : (Strips the link path from the table name)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetRelationFileName(objRelation As Relation) As String\r\n\r\n    Dim strName As String\r\n\r\n    strName = objRelation.Name\r\n\r\n    If InStr(1, strName, \"].\") > 0 Then\r\n        ' Need to remove path to linked file\r\n        GetRelationFileName = GetSafeFileName(CStr(Split(strName, \"].\")(1)))\r\n    Else\r\n        GetRelationFileName = GetSafeFileName(strName)\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Database relationship\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/6/2023\r\n' Purpose   : Return a dictionary object of relationship definition.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary() As Dictionary\r\n\r\n    Dim dItem As Dictionary\r\n    Dim dField As Dictionary\r\n    Dim colItems As Collection\r\n    Dim fld As DAO.Field\r\n\r\n    ' Relation properties\r\n    Set dItem = New Dictionary\r\n    With dItem\r\n        .Add \"Name\", m_Relation.Name\r\n        .Add \"Attributes\", m_Relation.Attributes\r\n        .Add \"Table\", m_Relation.Table\r\n        .Add \"ForeignTable\", m_Relation.ForeignTable\r\n    End With\r\n\r\n    ' Fields\r\n    Set colItems = New Collection\r\n    For Each fld In m_Relation.Fields\r\n        Set dField = New Dictionary\r\n        With dField\r\n            .Add \"Name\", fld.Name\r\n            .Add \"ForeignName\", fld.ForeignName\r\n        End With\r\n        colItems.Add dField\r\n    Next fld\r\n    dItem.Add \"Fields\", colItems\r\n\r\n    ' Return dictionary object\r\n    Set GetDictionary = dItem\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.json\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Relations\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"relations\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Relation Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Relation.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Relation Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetRelationFileName(m_Relation) & \".json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    ' DCount is much faster than CurrentDb.Relations.Count in some cases.\r\n    If lngCount = 0 Then lngCount = DCount(\"*\", \"MSysRelationships\") - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbRelation\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Relation\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Relation = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbReport.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbReport\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Report As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strHash As String\r\n    strHash = SaveComponentAsText(acReport, m_Report.Name, Nz2(strAlternatePath, IDbComponent_SourceFile), Me)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), _\r\n        strHash, GetCodeModuleHash(IDbComponent_ComponentType, m_Report.Name)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim strName As String\r\n    Dim blnImportCheck As Boolean\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.bas\" Then Exit Sub\r\n\r\n    strName = GetObjectNameFromFileName(strFile)\r\n    blnImportCheck = LoadComponentFromText(acReport, strName, strFile)\r\n    If Not blnImportCheck Or Log.ErrorLevel = eelCritical Then Exit Sub\r\n    Set m_Report = CurrentProject.AllReports(strName)\r\n    VCSIndex.Update Me, eatImport, GetFileHash(strFile), GetCodeModuleHash(IDbComponent_ComponentType, strName)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    DeleteObjectIfExists acReport, GetObjectNameFromFileName(strFile)\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\n    MoveFileIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile) & \".json\", strToFolder\r\n    MoveFileIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile) & \".cls\", strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim rpt As AccessObject\r\n    Dim cReport As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each rpt In CurrentProject.AllReports\r\n            Set cReport = New clsDbReport\r\n            Set cReport.DbObject = rpt\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cReport.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cReport.SourceFile, cReport\r\n        Next rpt\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.bas\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n\r\n    ' Item is considered modified unless proven otherwise.\r\n    IDbComponent_IsModified = True\r\n\r\n    ' Check the modified date first.\r\n    ' (This may not reflect some code changes)\r\n    If m_Report.DateModified <= VCSIndex.Item(Me).ExportDate Then\r\n\r\n        ' Date is okay, check hash\r\n        IDbComponent_IsModified = VCSIndex.Item(Me).OtherHash <> GetCodeModuleHash(IDbComponent_ComponentType, m_Report.Name)\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_Report Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = m_Report.DateModified\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Reports\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"reports\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"bas\"\r\n    IDbComponent_FileExtensions.Add \"cls\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Report Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Report.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Report Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Report.Name) & \".bas\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentProject.AllReports.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbReport\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Report\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Report = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbSavedSpec.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbSavedSpec\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Spec As ImportExportSpecification\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim proj As CurrentProject\r\n    Dim dSpec As Dictionary\r\n    Dim dItem As Dictionary\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    Set dSpec = ReadJsonFile(strFile)\r\n    If Not dSpec Is Nothing Then\r\n        Set dItem = dSpec(\"Items\")\r\n        Set proj = CurrentProject\r\n        With proj.ImportExportSpecifications.Add(dItem(\"Name\"), dItem(\"XML\"))\r\n            ' Set description if provided.\r\n            If Nz(dItem(\"Description\")) <> vbNullString Then .Description = dItem(\"Description\")\r\n        End With\r\n    End If\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary)\r\n\r\n    CatchAny eelError, \"Error importing saved specification: \" & _\r\n        GetObjectNameFromFileName(strFile), ModuleName(Me) & \".Import\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    Dim strName As String\r\n\r\n    strName = GetObjectNameFromFileName(strFile)\r\n\r\n    ' Remove any existing object by this name\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    CurrentProject.ImportExportSpecifications(strName).Delete\r\n    If DebugMode(False) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' See if the source file exists. (It might not if we are removing the object.)\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        ' Remove index entry for a removed object\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim spec As ImportExportSpecification\r\n    Dim cSpec As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each spec In CurrentProject.ImportExportSpecifications\r\n            Set cSpec = New clsDbSavedSpec\r\n            Set cSpec.DbObject = spec\r\n            blnAdd = True\r\n            If blnModifiedOnly Then blnAdd = cSpec.IsModified\r\n            If blnAdd Then m_Items(blnModifiedOnly).Add cSpec.SourceFile, cSpec\r\n        Next spec\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Saved Import/Export Specification\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2023\r\n' Purpose   : Return a dictionary object with the export specification.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary() As Dictionary\r\n\r\n    Dim dSpec As Dictionary\r\n\r\n    Set dSpec = New Dictionary\r\n\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    ' For some reason it throws an error if there is no\r\n    ' description in the specification.\r\n    With dSpec\r\n        .Add \"Name\", m_Spec.Name\r\n        .Add \"Description\", m_Spec.Description\r\n        ' For some odd reason, duplicate return characters are added\r\n        ' with every export/build cycle. Purge any duplicate return\r\n        ' characters when creating source.\r\n        .Add \"XML\", DeDupString(m_Spec.XML, vbCr)\r\n    End With\r\n    CatchAny eelNoError, vbNullString, , False\r\n\r\n    ' Return dictionary\r\n    Set GetDictionary = dSpec\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.json\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    ' No modified date on a spec\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Saved Specs\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"savedspecs\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Spec Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Spec.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Spec Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Spec.Name) & \".json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentProject.ImportExportSpecifications.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbSavedSpec\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Spec\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Spec = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Helps us know whether we have already counted the objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    'm_Count = -1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbSharedImage.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbSharedImage\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\nPrivate m_Dbs As DAO.Database\r\n\r\n' This is used to pass a reference to the record back\r\n' into the class for loading the private variables\r\n' with the actual file information.\r\nPrivate m_Rst As DAO.Recordset\r\n\r\n' File details used for exporting/importing\r\nPrivate m_Name As String\r\nPrivate m_FileName As String\r\nPrivate m_Extension As String\r\nPrivate m_FileData() As Byte\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the shared image as a json file with file details, and a copy\r\n'           : of the binary image file saved as an image.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n\r\n    Dim strContent As String\r\n    Dim strFile As String\r\n    Dim dItem As Dictionary\r\n\r\n    ' Build header file\r\n    Set dItem = GetDictionary\r\n\r\n    ' Save json file with header details\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n\r\n    ' Save image file using extension from embedded file.\r\n    strFile = IDbComponent_BaseFolder & FSO.GetBaseName(IDbComponent_SourceFile) & \".\" & FSO.GetExtensionName(m_FileName)\r\n\r\n    ' Write binary content to file\r\n    WriteBinaryFile strFile, StripOLEHeader(m_Extension, m_FileData)\r\n\r\n    ' Update index after export\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), _\r\n        GetStringHash(strContent, True), GetSignatureHash\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n    Dim dItem As Dictionary\r\n    Dim strTemp As String\r\n    Dim strImageFile As String\r\n    Dim strOriginalName As String\r\n    Dim strBase As String\r\n    Dim lngIndex As Long\r\n    Dim proj As CurrentProject\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Read json header file\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If Not dFile Is Nothing Then\r\n        Set dItem = dFile(\"Items\")\r\n        ' Check for an existing file with the same name\r\n        strOriginalName = IDbComponent_BaseFolder & dItem(\"FileName\")\r\n        strImageFile = IDbComponent_BaseFolder & FSO.GetBaseName(strFile) & \".\" & FSO.GetExtensionName(strOriginalName)\r\n        If strOriginalName <> strImageFile Then\r\n            If FSO.FileExists(strOriginalName) Then\r\n                strTemp = IDbComponent_BaseFolder & FSO.GetTempName\r\n                ' Rename to temp file\r\n                Name strOriginalName As strTemp\r\n            End If\r\n            Name strImageFile As strOriginalName\r\n        End If\r\n        ' Rename image to original name\r\n        ' Import as image, then rename back to image file name that matches json file.\r\n        Set proj = CurrentProject\r\n        With proj\r\n            lngIndex = .Resources.Count\r\n            ' Import using the original file name as the resource name so the\r\n            ' embedded file has the correct name. (Ensuring that we are using a unique name)\r\n            strBase = GetUnusedResourceName(FSO.GetBaseName(strOriginalName))\r\n            .AddSharedImage strBase, strOriginalName\r\n            If .Resources.Count = lngIndex + 1 Then\r\n                ' Rename shared resource to saved name if different.\r\n                If strBase <> dItem(\"Name\") Then\r\n                    .Resources(GetResourceIndexByName(strBase)).Name = dItem(\"Name\")\r\n                End If\r\n            End If\r\n        End With\r\n        ' Restore temp file if needed\r\n        If strTemp <> vbNullString Then\r\n            Name strTemp As strImageFile\r\n        Else\r\n            ' Restore image file name if needed.\r\n            If strOriginalName <> strImageFile Then Name strOriginalName As strImageFile\r\n        End If\r\n\r\n        ' Update index after import\r\n        If LoadItem(dItem(\"Name\")) Then\r\n            VCSIndex.Update Me, eatExport, GetDictionaryHash(dItem)\r\n        Else\r\n            Log.Error eelError, \"Unable to find shared image '\" & dItem(\"Name\") & \"' after importing.\", ModuleName(Me) & \".Import\"\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    Dim strName As String\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Parse name from file name\r\n    strName = GetObjectNameFromFileName(strFile)\r\n\r\n    ' Remove the existing shared image, if it exists.\r\n    RemoveIfExists strName\r\n\r\n    ' Import if source file exists\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveIfExists\r\n' Author    : Adam Waller\r\n' Date      : 2/7/2023\r\n' Purpose   : Removes a shared resource if it exists\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RemoveIfExists(strName As String)\r\n\r\n    Dim resImage As SharedResource\r\n\r\n    Perf.OperationStart \"Check/Remove Shared Resource\"\r\n    For Each resImage In CurrentProject.Resources\r\n        If resImage.Type = acResourceImage Then\r\n            If resImage.Name = strName Then\r\n                resImage.Delete\r\n                Exit For\r\n            End If\r\n        End If\r\n    Next resImage\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadItem\r\n' Author    : Adam Waller\r\n' Date      : 11/1/2021\r\n' Purpose   : Load a shared resource by name into this class so we can reconstruct the\r\n'           : hash when updating the index after import.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function LoadItem(strName As String) As Boolean\r\n\r\n    Dim rst As DAO.Recordset\r\n    Dim strSql As String\r\n\r\n    ' This system table should exist, but just in case...\r\n    If TableExists(\"MSysResources\") Then\r\n\r\n        If m_Dbs Is Nothing Then Set m_Dbs = CurrentDb\r\n        strSql = \"SELECT * FROM MSysResources WHERE Type='img'\"\r\n        Set rst = m_Dbs.OpenRecordset(strSql, dbOpenSnapshot, dbOpenForwardOnly)\r\n        With rst\r\n            Do While Not .EOF\r\n                If Nz(!Name) = strName Then\r\n                    ' Set the DB object, reloading the properties\r\n                    Set IDbComponent_DbObject = rst\r\n                    Exit Do\r\n                End If\r\n                .MoveNext\r\n            Loop\r\n            .Close\r\n        End With\r\n        LoadItem = True\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSignatureHash\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Return a simple hash that should give us a pretty good idea if the image\r\n'           : has changed since it was last exported.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSignatureHash() As String\r\n\r\n    Dim dblCnt As Double\r\n    Dim dblLen As Double\r\n    Dim strData As String\r\n\r\n    ' Get length (bytes) of file data\r\n    If StrPtr(m_FileData) <> 0 Then dblLen = UBound(m_FileData)\r\n\r\n    With New clsConcat\r\n        ' Compile string with file information\r\n        .Add m_Name, m_FileName, m_Extension, CStr(dblLen)\r\n        ' Add in last 100 characters of file content (byte numbers)\r\n        ' Since this is likely a compressed file format, we will probably\r\n        ' see a change here if the contents were modified.\r\n        If dblLen > 100 Then\r\n            For dblCnt = dblLen To dblLen - 100 Step -1\r\n                .Add CStr(m_FileData(dblCnt))\r\n            Next dblCnt\r\n        Else\r\n            ' Convert byte array to string and include in hash.\r\n            strData = m_FileData\r\n            .Add strData\r\n        End If\r\n        ' Return hash of this string\r\n        GetSignatureHash = GetStringHash(.GetStr)\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Shared Image Gallery Item\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 11/1/2021\r\n' Purpose   : Return a dictionary object representing the content for the json file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary() As Dictionary\r\n    Set GetDictionary = New Dictionary\r\n    With GetDictionary\r\n        .Add \"Name\", m_Name\r\n        .Add \"FileName\", m_FileName\r\n        .Add \"Extension\", m_Extension\r\n        ' Store a hash of the binary image content, so we can verify\r\n        ' that the file content itself has not changed.\r\n        .Add \"ContentHash\", GetBytesHash(StripOLEHeader(m_Extension, m_FileData))\r\n    End With\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetResourceIndexByName\r\n' Author    : Adam Waller\r\n' Date      : 5/29/2020\r\n' Purpose   : Return the index of the shared resource after locating by name.\r\n'           : (This is needed because the new resource doesn't always have the\r\n'           :  highest index.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetResourceIndexByName(strName As String) As Long\r\n\r\n    Dim lngIndex As Long\r\n    Dim resShared As SharedResources\r\n\r\n    Set resShared = CurrentProject.Resources\r\n    For lngIndex = 0 To resShared.Count - 1\r\n        If resShared(lngIndex).Name = strName Then\r\n            GetResourceIndexByName = lngIndex\r\n            Exit For\r\n        End If\r\n    Next lngIndex\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetUnusedResourceName\r\n' Author    : Adam Waller\r\n' Date      : 2/7/2023\r\n' Purpose   : Return the original name if possible, or a similar name that does not\r\n'           : conflict with any existing resource names.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetUnusedResourceName(strName As String) As String\r\n\r\n    Dim strSql As String\r\n    Dim strTest As String\r\n    Dim lngCnt As Long\r\n    Dim rst As DAO.Recordset\r\n\r\n    Perf.OperationStart \"Get Unused Resource Name\"\r\n\r\n    ' Use a dictionary to easily test if a name is in use.\r\n    With New Dictionary\r\n\r\n        ' Build dictionary of existing items\r\n        ' (Reading directly from the table is MUCH faster than iterating through\r\n        '  the CurrentProject.Resources object model.)\r\n        If TableExists(\"MSysResources\") Then\r\n            If m_Dbs Is Nothing Then Set m_Dbs = CurrentDb\r\n            strSql = \"SELECT * FROM MSysResources WHERE Type='img'\"\r\n            Set rst = m_Dbs.OpenRecordset(strSql, dbOpenSnapshot, dbOpenForwardOnly)\r\n            Do While Not rst.EOF\r\n                If Not .Exists(Nz(rst!Name)) Then .Add Nz(rst!Name), vbNullString\r\n                rst.MoveNext\r\n            Loop\r\n            rst.Close\r\n        End If\r\n\r\n        ' Start with the initial name\r\n        strTest = strName\r\n\r\n        ' Loop through name alternatives to find an unused one.\r\n        ' (Typically we would only have to do one or two iterations)\r\n        For lngCnt = 1 To 100\r\n            If Not .Exists(strTest) Then\r\n                ' Found an unused name\r\n                Exit For\r\n            End If\r\n            ' Build an alternate name with a numerical prefix for the next test.\r\n            strTest = lngCnt & \"_\" & strName\r\n        Next lngCnt\r\n    End With\r\n\r\n    Perf.OperationEnd\r\n\r\n    ' Return the tested and available name\r\n    GetUnusedResourceName = strTest\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StripOLEHeader\r\n' Author    : Adam Waller\r\n' Date      : 5/12/2020\r\n' Purpose   : Strip out the OLE header so we can save the raw binary image as a\r\n'           : readable file. (First 20 bytes (10 chars) of the data)\r\n'           : Tested with jpg, gif, png, tiff\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function StripOLEHeader(strExt As String, bteData() As Byte) As Byte()\r\n\r\n    Dim strData As String\r\n\r\n    ' Convert to string\r\n    strData = bteData\r\n\r\n    ' Strip off header, and convert back to byte array\r\n    StripOLEHeader = Mid$(strData, 8 + Len(strExt))\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\n    ' Move actual image file\r\n    MoveFileIfExists strFromFolder & _\r\n        FSO.GetBaseName(IDbComponent_SourceFile) & \".\" & FSO.GetExtensionName(m_FileName), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cImg As IDbComponent\r\n    Dim rst As DAO.Recordset\r\n    Dim strSql As String\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n\r\n        ' This system table should exist, but just in case...\r\n        If TableExists(\"MSysResources\") Then\r\n\r\n            Set m_Dbs = CurrentDb\r\n            ' We will use an explicit ordering because it can potentially contain duplicate entries due to the fact\r\n            ' that the Name column is not unique or a primary key. Any duplicate images are useless and will be\r\n            ' skipped but to ensure we provide a consistent result, we need to make sure we keep the image with the\r\n            ' lowest ID and discard any duplicate images with a higher Id.\r\n            strSql = \"SELECT * FROM MSysResources WHERE Type='img' ORDER BY Id;\"\r\n            Set rst = m_Dbs.OpenRecordset(strSql, dbOpenSnapshot, dbOpenForwardOnly)\r\n            With rst\r\n                Do While Not .EOF\r\n                    Set cImg = New clsDbSharedImage\r\n                    ' Also sets reference to OLE object recordset2\r\n                    Set cImg.DbObject = rst\r\n                    blnAdd = True\r\n                    If blnModifiedOnly Then blnAdd = cImg.IsModified\r\n                    If blnAdd Then\r\n                        If m_Items(blnModifiedOnly).Exists(cImg.SourceFile) Then\r\n                            ' This is a duplicate image. We won't add it to the list but we need to issue a warning in log.\r\n                            Log.Error eelWarning, \"Duplicate record found for '\" & cImg.Name & \"' at ID \" & rst.Fields(\"ID\").Value & \". This record will not be exported as this is likely an error and not available for use via Access.\", ModuleName(Me) & \".GetAllFromDB\"\r\n                        Else\r\n                            m_Items(blnModifiedOnly).Add cImg.SourceFile, cImg\r\n                        End If\r\n                    End If\r\n                    .MoveNext\r\n                Loop\r\n                .Close\r\n            End With\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.json\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'           : Compare the index hash to the existing database content\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n\r\n    ' Check file hash of definition\r\n    With VCSIndex.Item(Me)\r\n        If .FileHash <> GetStringHash(GetSource, True) Then\r\n            ' Definition file is different\r\n            IDbComponent_IsModified = True\r\n        Else\r\n            ' Check signature of actual image content\r\n            IDbComponent_IsModified = (.OtherHash <> GetSignatureHash)\r\n        End If\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Shared Images\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"images\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    With IDbComponent_FileExtensions\r\n        .Add \"json\"\r\n        .Add \"jpg\"\r\n        .Add \"jpeg\"\r\n        .Add \"jpe\"\r\n        .Add \"gif\"\r\n        .Add \"png\"\r\n        .Add \"ico\"\r\n    End With\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = m_Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'           : In this case, we are building the name to include the info needed to\r\n'           : recreate the record in the MSysResource table.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Name) & \".json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbSharedImage\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    ' Not used\r\n    Set IDbComponent_DbObject = Nothing\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_DbObject\r\n' Author    : Adam Waller\r\n' Date      : 5/11/2020\r\n' Purpose   : Load in the class values from the recordset\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n\r\n    ' IMPORTANT: These variables must be declared as the\r\n    ' generic parent classes to avoid problems in newer\r\n    ' releases of Microsoft Access. (See issue #341)\r\n    Dim fld2 As DAO.Field       ' Field2\r\n    Dim rst2 As DAO.Recordset   ' Recordset2\r\n\r\n    Set m_Rst = RHS\r\n\r\n    ' Load in the object details.\r\n    m_Name = Nz(m_Rst!Name)\r\n    m_Extension = Nz(m_Rst!Extension)\r\n    '@Ignore SetAssignmentWithIncompatibleObjectType\r\n    Set fld2 = m_Rst!Data\r\n    Set rst2 = fld2.Value\r\n    m_FileName = Nz(rst2.Fields(\"FileName\"))\r\n    m_FileData = rst2.Fields(\"FileData\")\r\n\r\n    ' Clear the object references\r\n    Set rst2 = Nothing\r\n    Set fld2 = Nothing\r\n    Set m_Rst = Nothing\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Terminate\r\n' Author    : Adam Waller\r\n' Date      : 5/13/2020\r\n' Purpose   : Clear reference to database object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Terminate()\r\n    Set m_Dbs = Nothing\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbTableData.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbTableData\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPublic Format As eTableDataExportFormat\r\n\r\nConst UNSUPPORTED_DATA_TYPE As String = \"UNSUPPORTED DATA TYPE\"\r\n\r\nPrivate m_Table As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n\r\n    Dim strFile As String\r\n    Dim intFormat As eTableDataExportFormat\r\n\r\n    ' Use error handling in case we encouter errors during export\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Make sure the table exists, and is accessible\r\n    If Not TableExists(m_Table.Name) Then\r\n        Log.Error eelError, \"Unable to access table \" & m_Table.Name & _\r\n            \". Please ensure that this table exists and is accessible in the current database.\", _\r\n            ModuleName(Me) & \".Export\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Save as selected format, and remove other formats if they exist.\r\n    For intFormat = 1 To eTableDataExportFormat.[_Last] - 1\r\n        ' Build file name for this format\r\n        strFile = Nz2(strAlternatePath, IDbComponent_BaseFolder & GetSafeFileName(m_Table.Name) & \".\" & GetExtByFormat(intFormat))\r\n        If FSO.FileExists(strFile) Then DeleteFile strFile, True\r\n        If intFormat = Me.Format Then\r\n            ' Export the table using this format.\r\n            Select Case intFormat\r\n                Case etdTabDelimited\r\n                    ' Export in tab delimited text file\r\n                    ExportTableDataAsTDF m_Table.Name, strFile\r\n                Case etdXML\r\n                    ' Export data rows as XML (encoding default is UTF-8)\r\n                    VerifyPath strFile\r\n                    Perf.OperationStart \"App.ExportXML()\"\r\n                    If IsLocalTable(m_Table.Name) Then\r\n                        ' Embedded schema for local tables to support calculated fields\r\n                        Application.ExportXML acExportTable, m_Table.Name, strFile, , , , , acEmbedSchema\r\n                    Else\r\n                        ' Export data only for linked tables\r\n                        Application.ExportXML acExportTable, m_Table.Name, strFile\r\n                    End If\r\n                    Perf.OperationEnd\r\n                    With New clsSourceParser\r\n                        .LoadSourceFile strFile, IDbComponent_ComponentType\r\n                        WriteFile .Sanitize(ectXML), strFile\r\n                    End With\r\n            End Select\r\n        End If\r\n    Next intFormat\r\n\r\n    ' Trap any errors while exporting table data.\r\n    CatchAny eelError, \"Error exporting table data for \" & m_Table.Name, ModuleName(Me) & \".Export\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportTableData\r\n' Author    : Adam Waller\r\n' Date      : 1/18/2019\r\n' Purpose   : Export the data from the table.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ExportTableDataAsTDF(strTable As String, strFile As String)\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim fld As DAO.Field\r\n    Dim cData As New clsConcat\r\n    Dim intFields As Integer\r\n    Dim intCnt As Integer\r\n    Dim lngProgMax As Long\r\n    Dim lngProgValue As Long\r\n\r\n    Perf.OperationStart \"Export Table Data as TDF\"\r\n\r\n    ' Open table in fast read-only view\r\n    Set dbs = CurrentDb\r\n    Set rst = dbs.OpenRecordset(GetTableExportSql(strTable), dbOpenSnapshot, dbReadOnly)\r\n    intFields = rst.Fields.Count\r\n\r\n    ' Add header row\r\n    For Each fld In rst.Fields\r\n        cData.Add fld.Name\r\n        intCnt = intCnt + 1\r\n        If intCnt < intFields Then cData.Add vbTab\r\n    Next fld\r\n    cData.Add vbCrLf\r\n\r\n    ' Make sure we actually have records\r\n    If Not rst.EOF Then\r\n\r\n        ' Stash current progress bar values so we can reuse the\r\n        ' progress bar to show the progress through the rows.\r\n        With Log.ProgressBar\r\n            lngProgMax = .Max\r\n            lngProgValue = .Value\r\n            ' Set to recordset after accessing all records\r\n            rst.MoveLast\r\n            .Max = rst.RecordCount\r\n            .Value = 1\r\n            rst.MoveFirst\r\n        End With\r\n\r\n        ' Add data rows\r\n        Do While Not rst.EOF\r\n            intCnt = 0\r\n            For Each fld In rst.Fields\r\n                If IsBinaryType(fld) Then\r\n                    ' Binary data not supported\r\n                    cData.Add UNSUPPORTED_DATA_TYPE\r\n                Else\r\n                    ' Format for TDF format without line breaks\r\n                    cData.Add FormatStringForTDF(Nz(fld.Value))\r\n                End If\r\n                intCnt = intCnt + 1\r\n                If intCnt < intFields Then cData.Add vbTab\r\n            Next fld\r\n            cData.Add vbCrLf\r\n            rst.MoveNext\r\n            Log.Increment ' Increment log, in case this takes a while\r\n        Loop\r\n\r\n        ' Restore progress bar values for object iteration\r\n        With Log.ProgressBar\r\n             .Max = lngProgMax\r\n             .Value = lngProgValue\r\n        End With\r\n    End If\r\n\r\n    ' Close database objects\r\n    rst.Close\r\n    Set rst = Nothing\r\n\r\n    Perf.OperationEnd\r\n\r\n    ' Save output file\r\n    WriteFile cData.GetStr, strFile\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ImportTableDataTDF\r\n' Author    : Adam Waller\r\n' Date      : 5/7/2020\r\n' Purpose   : Imports the data from a TDF file, loading it into the table\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ImportTableDataTDF(strFile As String)\r\n\r\n    Dim strTable As String\r\n    Dim dCols As Dictionary\r\n    Dim fld As DAO.Field\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim stm As ADODB.Stream\r\n    Dim strLine As String\r\n    Dim varLine As Variant\r\n    Dim varHeader As Variant\r\n    Dim intCol As Integer\r\n    Dim strValue As String\r\n\r\n    Perf.OperationStart \"Import TDF Data\"\r\n\r\n    ' Build a dictionary of column names so we can load the data\r\n    ' into the matching columns.\r\n    strTable = GetObjectNameFromFileName(strFile)\r\n    Set dbs = CurrentDb\r\n    Set dCols = New Dictionary\r\n    For Each fld In dbs.TableDefs(strTable).Fields\r\n        dCols.Add fld.Name, fld.Name\r\n    Next fld\r\n\r\n    ' Clear any existing records before importing this data.\r\n    dbs.Execute \"delete from [\" & strTable & \"]\", dbFailOnError\r\n    Set rst = dbs.OpenRecordset(strTable)\r\n\r\n    ' Read file line by line\r\n    Set stm = New ADODB.Stream\r\n    With stm\r\n        .Charset = \"utf-8\"\r\n        .Open\r\n        .LoadFromFile strFile\r\n    End With\r\n\r\n    ' Loop through lines in file\r\n    Do While Not stm.EOS\r\n        strLine = stm.ReadText(adReadLine)\r\n        ' See if the header has already been parsed.\r\n        If Not IsArray(varHeader) Then\r\n            ' Skip past any UTF-8 BOM header\r\n            If Left$(strLine, 3) = UTF8_BOM Then strLine = Mid$(strLine, 4)\r\n            ' Read header line\r\n            varHeader = Split(strLine, vbTab)\r\n        Else\r\n            ' Data line\r\n            varLine = Split(strLine, vbTab)\r\n            rst.AddNew\r\n                ' Loop through fields\r\n                For intCol = 0 To UBound(varHeader)\r\n                    ' Check to see if field exists in the table\r\n                    If dCols.Exists(varHeader(intCol)) Then\r\n                        ' Check for empty string or null.\r\n                        If varLine(intCol) = vbNullString Then\r\n                            With rst.Fields(varHeader(intCol))\r\n                                If IsNull(.Value) Then\r\n                                    ' Could possibly hit a problem with the storage of\r\n                                    ' zero length strings instead of nulls. Since we can't\r\n                                    ' really differentiate between these in a TDF file,\r\n                                    ' we will do some additional probing...\r\n                                    If .AllowZeroLength And .Required Then\r\n                                        ' Use empty string instead of null value\r\n                                        .Value = vbNullString\r\n                                    End If\r\n                                End If\r\n                            End With\r\n                        Else\r\n                            ' Perform any needed replacements\r\n                            strValue = FormatStringFromTDF(CStr(varLine(intCol)))\r\n                            If strValue <> CStr(varLine(intCol)) Then\r\n                                ' Use replaced string value\r\n                                rst.Fields(varHeader(intCol)).Value = strValue\r\n                            Else\r\n                                If strValue <> UNSUPPORTED_DATA_TYPE Then\r\n                                    ' Use variant value without the string conversion\r\n                                    rst.Fields(varHeader(intCol)).Value = varLine(intCol)\r\n                                End If\r\n                            End If\r\n                        End If\r\n                    End If\r\n                Next intCol\r\n            rst.Update\r\n        End If\r\n        ' Increment log, just in case this takes a while.\r\n        Log.Increment\r\n    Loop\r\n    stm.Close\r\n    Set stm = Nothing\r\n    rst.Close\r\n    Set rst = Nothing\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FormatStringForTDF\r\n' Author    : Adam Waller\r\n' Date      : 7/16/2021\r\n' Purpose   : Replace line feeds and similar characters with escaped codes for\r\n'           : representation in tab-delimited format.\r\n'           : (Using Chr(26) as interim placeholder) See #251\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function FormatStringForTDF(strValue As String) As String\r\n    FormatStringForTDF = MultiReplace(strValue, _\r\n        \"\\\", Chr$(26), _\r\n        vbCrLf, \"\\r\\n\", _\r\n        vbCr, \"\\r\", _\r\n        vbLf, \"\\n\", _\r\n        vbTab, \"\\t\", _\r\n        Chr$(26), \"\\\\\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FormatStringFromTDF\r\n' Author    : Adam Waller\r\n' Date      : 7/16/2021\r\n' Purpose   : Restore original characters from escaped codes.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function FormatStringFromTDF(strTDFValue) As String\r\n    FormatStringFromTDF = MultiReplace(strTDFValue, _\r\n        \"\\\\\", Chr$(26), _\r\n        \"\\r\\n\", vbCrLf, _\r\n        \"\\r\", vbCr, _\r\n        \"\\n\", vbLf, _\r\n        \"\\t\", vbTab, _\r\n        Chr$(26), \"\\\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTableExportSql\r\n' Author    : Adam Waller\r\n' Date      : 1/18/2019\r\n' Purpose   : Build SQL to export `tbl_name` sorted by each field from first to last\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetTableExportSql(strTable As String) As String\r\n\r\n    Dim tdf As DAO.TableDef\r\n    Dim fld As DAO.Field\r\n    Dim intFields As Integer\r\n    Dim cText As New clsConcat\r\n    Dim cFieldList As New clsConcat\r\n    Dim cSortList As New clsConcat\r\n    Dim dbs As Database\r\n\r\n    Set dbs = CurrentDb\r\n    Set tdf = dbs.TableDefs(strTable)\r\n    intFields = tdf.Fields.Count\r\n\r\n    ' Build list of fields\r\n    With cFieldList\r\n        For Each fld In tdf.Fields\r\n            .Add \"[\", fld.Name, \"], \"\r\n            If Not IsBinaryType(fld) Then cSortList.Add \"[\", fld.Name, \"], \"\r\n        Next fld\r\n    End With\r\n\r\n    ' Remove any trailing commas\r\n    cFieldList.Remove 2\r\n    cSortList.Remove 2\r\n\r\n    ' Build select statement\r\n    With cText\r\n        .Add \"SELECT \", cFieldList.GetStr\r\n        .Add \" FROM [\", strTable, \"] ORDER BY \"\r\n        .Add cSortList.GetStr\r\n    End With\r\n\r\n    GetTableExportSql = cText.GetStr\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller, Florian Jenn\r\n' Date      : 4/23/2020, 2020-10-26\r\n' Purpose   : Import the table data from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim blnUseTemp As Boolean\r\n    Dim strTempFile As String\r\n    Dim strTable As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Make sure table exists before importing data to it.\r\n    strTable = GetObjectNameFromFileName(strFile)\r\n\r\n    ' Only allow import into local tables (not linked tables)\r\n    If IsLocalTable(strTable) Then\r\n\r\n        ' Import from different formats (XML is preferred for data integrity)\r\n        Select Case GetFormatByExt(strFile)\r\n            Case etdXML\r\n\r\n                    ' The ImportXML function does not properly handle UrlEncoded paths\r\n                    blnUseTemp = (InStr(1, strFile, \"%\") > 0)\r\n                    If blnUseTemp Then\r\n                        ' Import from (safe) temporary file name.\r\n                        strTempFile = GetTempFile\r\n                        FSO.CopyFile strFile, strTempFile\r\n                        Application.ImportXML strTempFile, acAppendData\r\n                        DeleteFile strTempFile\r\n                    Else\r\n                        Application.ImportXML strFile, acAppendData\r\n                    End If\r\n\r\n                    ' Trap any XML import errors\r\n                    CatchAny eelError, \"Error importing XML data from '\" & strFile & \"'\", ModuleName(Me) & \".Import\"\r\n\r\n            Case etdTabDelimited\r\n                ImportTableDataTDF strFile\r\n        End Select\r\n\r\n    Else\r\n        ' Either not a local table, or it doesn't exist\r\n        If TableExists(strTable) Then\r\n            Log.Error eelWarning, \"Table data may only be imported into local tables\", \"clsDbTableData.Import\"\r\n        Else\r\n            ' Warn user that table does not exist.\r\n            Log.Error eelError, \"Table structure not found for '\" & strTable & \"'.\", \"clsDbTableData.Import\"\r\n            Log.Add \"Table definition does not exist for '\" & strTable & _\r\n                \"'. This must be created before importing table data.\", False\r\n        End If\r\n        Log.Add \"Table data from '\" & strTable & \"' was not imported.\", False\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    Log.Error eelWarning, \"Merge not supported for table data. (\" & _\r\n        GetObjectNameFromFileName(strFile) & \")\", \"clsDbTableData.Merge\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim tbl As AccessObject\r\n    Dim cTable As clsDbTableData\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n\r\n        ' No need to go any further if we don't have any saved tables defined\r\n        If Options.TablesToExportData.Count > 0 Then\r\n\r\n            ' We have at least one table defined. Loop through the tables looking\r\n            ' for a matching name.\r\n            With Options\r\n                For Each tbl In CurrentData.AllTables\r\n                    If .TablesToExportData.Exists(tbl.Name) Then\r\n                        Set cTable = New clsDbTableData\r\n                        cTable.Format = .GetTableExportFormat(CStr(.TablesToExportData(tbl.Name)(\"Format\")))\r\n                        Set cTable.Parent.DbObject = tbl\r\n                        blnAdd = True\r\n                        If blnModifiedOnly Then blnAdd = cTable.IDbComponent_IsModified\r\n                        If blnAdd Then m_Items(blnModifiedOnly).Add cTable.Parent.SourceFile, cTable\r\n                    End If\r\n                Next tbl\r\n            End With\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type. (Could be\r\n'           : a couple different file extensions involved.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then\r\n        Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.\" & GetExtByFormat(etdTabDelimited))\r\n        MergeDictionary m_FileList, GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.\" & GetExtByFormat(etdXML))\r\n    End If\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsBinaryType\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Returns true if the field type uses binary content. (Export/import on\r\n'           : binary data through XML or text is not well supported.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IsBinaryType(fld As DAO.Field) As Boolean\r\n    Select Case fld.Type\r\n        Case dbLongBinary, dbVarBinary, dbAttachment: IsBinaryType = True\r\n        Case Else: IsBinaryType = False\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetExtByFormat\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the expected file extension by format.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetExtByFormat(intFormat As eTableDataExportFormat) As String\r\n    Select Case intFormat\r\n        Case etdTabDelimited:   GetExtByFormat = \"txt\"\r\n        Case etdXML:            GetExtByFormat = \"xml\"\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFormatByExt\r\n' Author    : Adam Waller\r\n' Date      : 5/7/2020\r\n' Purpose   : Look up the format from the extension name\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetFormatByExt(strFile As String) As eTableDataExportFormat\r\n    Select Case FSO.GetExtensionName(strFile)\r\n        Case \"txt\": GetFormatByExt = etdTabDelimited\r\n        Case \"xml\": GetFormatByExt = etdXML\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    ' It's not really worth it to try to determine if the table data has been changed.\r\n    ' We would have to fully export anyway just to check, so let's always export for\r\n    ' table data. (This is handled different from just about every other object type.)\r\n    IDbComponent_IsModified = True\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    ' We cannot determine when *records* were modified in a table.\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Table Data\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"tables\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"txt\"\r\n    IDbComponent_FileExtensions.Add \"tdf\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Table Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Table.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Table Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Table.Name) & \".\" & GetExtByFormat(Me.Format)\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    ' In most cases very few tables will be involved here\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbTableData\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Table\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Table = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbTableDataMacro.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbTableDataMacro\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Table As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n\r\n    Dim strHash As String\r\n\r\n    ' Make sure the table exists, and is accessible\r\n    If Not TableExists(m_Table.Name) Then\r\n        Log.Error eelError, \"Unable to access table \" & m_Table.Name & _\r\n            \". Please ensure that this table exists and is accessible in the current database.\", _\r\n            ModuleName(Me) & \".Export\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' We THINK that this table probably contains a table macro, but just in case it\r\n    ' doesn't, we have some error handling built into the export function below.\r\n\r\n    ' Save structure in XML format\r\n    strHash = SaveComponentAsText(acTableDataMacro, m_Table.Name, Nz2(strAlternatePath, IDbComponent_SourceFile))\r\n\r\n    ' Save index if we found an item to export\r\n    If strHash <> vbNullString Then\r\n        VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), strHash\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim blnImportCheck As Boolean\r\n    Dim strName As String\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.xml\" Then Exit Sub\r\n\r\n    strName = GetObjectNameFromFileName(strFile)\r\n    blnImportCheck = LoadComponentFromText(acTableDataMacro, strName, strFile)\r\n    If Not blnImportCheck Or Log.ErrorLevel = eelCritical Then Exit Sub\r\n    ' Update index\r\n    Set m_Table = CurrentData.AllTables(strName)\r\n    VCSIndex.Update Me, eatImport, GetFileHash(strFile)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    Dim strTemp As String\r\n\r\n    If FSO.FileExists(strFile) Then\r\n        ' Load the table data macro into the existing table, overwriting any existing macro\r\n        IDbComponent_Import strFile\r\n    Else\r\n        ' Create and load a blank file to clear any existing table data macros.\r\n        ' (Tested in Access 2010 with no errors on 6/15/2022)\r\n        strTemp = GetTempFile   ' Create an empty temporary file.\r\n        LoadFromText acTableDataMacro, GetObjectNameFromFileName(strFile), strTemp\r\n        DeleteFile strTemp\r\n\r\n        ' Remove entry from index\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'           : Credit to StackOverflow for the clue on how to determine if a table has\r\n'           : a data macro. https://stackoverflow.com/questions/31755802/\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim dbs As Database\r\n    Dim tdf As TableDef\r\n    Dim cTable As IDbComponent\r\n    Dim strSql As String\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        Set dbs = CurrentDb\r\n        For Each tdf In dbs.TableDefs\r\n            ' Skip system, temp, and linked tables\r\n            If Left$(tdf.Name, 4) <> \"MSys\" Then\r\n                If Left$(tdf.Name, 1) <> \"~\" Then\r\n                    If Len(tdf.Connect) = 0 Then\r\n                        ' Check to see if the table has a data macro\r\n                        strSql = \"(LvExtra is not null) and (Type = 1) and ([Name] = \"\"\" & tdf.Name & \"\"\")\"\r\n                        If DCount(\"[Name]\", \"MSysObjects\", strSql) > 0 Then\r\n                            Set cTable = New clsDbTableDataMacro\r\n                            Set cTable.DbObject = CurrentData.AllTables(tdf.Name)\r\n                            blnAdd = True\r\n                            If blnModifiedOnly Then blnAdd = cTable.IsModified\r\n                            If blnAdd Then m_Items(blnModifiedOnly).Add cTable.SourceFile, cTable\r\n                        End If\r\n                    End If\r\n                End If\r\n            End If\r\n        Next tdf\r\n\r\n        Set tdf = Nothing\r\n        Set dbs = Nothing\r\n\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.xml\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    ' A table modification doesn't always mean the macro was modified, but we will\r\n    ' export on any table modification, just to be on the safe side.\r\n    IDbComponent_IsModified = (m_Table.DateModified > VCSIndex.Item(Me).ExportDate)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_Table Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = m_Table.DateModified\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Table Data Macros\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"tdmacros\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"xml\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Table Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Table.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Table Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Table.Name) & \".xml\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbTableDataMacro\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Table\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Table = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbTableDef.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbTableDef\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Table As AccessObject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n\r\n    Dim strFile As String\r\n    Dim dbs As Database\r\n    Dim tdf As DAO.TableDef\r\n    Dim idx As DAO.Index\r\n    Dim dItem As Dictionary\r\n    Dim strHash As String\r\n    Dim strContent As String\r\n\r\n    ' Make sure the table exists, and is accessible\r\n    If Not TableExists(m_Table.Name) Then\r\n        Log.Error eelError, \"Unable to access table \" & m_Table.Name & _\r\n            \". Please ensure that this table exists and is accessible in the current database.\", _\r\n            ModuleName(Me) & \".Export\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Get the export file name\r\n    strFile = Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n\r\n    ' For internal tables, we can export them as XML.\r\n    If Not IsLinkedTable Then\r\n\r\n        ' Save structure in XML format\r\n        VerifyPath strFile\r\n        Perf.OperationStart \"App.ExportXML()\"\r\n        ' Note that the additional properties are important to accurately reconstruct the table.\r\n        Application.ExportXML acExportTable, m_Table.Name, , strFile, , , , acExportAllTableAndFieldProperties\r\n        Perf.OperationEnd\r\n\r\n        ' Rewrite sanitized XML as formatted UTF-8 content\r\n        With New clsSourceParser\r\n            .LoadSourceFile strFile, IDbComponent_ComponentType\r\n            DeleteFile strFile\r\n            WriteFile .Sanitize(ectXML), strFile\r\n            strHash = .Hash\r\n        End With\r\n\r\n    Else\r\n        ' Linked table - Save as JSON\r\n        Set dbs = CurrentDb\r\n        Set tdf = dbs.TableDefs(m_Table.Name)\r\n\r\n        Set dItem = New Dictionary\r\n        With dItem\r\n            .Add \"Name\", m_Table.Name\r\n            .Add \"Connect\", SanitizeConnectionString(tdf.Connect)\r\n            .Add \"SourceTableName\", tdf.SourceTableName\r\n            .Add \"Attributes\", tdf.Attributes\r\n            ' indexes (Find primary key)\r\n            If IndexAvailable(tdf) Then\r\n                For Each idx In tdf.Indexes\r\n                    If idx.Primary Then\r\n                        ' Add the primary key columns, using brackets just in case the field names have spaces.\r\n                        .Add \"PrimaryKey\", \"[\" & MultiReplace(CStr(idx.Fields), \"+\", vbNullString, \";\", \"], [\") & \"]\"\r\n                        Exit For\r\n                    End If\r\n                Next idx\r\n            End If\r\n        End With\r\n\r\n        ' Write export file.\r\n        strContent = BuildJsonFile(TypeName(Me), dItem, \"Linked Table\")\r\n        strHash = GetStringHash(strContent, True)\r\n        WriteFile strContent, strFile\r\n\r\n    End If\r\n\r\n    ' Additional processing when exporting to source folder\r\n    If strAlternatePath = vbNullString Then\r\n\r\n        ' Remove any alternate source file in case we have switched formats\r\n        RemoveAlternateFormatSourceFile\r\n\r\n        ' Optionally save in SQL format\r\n        strFile = IDbComponent_BaseFolder & GetSafeFileName(m_Table.Name) & \".sql\"\r\n        If Options.SaveTableSQL And (Not IsLinkedTable) Then\r\n            Log.Add \"  \" & m_Table.Name & \" (SQL)\", Options.ShowDebug\r\n            SaveTableSqlDef m_Table.Name, IDbComponent_BaseFolder\r\n        Else\r\n            ' Remove file if it exists\r\n            If FSO.FileExists(strFile) Then DeleteFile strFile\r\n        End If\r\n    End If\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), strHash\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim blnUseTemp As Boolean\r\n    Dim strTempFile As String\r\n    Dim strName As String\r\n\r\n    ' Determine import type from extension\r\n    Select Case LCase$(FSO.GetExtensionName(strFile))\r\n\r\n        Case \"json\"\r\n            If Not ImportLinkedTable(strFile) Then Exit Sub\r\n\r\n        Case \"xml\"\r\n            ' Watch out for errors when importing XML\r\n            LogUnhandledErrors\r\n            On Error Resume Next\r\n\r\n            ' The ImportXML function does not properly handle UrlEncoded paths\r\n            blnUseTemp = (InStr(1, strFile, \"%\") > 0)\r\n            If blnUseTemp Then\r\n                ' Import from (safe) temporary file name.\r\n                strTempFile = GetTempFile\r\n                FSO.CopyFile strFile, strTempFile\r\n                Application.ImportXML strTempFile, acStructureOnly\r\n                DeleteFile strTempFile\r\n            Else\r\n                Application.ImportXML strFile, acStructureOnly\r\n            End If\r\n\r\n            ' Importing XML can throw an error if ActiveX security is too restrictive.\r\n            If Catch(31521) Then\r\n                Log.Error eelError, \"ActiveX security settings blocked the import of \" & _\r\n                \"table definition XML file: \" & FSO.GetFileName(strFile) & \".\", ModuleName(Me) & \".Import\"\r\n                Log.Add \"Importing XML files requires ActiveX to be enabled.\"\r\n            Else\r\n                ' Log any other error.\r\n                CatchAny eelError, \"Unable to import XML for \" & FSO.GetFileName(strFile), ModuleName(Me) & \".Import\"\r\n            End If\r\n\r\n        Case Else\r\n            ' Unsupported file\r\n            Exit Sub\r\n\r\n    End Select\r\n\r\n    ' Update index\r\n    strName = GetObjectNameFromFileName(strFile)\r\n    Set m_Table = CurrentData.AllTables(strName)\r\n    VCSIndex.Update Me, eatImport, GetFileHash(strFile)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    Dim strName As String\r\n    Dim dRelations As Dictionary\r\n\r\n    ' Stage any affected relationships\r\n    strName = GetObjectNameFromFileName(strFile)\r\n    Set dRelations = StageRelations(strName)\r\n\r\n    ' Delete any existing table with this name\r\n    Set m_Table = Nothing\r\n    DeleteObjectIfExists acTable, strName\r\n\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n        ' Restore any affected relationships\r\n        RestoreRelations dRelations\r\n    Else\r\n        ' If the table is deleted, discard any staged relationships\r\n        ' since we can't restore them when the table does not exist.\r\n\r\n        ' Also remove table def from index.\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StageRelations\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2023\r\n' Purpose   : Return a dictionary of the database relationships involving this table.\r\n'           : Each relation is represented by a string value of the JSON source file\r\n'           : content for that relation. (This way we can release the object reference\r\n'           : and delete the actual relation object, but still recreate it later.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function StageRelations(strTableName As String) As Dictionary\r\n\r\n    Dim dbs As Database\r\n    Dim rel As Relation\r\n    Dim cRel As IDbComponent\r\n    Dim strTempFile As String\r\n    Dim varKey As Variant\r\n\r\n    Set StageRelations = New Dictionary\r\n\r\n    ' Loop through relations, looking for matching table name\r\n    Set dbs = CurrentDb\r\n    For Each rel In dbs.Relations\r\n        If StrComp(rel.Table, strTableName, vbTextCompare) = 0 Or _\r\n            StrComp(rel.ForeignTable, strTableName, vbTextCompare) = 0 Then\r\n            If strTempFile = vbNullString Then\r\n                ' Create temp file for export\r\n                strTempFile = GetTempFile\r\n                Name strTempFile As strTempFile & \".json\"\r\n                strTempFile = strTempFile & \".json\"\r\n            End If\r\n            ' Set up DB component class targeting this object\r\n            Set cRel = New clsDbRelation\r\n            Set cRel.DbObject = rel\r\n            cRel.Export strTempFile\r\n            ' Add file content to dictionary\r\n            StageRelations.Add rel.Name, ReadFile(strTempFile)\r\n        End If\r\n    Next rel\r\n\r\n    ' Remove the existing relationships affecting this table\r\n    For Each varKey In StageRelations.Keys\r\n        dbs.Relations.Delete varKey\r\n    Next varKey\r\n\r\n    ' Remove any temp file\r\n    If FSO.FileExists(strTempFile) Then DeleteFile strTempFile\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RestoreRelations\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2023\r\n' Purpose   : Restore any staged relationships.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RestoreRelations(dRelations As Dictionary)\r\n\r\n    Dim varKey As Variant\r\n    Dim strTempFile As String\r\n\r\n    If dRelations.Count > 0 Then\r\n        strTempFile = GetTempFile\r\n        ' Add .json file extension\r\n        Name strTempFile As strTempFile & \".json\"\r\n        strTempFile = strTempFile & \".json\"\r\n        ' Loop through relations, importing back to database from the temp file.\r\n        For Each varKey In dRelations.Keys\r\n            WriteFile dRelations.Item(varKey), strTempFile\r\n            With New clsDbRelation\r\n                .Parent.Import strTempFile\r\n            End With\r\n        Next varKey\r\n        DeleteFile strTempFile\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveAlternateFormatSourceFile\r\n' Author    : Adam Waller\r\n' Date      : 1/25/2022\r\n' Purpose   : Remove any existing alternate format source file. (When switching a table\r\n'           : from local to linked, or vice versa.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RemoveAlternateFormatSourceFile()\r\n    Dim strAltFile As String\r\n    strAltFile = IDbComponent_BaseFolder & GetSourceFileName(m_Table.Name, Not IsLinkedTable)\r\n    If FSO.FileExists(strAltFile) Then DeleteFile strAltFile\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveTableSqlDef\r\n' Author    : Adam Waller\r\n' Date      : 1/28/2019\r\n' Purpose   : Save a version of the table formatted as a SQL statement.\r\n'           : (Makes it easier to see table changes in version control systems.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SaveTableSqlDef(strTable As String, strFolder As String)\r\n\r\n    Dim cData As New clsConcat\r\n    Dim cAttr As New clsConcat\r\n    Dim idx As DAO.Index\r\n    Dim fld As DAO.Field\r\n    Dim strFile As String\r\n    Dim dbs As DAO.Database\r\n    Dim tdf As DAO.TableDef\r\n\r\n    Perf.OperationStart \"Save Table SQL\"\r\n\r\n    Set dbs = CurrentDb\r\n    Set tdf = dbs.TableDefs(strTable)\r\n\r\n    With cData\r\n        .Add \"CREATE TABLE [\", DblQ(strTable), \"] (\", vbCrLf\r\n\r\n        ' Loop through fields\r\n        For Each fld In tdf.Fields\r\n            .Add \"  [\", DblQ(fld.Name), \"] \"\r\n            If (fld.Attributes And dbAutoIncrField) Then\r\n                .Add \"AUTOINCREMENT\"\r\n            Else\r\n                .Add GetTypeString(fld.Type)\r\n            End If\r\n            Select Case fld.Type\r\n                Case dbText, dbVarBinary\r\n                    .Add \" (\", fld.Size, \")\"\r\n            End Select\r\n\r\n            ' Indexes\r\n            For Each idx In tdf.Indexes\r\n                Set cAttr = New clsConcat\r\n                If idx.Fields.Count = 1 And idx.Fields(0).Name = fld.Name Then\r\n                    If idx.Primary Then cAttr.Add \" PRIMARY KEY\"\r\n                    If idx.Unique Then cAttr.Add \" UNIQUE\"\r\n                    If idx.Required Then cAttr.Add \" NOT NULL\"\r\n                    If idx.Foreign Then AddFieldReferences dbs, idx.Fields, strTable, cAttr\r\n                    If Len(cAttr.GetStr) > 0 Then .Add \" CONSTRAINT [\", DblQ(idx.Name), \"]\"\r\n                End If\r\n                .Add cAttr.GetStr\r\n            Next\r\n            .Add \",\", vbCrLf\r\n        Next fld\r\n        .Remove 3   ' strip off last comma and crlf\r\n\r\n        ' Constraints\r\n        If IndexAvailable(tdf) Then\r\n            Set cAttr = New clsConcat\r\n            For Each idx In tdf.Indexes\r\n                If idx.Fields.Count > 1 Then\r\n                    If Len(cAttr.GetStr) = 0 Then cAttr.Add \" CONSTRAINT \"\r\n                    If idx.Primary Then\r\n                        cAttr.Add \"[\", DblQ(idx.Name), \"] PRIMARY KEY (\"\r\n                        For Each fld In idx.Fields\r\n                            cAttr.Add \"[\", DblQ(fld.Name), \"], \"\r\n                        Next fld\r\n                        cAttr.Remove 2\r\n                        cAttr.Add \")\"\r\n                    End If\r\n                    If Not idx.Foreign Then\r\n                        If Len(cAttr.GetStr) > 0 Then\r\n                            .Add \",\", vbCrLf\r\n                            .Add \"  \", cAttr.GetStr\r\n                            AddFieldReferences dbs, idx.Fields, strTable, cData\r\n                        End If\r\n                    End If\r\n                End If\r\n            Next idx\r\n        End If\r\n        .Add vbCrLf, \")\"\r\n\r\n        ' Build file name and create file.\r\n        strFile = strFolder & GetSafeFileName(strTable) & \".sql\"\r\n        WriteFile .GetStr, strFile\r\n        Perf.OperationEnd\r\n\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddFieldReferences\r\n' Author    : Adam Waller\r\n' Date      : 1/18/2019\r\n' Purpose   : Add references to other fields in table definition.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub AddFieldReferences(dbs As Database, fld As Object, strTable As String, cData As clsConcat)\r\n\r\n    Dim rel As DAO.Relation\r\n    Dim fld2 As DAO.Field\r\n\r\n    For Each rel In dbs.Relations\r\n        If (rel.ForeignTable = strTable) Then\r\n            If FieldsIdentical(fld, rel.Fields) Then\r\n\r\n                ' References\r\n                cData.Add \" REFERENCES [\", DblQ(rel.Table), \"] (\"\r\n                For Each fld2 In rel.Fields\r\n                    cData.Add \"[\", DblQ(fld2.Name), \"],\"\r\n                Next fld2\r\n                ' Remove trailing comma\r\n                If rel.Fields.Count > 0 Then cData.Remove 1\r\n                cData.Add \")\"\r\n\r\n                ' Attributes for cascade update or delete\r\n                If rel.Attributes And dbRelationUpdateCascade Then cData.Add \" ON UPDATE CASCADE \"\r\n                If rel.Attributes And dbRelationDeleteCascade Then cData.Add \" ON DELETE CASCADE \"\r\n\r\n                ' Exit now that we have found the matching relationship.\r\n                Exit For\r\n\r\n            End If\r\n        End If\r\n    Next rel\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FieldsIdentical\r\n' Author    : Adam Waller\r\n' Date      : 1/21/2019\r\n' Purpose   : Return true if the two collections of fields have the same field names.\r\n'           : (Even if the order of the fields is different.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function FieldsIdentical(oFields1 As Object, oFields2 As Object) As Boolean\r\n\r\n    Dim fld As Object\r\n    Dim fld2 As Object\r\n    Dim blnMismatch As Boolean\r\n    Dim blnFound As Boolean\r\n\r\n    If oFields1.Count <> oFields2.Count Then\r\n        blnMismatch = True\r\n    Else\r\n        ' Set this flag to false after going through each field.\r\n        For Each fld In oFields1\r\n            blnFound = False\r\n            For Each fld2 In oFields2\r\n                If fld.Name = fld2.Name Then\r\n                    blnFound = True\r\n                    Exit For\r\n                End If\r\n            Next fld2\r\n            If Not blnFound Then\r\n                blnMismatch = True\r\n                Exit For\r\n            End If\r\n        Next\r\n    End If\r\n\r\n    ' Return result\r\n    FieldsIdentical = Not blnMismatch\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTypeString\r\n' Author    : Adam Waller\r\n' Date      : 1/18/2019\r\n' Purpose   : Get the type string used by Access SQL\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetTypeString(intType As DAO.DataTypeEnum) As String\r\n    Select Case intType\r\n        Case dbLongBinary:      GetTypeString = \"LONGBINARY\"\r\n        Case dbBinary:          GetTypeString = \"BINARY\"\r\n        Case dbBoolean:         GetTypeString = \"BIT\"\r\n        Case dbAutoIncrField:   GetTypeString = \"COUNTER\"\r\n        Case dbCurrency:        GetTypeString = \"CURRENCY\"\r\n        Case dbDate, dbTime:    GetTypeString = \"DATETIME\"\r\n        Case dbGUID:            GetTypeString = \"GUID\"\r\n        Case dbMemo:            GetTypeString = \"LONGTEXT\"\r\n        Case dbDouble:          GetTypeString = \"DOUBLE\"\r\n        Case dbSingle:          GetTypeString = \"SINGLE\"\r\n        Case dbByte:            GetTypeString = \"BYTE\"\r\n        Case dbInteger:         GetTypeString = \"SHORT\"\r\n        Case dbLong:            GetTypeString = \"LONG\"\r\n        Case dbNumeric:         GetTypeString = \"NUMERIC\"\r\n        Case dbText:            GetTypeString = \"VARCHAR\"\r\n        Case Else:              GetTypeString = \"VARCHAR\"\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IndexAvailable\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return true if the index collection is avilable. Without the error handling\r\n'           : this may throw an error if a linked table is not accessible during export.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IndexAvailable(tdf As TableDef) As Boolean\r\n\r\n    Dim lngTest As Long\r\n\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    lngTest = tdf.Indexes.Count\r\n    If Err Then\r\n        Err.Clear\r\n    Else\r\n        IndexAvailable = True\r\n    End If\r\n    CatchAny eelNoError, vbNullString, , False\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ImportLinkedTable\r\n' Author    : Adam Waller\r\n' Date      : 5/6/2020\r\n' Purpose   : Recreate a linked table from the JSON source file.\r\n'           : Returns true if successful.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ImportLinkedTable(strFile As String) As Boolean\r\n\r\n    Dim dTable As Dictionary\r\n    Dim dItem As Dictionary\r\n    Dim dbs As DAO.Database\r\n    Dim tdf As DAO.TableDef\r\n    Dim strSql As String\r\n    Dim strConnect As String\r\n\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    ' Read json file\r\n    Set dTable = ReadJsonFile(strFile)\r\n    If Not dTable Is Nothing Then\r\n\r\n        ' Link the table\r\n        Set dItem = dTable(\"Items\")\r\n        Set dbs = CurrentDb\r\n        Set tdf = dbs.CreateTableDef(dItem(\"Name\"))\r\n        strConnect = GetFullConnect(dItem(\"Connect\"))\r\n        With tdf\r\n            .Connect = strConnect\r\n            .SourceTableName = dItem(\"SourceTableName\")\r\n            .Attributes = SafeAttributes(dItem(\"Attributes\"))\r\n        End With\r\n        dbs.TableDefs.Append tdf\r\n        If Catch(3011, 3024, 3044) Then\r\n            Log.Error eelError, \"Could not link table '\" & dItem(\"SourceTableName\") & \"'\", _\r\n            ModuleName(Me) & \".ImportLinkedTable\"\r\n            Log.Add \"Linked table object not found in \" & strFile, False\r\n            Log.Add \"Connection String: \" & strConnect, False\r\n        ElseIf CatchAny(eelError, vbNullString, ModuleName(Me) & \".ImportLinkedTable\") Then\r\n            ' May have encountered other issue like a missing link specification.\r\n        Else\r\n            ' Verify that the connection matches the source file. (Issue #192)\r\n            If tdf.Connect <> strConnect Then\r\n                tdf.Connect = strConnect\r\n                tdf.RefreshLink\r\n                If Catch(3283) Then Log.Add \"Encountered error 3283 when refreshing link for \" & tdf.Name & \". \" & _\r\n                    \"You can probably safely ignore this error. (See issue #484)\"\r\n                CatchAny eelError, \"Error refreshing link for \" & tdf.Name, ModuleName(Me) & \".ImportLinkedTable\"\r\n            End If\r\n            dbs.TableDefs.Refresh\r\n\r\n            ' Set index on linked table.\r\n            If InStr(1, tdf.Connect, \";DATABASE=\", vbTextCompare) = 1 Then\r\n                ' Can't create a key on a linked Access database table.\r\n                ' Presumably this would use the Access index instead of needing the pseudo index\r\n            Else\r\n                ' Check for a primary key index (Linked SQL tables may bring over the index, but linked views won't.)\r\n                If dItem.Exists(\"PrimaryKey\") And Not HasUniqueIndex(tdf) Then\r\n                    ' Create a pseudo index on the linked table\r\n                    strSql = \"CREATE UNIQUE INDEX __uniqueindex ON [\" & DblQ(tdf.Name) & \"] (\" & dItem(\"PrimaryKey\") & \") WITH PRIMARY\"\r\n                    dbs.Execute strSql, dbFailOnError\r\n                    dbs.TableDefs.Refresh\r\n                End If\r\n            End If\r\n            ImportLinkedTable = (Err.Number = 0)\r\n        End If\r\n    End If\r\n\r\n    ' Report any unhandled errors\r\n    CatchAny eelError, \"Error importing \" & strFile, \".ImportLinkedTable\"\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SafeAttributes\r\n' Author    : Adam Waller\r\n' Date      : 6/29/2020\r\n' Purpose   : Rebuild new attributes flag using attributes that we can actually set.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SafeAttributes(lngAttributes As Long) As Long\r\n\r\n    Dim colAtts As Collection\r\n    Dim varAtt As Variant\r\n    Dim lngNew As Long\r\n\r\n    Set colAtts = New Collection\r\n    With colAtts\r\n        '.Add dbAttachedODBC\r\n        '.Add dbAttachedTable\r\n        .Add dbAttachExclusive\r\n        .Add dbAttachSavePWD\r\n        .Add dbHiddenObject\r\n        .Add dbSystemObject\r\n    End With\r\n\r\n    For Each varAtt In colAtts\r\n        ' Use boolean logic to check for bit flag\r\n        If CBool((lngAttributes And varAtt) = varAtt) Then\r\n            ' Add to our rebuilt flag value.\r\n            lngNew = lngNew + varAtt\r\n        End If\r\n    Next varAtt\r\n\r\n    ' Return attributes value after rebuilding from scratch.\r\n    SafeAttributes = lngNew\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasUniqueIndex\r\n' Author    : Adam Waller\r\n' Date      : 2/22/2021\r\n' Purpose   :\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function HasUniqueIndex(tdf As TableDef) As Boolean\r\n\r\n    Dim idx As DAO.Index\r\n\r\n    If IndexAvailable(tdf) Then\r\n        For Each idx In tdf.Indexes\r\n            If idx.Unique Then\r\n                HasUniqueIndex = True\r\n                Exit For\r\n            End If\r\n        Next idx\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\n    MoveFileIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile) & \".sql\", strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim tdf As AccessObject\r\n    Dim cTable As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each tdf In CurrentData.AllTables\r\n            If tdf.Name Like \"MSys*\" Or tdf.Name Like \"~*\" Then\r\n                ' Skip system and temporary tables\r\n            Else\r\n                Set cTable = New clsDbTableDef\r\n                Set cTable.DbObject = tdf\r\n                blnAdd = True\r\n                If blnModifiedOnly Then blnAdd = cTable.IsModified\r\n                If blnAdd Then m_Items(blnModifiedOnly).Add cTable.SourceFile, cTable\r\n            End If\r\n        Next tdf\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then\r\n        Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.xml\")\r\n        MergeDictionary m_FileList, GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.json\")\r\n    End If\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = (m_Table.DateModified > VCSIndex.Item(Me).ExportDate)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    If m_Table Is Nothing Then Exit Function\r\n    IDbComponent_DateModified = m_Table.DateModified\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Tables\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"tbldefs\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"xml\"\r\n    IDbComponent_FileExtensions.Add \"json\"\r\n    IDbComponent_FileExtensions.Add \"sql\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Table Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Table.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Table Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSourceFileName(m_Table.Name, IsLinkedTable)\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsLinkedTable\r\n' Author    : Adam Waller\r\n' Date      : 2/12/2022\r\n' Purpose   : Returns true if this table is a linked table\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IsLinkedTable() As Boolean\r\n\r\n    ' Cache the value to save lookups\r\n    Static lngType As Long\r\n\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n\r\n    ' Nothing to check if we don't have a table name\r\n    If m_Table Is Nothing Then Exit Property\r\n\r\n    ' Use cached value if possible\r\n    If lngType > 0 Then\r\n        IsLinkedTable = (lngType <> 1)\r\n        Exit Property\r\n    End If\r\n\r\n    Perf.OperationStart \"Check for linked table\"\r\n\r\n    ' This is the simple way to check for a linked table, but it is not very performant\r\n    ' when called hundreds of times while scanning tables for changes.\r\n    'IsLinkedTable = CurrentDb.TableDefs(m_Table.Name).Connect <> vbNullString\r\n\r\n    ' A faster way is to read the object type from MSysObjects\r\n    Set dbs = CurrentDb\r\n    Set rst = dbs.OpenRecordset( _\r\n        \"select Type from MSysObjects\" & _\r\n        \" where name =\"\"\" & m_Table.Name & \"\"\"\" & _\r\n        \" and Type in(1,4,6)\", _\r\n        dbOpenDynaset, dbReadOnly)\r\n    ' Read the table type from the system objects record.\r\n    lngType = Nz(rst!Type, 1)\r\n    rst.Close\r\n    Set rst = Nothing\r\n    Set dbs = Nothing\r\n\r\n    ' A value of 1 means a local Access table. Any other value\r\n    ' is almost certainly a linked table of some type. (ODBC, Excel, etc)\r\n    IsLinkedTable = (lngType <> 1)\r\n    Perf.OperationEnd\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSourceFileName\r\n' Author    : Adam Waller\r\n' Date      : 1/25/2022\r\n' Purpose   : Return a safe file name with the correct file extension.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSourceFileName(strName As String, blnLinkedTable As Boolean) As String\r\n    If blnLinkedTable Then\r\n        GetSourceFileName = GetSafeFileName(strName) & \".json\"\r\n    Else\r\n        GetSourceFileName = GetSafeFileName(strName) & \".xml\"\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    Static lngCount As Long\r\n    If lngCount = 0 Then lngCount = CurrentData.AllTables.Count - 1\r\n    IDbComponent_QuickCount = lngCount + 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbTableDef\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Table\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Table = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbTheme.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbTheme\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' File details used for exporting/importing\r\nPrivate m_Name As String\r\nPrivate m_FileName As String\r\nPrivate m_Extension As String\r\nPrivate m_FileData() As Byte\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the theme file as either a zipped thmx file, or an extracted\r\n'           : folder with the theme source files. (Depending on the specified options.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n\r\n    Dim strFile As String\r\n    Dim strZip As String\r\n    Dim strFolder As String\r\n    Dim dbs As DAO.Database\r\n    Dim rst As Recordset2\r\n    Dim rstAtc As Recordset2\r\n    Dim strSql As String\r\n    Dim strHash As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Query theme file details\r\n    strSql = \"SELECT [Data] FROM MSysResources WHERE [Name]='\" & m_Name & \"' AND Extension='\" & m_Extension & \"'\"\r\n    Set dbs = CurrentDb\r\n    Set rst = dbs.OpenRecordset(strSql, dbOpenSnapshot, dbOpenForwardOnly)\r\n\r\n    ' If we get multiple records back we don't know which to use\r\n    If rst.RecordCount > 1 Then\r\n        Log.Error eelCritical, \"Multiple records in MSysResources table were found that matched this name. \" & _\r\n            \"Compact and repair database and try again. Theme Name: \" & LCase(m_Name) & \".\" & m_Extension, ModuleName(Me) & \".Export\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Get full name of theme file. (*.thmx)\r\n    strFile = Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n\r\n    ' Save as file\r\n    If Not rst.EOF Then\r\n        Set rstAtc = rst!Data.Value\r\n        If FSO.FileExists(strFile) Then DeleteFile strFile, True\r\n        If FSO.FolderExists(strFile) Then FSO.DeleteFolder strFile, True\r\n        VerifyPath strFile\r\n        Perf.OperationStart \"Export Theme\"\r\n        rstAtc!FileData.SaveToFile strFile\r\n        Perf.OperationEnd\r\n        rstAtc.Close\r\n        Set rstAtc = Nothing\r\n    End If\r\n    rst.Close\r\n    Set rst = Nothing\r\n\r\n    CatchAny eelError, \"Error exporting theme file: \" & strFile, ModuleName(Me) & \".Export\", True, True\r\n\r\n    ' Get hash before extracting the theme\r\n    strHash = GetFileHash(strFile)\r\n\r\n    ' See if we need to extract the theme source files.\r\n    ' (Only really needed when you are tracking themes customizations.)\r\n    If Options.ExtractThemeFiles Then\r\n        Perf.OperationStart \"Extract Theme\"\r\n        ' Extract to folder and delete zip file.\r\n        strFolder = FSO.BuildPath(FSO.GetParentFolderName(strFile), FSO.GetBaseName(strFile))\r\n        If FSO.FolderExists(strFolder) Then FSO.DeleteFolder strFolder, True\r\n        DoEvents ' Make sure the folder is deleted before we recreate it.\r\n        ' Rename to zip file before extracting\r\n        strZip = strFolder & \".zip\"\r\n        If FSO.FileExists(strZip) Then DeleteFile (strZip)\r\n        Name strFile As strZip\r\n        ExtractFromZip strZip, strFolder, False\r\n        ' Rather than holding up the export while we extract the file,\r\n        ' use a cleanup sub to do this after the export.\r\n        Perf.OperationEnd\r\n        CatchAny eelError, \"Error extracting theme. Folder: \" & strFolder, ModuleName(Me) & \".Export\", True, True\r\n    End If\r\n\r\n    ' Update index after exporting theme\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), strHash, GetSignatureHash\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    ' IMPORTANT: These variables must be declared as the\r\n    ' generic parent classes to avoid problems in newer\r\n    ' releases of Microsoft Access. (See issue #341)\r\n    Dim rstResources As DAO.Recordset   ' Recordset2\r\n    Dim rstAttachment As DAO.Recordset  ' Recordset2\r\n    Dim fldFile As DAO.Field            ' Field2\r\n\r\n    Dim strZip As String\r\n    Dim strThemeFile As String\r\n    Dim strThemeName As String\r\n    Dim strSql As String\r\n    Dim blnIsFolder As Boolean\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Are we dealing with a folder, or a file?\r\n    blnIsFolder = (Right$(strFile, 5) <> \".thmx\")\r\n\r\n    If blnIsFolder Then\r\n        ' We need to compress this folder back into a zipped theme file.\r\n        ' Build zip file name; if it's a folder, just add the extension.\r\n        strZip = strFile & \".zip\"\r\n        ' Get theme name\r\n        strThemeName = GetObjectNameFromFileName(FSO.GetBaseName(strZip))\r\n        ' Remove any existing zip file\r\n        If FSO.FileExists(strZip) Then DeleteFile strZip, True\r\n        ' Copy source files into new zip file\r\n        CreateZipFile strZip\r\n        CopyFolderToZip strFile, strZip\r\n        DoEvents\r\n        strThemeFile = strFile & \".thmx\"\r\n        If FSO.FileExists(strThemeFile) Then DeleteFile strThemeFile, True\r\n        Name strZip As strThemeFile\r\n    Else\r\n        ' Skip if file no longer exists. (Such as if we already\r\n        ' imported this theme from a folder.)\r\n        If Not FSO.FileExists(strFile) Then Exit Sub\r\n        ' Theme file is ready to go\r\n        strThemeFile = strFile\r\n    End If\r\n\r\n    ' Log any errors encountered.\r\n    CatchAny eelError, \"Error getting theme file. File: \" & strThemeFile & \", IsFolder: \" & blnIsFolder, ModuleName(Me) & \".Import\", True, True\r\n\r\n    ' Create/edit record in resources table.\r\n    strThemeName = GetObjectNameFromFileName(FSO.GetBaseName(strFile))\r\n    ' Make sure we have a resources table before we try to query the records.\r\n    If VerifyResourcesTable Then\r\n        strSql = \"SELECT * FROM MSysResources WHERE [Type] = 'thmx' AND [Name]=\"\"\" & strThemeName & \"\"\"\"\r\n        Set rstResources = CurrentDb.OpenRecordset(strSql, dbOpenDynaset)\r\n        With rstResources\r\n            If .EOF Then\r\n                ' No existing record found. Add a record\r\n                .AddNew\r\n                !Name = strThemeName\r\n                !Extension = \"thmx\"\r\n                !Type = \"thmx\"\r\n                Set rstAttachment = .Fields(\"Data\").Value\r\n            Else\r\n                ' Found theme record with the same name.\r\n                ' Remove the attached theme file.\r\n                .Edit\r\n                Set rstAttachment = .Fields(\"Data\").Value\r\n                If Not rstAttachment.EOF Then rstAttachment.Delete\r\n            End If\r\n\r\n            ' Upload theme file into OLE field\r\n            DoEvents\r\n            With rstAttachment\r\n                .AddNew\r\n                Set fldFile = .Fields(\"FileData\")\r\n                fldFile.LoadFromFile strThemeFile\r\n                m_FileData = fldFile\r\n                .Update\r\n                .Close\r\n            End With\r\n\r\n            ' Update class variables so we can recalculate the index hash\r\n            m_Name = strThemeName\r\n            m_FileName = strThemeName & \".thmx\"\r\n            m_Extension = \"thmx\"\r\n\r\n            ' Save and close record\r\n            .Update\r\n            .Close\r\n        End With\r\n    End If\r\n\r\n    ' Remove compressed theme file if we are using a folder.\r\n    If blnIsFolder Then DeleteFile strThemeFile, True\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetSignatureHash\r\n\r\n    ' Log any errors\r\n    CatchAny eelError, \"Error importing theme. File: \" & strThemeFile & \", IsFolder: \" & blnIsFolder, ModuleName(Me) & \".Import\", True, True\r\n\r\n    ' Clear object (Important with DAO/ADO)\r\n    Set rstAttachment = Nothing\r\n    Set rstResources = Nothing\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Import if file exists\r\n    If FSO.FileExists(strFile) Then\r\n        ' The import will replace the existing theme with the same name.\r\n        IDbComponent_Import strFile\r\n    Else\r\n        'TODO: Remove existing theme\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSignatureHash\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Return a simple hash that should give us a pretty good idea if the theme\r\n'           : has changed since it was last exported.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSignatureHash() As String\r\n\r\n    Dim dblCnt As Double\r\n    Dim dblLen As Double\r\n\r\n    ' Get length (bytes) of file data\r\n    If StrPtr(m_FileData) <> 0 Then dblLen = UBound(m_FileData)\r\n\r\n    With New clsConcat\r\n        ' Compile string with file information\r\n        .Add m_Name, m_FileName, m_Extension, CStr(dblLen)\r\n        ' Add in last 100 characters of file content (byte numbers)\r\n        ' Since this is a compressed (zip) file, we will probably\r\n        ' see a change here if the contents were modified.\r\n        If dblLen > 100 Then\r\n            For dblCnt = dblLen To dblLen - 100 Step -1\r\n                .Add CStr(m_FileData(dblCnt))\r\n            Next dblCnt\r\n        End If\r\n        ' Return hash of this string\r\n        GetSignatureHash = GetStringHash(.GetStr)\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\n    ' Check for extracted theme files\r\n    MoveFolderIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cTheme As IDbComponent\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim strSql As String\r\n    Dim strKey As String\r\n    Dim dItems As Dictionary\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n\r\n        ' Use dictionary to make sure we don't add duplicate records if we have\r\n        ' both a folder and a theme file for the same theme.\r\n        Set dItems = New Dictionary\r\n\r\n        ' This system table should exist, but just in case...\r\n        If TableExists(\"MSysResources\") Then\r\n\r\n            Set dbs = CurrentDb\r\n            strSql = \"SELECT * FROM MSysResources WHERE Type='thmx'\"\r\n            Set rst = dbs.OpenRecordset(strSql, dbOpenSnapshot, dbOpenForwardOnly)\r\n            With rst\r\n                Do While Not .EOF\r\n                    strKey = Nz(!Name)\r\n                    If Not dItems.Exists(strKey) Then\r\n                        ' Load theme details so we can check modified status\r\n                        Set cTheme = New clsDbTheme\r\n                        Set cTheme.DbObject = rst    ' Reference to OLE object recordset2\r\n                        blnAdd = True\r\n                        If blnModifiedOnly Then blnAdd = cTheme.IsModified\r\n                        If blnAdd Then m_Items(blnModifiedOnly).Add cTheme.SourceFile, cTheme\r\n                        dItems.Add strKey, strKey\r\n                    End If\r\n                    .MoveNext\r\n                Loop\r\n                .Close\r\n            End With\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyResourceTable\r\n' Author    : Adam Waller\r\n' Date      : 6/3/2020\r\n' Purpose   : Make sure the resources table exists, creating it if needed.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function VerifyResourcesTable() As Boolean\r\n\r\n    Dim blnExists As Boolean\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Make sure we actually have a resources table.\r\n    blnExists = TableExists(\"MSysResources\")\r\n    If Not blnExists Then\r\n        CreateResourcesTable\r\n        blnExists = TableExists(\"MSysResources\")\r\n    End If\r\n\r\n    ' Return true if the table exists\r\n    VerifyResourcesTable = blnExists\r\n\r\n    ' Log any errors\r\n    CatchAny eelError, \"Error verifying MSysResources table\", ModuleName(Me) & \".VerifyResourcesTable\"\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CreateResourcesTable\r\n' Author    : Adam Waller\r\n' Date      : 7/9/2021\r\n' Purpose   : The resources table is also used for shared images. We can add and remove\r\n'           : a temporary shared image to cause this table to be generated.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub CreateResourcesTable()\r\n\r\n    Dim strTempFile As String\r\n    Dim strName As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n    Perf.OperationStart \"Create MSysResources Table\"\r\n\r\n    ' Create a temporary image file\r\n    strTempFile = GetTempFile(\"IMG\")\r\n    Create1x1pxImage strTempFile\r\n\r\n    ' Add to database as shared image\r\n    strName = FSO.GetBaseName(strTempFile)\r\n    CurrentProject.AddSharedImage strName, strTempFile\r\n\r\n    ' Now that we have the table, remove temporary shared image record and temp file.\r\n    CurrentDb.Execute \"DELETE * FROM MSysResources WHERE Name='\" & strName & \"'\", dbFailOnError\r\n    DeleteFile strTempFile\r\n\r\n    ' Log any errors\r\n    CatchAny eelError, \"Error creating MSysResources table\", ModuleName(Me) & \".CreateResourcesTable\"\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Create1x1pxImage\r\n' Author    : Adam Waller\r\n' Date      : 7/9/2021\r\n' Purpose   : Creates a 1 pixel by 1 pixel png image.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Create1x1pxImage(strPath As String)\r\n\r\n    Dim intCnt As Integer\r\n    Dim varBytes As Variant\r\n    Dim bteImg() As Byte\r\n\r\n    ' Byte array for binary 1x1 pixel png file.\r\n    varBytes = Array((137), (80), (78), (71), (13), (10), (26), (10), (0), (0), (0), (13), (73), (72), (68), _\r\n        (82), (0), (0), (0), (1), (0), (0), (0), (1), (1), (3), (0), (0), (0), (37), (219), (86), (202), (0), _\r\n        (0), (0), (3), (80), (76), (84), (69), (0), (0), (0), (167), (122), (61), (218), (0), (0), (0), (1), _\r\n        (116), (82), (78), (83), (0), (64), (230), (216), (102), (0), (0), (0), (10), (73), (68), (65), (84), _\r\n        (8), (215), (99), (96), (0), (0), (0), (2), (0), (1), (226), (33), (188), (51), (0), (0), (0), (0), _\r\n        (73), (69), (78), (68), (174), (66), (96), (130))\r\n\r\n    ' Convert to byte array\r\n    ReDim bteImg(0 To UBound(varBytes)) As Byte\r\n    For intCnt = 0 To UBound(varBytes)\r\n        bteImg(intCnt) = varBytes(intCnt)\r\n    Next intCnt\r\n\r\n    ' Write to file\r\n    WriteBinaryFile strPath, bteImg\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    ' Get list of folders (extracted files) as well as zip files.\r\n    If m_FileList Is Nothing Then\r\n        Set m_FileList = GetSubfolderPaths(IDbComponent_BaseFolder)\r\n        MergeDictionary m_FileList, GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.thmx\")\r\n    End If\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    ' Compare the index hash with the current theme signature hash\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).OtherHash <> GetSignatureHash\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"Themes\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"themes\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"thmx\"\r\nEnd Property\r\n\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    IDbComponent_Name = m_Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'           : In this case, we are returning the theme file name.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Name) & \".thmx\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbTheme\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    ' Not used\r\n    Set IDbComponent_DbObject = Nothing\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_DbObject\r\n' Author    : Adam Waller\r\n' Date      : 5/11/2020\r\n' Purpose   : Load in the class values from the recordset\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n\r\n    ' Recordset to handle the incoming reference\r\n    Dim rst As DAO.Recordset\r\n\r\n    ' IMPORTANT: These variables must be declared as the\r\n    ' generic parent classes to avoid problems in newer\r\n    ' releases of Microsoft Access. (See issue #341)\r\n    Dim fld2 As DAO.Field       ' Field2\r\n    Dim rst2 As DAO.Recordset   ' Recordset2\r\n\r\n    ' Load in the object details.\r\n    Set rst = RHS\r\n    m_Name = Nz(rst!Name)\r\n    m_Extension = Nz(rst!Extension)\r\n    '@Ignore SetAssignmentWithIncompatibleObjectType\r\n    Set fld2 = rst!Data\r\n    Set rst2 = fld2.Value\r\n    m_FileName = Nz(rst2.Fields(\"FileName\"))\r\n    m_FileData = rst2.Fields(\"FileData\")\r\n\r\n    ' Clear the object references\r\n    Set rst2 = Nothing\r\n    Set fld2 = Nothing\r\n    Set rst = Nothing\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbVbeForm.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbVbeForm\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Form As VBIDE.VBComponent\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_FileList As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2023\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'           : NOTE: This has been expanded to include an export (for reference only) of\r\n'           : a serialized representation of the form properties, controls, and layout.\r\n'           : ALSO NOTE: This behaves a little differently than other objects. Because\r\n'           : the binary *.frx file changes EVERY time you export it, we should avoid\r\n'           : actually exporting it if nothing has changed in the serialized file.\r\n'           : It is *technically* possible to make a change on the form that would not\r\n'           : be included in the serialized output (such as a property change on an\r\n'           : active-x control) but this is extremely unlikely in contrast to the noise\r\n'           : produced by the binary file showing changes on every full export.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n\r\n    Dim strContent As String\r\n    Dim strBasePath As String\r\n    Dim strJsonPath  As String\r\n    Dim strFormPath As String\r\n    Dim strBinaryPath As String\r\n    Dim blnUnchanged As Boolean\r\n\r\n    ' Get the JSON representation of the current database object\r\n    strContent = GetSource\r\n\r\n    ' Build out all three normal source file paths.\r\n    strBasePath = IDbComponent_BaseFolder & GetSafeFileName(m_Form.Name)\r\n    strJsonPath = strBasePath & \".json\"\r\n    strBinaryPath = strBasePath & \".frx\"\r\n    strFormPath = strBasePath & \".frm\"\r\n\r\n    ' Before exporting the VBE file, let's see if the source files exist, just in case\r\n    ' the database object already matches the source file.\r\n    If FSO.FileExists(strJsonPath) And FSO.FileExists(strBinaryPath) And FSO.FileExists(strFormPath) Then\r\n        ' If so, compare the hash of the json file to the database object.\r\n        blnUnchanged = (GetStringHash(strContent, True) = GetFileHash(strJsonPath))\r\n    End If\r\n\r\n    ' This is the serialized output file in JSON format to track changes in version control\r\n    ' Always ouput this file during an export\r\n    WriteFile strContent, strJsonPath\r\n\r\n    ' Only export the VBE object (including binary source) if the form has changed\r\n    ' or if the VBE files are missing in the original source location\r\n    If Not blnUnchanged Then\r\n        If Len(strAlternatePath) Then\r\n            ' Save to alternate path\r\n            m_Form.Export SwapExtension(strAlternatePath, \"frm\")\r\n        Else\r\n            ' This is the binary export file used when building from source\r\n            m_Form.Export strFormPath\r\n        End If\r\n    End If\r\n\r\n    ' Update index with a hash of the serialized content. (Since the binary content changes frequently)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/30/2021\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim proj As VBProject\r\n    Dim strTestFile As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Only import files with the correct (primary) extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    ' Make sure the other two companion files also exist\r\n    strTestFile = SwapExtension(strFile, \"frm\")\r\n    If Not FSO.FileExists(strTestFile) Then\r\n        Log.Error eelError, \"VBE Form definition file not found: \" & strTestFile, ModuleName(Me) & \".Import\"\r\n        Exit Sub\r\n    Else\r\n        ' Check binary file\r\n        strTestFile = SwapExtension(strFile, \"frx\")\r\n        If Not FSO.FileExists(strTestFile) Then\r\n            Log.Error eelError, \"VBE Form binary file not found: \" & strTestFile, ModuleName(Me) & \".Import\"\r\n            Exit Sub\r\n        End If\r\n    End If\r\n\r\n    ' With the files verified, we can move forward with the actual import\r\n    Set proj = CurrentVBProject\r\n    With proj.VBComponents\r\n\r\n        ' Import the VBE source file\r\n        .Import SwapExtension(strFile, \"frm\")\r\n\r\n        ' Set reference to form after import\r\n        Set m_Form = .Item(GetObjectNameFromFileName(strFile))\r\n\r\n        ' Check for extra blank line that may get added during import (VBE bug?)\r\n        With m_Form\r\n            If Not .CodeModule Is Nothing Then\r\n                If .CodeModule.Lines(1, 1) = vbNullString Then\r\n                    ' Remove blank line added during import (Known VBA Bug)\r\n                    .CodeModule.DeleteLines 1\r\n                    ' Note, this change is not saved at this time. It will be saved during\r\n                    ' the next compile/save operation.\r\n                    Log.Add \"Removed blank line from the top of the code module for \" & .Name, False\r\n                End If\r\n            End If\r\n        End With\r\n    End With\r\n\r\n    ' Update index (based on serialized representation)\r\n    VCSIndex.Update Me, eatImport, GetStringHash(GetSource, True)\r\n\r\n    ' Log any errors while importing the VBE form\r\n    CatchAny eelError, \"Error importing \" & strFile, ModuleName(Me) & \".Import\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, replacing or removing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    DeleteVbeFormIfExists GetObjectNameFromFileName(strFile)\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DeleteVbeFormIfExists\r\n' Author    : Adam Waller\r\n' Date      : 2/5/2022\r\n' Purpose   : Remove any existing VBE form\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub DeleteVbeFormIfExists(strName)\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    With CurrentVBProject.VBComponents\r\n        .Remove .Item(strName)\r\n    End With\r\n    Catch 9 ' Subscript out of range (name not found)\r\n    CatchAny eelError, \"Deleting VBE Form \" & strName\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\n    MoveFileIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile) & \".frm\", strToFolder\r\n    MoveFileIfExists strFromFolder & FSO.GetBaseName(IDbComponent_SourceFile) & \".frx\", strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim frm As VBComponent\r\n    Dim cForm As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        For Each frm In CurrentVBProject.VBComponents\r\n            If frm.Type = vbext_ct_MSForm Then\r\n                Set cForm = New clsDbVbeForm\r\n                Set cForm.DbObject = frm\r\n                blnAdd = True\r\n                If blnModifiedOnly Then blnAdd = cForm.IsModified\r\n                If blnAdd Then m_Items(blnModifiedOnly).Add cForm.SourceFile, cForm\r\n            End If\r\n        Next frm\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"Serialized VBE Form\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2023\r\n' Purpose   : Return a dictionary of the serialized form structure\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary() As Dictionary\r\n    Set GetDictionary = SerializeMSForm(m_Form)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    If m_FileList Is Nothing Then Set m_FileList = GetFilePathsInFolder(IDbComponent_BaseFolder, \"*.json\")\r\n    Set IDbComponent_GetFileList = m_FileList\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    ' Compare the hash of the serialized JSON output, not the ever-changing binary source file.\r\n    ' NOTE: This will not detect changes in unserialized objects such as ActiveX controls.\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"VBE Forms\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder & \"vbeforms\" & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\n    IDbComponent_FileExtensions.Add \"frx\"\r\n    IDbComponent_FileExtensions.Add \"frm\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Form Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Form.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    If m_Form Is Nothing Then Exit Property\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & GetSafeFileName(m_Form.Name) & \".json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    ' No simple way to pull a quick count of these\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbVbeForm\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Form\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Form = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = False\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbVbeProject.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbVbeProject\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Project As VBIDE.VBProject\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_dItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    Dim dProject As Dictionary\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n    Set dProject = ReadJsonFile(strFile)\r\n    Set m_Project = CurrentVBProject\r\n\r\n    ' Update project properties\r\n    With m_Project\r\n        .Name = dNZ(dProject, \"Items\\Name\")\r\n        .Description = dNZ(dProject, \"Items\\Description\")\r\n\r\n        ' Setting the HelpContextId can throw random automation errors.\r\n        SafeSetProperty m_Project, \"HelpContextId\", ValidHelpContextId(dNZ(dProject, \"Items\\HelpContextId\"))\r\n        SafeSetProperty m_Project, \"HelpFile\", ValidHelpFile(dNZ(dProject, \"Items\\HelpFile\"))\r\n\r\n        ' This property is not exposed through the VBProject object model\r\n        Application.SetOption \"Conditional Compilation Arguments\", dNZ(dProject, \"Items\\ConditionalCompilationArguments\")\r\n\r\n        ' // Read-only properties\r\n        '.FileName = dNZ(dProject, \"Items\\FileName\")\r\n        '.Mode = dNZ(dProject, \"Items\\Mode\")\r\n        '.Protection = dNZ(dProject, \"Items\\Protection\")\r\n        '.Type = dNZ(dProject, \"Items\\Type\")\r\n    End With\r\n\r\n    CatchAny eelError, \"Importing VBE Project\", ModuleName(Me) & \".Import\"\r\n\r\n    ' Save to index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary(False))\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SafeSetProperty\r\n' Author    : Adam Waller\r\n' Date      : 3/26/2021\r\n' Purpose   : For some reason the help properties can sometimes throw strange runtime\r\n'           : errors when setting them. This function handles the extra error handling\r\n'           : involved in setting and verifying these properties.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SafeSetProperty(cProj As VBProject, strProperty As String, varValue As Variant)\r\n\r\n    Dim varCurrent As Variant\r\n\r\n    ' Get current property value\r\n    varCurrent = CallByName(cProj, strProperty, VbGet)\r\n\r\n    ' No need to set if the current value already matches\r\n    If varValue = varCurrent Then Exit Sub\r\n\r\n    ' Switch to on error resume next after checking for current errors\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    ' Attempt to set the property\r\n    CallByName cProj, strProperty, VbLet, varValue\r\n\r\n    ' Read the value after setting it\r\n    varCurrent = CallByName(cProj, strProperty, VbGet)\r\n\r\n    ' Verify that the property was set correctly\r\n    If varCurrent <> varValue Then\r\n        ' We might have thrown an actual error.\r\n        If Not CatchAny(eelError, \"Failed to set \" & strProperty & \" to '\" & _\r\n            CStr(varValue) & \"'\", ModuleName(Me) & \".SafeSetProperty\") Then\r\n            ' No error, but property not set correctly.\r\n            Log.Error eelError, \"Failed to set \" & strProperty & \". Set value to '\" & _\r\n                varValue & \"' but afterwards it returned '\" & varCurrent & \"'.\", _\r\n                ModuleName(Me) & \".SafeSetProperty\"\r\n        End If\r\n    Else\r\n        ' Clear any errors that may have been thrown, even if the change was successful.\r\n        If Err Then Err.Clear\r\n    End If\r\n\r\n    ' Log any uncaught errors\r\n    CatchAny eelError, \"Setting Property '\" & strProperty & \"' to value '\" & CStr(varValue) & \"'\", ModuleName(Me) & \".SafeSetProperty\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ValidHelpContextId\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Don't attempt to set the help context id to anything other than a number.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ValidHelpContextId(strHelpID As String) As Long\r\n    If strHelpID = vbNullString Then\r\n        ValidHelpContextId = 0\r\n    ElseIf Not IsNumeric(strHelpID) Then\r\n        Log.Error eelWarning, \"HelpContextID should be a number. \" & _\r\n            \"Found '\" & strHelpID & \"' instead.\", ModuleName(Me) & \".ValidHelpContextId\"\r\n        ValidHelpContextId = 0\r\n    Else\r\n        ValidHelpContextId = CLng(strHelpID)\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ValidHelpFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Get help file path saved as a relative path.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ValidHelpFile(strHelpFile As String) As String\r\n\r\n    Dim strValid As String\r\n\r\n    If strHelpFile <> vbNullString Then\r\n\r\n        ' Check for a Rubber Duck Identifier value\r\n        If InStr(1, strHelpFile, \".\") = 0 And IsNumeric(strHelpFile) Then\r\n            If Options.PreserveRubberDuckID Then\r\n                ' Allow user to save this identifier in the exported source, if they really want to.\r\n                ' See issue #197 for more details on this.\r\n                Log.Add \"RubberDuck Identifier \" & strHelpFile & \" found in VBE Project HelpFile field. \" & _\r\n                    \"If you don't want to save this to source, \" & _\r\n                    \"set PreserveRubberDuckID = False in the options file.\", False\r\n\r\n                strValid = strHelpFile\r\n            End If\r\n\r\n        Else\r\n            ' Might actually be a help file name/path\r\n\r\n            ' Build out any relative path\r\n            strValid = GetPathFromRelative(strHelpFile)\r\n\r\n            ' Make sure this is a valid help file\r\n            If strValid Like \"*.hlp\" Or strValid Like \"*.chm\" Then\r\n                If Not FSO.FileExists(strValid) Then\r\n                    Log.Error eelWarning, \"Help file not found: \" & strValid, ModuleName(Me) & \".ValidHelpFile\"\r\n                End If\r\n            Else\r\n                ' Does not appear to be a help file extension\r\n                Log.Error eelWarning, \"'\" & strValid & \"' is not a valid help file name. \" & _\r\n                    \"(Expecting *.hlp or *.chm)\", ModuleName(Me) & \".ValidHelpFile\"\r\n                strValid = vbNullString\r\n            End If\r\n\r\n        End If\r\n    End If\r\n\r\n    ' Return validated help file string\r\n    ValidHelpFile = strValid\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"VBE Project\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2020\r\n' Purpose   : Return a dictionary object of project properties.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary(Optional blnUseCache As Boolean = True) As Dictionary\r\n\r\n    ' Check cache parameter\r\n    If blnUseCache And Not m_dItems Is Nothing Then\r\n        ' Return cached dictionary\r\n        Set GetDictionary = m_dItems\r\n        Exit Function\r\n    End If\r\n\r\n    ' Make sure we have a reference to the VB project\r\n    If m_Project Is Nothing Then Set m_Project = CurrentVBProject\r\n\r\n    ' Read project properties\r\n    Set GetDictionary = New Dictionary\r\n    With GetDictionary\r\n        .Add \"Name\", m_Project.Name\r\n        .Add \"Description\", m_Project.Description\r\n        .Add \"FileName\", FSO.GetFileName(m_Project.FileName)\r\n        .Add \"HelpFile\", ValidHelpFile(m_Project.HelpFile)\r\n        .Add \"HelpContextId\", ValidHelpContextId(m_Project.HelpContextId)\r\n        .Add \"ConditionalCompilationArguments\", Application.GetOption(\"Conditional Compilation Arguments\")\r\n        .Add \"Mode\", m_Project.Mode\r\n        .Add \"Protection\", m_Project.Protection\r\n        .Add \"Type\", m_Project.Type\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n    ' Import will replace existing values\r\n    IDbComponent_Import strFile\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim cProj As IDbComponent\r\n    Dim blnAdd As Boolean\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        ' Load class details\r\n        Set m_Project = CurrentVBProject\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        Set cProj = New clsDbVbeProject\r\n        Set cProj.DbObject = m_Project\r\n        blnAdd = True\r\n        If blnModifiedOnly Then blnAdd = cProj.IsModified\r\n        If blnAdd Then m_Items(blnModifiedOnly).Add cProj.SourceFile, cProj\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    Set IDbComponent_GetFileList = New Dictionary\r\n    If FSO.FileExists(IDbComponent_SourceFile) Then IDbComponent_GetFileList.Add IDbComponent_SourceFile, vbNullString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"VB Project\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Project Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Project.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & \"vbe-project.json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbVbeProject\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Project\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Project = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = True\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Helps us know whether we have already counted the objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    'm_Count = -1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDbVbeReference.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDbVbeReference\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbComponent class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate m_Ref As VBIDE.Reference\r\nPrivate m_Items(True To False) As Dictionary\r\nPrivate m_dItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the component classes consistent in how they are used in the export\r\n' and import process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbComponent\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Export the individual database component (table, form, query, etc...)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Export(Optional strAlternatePath As String)\r\n    Dim strContent As String\r\n    strContent = GetSource\r\n    WriteFile strContent, Nz2(strAlternatePath, IDbComponent_SourceFile)\r\n    VCSIndex.Update Me, IIf(strAlternatePath = vbNullString, eatExport, eatAltExport), GetStringHash(strContent, True)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Import\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Import the individual database component from a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Import(strFile As String)\r\n\r\n    ' Import the references\r\n    ImportReferences strFile\r\n\r\n    ' Update index\r\n    VCSIndex.Update Me, eatImport, GetDictionaryHash(GetDictionary)\r\n\r\n    CatchAny eelError, \"Importing VBE references\", ModuleName(Me) & \".Import\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ImportReferences\r\n' Author    : Adam Waller\r\n' Date      : 4/21/2021\r\n' Purpose   : Wrapper to import references with the option of only loading the\r\n'           : GUID references. (This is used when preparing a bootstrap module.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ImportReferences(strFile As String, Optional blnGuidOnly As Boolean = False)\r\n\r\n    Dim dRef As Dictionary\r\n    Dim dItems As Dictionary\r\n    Dim varKey As Variant\r\n    Dim ref As VBIDE.Reference\r\n    Dim dFile As Dictionary\r\n    Dim proj As VBProject\r\n    Dim varVersion As Variant\r\n    Dim strPath As String\r\n    Dim dExisting As Dictionary\r\n\r\n    ' Only import files with the correct extension.\r\n    If Not strFile Like \"*.json\" Then Exit Sub\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Read in references from file\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If Not dFile Is Nothing Then\r\n\r\n        ' Build list of current references so we can avoid conflicts\r\n        Set proj = CurrentVBProject\r\n        Set dExisting = New Dictionary\r\n        For Each ref In proj.References\r\n            dExisting.Add ref.Name, ref.Guid\r\n        Next ref\r\n\r\n        ' Add any references from file that don't already exist\r\n        Set dItems = dFile(\"Items\")\r\n        For Each varKey In dItems.Keys\r\n            Set dRef = dItems(varKey)\r\n            If Not dExisting.Exists(CStr(varKey)) Then\r\n                If dRef.Exists(\"GUID\") Then\r\n                    varVersion = Split(dRef(\"Version\"), \".\")\r\n                    AddFromGuid proj, CStr(varKey), dRef(\"GUID\"), CLng(varVersion(0)), CLng(varVersion(1))\r\n                ElseIf dRef.Exists(\"FullPath\") Then\r\n                    If Not blnGuidOnly Then\r\n                        strPath = GetPathFromRelative(dRef(\"FullPath\"))\r\n                        If Not FSO.FileExists(strPath) Then\r\n                            Log.Error eelError, \"File not found. Unable to add reference to \" & strPath, _\r\n                                ModuleName(Me) & \".ImportReferences\"\r\n                        Else\r\n                            Perf.OperationStart \"Add Library References\"\r\n                            proj.References.AddFromFile strPath\r\n                            Perf.OperationEnd\r\n                            CatchAny eelError, \"Adding VBE reference from \" & strPath, ModuleName(Me) & \".ImportReferences\"\r\n                        End If\r\n                    End If\r\n                End If\r\n            End If\r\n        Next varKey\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSource\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2022\r\n' Purpose   : Return the full content that will be saved to the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetSource() As String\r\n    GetSource = BuildJsonFile(TypeName(Me), GetDictionary, \"VBE References\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Return a dictionary of the ordered, unique VBE references\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDictionary(Optional blnUseCache As Boolean) As Dictionary\r\n\r\n    Dim proj As VBProject\r\n    Dim dRef As Dictionary\r\n    Dim ref As VBIDE.Reference\r\n    Dim strName As String\r\n    Dim dNames As Dictionary\r\n\r\n    ' Check cache parameter\r\n    If blnUseCache And Not m_dItems Is Nothing Then\r\n        ' Return cached dictionary\r\n        Set GetDictionary = m_dItems\r\n        Exit Function\r\n    End If\r\n\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    Set proj = CurrentVBProject\r\n    Set dNames = New Dictionary\r\n    Set GetDictionary = New Dictionary\r\n\r\n    With GetDictionary\r\n        ' Loop through cached references (Duplicates have already been removed)\r\n        For Each ref In proj.References\r\n            If Not ref.BuiltIn Then\r\n                ' A broken reference doesn't necessarily trigger an error.\r\n                If ref.IsBroken Then\r\n                    If ref.Type = vbext_rk_Project Then\r\n                        strName = ref.Name\r\n                    Else\r\n                        ' Can't access .Name property on broken type lib reference.\r\n                        strName = ref.Guid\r\n                    End If\r\n                    Log.Error eelWarning, \"Broken reference for \" & strName & _\r\n                        \"This may cause errors in the export process.\", ModuleName(Me) & \".GetDictionary\"\r\n                    ' Continue export, as this may not affect the actual output files.\r\n                Else\r\n                    ' Use name for unbroken reference\r\n                    strName = ref.Name\r\n                End If\r\n                ' Add new unique entries to dictionary\r\n                If Not dNames.Exists(strName) Then\r\n                    Set dRef = New Dictionary\r\n                    With dRef\r\n                        If ref.Type = vbext_rk_Project Then\r\n                            ' references of types mdb,accdb,mde etc don't have a GUID\r\n                            .Add \"FullPath\", GetRelativePath(ref.FullPath)\r\n                        Else\r\n                            If ref.Guid <> vbNullString Then .Add \"GUID\", ref.Guid\r\n                            .Add \"Version\", CStr(ref.Major) & \".\" & CStr(ref.Minor)\r\n                        End If\r\n                    End With\r\n                    .Add strName, dRef\r\n                    ' Don't attempt add two references with the same name, such as\r\n                    ' circular references to nested library database files.\r\n                    dNames.Add strName, vbNullString\r\n                End If\r\n                ' Log any errors encountered.\r\n                CatchAny eelError, \"Exporting reference \" & strName, ModuleName(Me) & \".GetDictionary\"\r\n            End If\r\n        Next ref\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddFromGuid\r\n' Author    : Adam Waller / Indigo744\r\n' Date      : 11/22/2020\r\n' Purpose   : Try to add a GUID with a specific version, then with version 0.0\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub AddFromGuid(proj As VBIDE.VBProject, strName As String, strGuid As String, lngMajor As Long, lngMinor As Long)\r\n\r\n    ' Try to add the GUID with the specific version requested\r\n    ' We might encounter a reference that is not available in this version\r\n    On Error GoTo ErrHandlerWithVersion\r\n    Perf.OperationStart \"Add GUID References\"\r\n    proj.References.AddFromGuid strGuid, lngMajor, lngMinor\r\n    Perf.OperationEnd\r\n\r\n    ' Normal exit\r\n    On Error GoTo 0\r\n    Exit Sub\r\n\r\nErrHandlerWithVersion:\r\n    ' The version specified may not be available, try to add with version 0.0\r\n    ' We might still encounter a reference that is still not available\r\n    On Error GoTo ErrHandler\r\n    proj.References.AddFromGuid strGuid, 0, 0\r\n\r\n    ' Resume on next line\r\n    Err.Clear\r\n    Resume Next\r\n\r\nErrHandler:\r\n\r\n    ' Log error\r\n    Log.Add \"ERROR: Could not add VBE reference to \" & strName\r\n\r\n    If Err.Number = -2147319779 Then\r\n        ' Object library not registered\r\n        Log.Add \"Encountered error \" & Err.Number & \": '\" & Err.Description & _\r\n            \"' while attempting to add GUID \" & strGuid & \" version \" & lngMajor & \".\" & lngMinor & _\r\n            \" to this project. This may occur when the library does not exist on the build machine,\" & _\r\n            \" or when the version on the build machine is lower than the source file reference version.\" & _\r\n            \" See GitHub issue #96 for an example of how to resolve this problem.\", Options.ShowDebug\r\n\r\n    Else\r\n        ' Other error\r\n        Log.Add \"Encountered error \" & Err.Number & \": '\" & Err.Description & _\r\n            \"' while attempting to add GUID \" & strGuid & \" version \" & lngMajor & \".\" & lngMinor & _\r\n            \" to this project.\", Options.ShowDebug\r\n    End If\r\n\r\n    ' Resume on next line\r\n    Err.Clear\r\n    Resume Next\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Merge\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Merge the source file into the existing database, updating or replacing\r\n'           : any existing object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_Merge(strFile As String)\r\n\r\n    ' Remove existing references first.\r\n    RemoveNonBuiltInReferences\r\n\r\n    ' Import the references if the file exists\r\n    If FSO.FileExists(strFile) Then\r\n        IDbComponent_Import strFile\r\n    Else\r\n        VCSIndex.Remove Me, strFile\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbComponent_MoveSource\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move the component's source file(s) from one folder to another\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbComponent_MoveSource(strFromFolder As String, strToFolder As String)\r\n    MoveFileIfExists strFromFolder & FSO.GetFileName(IDbComponent_SourceFile), strToFolder\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllFromDB\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a collection of class objects represented by this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetAllFromDB(Optional blnModifiedOnly As Boolean = False) As Dictionary\r\n\r\n    Dim ref As VBIDE.Reference\r\n    Dim cRef As IDbComponent\r\n    Dim dNames As Dictionary\r\n    Dim dItems As Dictionary\r\n\r\n    ' Build collection if not already cached\r\n    If m_Items(blnModifiedOnly) Is Nothing Then\r\n        Set m_Items(blnModifiedOnly) = New Dictionary\r\n        Set dNames = New Dictionary\r\n        Set dItems = GetDictionary\r\n\r\n        ' This is where references work a little differently than most other objects.\r\n        ' If ANY changes are detected in ANY of the references, the whole list of\r\n        ' references will be returned as changed.\r\n        If (Not blnModifiedOnly) Or IDbComponent_IsModified Then\r\n            For Each ref In CurrentVBProject.References\r\n                If Not ref.BuiltIn Then\r\n                    If Not dNames.Exists(ref.Name) Then\r\n                        Set cRef = New clsDbVbeReference\r\n                        Set cRef.DbObject = ref\r\n                        m_Items(blnModifiedOnly).Add cRef.SourceFile & \":\" & ref.Name, cRef\r\n                        ' Don't attempt add two references with the same name, such as\r\n                        ' circular references to nested library database files.\r\n                        dNames.Add ref.Name, ref.Name\r\n                    End If\r\n                End If\r\n            Next ref\r\n\r\n        End If\r\n    End If\r\n\r\n    ' Return cached collection\r\n    Set IDbComponent_GetAllFromDB = m_Items(blnModifiedOnly)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a list of file names to import for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_GetFileList() As Dictionary\r\n    Set IDbComponent_GetFileList = New Dictionary\r\n    If FSO.FileExists(IDbComponent_SourceFile) Then IDbComponent_GetFileList.Add IDbComponent_SourceFile, vbNullString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsModified\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Returns true if the object in the database has been modified since\r\n'           : the last export of the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IDbComponent_IsModified() As Boolean\r\n    IDbComponent_IsModified = VCSIndex.Item(Me).FileHash <> GetStringHash(GetSource, True)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateModified\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The date/time the object was modified. (If possible to retrieve)\r\n'           : If the modified date cannot be determined (such as application\r\n'           : properties) then this function will return 0.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbComponent_DateModified() As Date\r\n    ' No date value here.\r\n    IDbComponent_DateModified = 0\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Category\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a category name for this type. (I.e. forms, queries, macros)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Category() As String\r\n    IDbComponent_Category = \"VBE References\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the base folder for import/export of this component.\r\n'---------------------------------------------------------------------------------------\r\nPrivate Property Get IDbComponent_BaseFolder() As String\r\n    IDbComponent_BaseFolder = Options.GetExportFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileExtensions\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : A collection of the file extensions used in source files for this\r\n'           : component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_FileExtensions() As Collection\r\n    Set IDbComponent_FileExtensions = New Collection\r\n    IDbComponent_FileExtensions.Add \"json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Name\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a name to reference the object for use in logs and screen output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Name() As String\r\n    If m_Ref Is Nothing Then Exit Property\r\n    IDbComponent_Name = m_Ref.Name\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceFile\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return the full path of the source file for the current object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SourceFile() As String\r\n    IDbComponent_SourceFile = IDbComponent_BaseFolder & \"vbe-references.json\"\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Count\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Return a count of how many items are in this category.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_Count(Optional blnModifiedOnly As Boolean = False) As Long\r\n    IDbComponent_Count = IDbComponent_GetAllFromDB(blnModifiedOnly).Count\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a cached, non-iterative approximate count of database objects\r\n'           : for use with progress indicators when scanning for changes. Single file\r\n'           : objects like database properties can simply return 1.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_QuickCount() As Long\r\n    IDbComponent_QuickCount = 1\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ComponentType\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : The type of component represented by this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_ComponentType() As eDatabaseComponentType\r\n    IDbComponent_ComponentType = edbVbeReference\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbObject\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This represents the database object we are dealing with.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_DbObject() As Object\r\n    Set IDbComponent_DbObject = m_Ref\r\nEnd Property\r\nPrivate Property Set IDbComponent_DbObject(ByVal RHS As Object)\r\n    Set m_Ref = RHS\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SingleFile\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Returns true if the export of all items is done as a single file instead\r\n'           : of individual files for each component. (I.e. properties, references)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbComponent_SingleFile() As Boolean\r\n    IDbComponent_SingleFile = True\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Parent\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Return a reference to this class as an IDbComponent. This allows you\r\n'           : to reference the public methods of the parent class without needing\r\n'           : to create a new class object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Parent() As IDbComponent\r\n    Set Parent = Me\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDevMode.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDevMode\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsDevMode\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2020\r\n' Purpose   : Helper class to handle the parsing of saved print settings.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' See the following links for additional technical details regarding the DEVMODE strcture:\r\n' https://docs.microsoft.com/en-us/office/vba/api/access.report.prtdevmode\r\n' https://stackoverflow.com/questions/49560317/64-bit-word-vba-devmode-dmduplex-returns-4\r\n' http://toddmcdermid.blogspot.com/2009/02/microsoft-access-2003-and-printer.html\r\n' https://github.com/x-ware-ltd/access-scc-addin/blob/master/Modules/modExtendSaveAsText.ACM\r\n\r\n' Constant to convert tenths of millimeters to inches for human readability\r\nPrivate Const TEN_MIL As Double = 0.00393701\r\n\r\n' API constants for reading printer properties\r\n' These may not be needed any longer but are kept here for referencing.\r\n'Private Const READ_CONTROL = &H20000\r\n'Private Const PRINTER_ACCESS_USE = &H8\r\n'Private Const GENERIC_READ = &H80000000\r\nPrivate Const DM_OUT_BUFFER = 2\r\n\r\n' DevMode for printer details\r\nPrivate Type tDevModeBuffer\r\n    strBuffer As String * 220 ' Pad with plenty of extra room.\r\nEnd Type\r\n\r\nPrivate Type tDevMode\r\n    strDeviceName(1 To 32) As Byte\r\n    intSpecVersion As Integer\r\n    intDriverVersion As Integer\r\n    intSize As Integer\r\n    intDriverExtra As Integer\r\n    lngFields As Long\r\n    intOrientation As Integer\r\n    intPaperSize As Integer\r\n    intPaperLength As Integer\r\n    intPaperWidth As Integer\r\n    intScale As Integer\r\n    intCopies As Integer\r\n    intDefaultSource As Integer\r\n    intPrintQuality As Integer\r\n    intColor As Integer\r\n    intDuplex As Integer\r\n    intResolution As Integer\r\n    intTTOption As Integer\r\n    intCollate As Integer\r\n    strFormName(1 To 32) As Byte\r\n    intUnusedPadding As Integer\r\n    intBitsPerPel As Integer\r\n    lngPelsWidth As Long\r\n    lngPelsHeight As Long\r\n    lngDisplayFlags As Long\r\n    lngDisplayFrequency As Long\r\n    lngICMMethod As Long\r\n    lngICMIntent As Long\r\n    lngMediaType As Long\r\n    lngDitherType As Long\r\n    lngReserved1 As Long\r\n    lngReserved2 As Long\r\nEnd Type\r\n\r\n\r\n' Printer Margins\r\nPrivate Type tMipBuffer\r\n    strBuffer As String * 28\r\nEnd Type\r\n\r\nPrivate Type tMip\r\n    xLeftMargin As Long\r\n    yTopMargin As Long\r\n    xRightMargin As Long\r\n    yBotMargin As Long\r\n    fDataOnly As Long\r\n    xWidth As Long\r\n    yHeight As Long\r\n    fDefaultSize As Long\r\n    cxColumns As Long\r\n    yColumnSpacing As Long\r\n    xRowSpacing As Long\r\n    rItemLayout As Long\r\n    fFastPrint As Long\r\n    fDatasheet As Long\r\nEnd Type\r\n\r\n\r\n' Device Name (and if default printer)\r\nPrivate Type tDevNamesBuffer\r\n    strBuffer As String * 255\r\nEnd Type\r\n\r\nPrivate Type tDevNames\r\n    intDriverOffset As Integer\r\n    intDeviceOffset As Integer\r\n    intOutputOffset As Integer\r\n    intDefault As Integer\r\n    strData(1 To 255) As Byte\r\nEnd Type\r\n\r\n\r\n' API calls for reading the DevMode for the default printer\r\nPrivate Type PRINTER_DEFAULTS\r\n   pDatatype As Long\r\n   pDevmode As Long\r\n   DesiredAccess As Long\r\nEnd Type\r\n\r\nPrivate Declare PtrSafe Function OpenPrinter Lib \"winspool.drv\" Alias \"OpenPrinterA\" ( _\r\n    ByVal pPrinterName As String, phPrinter As LongPtr, pDefault As Any) As Long\r\nPrivate Declare PtrSafe Function ClosePrinter Lib \"winspool.drv\" (ByVal hPrinter As LongPtr) As Long\r\nPrivate Declare PtrSafe Function DocumentProperties Lib \"winspool.drv\" Alias \"DocumentPropertiesA\" ( _\r\n    ByVal hwnd As Long, ByVal hPrinter As LongPtr, ByVal pDeviceName As String, _\r\n    ByVal pDevModeOutput As LongPtr, ByVal pDevModeInput As LongPtr, ByVal fMode As Long) As Long\r\n\r\n' Enum for types that can be expanded to friendly\r\n' values for storing in version control.\r\nPublic Enum ePrintEnum\r\n    ' Access constants\r\n    epeColor\r\n    epeColumnLayout\r\n    epeDuplex\r\n    epeOrientation\r\n    epePaperBin\r\n    epePaperSize\r\n    epePrintQuality\r\n    ' API values\r\n    epeTTOption\r\n    epeCollate\r\n    epeDisplayFlags\r\n    epeICMMethod\r\n    epeICMIntent\r\n    epeMediaType\r\n    epeDitherType\r\n    [_Last]\r\nEnd Enum\r\n\r\n' How to convert the enum value\r\nPublic Enum eEnumConversion\r\n    eecAuto\r\n    eecToEnum\r\n    eecToName\r\nEnd Enum\r\n\r\n' Constants to verify that the property is available\r\nPrivate Enum edmFlags\r\n    DM_ORIENTATION = &H1\r\n    DM_PAPERSIZE = &H2\r\n    DM_PAPERLENGTH = &H4\r\n    DM_PAPERWIDTH = &H8\r\n    DM_SCALE = &H10\r\n    DM_COPIES = &H100\r\n    DM_DEFAULTSOURCE = &H200\r\n    DM_PRINTQUALITY = &H400\r\n    DM_COLOR = &H800\r\n    DM_DUPLEX = &H1000\r\n    DM_YRESOLUTION = &H2000\r\n    DM_TTOPTION = &H4000\r\n    DM_COLLATE = &H8000\r\n    DM_FORMNAME = &H10000\r\n    'DM_LOGPIXELS = &H20000\r\n    'DM_BITSPERPEL = &H40000\r\n    'DM_PELSWIDTH = &H80000\r\n    'DM_PELSHEIGHT = &H100000\r\n    DM_DISPLAYFLAGS = &H200000\r\n    DM_DISPLAYFREQUENCY = &H400000\r\n    DM_ICMMETHOD = &H800000\r\n    DM_ICMINTENT = &H1000000\r\n    DM_MEDIATYPE = &H2000000\r\n    DM_DITHERTYPE = &H4000000\r\n    'DM_PANNINGWIDTH = &H20000000\r\n    'DM_PANNINGHEIGHT = &H40000000\r\nEnd Enum\r\n\r\n\r\n' Enums of printer constants for translating between\r\n' values and friendly names.\r\nPrivate m_dEnum(0 To ePrintEnum.[_Last] - 1) As Dictionary\r\n\r\n' Printer structures in native mode\r\n' (Ready to write back to object)\r\nPrivate m_tDevMode As tDevMode\r\nPrivate m_tMip As tMip\r\nPrivate m_tDevNames As tDevNames\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasPrinterAssigned\r\n' Author    : Adam Waller\r\n' Date      : 5/18/2020\r\n' Purpose   : Returns true if the report or form has a printer specifically assigned\r\n'           : versus just using the default printer. (Make sure you load structures\r\n'           : before checking this value.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function HasPrinterAssigned() As Boolean\r\n    HasPrinterAssigned = (m_tDevNames.intDefault = 0)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadFromExportFile\r\n' Author    : Adam Waller\r\n' Date      : 5/20/2020\r\n' Purpose   : Load sections from export file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadFromExportFile(strFileContent As String)\r\n\r\n    Dim varLines As Variant\r\n    Dim lngLine As Long\r\n    Dim cBlock(1 To 3) As clsConcat\r\n    Dim cBuffer(1 To 3) As clsConcat\r\n    Dim strHex As String\r\n    Dim strChar As String\r\n    Dim bteBuffer() As Byte\r\n    Dim intBlock As Integer\r\n    Dim strLine As String\r\n    Dim lngChar As Long\r\n    Dim lngPos As Long\r\n    Dim udtMipBuffer As tMipBuffer\r\n    Dim udtDevModeBuffer As tDevModeBuffer\r\n    Dim udtDevNamesBuffer As tDevNamesBuffer\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Blocks: 1=Mip, 2=DevMode, 3=DevNames\r\n\r\n    ' Clear existing structures and create block classes.\r\n    ClearStructures\r\n\r\n    If Len(strFileContent) = 0 Then Exit Sub\r\n\r\n    ' Read the text file line by line, loading the block data\r\n    Perf.OperationStart \"Read File DevMode\"\r\n    varLines = Split(strFileContent, vbCrLf)\r\n    For lngLine = 0 To UBound(varLines)\r\n        strLine = Trim$(varLines(lngLine))\r\n        ' Look for header if not inside block\r\n        If intBlock = 0 Then\r\n            ' Check for header\r\n            Select Case strLine\r\n                Case \"PrtMip = Begin\":      intBlock = 1\r\n                Case \"PrtDevMode = Begin\":  intBlock = 2\r\n                Case \"PrtDevNames = Begin\": intBlock = 3\r\n            End Select\r\n        Else\r\n            ' Inside block\r\n            If strLine = \"End\" Then\r\n                intBlock = 0\r\n                ' Exit loop after adding all the blocks\r\n                If Not (cBlock(1) Is Nothing _\r\n                    Or cBlock(2) Is Nothing _\r\n                    Or cBlock(3) Is Nothing) Then Exit For\r\n            ElseIf Left$(strLine, 2) = \"0x\" Then\r\n                ' Create block class, if it doesn't exist\r\n                If cBlock(intBlock) Is Nothing Then Set cBlock(intBlock) = New clsConcat\r\n                ' Add bytes after the \"0x\" prefix, and before the \" ,\"\r\n                ' at the end of the line.\r\n                cBlock(intBlock).Add Mid$(strLine, 3, Len(strLine) - 4)\r\n            ElseIf strLine = \"Begin\" Then\r\n                ' Reached the end of the header section. We should\r\n                ' have already exited the loop, but just in case...\r\n                Exit For\r\n            End If\r\n        End If\r\n    Next lngLine\r\n\r\n    ' Convert hex block data to string\r\n    strChar = \"&h00\"\r\n    For intBlock = 1 To 3\r\n        ' Block will not be created if not found in source file.\r\n        ' (Such as a file that was already sanitized.)\r\n        If Not cBlock(intBlock) Is Nothing Then\r\n            strHex = cBlock(intBlock).GetStr\r\n            Set cBuffer(intBlock) = New clsConcat\r\n            ' Each two hex characters represent one bit\r\n            ReDim bteBuffer(0 To (Len(strHex) / 2) + 1)\r\n            ' Loop through each set of 2 characters to get bytes\r\n            For lngChar = 1 To Len(strHex) Step 2\r\n                ' Apply two characters to buffer. (Faster than concatenating strings)\r\n                Mid$(strChar, 3, 2) = Mid$(strHex, lngChar, 2)\r\n                lngPos = ((lngChar + 1) / 2) - 1\r\n                bteBuffer(lngPos) = CLng(strChar)\r\n            Next lngChar\r\n            Select Case intBlock\r\n                Case 1\r\n                    udtMipBuffer.strBuffer = bteBuffer\r\n                    LSet m_tMip = udtMipBuffer\r\n                Case 2\r\n                    udtDevModeBuffer.strBuffer = bteBuffer\r\n                    LSet m_tDevMode = udtDevModeBuffer\r\n                Case 3\r\n                    udtDevNamesBuffer.strBuffer = bteBuffer\r\n                    LSet m_tDevNames = udtDevNamesBuffer\r\n            End Select\r\n        End If\r\n    Next intBlock\r\n    Perf.OperationEnd\r\n\r\n    CatchAny eelError, \"Error loading printer settings from file content.\", _\r\n        ModuleName(Me) & \".LoadFromExportFile\", True, True\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadFromDefaultPrinter\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2020\r\n' Purpose   : Loads print settings from the default printer.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadFromDefaultPrinter()\r\n    LoadFromPrinter Application.Printer.DeviceName\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadFromPrinter\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2020\r\n' Purpose   : Loads print settings for the specified printer through the API. This gives\r\n'           : us a set of the basic print settings to which we can apply customizations.\r\n'           : The following link was helpful in working out the details of doing this\r\n'           : through the Windows API:\r\n'           : http://www.lessanvaezi.com/changing-printer-settings-using-the-windows-api/\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadFromPrinter(strPrinter As String)\r\n\r\n    Dim hPrinter As LongPtr\r\n    Dim lngReturn As Long\r\n    Dim strBuffer As String\r\n    Dim udtBuffer As tDevModeBuffer\r\n    Dim objPrinter As Access.Printer\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Clear our existing devmode structures\r\n    ClearStructures\r\n\r\n    ' Open a handle to read the default printer\r\n    lngReturn = OpenPrinter(strPrinter, hPrinter, ByVal 0&)\r\n\r\n    CatchAny eelError, \"Error getting printer pointer \" & strPrinter, ModuleName(Me) & \".LoadFromPrinter\", True, True\r\n    If lngReturn <> 0 And hPrinter <> 0 Then\r\n\r\n        ' Check size of DevMode structure to make sure it fits in our buffer.\r\n        lngReturn = DocumentProperties(0, hPrinter, strPrinter, 0, 0, 0)\r\n        If lngReturn > 0 Then\r\n            ' Read the devmode structure\r\n            strBuffer = NullPad(lngReturn + 100)\r\n            lngReturn = DocumentProperties(0, hPrinter, strPrinter, StrPtr(strBuffer), 0, DM_OUT_BUFFER)\r\n\r\n            If lngReturn > 0 Then\r\n                ' Load into DevMode type\r\n                udtBuffer.strBuffer = strBuffer\r\n                LSet m_tDevMode = udtBuffer\r\n\r\n            End If\r\n        Else\r\n            Log.Error eelWarning, \"There has been an error with loading DevMode structure. lngReturn:'\" & lngReturn & \"'\", _\r\n                ModuleName(Me) & \".LoadFromPrinter\"\r\n        End If\r\n    End If\r\n\r\n    CatchAny eelError, \"Error getting printer devMode \" & strPrinter, ModuleName(Me) & \".LoadFromPrinter\", True, True\r\n    ' Close printer handle\r\n    If hPrinter <> 0 Then ClosePrinter hPrinter\r\n\r\n    ' Attempt to load the printer object\r\n    Set objPrinter = GetPrinterByName(strPrinter)\r\n\r\n    If objPrinter Is Nothing Then\r\n        Log.Error eelWarning, \"Could not find printer '\" & strPrinter & \"' on this system.\", _\r\n            ModuleName(Me) & \".LoadFromPrinter\"\r\n    Else\r\n        ' Load in the DevNames structure\r\n        If Options.ShowDebug Then Log.Add \"Loading Printer info for: '\" & strPrinter & \"'.\"\r\n\r\n        SetDevNames objPrinter\r\n        ' Load in the margin defaults\r\n        SetMipFromPrinter objPrinter\r\n    End If\r\n\r\n    CatchAny eelError, \"Error with printer devMode \" & strPrinter, _\r\n        ModuleName(Me) & \".LoadFromPrinter\", True, True\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadFromReport/Form\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2020\r\n' Purpose   : Wrapper functions for loading objects by type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadFromReport(rptReport As Access.Report)\r\n    LoadFromObject rptReport\r\nEnd Sub\r\nPublic Sub LoadFromForm(frmForm As Access.Form)\r\n    LoadFromObject frmForm\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2020\r\n' Purpose   : Return the loaded structures in a dictionary format. (For saving to\r\n'           : Version Control.) Enums are translated to appropriate values.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetDictionary() As Dictionary\r\n    Set GetDictionary = New Dictionary\r\n    With GetDictionary\r\n        ' Only add device information if not using the default printer.\r\n        If DevNamesHasData And (m_tDevNames.intDefault = 0) Then .Add \"Device\", DevNamesToDictionary()\r\n        If DevModeHasData Then .Add \"Printer\", DevModeToExport()\r\n        If MipHasData Then .Add \"Margins\", MipToDictionary()\r\n    End With\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DevModeToExport\r\n' Author    : Adam Waller\r\n' Date      : 11/9/2020\r\n' Purpose   : Return a dictionary of the DevMode settings that we have selected\r\n'           : to export, based on the current options.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function DevModeToExport() As Dictionary\r\n\r\n    Dim varKey As Variant\r\n    Dim dDM As Dictionary\r\n    Dim dOpt As Dictionary\r\n\r\n    Set dDM = DevModeToDictionary\r\n    Set dOpt = Options.ExportPrintSettings\r\n    Set DevModeToExport = New Dictionary\r\n\r\n    With DevModeToExport\r\n        For Each varKey In dDM.Keys\r\n            If dOpt.Exists(varKey) Then\r\n                If CBool(dOpt(varKey)) Then\r\n                    .Add varKey, dDM(varKey)\r\n                End If\r\n            End If\r\n        Next varKey\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DevModeHasData\r\n' Author    : Adam Waller\r\n' Date      : 11/4/2020\r\n' Purpose   : Wrapper functions to ensure that we have loaded data into the following\r\n'           : structures. Sometimes an export file may not contain all of these\r\n'           : sections, in which case we should not attempt to map it to a dictionary.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function DevModeHasData() As Boolean\r\n    ' Should have fields flag set\r\n    DevModeHasData = (m_tDevMode.lngFields > 0)\r\nEnd Function\r\nPrivate Function DevNamesHasData() As Boolean\r\n    ' Look for a driver offset. (Should always have this, if set.)\r\n    DevNamesHasData = (m_tDevNames.intDriverOffset > 0)\r\nEnd Function\r\nPrivate Function MipHasData() As Boolean\r\n    ' Item layout should either be 1953 or 1954\r\n    MipHasData = (m_tMip.rItemLayout > 0)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasData\r\n' Author    : Adam Waller\r\n' Date      : 1/14/2021\r\n' Purpose   : Returns true if we have data in any of the three structures.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function HasData() As Boolean\r\n    HasData = (DevModeHasData Or DevNamesHasData Or MipHasData)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadFromObject\r\n' Author    : Adam Waller\r\n' Date      : 10/22/2020\r\n' Purpose   : Load settings from a form or report object\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub LoadFromObject(objSource As Object)\r\n\r\n    Dim udtDevModeBuffer As tDevModeBuffer\r\n    Dim udtDevNamesBuffer As tDevNamesBuffer\r\n    Dim udtMipBuffer As tMipBuffer\r\n\r\n    ' Clear any existing structure data\r\n    ClearStructures\r\n\r\n    ' DevMode\r\n    If Not IsNull(objSource.PrtDevMode) Then\r\n        udtDevModeBuffer.strBuffer = objSource.PrtDevMode\r\n        LSet m_tDevMode = udtDevModeBuffer\r\n    End If\r\n\r\n    ' DevNames\r\n    If Not IsNull(objSource.PrtDevNames) Then\r\n        With udtDevNamesBuffer\r\n            ' Pad right side of buffer with nulls rather than spaces.\r\n            .strBuffer = objSource.PrtDevNames & NullPad(Len(.strBuffer) - Len(objSource.PrtDevNames))\r\n        End With\r\n        LSet m_tDevNames = udtDevNamesBuffer\r\n    End If\r\n\r\n    ' Mip (Margins)\r\n    If Not IsNull(objSource.PrtMip) Then\r\n        udtMipBuffer.strBuffer = objSource.PrtMip\r\n        LSet m_tMip = udtMipBuffer\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ClearStructures\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2020\r\n' Purpose   : Clear the existing structures.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ClearStructures()\r\n\r\n    Dim tDevModeBlank As tDevMode\r\n    Dim tMipBlank As tMip\r\n    Dim tDevNamesBlank As tDevNames\r\n\r\n    m_tDevMode = tDevModeBlank\r\n    m_tMip = tMipBlank\r\n    m_tDevNames = tDevNamesBlank\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DevModeToDictionary\r\n' Author    : Adam Waller\r\n' Date      : 5/7/2020\r\n' Purpose   : Convert a DEVMODE type to a dictionary. Only outputs meaningful fields.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function DevModeToDictionary() As Dictionary\r\n\r\n    Dim strName As String\r\n    Dim lngFld As Long\r\n    Dim cDM As tDevMode\r\n\r\n    LSet cDM = m_tDevMode\r\n    lngFld = cDM.lngFields\r\n\r\n    Set DevModeToDictionary = New Dictionary\r\n\r\n    With DevModeToDictionary\r\n        strName = NTrim(StrConv(cDM.strDeviceName, vbUnicode))\r\n        ' Only save the printer name here if it is not the default printer.\r\n        If strName <> vbNullString And m_tDevNames.intDefault = 0 Then .Add \"DeviceName\", strName\r\n        '.Add \"SpecVersion\", cDM.intSpecVersion\r\n        '.Add \"DriverVersion\", cDM.intDriverVersion\r\n        '.Add \"Size\", cDM.intSize\r\n        '.Add \"DriverExtra\", cDM.intDriverExtra\r\n        '.Add \"Fields\", cDM.lngFields\r\n        If BitSet(lngFld, DM_ORIENTATION) Then .Add \"Orientation\", GetEnum(epeOrientation, cDM.intOrientation)\r\n        If BitSet(lngFld, DM_PAPERSIZE) Then .Add \"PaperSize\", GetEnum(epePaperSize, cDM.intPaperSize)\r\n        If BitSet(lngFld, DM_PAPERLENGTH) Then .Add \"PaperLength\", Round(cDM.intPaperLength * TEN_MIL, 2)\r\n        If BitSet(lngFld, DM_PAPERWIDTH) Then .Add \"PaperWidth\", Round(cDM.intPaperWidth * TEN_MIL, 2)\r\n        If BitSet(lngFld, DM_SCALE) Then .Add \"Scale\", cDM.intScale\r\n        If BitSet(lngFld, DM_COPIES) And cDM.intCopies > 1 Then .Add \"Copies\", cDM.intCopies    ' Only add for more than 1 copy\r\n        If BitSet(lngFld, DM_DEFAULTSOURCE) Then .Add \"DefaultSource\", GetEnum(epePaperBin, cDM.intDefaultSource)\r\n        If BitSet(lngFld, DM_PRINTQUALITY) Then .Add \"PrintQuality\", GetEnum(epePrintQuality, cDM.intPrintQuality)\r\n        If BitSet(lngFld, DM_COLOR) Then .Add \"Color\", GetEnum(epeColor, cDM.intColor)\r\n        If BitSet(lngFld, DM_DUPLEX) Then .Add \"Duplex\", GetEnum(epeDuplex, cDM.intDuplex)\r\n        If BitSet(lngFld, DM_YRESOLUTION) Then .Add \"Resolution\", cDM.intResolution\r\n        If BitSet(lngFld, DM_TTOPTION) Then .Add \"TTOption\", GetEnum(epeTTOption, cDM.intTTOption)\r\n        If BitSet(lngFld, DM_COLLATE) Then .Add \"Collate\", GetEnum(epeCollate, cDM.intCollate)\r\n        If BitSet(lngFld, DM_FORMNAME) Then .Add \"FormName\", NTrim(StrConv(cDM.strFormName, vbUnicode))\r\n        '.Add \"UnusedPadding\", cDM.intUnusedPadding\r\n        '.Add \"BitsPerPel\", cDM.intBitsPerPel\r\n        '.Add \"PelsWidth\", cDM.lngPelsWidth\r\n        '.Add \"PelsHeight\", cDM.lngPelsHeight\r\n        If BitSet(lngFld, DM_DISPLAYFLAGS) Then .Add \"DisplayFlags\", GetEnum(epeDisplayFlags, cDM.lngDisplayFlags)\r\n        If BitSet(lngFld, DM_DISPLAYFREQUENCY) Then .Add \"DisplayFrequency\", cDM.lngDisplayFrequency\r\n        If BitSet(lngFld, DM_ICMMETHOD) Then .Add \"ICMMethod\", GetEnum(epeICMMethod, cDM.lngICMMethod)\r\n        If BitSet(lngFld, DM_ICMINTENT) Then .Add \"ICMIntent\", GetEnum(epeICMIntent, cDM.lngICMIntent)\r\n        If BitSet(lngFld, DM_MEDIATYPE) Then .Add \"MediaType\", GetEnum(epeMediaType, cDM.lngMediaType)\r\n        If BitSet(lngFld, DM_DITHERTYPE) Then .Add \"DitherType\", GetEnum(epeDitherType, cDM.lngDitherType)\r\n        '.Add \"Reserved1\", cDM.lngReserved1\r\n        '.Add \"Reserved2\", cDM.lngReserved2\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MipToDictionary\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2020\r\n' Purpose   : Convert the printer margins to a dictionary object. Use inches for sizes\r\n'           : to match what the user sees in the margin dialogs. (1440 twips per inch)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function MipToDictionary() As Dictionary\r\n\r\n    Dim cMip As tMip\r\n\r\n    LSet cMip = m_tMip\r\n    Set MipToDictionary = New Dictionary\r\n\r\n    With MipToDictionary\r\n        .Add \"LeftMargin\", GetInch(cMip.xLeftMargin)\r\n        .Add \"TopMargin\", GetInch(cMip.yTopMargin)\r\n        .Add \"RightMargin\", GetInch(cMip.xRightMargin)\r\n        .Add \"BotMargin\", GetInch(cMip.yBotMargin)\r\n        .Add \"DataOnly\", CBool(cMip.fDataOnly)\r\n        .Add \"Width\", GetInch(cMip.xWidth)\r\n        .Add \"Height\", GetInch(cMip.yHeight)\r\n        .Add \"DefaultSize\", CBool(cMip.fDefaultSize)\r\n        .Add \"Columns\", cMip.cxColumns\r\n        .Add \"ColumnSpacing\", GetInch(cMip.yColumnSpacing)\r\n        .Add \"RowSpacing\", GetInch(cMip.xRowSpacing)\r\n        .Add \"ItemLayout\", GetEnum(epeColumnLayout, cMip.rItemLayout)\r\n        .Add \"FastPrint\", cMip.fFastPrint  ' Reserved\r\n        .Add \"Datasheet\", cMip.fDatasheet  ' Reserved\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DevNamesToDictionary\r\n' Author    : Adam Waller\r\n' Date      : 10/30/2020\r\n' Purpose   : Return a dictionary object with the PrtDevNames values.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function DevNamesToDictionary() As Dictionary\r\n\r\n    Dim cDN As tDevNames\r\n\r\n    LSet cDN = m_tDevNames\r\n    Set DevNamesToDictionary = New Dictionary\r\n\r\n    With DevNamesToDictionary\r\n        .Add \"DriverName\", NTrim(Mid$(StrConv(cDN.strData, vbUnicode), cDN.intDriverOffset - 7))\r\n        .Add \"DeviceName\", NTrim(Mid$(StrConv(cDN.strData, vbUnicode), cDN.intDeviceOffset - 7))\r\n        .Add \"Port\", NTrim(Mid$(StrConv(cDN.strData, vbUnicode), cDN.intOutputOffset - 7))\r\n        .Add \"Default\", (cDN.intDefault = 1)\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetMargins\r\n' Author    : Adam Waller\r\n' Date      : 10/22/2020\r\n' Purpose   : Sets the printer margins based on dictionary values, as read from\r\n'           : source file or MIP structure. Thankfully we can set all of these\r\n'           : using the Access object model.  :-)\r\n'           : Note that this does not use type checking to verify that values\r\n'           : have not been messed up. If you put a string value in a margin property\r\n'           : for example, it will throw an error.\r\n'           : Reference: http://etutorials.org/Microsoft+Products/access/Chapter+5.+Printers/Recipe+5.3+Programmatically+Change+Margin+and+Column+Settings+for+Reports/\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetPrinterMargins(oPrinter As Access.Printer, dMargins As Dictionary)\r\n\r\n    Dim varKey As Variant\r\n\r\n    ' Loop through properties.\r\n    With oPrinter\r\n        For Each varKey In dMargins.Keys\r\n            Select Case varKey\r\n\r\n                ' Set margins from dictionary values\r\n                Case \"LeftMargin\": .LeftMargin = GetTwips(dMargins(varKey))\r\n                Case \"TopMargin\": .TopMargin = GetTwips(dMargins(varKey))\r\n                Case \"RightMargin\": .RightMargin = GetTwips(dMargins(varKey))\r\n                Case \"BotMargin\": .BottomMargin = GetTwips(dMargins(varKey))\r\n                Case \"DataOnly\": .DataOnly = dMargins(varKey)\r\n                Case \"Columns\": .ItemsAcross = dMargins(varKey)\r\n                Case \"ColumnSpacing\": .ColumnSpacing = GetTwips(dMargins(varKey))\r\n                Case \"RowSpacing\": .RowSpacing = GetTwips(dMargins(varKey))\r\n                Case \"ItemLayout\": .ItemLayout = GetEnum(epeColumnLayout, dMargins(varKey))\r\n\r\n                ' Special handling for paper size\r\n                Case \"DefaultSize\": .DefaultSize = dMargins(varKey)\r\n                Case \"Width\":\r\n                    If .ItemSizeWidth <> GetTwips(dMargins(varKey)) Then\r\n                        If .DefaultSize Then .DefaultSize = False\r\n                        .ItemSizeWidth = GetTwips(dMargins(varKey))\r\n                    End If\r\n                Case \"Height\":\r\n                    If .ItemSizeHeight <> GetTwips(dMargins(varKey)) Then\r\n                        If .DefaultSize Then .DefaultSize = False\r\n                        .ItemSizeHeight = GetTwips(dMargins(varKey))\r\n                    End If\r\n\r\n                Case Else\r\n                    ' Could not find that property.\r\n                    MsgBox \"Margin property \" & CStr(varKey) & \" not found.\", vbExclamation\r\n            End Select\r\n        Next varKey\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetPrinterOptions\r\n' Author    : Adam Waller\r\n' Date      : 10/22/2020\r\n' Purpose   : Applies the dictionary of options to a printer. For the properties that\r\n'           : can be set through the object model, they are applied that way. For those\r\n'           : that can only be set through the DevMode API structure, they are applied\r\n'           : through updating the DevMode.\r\n'           : Reference: http://etutorials.org/Microsoft+Products/access/Chapter+5.+Printers/Recipe+5.4+Programmatically+Change+Printer+Options/\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetPrinterOptions(objFormOrReport As Object, dSettings As Dictionary)\r\n\r\n    Dim oPrinter As Access.Printer\r\n    Dim intType As Integer\r\n    Dim intCnt As Integer\r\n    Dim strForm As String\r\n    Dim bteForm() As Byte\r\n    Dim varKey As Variant\r\n    Dim blnSetDevMode As Boolean\r\n    Dim strDevModeExtra As String\r\n    Dim tBuffer As tDevModeBuffer\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Make sure we are using the correct object type\r\n    If TypeOf objFormOrReport Is Access.Report Then\r\n        intType = acReport\r\n    ElseIf TypeOf objFormOrReport Is Access.Form Then\r\n        intType = acForm\r\n    Else\r\n        Log.Error eelCritical, \"Can only set printer options for a form or report object: \" & _\r\n            objFormOrReport.Name, ModuleName(Me) & \".SetPrinterOptions\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Check printer device to see if we are using a specific printer\r\n    If dSettings.Exists(\"DeviceName\") Then\r\n        Set oPrinter = GetPrinterByName(dSettings(\"DeviceName\"))\r\n        If oPrinter Is Nothing Then\r\n            Log.Error eelWarning, \"Printer \" & dSettings(\"DeviceName\") & \" not found for \" & objFormOrReport.Name, _\r\n                ModuleName(Me) & \":SetPrinterOptions\"\r\n            Exit Sub\r\n        End If\r\n        ' Set as printer for this report or form.\r\n        With objFormOrReport\r\n            Set .Printer = oPrinter\r\n            .UseDefaultPrinter = False\r\n        End With\r\n    Else\r\n        ' Use default printer (If not already set)\r\n        objFormOrReport.UseDefaultPrinter = True\r\n    End If\r\n\r\n    ' Apply regular printer options\r\n    Set oPrinter = objFormOrReport.Printer\r\n    With oPrinter\r\n        For Each varKey In dSettings.Keys\r\n            Select Case varKey\r\n                Case \"Orientation\": .Orientation = GetEnum(epeOrientation, dSettings(varKey))\r\n                Case \"PaperSize\": .PaperSize = GetEnum(epePaperSize, dSettings(varKey))\r\n                Case \"Copies\": .Copies = dSettings(varKey)\r\n                Case \"PrintQuality\": .PrintQuality = GetEnum(epePrintQuality, dSettings(varKey))\r\n                Case \"Color\": .ColorMode = GetEnum(epeColor, dSettings(varKey))\r\n                Case \"Duplex\": .Duplex = GetEnum(epeDuplex, dSettings(varKey))\r\n                Case \"DefaultSource\": .PaperBin = GetEnum(epePaperBin, dSettings(varKey))\r\n            End Select\r\n        Next varKey\r\n    End With\r\n\r\n    ' Other properties will require some more work, since we need to interact\r\n    ' with the DevMode structure.\r\n    LoadFromObject objFormOrReport\r\n    strDevModeExtra = objFormOrReport.PrtDevMode\r\n\r\n    ' Loop through properties again, this time applying change to DevMode structure.\r\n    With m_tDevMode\r\n        For Each varKey In dSettings.Keys\r\n            Select Case varKey\r\n                Case \"PaperLength\": SetDmProp .intPaperLength, DM_PAPERLENGTH, Round(dSettings(varKey) / TEN_MIL, 0), .lngFields, blnSetDevMode\r\n                Case \"PaperWidth\":  SetDmProp .intPaperWidth, DM_PAPERWIDTH, Round(dSettings(varKey) / TEN_MIL, 0), .lngFields, blnSetDevMode\r\n                Case \"Scale\":       SetDmProp .intScale, DM_SCALE, dSettings(varKey), .lngFields, blnSetDevMode\r\n                Case \"Resolution\":  SetDmProp .intResolution, DM_YRESOLUTION, dSettings(varKey), .lngFields, blnSetDevMode\r\n                Case \"TTOption\":    SetDmProp .intTTOption, DM_TTOPTION, GetEnum(epeTTOption, dSettings(varKey)), .lngFields, blnSetDevMode\r\n                Case \"Collate\":     SetDmProp .intCollate, DM_COLLATE, GetEnum(epeCollate, dSettings(varKey)), .lngFields, blnSetDevMode\r\n                Case \"DisplayFlags\":        SetDmProp .lngDisplayFlags, DM_DISPLAYFLAGS, GetEnum(epeDisplayFlags, dSettings(varKey)), .lngFields, blnSetDevMode\r\n                Case \"DisplayFrequency\":    SetDmProp .lngDisplayFrequency, DM_DISPLAYFREQUENCY, dSettings(varKey), .lngFields, blnSetDevMode\r\n                Case \"ICMMethod\":   SetDmProp .lngICMMethod, DM_ICMMETHOD, GetEnum(epeICMMethod, dSettings(varKey)), .lngFields, blnSetDevMode\r\n                Case \"ICMIntent\":   SetDmProp .lngICMIntent, DM_ICMINTENT, GetEnum(epeICMIntent, dSettings(varKey)), .lngFields, blnSetDevMode\r\n                Case \"MediaType\":   SetDmProp .lngMediaType, DM_MEDIATYPE, GetEnum(epeMediaType, dSettings(varKey)), .lngFields, blnSetDevMode\r\n                Case \"DitherType\":  SetDmProp .lngDitherType, DM_DITHERTYPE, GetEnum(epeDitherType, dSettings(varKey)), .lngFields, blnSetDevMode\r\n                Case \"FormName\"\r\n                    ' This one is a little more fun...\r\n                    If (Not BitSet(.lngFields, DM_FORMNAME)) _\r\n                        Or (dSettings(varKey) <> NTrim(StrConv(.strFormName, vbUnicode))) Then\r\n                        ' Assign byte arrays for string values\r\n                        strForm = StrConv(dSettings(varKey) & vbNullChar, vbFromUnicode)\r\n                        bteForm = strForm & NullPad(32 - Len(strForm))\r\n                        For intCnt = 1 To 32\r\n                            .strFormName(intCnt) = bteForm(intCnt - 1)\r\n                        Next intCnt\r\n                        blnSetDevMode = True\r\n                        ' Update fields flag\r\n                        If Not BitSet(.lngFields, DM_FORMNAME) Then\r\n                            .lngFields = .lngFields Or DM_FORMNAME\r\n                        End If\r\n                    End If\r\n            End Select\r\n        Next varKey\r\n    End With\r\n\r\n    ' Check flag to see if we have changed anything\r\n    If blnSetDevMode Then\r\n        tBuffer.strBuffer = Replace(tBuffer.strBuffer, \" \", vbNullChar)\r\n        LSet tBuffer = m_tDevMode\r\n        ' Overwrite first part of structure while preserving possible\r\n        ' extra data used by print driver.\r\n        Mid(strDevModeExtra, 1, 94) = tBuffer.strBuffer\r\n        objFormOrReport.PrtDevMode = strDevModeExtra\r\n    End If\r\n\r\n    ' Tweak a property so the report knows it needs to be saved.\r\n    With objFormOrReport\r\n        .Caption = .Caption\r\n    End With\r\n    CatchAny eelError, \"Error setting print settings for: \" & objFormOrReport.Name, _\r\n        ModuleName(Me) & \".SetPrinterOptions\", True, True\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ApplySettings\r\n' Author    : Adam Waller\r\n' Date      : 10/28/2020\r\n' Purpose   : Applies the dictionary object of printer settings to the current\r\n'           : DevMode, MIP and DevNames structures. Expects a dictionary structure\r\n'           : like the \"items\" collection that is created when saving print settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ApplySettings(dSettings As Dictionary)\r\n\r\n    Dim intCnt As Integer\r\n    Dim strForm As String\r\n    Dim bteForm() As Byte\r\n    Dim varKey As Variant\r\n    Dim blnSetDevMode As Boolean\r\n    Dim dItems As Dictionary\r\n    Dim strPrinter As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Set the properties in the DevNames structure.\r\n    ' Note that this simply sets the printer to one with a matching name. It doesn't try to reconstruct\r\n    ' an identical share and port name or install a missing printer.\r\n    strPrinter = dNZ(dSettings, \"Device\\DeviceName\")\r\n    If strPrinter = vbNullString Then\r\n        ' Use default printer\r\n        LoadFromDefaultPrinter\r\n        ' Clear the device name, since we are not binding this\r\n        ' form/report to a specific printer.\r\n        For intCnt = 1 To 32\r\n            m_tDevMode.strDeviceName(intCnt) = 0\r\n        Next intCnt\r\n    Else\r\n        ' Load defaults from specific printer\r\n        LoadFromPrinter strPrinter\r\n    End If\r\n\r\n    ' Set the properties in the DevMode structure.\r\n    If IsObject(dSettings(\"Printer\")) Then\r\n        With m_tDevMode\r\n            Set dItems = dSettings(\"Printer\")\r\n            For Each varKey In dItems.Keys\r\n                Select Case varKey\r\n                    ' Note that any specified DeviceName in m_tDevMode would have already been set through\r\n                    ' the intial call that loaded the DevMode structure directly from the printer using the Windows API.\r\n\r\n                    ' These properties can be set on the report/form object, or through PrtDevMode\r\n                    Case \"Orientation\": SetDmProp .intOrientation, DM_ORIENTATION, GetEnum(epeOrientation, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"PaperSize\":   SetDmProp .intPaperSize, DM_PAPERSIZE, GetEnum(epePaperSize, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"Copies\":      SetDmProp .intCopies, DM_COPIES, dItems(varKey), .lngFields, blnSetDevMode\r\n                    Case \"PrintQuality\":    SetDmProp .intPrintQuality, DM_PRINTQUALITY, GetEnum(epePrintQuality, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"Color\":       SetDmProp .intColor, DM_COLOR, GetEnum(epeColor, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"Duplex\":      SetDmProp .intDuplex, DM_DUPLEX, GetEnum(epeDuplex, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"DefaultSource\":   SetDmProp .intDefaultSource, DM_DEFAULTSOURCE, GetEnum(epePaperBin, dItems(varKey)), .lngFields, blnSetDevMode\r\n\r\n                    ' These can only be set through PrtDevMode\r\n                    Case \"PaperLength\": SetDmProp .intPaperLength, DM_PAPERLENGTH, Round(dItems(varKey) / TEN_MIL, 0), .lngFields, blnSetDevMode\r\n                    Case \"PaperWidth\":  SetDmProp .intPaperWidth, DM_PAPERWIDTH, Round(dItems(varKey) / TEN_MIL, 0), .lngFields, blnSetDevMode\r\n                    Case \"Scale\":       SetDmProp .intScale, DM_SCALE, dItems(varKey), .lngFields, blnSetDevMode\r\n                    Case \"Resolution\":  SetDmProp .intResolution, DM_YRESOLUTION, dItems(varKey), .lngFields, blnSetDevMode\r\n                    Case \"TTOption\":    SetDmProp .intTTOption, DM_TTOPTION, GetEnum(epeTTOption, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"Collate\":     SetDmProp .intCollate, DM_COLLATE, GetEnum(epeCollate, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"DisplayFlags\":        SetDmProp .lngDisplayFlags, DM_DISPLAYFLAGS, GetEnum(epeDisplayFlags, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"DisplayFrequency\":    SetDmProp .lngDisplayFrequency, DM_DISPLAYFREQUENCY, dItems(varKey), .lngFields, blnSetDevMode\r\n                    Case \"ICMMethod\":   SetDmProp .lngICMMethod, DM_ICMMETHOD, GetEnum(epeICMMethod, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"ICMIntent\":   SetDmProp .lngICMIntent, DM_ICMINTENT, GetEnum(epeICMIntent, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"MediaType\":   SetDmProp .lngMediaType, DM_MEDIATYPE, GetEnum(epeMediaType, dItems(varKey)), .lngFields, blnSetDevMode\r\n                    Case \"DitherType\":  SetDmProp .lngDitherType, DM_DITHERTYPE, GetEnum(epeDitherType, dItems(varKey)), .lngFields, blnSetDevMode\r\n\r\n                    ' String values are a little more fun...\r\n                    Case \"FormName\"\r\n                        If (Not BitSet(.lngFields, DM_FORMNAME)) _\r\n                            Or (dItems(varKey) <> NTrim(StrConv(.strFormName, vbUnicode))) Then\r\n                            ' Assign byte arrays for string values\r\n                            strForm = StrConv(dItems(varKey) & vbNullChar, vbFromUnicode)\r\n                            bteForm = strForm & NullPad(32 - Len(strForm))\r\n                            For intCnt = 1 To 32\r\n                                .strFormName(intCnt) = bteForm(intCnt - 1)\r\n                            Next intCnt\r\n                            blnSetDevMode = True\r\n                            ' Update fields flag\r\n                            If Not BitSet(.lngFields, DM_FORMNAME) Then\r\n                                .lngFields = .lngFields Or DM_FORMNAME\r\n                            End If\r\n                        End If\r\n                End Select\r\n            Next varKey\r\n        End With\r\n    End If\r\n\r\n    ' Set the printer margins in the MIP structure\r\n    If IsObject(dSettings(\"Margins\")) Then\r\n        With m_tMip\r\n            Set dItems = dSettings(\"Margins\")\r\n            For Each varKey In dItems.Keys\r\n                Select Case varKey\r\n\r\n                    ' Set margins from dictionary values\r\n                    Case \"LeftMargin\": .xLeftMargin = GetTwips(dItems(varKey))\r\n                    Case \"TopMargin\": .yTopMargin = GetTwips(dItems(varKey))\r\n                    Case \"RightMargin\": .xRightMargin = GetTwips(dItems(varKey))\r\n                    Case \"BotMargin\": .yBotMargin = GetTwips(dItems(varKey))\r\n                    Case \"DataOnly\": .fDataOnly = dItems(varKey)\r\n                    Case \"Columns\": .cxColumns = dItems(varKey)\r\n                    Case \"ColumnSpacing\": .yColumnSpacing = GetTwips(dItems(varKey))\r\n                    Case \"RowSpacing\": .xRowSpacing = GetTwips(dItems(varKey))\r\n                    Case \"ItemLayout\": .rItemLayout = GetEnum(epeColumnLayout, dItems(varKey))\r\n                    Case \"FastPrint\": .fFastPrint = Abs(CBool(dItems(varKey))) 'These are quite likely unneded; they do not appear to have an effect on the file creation/export.\r\n                    Case \"Datasheet\": .fDatasheet = Abs(CBool(dItems(varKey))) 'These are quite likely unneded; they do not appear to have an effect on the file creation/export.\r\n\r\n                    ' Special handling for paper size\r\n                    Case \"DefaultSize\": .fDefaultSize = Abs(dItems(varKey))\r\n                    Case \"Width\":\r\n                        If .xWidth <> GetTwips(dItems(varKey)) Then\r\n                            If CBool(.fDefaultSize) Then .fDefaultSize = Abs(False)\r\n                            .xWidth = GetTwips(dItems(varKey))\r\n                        End If\r\n                    Case \"Height\":\r\n                        If .yHeight <> GetTwips(dItems(varKey)) Then\r\n                            If CBool(.fDefaultSize) Then .fDefaultSize = Abs(False)\r\n                            .yHeight = GetTwips(dItems(varKey))\r\n                        End If\r\n\r\n                    Case Else\r\n                        ' Could not find that property.\r\n                        Log.Error eelWarning, \"Margin property \" & CStr(varKey) & \" not found.\", _\r\n                            ModuleName(Me) & \":ApplySettings\"\r\n                End Select\r\n            Next varKey\r\n        End With\r\n    End If\r\n    CatchAny eelError, \"Error applying print settings for: \" & strPrinter, _\r\n        ModuleName(Me) & \".ApplySettings\", True, True\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddToExportFile\r\n' Author    : Adam Waller\r\n' Date      : 11/2/2020\r\n' Purpose   : Creates a temporary file from the contents of strFile, inserting the\r\n'           : DevMode, DevNames and MIP blocks into the file header. This prepares the\r\n'           : file for import into the database using the loaded print settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddToExportFile(strFileContent As String) As String\r\n\r\n    Dim strLine As String\r\n    Dim varLines As Variant\r\n    Dim lngLine As Long\r\n    Dim blnFound As Boolean\r\n    Dim blnInBlock As Boolean\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Use concatenation class for performance reasons.\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n\r\n        ' Loop through lines in file, searching for location to insert blocks.\r\n        varLines = Split(strFileContent, vbCrLf)\r\n        For lngLine = LBound(varLines) To UBound(varLines)\r\n\r\n            ' Get single line\r\n            strLine = varLines(lngLine)\r\n\r\n            ' Check line contents till we reach the insertion point.\r\n            If Not blnFound Then\r\n                Select Case Trim$(strLine)\r\n                    Case \"PrtMip = Begin\", \"PrtDevMode = Begin\", \"PrtDevNames = Begin\", _\r\n                        \"PrtDevModeW = Begin\", \"PrtDevNamesW = Begin\"\r\n                        ' If we find any of these blocks in the file, we should remove\r\n                        ' them since they are being replaced with the inserted ones.\r\n                        blnInBlock = True\r\n                    Case \"End\"\r\n                        ' End of a block section.\r\n                        If Not blnInBlock Then .Add strLine\r\n                        blnInBlock = False\r\n                    Case \"Begin\"\r\n                        'Verify indent level\r\n                        If strLine <> \"    Begin\" Then\r\n                            .Add strLine\r\n                        Else\r\n                            ' Insert our blocks before this line.\r\n                            .Add GetPrtMipBlock\r\n                            .Add GetPrtDevModeBlock\r\n                            .Add GetPrtDevNamesBlock\r\n                            .Add strLine\r\n                            blnFound = True\r\n                        End If\r\n                    Case Else\r\n                        ' Continue building file contents\r\n                        .Add strLine\r\n                End Select\r\n            Else\r\n                ' Already inserted block content.\r\n                .Add strLine\r\n            End If\r\n        Next lngLine\r\n\r\n        ' Return content\r\n        AddToExportFile = .GetStr\r\n    End With\r\n\r\n    CatchAny eelError, \"Error adding print settings to export file.\", _\r\n        ModuleName(Me) & \".AddToExportFile\", True, True\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetInch\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2020\r\n' Purpose   : Convert a twips value to inches, rounded to 4 decimal places.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetInch(lngTwips As Long) As Single\r\n    GetInch = Round(lngTwips / 1440, 4)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTwips\r\n' Author    : Adam Waller\r\n' Date      : 10/22/2020\r\n' Purpose   : Return twips from inches\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetTwips(sngInches As Single) As Long\r\n    GetTwips = Round(sngInches * 1440, 0)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BitSet\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2020\r\n' Purpose   : Returns true if the flag is set.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function BitSet(lngFlags As Long, lngValue As edmFlags) As Boolean\r\n    BitSet = CBool((lngFlags And lngValue) = lngValue)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetDmProp\r\n' Author    : Adam Waller\r\n' Date      : 10/22/2020\r\n' Purpose   : Set a DevMode property, including the fields flag, if the value has\r\n'           : changed from the existing value. Sets the blnChanged flag to true if\r\n'           : the property was set or changed.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SetDmProp(ByRef cDMProp As Variant, lngFlag As edmFlags, varValue As Variant, ByRef lngFields As Long, ByRef blnChanged As Boolean)\r\n\r\n    ' Check existing flag\r\n    If Not BitSet(lngFields, lngFlag) Then\r\n        blnChanged = True\r\n    Else\r\n        ' Check existing value\r\n        If cDMProp <> varValue Then\r\n            ' Set property by name\r\n            cDMProp = varValue\r\n            blnChanged = True\r\n        End If\r\n    End If\r\n\r\n    ' Check fields flag, and update flag if we have made a change.\r\n    If blnChanged And Not BitSet(lngFields, lngFlag) Then lngFields = lngFields Or lngFlag\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetDevNames\r\n' Author    : Adam Waller\r\n' Date      : 10/30/2020\r\n' Purpose   : Wrapper to encode PrtDevNames values from passed printer object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SetDevNames(objPrinter As Access.Printer)\r\n\r\n    Dim strDriver As String\r\n    Dim strDevice As String\r\n    Dim strPort As String\r\n    Dim strData As String\r\n    Dim bteData() As Byte\r\n    Dim intCnt As Integer\r\n    Dim blnDefault As Boolean\r\n\r\n    ' Get device properties\r\n    With objPrinter\r\n        ' Default printer is stored differently.\r\n        blnDefault = (.DeviceName = Application.Printer.DeviceName)\r\n        If blnDefault Then\r\n            ' Determined the size by reviewing an exported report\r\n            ' that uses the default printer and noting the offsets.\r\n            strDriver = NullPad(23)\r\n            strDevice = NullPad(23)\r\n        Else\r\n            strDriver = .DriverName & vbNullChar\r\n            strDevice = .DeviceName & vbNullChar\r\n        End If\r\n        strPort = .Port & vbNullChar\r\n    End With\r\n\r\n    ' Fill in DevNames structure\r\n    With m_tDevNames\r\n        strData = strDriver & strDevice & strPort\r\n        .intDriverOffset = 8  ' This seems to match what I typically see...\r\n        .intDeviceOffset = .intDriverOffset + Len(strDriver)\r\n        .intOutputOffset = .intDeviceOffset + Len(strDevice)\r\n        .intDefault = Abs(blnDefault)\r\n        ' Convert string data to byte array\r\n        strData = StrConv(strData, vbFromUnicode)\r\n        bteData = strData & NullPad(255 - Len(strData))\r\n        For intCnt = 1 To 255\r\n            .strData(intCnt) = bteData(intCnt - 1)\r\n        Next intCnt\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetMipFromPrinter\r\n' Author    : Adam Waller\r\n' Date      : 11/2/2020\r\n' Purpose   : Sets the margins (binary) structure from the default values of a printer\r\n'           : object. This is used when building the MIP blob section before importing\r\n'           : a report from export files. This function primarily sets the defaults,\r\n'           : then the actual report margins are loaded from the JSON file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SetMipFromPrinter(objPrinter As Access.Printer)\r\n\r\n    ' Set margins from printer object\r\n    With objPrinter\r\n        m_tMip.xLeftMargin = .LeftMargin\r\n        m_tMip.yTopMargin = .TopMargin\r\n        m_tMip.xRightMargin = .RightMargin\r\n        m_tMip.yBotMargin = .BottomMargin\r\n        m_tMip.fDataOnly = Abs(.DataOnly)\r\n        m_tMip.cxColumns = .ItemsAcross\r\n        m_tMip.yColumnSpacing = .ColumnSpacing\r\n        m_tMip.xRowSpacing = .RowSpacing\r\n        m_tMip.rItemLayout = .ItemLayout\r\n        ' Paper size should just map across...\r\n        m_tMip.fDefaultSize = Abs(.DefaultSize)\r\n        m_tMip.xWidth = .ItemSizeWidth\r\n        m_tMip.yHeight = .ItemSizeHeight\r\n        ' Reserved properties\r\n        ' (Maybe set these to default values?)\r\n        'm_tMip.fFastPrint =\r\n        'm_tmip.fDatasheet =\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetPrinterByName\r\n' Author    : Adam Waller\r\n' Date      : 10/22/2020\r\n' Purpose   : Return a printer object matching a specific printer name. (Or nothing if\r\n'           : the printer name is not found.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetPrinterByName(strName As String) As Access.Printer\r\n    Dim prt As Access.Printer\r\n    For Each prt In Access.Printers\r\n        If prt.DeviceName = strName Then\r\n            Set GetPrinterByName = prt\r\n            Exit For\r\n        End If\r\n    Next prt\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetEnum\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2020\r\n' Purpose   : Return an enum value, 0 or UNKNOWN if not found.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetEnum(eType As ePrintEnum, varValue As Variant, Optional Convert As eEnumConversion = eecAuto) As Variant\r\n\r\n    Dim varKey As Variant\r\n    Dim varReturn As Variant\r\n\r\n    ' Build cached enum on first request\r\n    If m_dEnum(eType) Is Nothing Then BuildEnum eType\r\n\r\n    ' Determine conversion type\r\n    If Convert = eecAuto Then\r\n        If IsNumeric(varValue) Then\r\n            Convert = eecToName\r\n        Else\r\n            Convert = eecToEnum\r\n        End If\r\n    End If\r\n\r\n    ' By default, just return the original value if we don't find a match.\r\n    varReturn = varValue\r\n\r\n    ' See if we are trying to return the description or the enum value\r\n    If Convert = eecToName Then\r\n        If m_dEnum(eType).Exists(varValue) Then varReturn = m_dEnum(eType)(varValue)\r\n    Else\r\n        ' Search for matching description\r\n        For Each varKey In m_dEnum(eType).Keys\r\n            If m_dEnum(eType)(varKey) = varValue Then\r\n                varReturn = varKey\r\n                Exit For\r\n            End If\r\n        Next varKey\r\n    End If\r\n\r\n    ' Return value (if found)\r\n    GetEnum = varReturn\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildEnum\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2020\r\n' Purpose   : Builds out the pseudo enum values for constants. (Makes the values easier\r\n'           : to understand when stored in version control.)\r\n'           : These values were from the Access 2010 help file.\r\n'           : Additional enums can be added later if desired, such as media type or\r\n'           : true type font handling. See the following link for constants:\r\n'           : http://www.jasinskionline.com/windowsapi/ref/d/devmode.html\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub BuildEnum(eType As ePrintEnum)\r\n\r\n    Set m_dEnum(eType) = New Dictionary\r\n    With m_dEnum(eType)\r\n\r\n        Select Case eType\r\n            Case epeColor\r\n                .Add acPRCMColor, \"Color\"\r\n                .Add acPRCMMonochrome, \"Monochrome\"\r\n\r\n            Case epeDuplex\r\n                .Add acPRDPHorizontal, \"Horizontal\"\r\n                .Add acPRDPSimplex, \"Simplex\"\r\n                .Add acPRDPVertical, \"Vertical\"\r\n\r\n            Case epePrintQuality\r\n                .Add acPRPQDraft, \"Draft\"\r\n                .Add acPRPQHigh, \"High\"\r\n                .Add acPRPQLow, \"Low\"\r\n                .Add acPRPQMedium, \"Medium\"\r\n\r\n            Case epeOrientation\r\n                .Add acPRORLandscape, \"Landscape\"\r\n                .Add acPRORPortrait, \"Portrait\"\r\n\r\n            Case epePaperBin\r\n                .Add acPRBNAuto, \"Auto\"\r\n                .Add acPRBNCassette, \"Cassette\"\r\n                .Add acPRBNEnvelope, \"Envelope\"\r\n                .Add acPRBNEnvManual, \"Envelope Manual\"\r\n                .Add acPRBNFormSource, \"Form Source\"\r\n                .Add acPRBNLargeCapacity, \"Large Capacity\"\r\n                .Add acPRBNLargeFmt, \"Large Format\"\r\n                .Add acPRBNLower, \"Lower\"\r\n                .Add acPRBNManual, \"Manual\"\r\n                .Add acPRBNMiddle, \"Middle\"\r\n                .Add acPRBNSmallFmt, \"Small Format\"\r\n                .Add acPRBNTractor, \"Tractor\"\r\n                .Add acPRBNUpper, \"Upper\"\r\n\r\n            Case epePaperSize\r\n                .Add acPRPS10x14, \"10x14\"\r\n                .Add acPRPS11x17, \"11x17\"\r\n                .Add acPRPSA3, \"A3\"\r\n                .Add acPRPSA4, \"A4\"\r\n                .Add acPRPSA4Small, \"A4 Small\"\r\n                .Add acPRPSA5, \"A5\"\r\n                .Add acPRPSB4, \"B4\"\r\n                .Add acPRPSB5, \"B5\"\r\n                .Add acPRPSCSheet, \"C Size Sheet\"\r\n                .Add acPRPSDSheet, \"D Size Sheet\"\r\n                .Add acPRPSEnv10, \"Envelope #10\"\r\n                .Add acPRPSEnv11, \"Envelope #11\"\r\n                .Add acPRPSEnv12, \"Envelope #12\"\r\n                .Add acPRPSEnv14, \"Envelope #14\"\r\n                .Add acPRPSEnv9, \"Envelope #9\"\r\n                .Add acPRPSEnvB4, \"Envelope B4\"\r\n                .Add acPRPSEnvB5, \"Envelope B5\"\r\n                .Add acPRPSEnvB6, \"Envelope B6\"\r\n                .Add acPRPSEnvC3, \"Envelope C3\"\r\n                .Add acPRPSEnvC4, \"Envelope C4\"\r\n                .Add acPRPSEnvC5, \"Envelope C5\"\r\n                .Add acPRPSEnvC6, \"Envelope C6\"\r\n                .Add acPRPSEnvC65, \"Envelope C65\"\r\n                .Add acPRPSEnvDL, \"Envelope DL\"\r\n                .Add acPRPSEnvItaly, \"Italian Envelope\"\r\n                .Add acPRPSEnvMonarch, \"Monarch Envelope\"\r\n                .Add acPRPSEnvPersonal, \"Envelope\"\r\n                .Add acPRPSESheet, \"E Size Sheet\"\r\n                .Add acPRPSExecutive, \"Executive\"\r\n                .Add acPRPSFanfoldLglGerman, \"German Legal Fanfold\"\r\n                .Add acPRPSFanfoldStdGerman, \"German Standard Fanfold\"\r\n                .Add acPRPSFanfoldUS, \"U.S. Standard Fanfold\"\r\n                .Add acPRPSFolio, \"Folio\"\r\n                .Add acPRPSLedger, \"Ledger\"\r\n                .Add acPRPSLegal, \"Legal\"\r\n                .Add acPRPSLetter, \"Letter\"\r\n                .Add acPRPSLetterSmall, \"Letter Small\"\r\n                .Add acPRPSNote, \"Note\"\r\n                .Add acPRPSQuarto, \"Quarto\"\r\n                .Add acPRPSStatement, \"Statement\"\r\n                .Add acPRPSTabloid, \"Tabloid\"\r\n                .Add acPRPSUser, \"User-Defined\"\r\n\r\n            Case epeColumnLayout\r\n                .Add acPRHorizontalColumnLayout, \"Horizontal Columns\"\r\n                .Add acPRVerticalColumnLayout, \"Vertical Columns\"\r\n\r\n            '--------------------\r\n            ' API constants\r\n            '--------------------\r\n\r\n            Case epeTTOption\r\n                .Add 1, \"Bitmap\"\r\n                .Add 2, \"Download\"\r\n                .Add 3, \"Substitute Device Font\"\r\n                .Add 4, \"Download Outline\"\r\n\r\n            Case epeCollate\r\n                .Add 0, \"False\"\r\n                .Add 1, \"True\"\r\n\r\n            Case epeDisplayFlags\r\n                .Add 1, \"Grayscale\"\r\n                .Add 2, \"Interlaced\"\r\n\r\n            Case epeICMMethod\r\n                .Add 1, \"None\"\r\n                .Add 2, \"System\"\r\n                .Add 3, \"Driver\"\r\n                .Add 4, \"Device\"\r\n\r\n            Case epeICMIntent\r\n                .Add 1, \"Saturate\"\r\n                .Add 2, \"Contrast\"\r\n                .Add 3, \"Colormetric\"\r\n\r\n            Case epeMediaType\r\n                .Add 1, \"Standard\"\r\n                .Add 2, \"Glossy\"\r\n                .Add 3, \"Transparency\"\r\n\r\n            Case epeDitherType\r\n                .Add 1, \"None\"\r\n                .Add 2, \"Coarse\"\r\n                .Add 3, \"Fine\"\r\n                .Add 4, \"Line Art\"\r\n                .Add 5, \"Grayscale\"\r\n\r\n        End Select\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : NTrim\r\n' Author    : Adam Waller\r\n' Date      : 5/18/2020\r\n' Purpose   : Trim a string to a null character terminator.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function NTrim(ByVal strText As String) As String\r\n    Dim lngPos As Long\r\n    lngPos = InStr(1, strText, vbNullChar)\r\n    If lngPos > 0 Then\r\n        NTrim = Left$(strText, lngPos - 1)\r\n    Else\r\n        NTrim = strText\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetHash\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Returns a hash of the devmode structure *without* the two reserved\r\n'           : settings of FastPrint and Datasheet. This is useful in comparing to\r\n'           : see if the print settings match the default printer settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetHash()\r\n\r\n    Dim dSettings As Dictionary\r\n\r\n    Set dSettings = Me.GetDictionary\r\n\r\n    ' Remove the reserved settings, which are Access specific and may not match the\r\n    ' settings retrieved from the default printer.\r\n    If dSettings.Exists(\"Margins\") Then\r\n        With dSettings(\"Margins\")\r\n            If .Exists(\"FastPrint\") Then .Remove \"FastPrint\"\r\n            If .Exists(\"Datasheet\") Then .Remove \"Datasheet\"\r\n        End With\r\n    End If\r\n\r\n    'Debug.Print ConvertToJson(dSettings, \"  \")\r\n    GetHash = GetDictionaryHash(dSettings)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetPrtDevModeBlock\r\n' Author    : Adam Waller\r\n' Date      : 10/27/2020\r\n' Purpose   : Return a formatted PrtDevMode block.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetPrtDevModeBlock() As String\r\n    Dim udtBuffer As tDevModeBuffer\r\n    udtBuffer.strBuffer = Replace(udtBuffer.strBuffer, \" \", vbNullChar)\r\n    LSet udtBuffer = m_tDevMode\r\n    GetPrtDevModeBlock = GetBlobFromString(\"PrtDevMode\", udtBuffer.strBuffer)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetPrtMipBlock\r\n' Author    : Adam Waller\r\n' Date      : 10/27/2020\r\n' Purpose   : Return a formatted PrtMip block.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetPrtMipBlock() As String\r\n    Dim udtBuffer As tMipBuffer\r\n    udtBuffer.strBuffer = Replace(udtBuffer.strBuffer, \" \", vbNullChar)\r\n    LSet udtBuffer = m_tMip\r\n    GetPrtMipBlock = GetBlobFromString(\"PrtMip\", udtBuffer.strBuffer)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetPrtDevNamesBlock\r\n' Author    : Adam Waller\r\n' Date      : 10/27/2020\r\n' Purpose   : Return a formatted PrtDevNames block.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetPrtDevNamesBlock() As String\r\n    Dim udtBuffer As tDevNamesBuffer\r\n    udtBuffer.strBuffer = Replace(udtBuffer.strBuffer, \" \", vbNullChar)\r\n    LSet udtBuffer = m_tDevNames\r\n    GetPrtDevNamesBlock = GetBlobFromString(\"PrtDevNames\", RTrimNulls(udtBuffer.strBuffer, 1))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetBlobFromString\r\n' Author    : Adam Waller\r\n' Date      : 10/27/2020\r\n' Purpose   : Convert a string to a hexidecimal binary representation used in Access\r\n'           : report sections like PrtDevMode.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetBlobFromString(strSection As String, strContent As String, Optional intIndent As Integer = 4) As String\r\n\r\n    Dim intCol As Integer\r\n    Dim lngPos As Long\r\n    Dim bteContent() As Byte\r\n    Dim lngLen As Long\r\n    Dim strBte As String\r\n\r\n    ' Use concatenation class to drastically improve string handling performance.\r\n    With New clsConcat\r\n\r\n        ' Start with section beginning\r\n        .Add Space$(intIndent), strSection, \" = Begin\", vbCrLf\r\n\r\n        ' Convert string to byte array\r\n        bteContent = strContent\r\n        lngLen = Len(strContent) * 2\r\n\r\n        ' Build content lines\r\n        Do While lngPos < lngLen\r\n            .Add Space$(intIndent + 4), \"0x\"\r\n            For intCol = 0 To 31\r\n                If lngPos + intCol >= lngLen Then\r\n                    lngPos = lngLen\r\n                    Exit For\r\n                Else\r\n                    strBte = LCase(Hex$(bteContent(lngPos + intCol)))\r\n                    If Len(strBte) = 1 Then .Add \"0\"\r\n                    .Add strBte\r\n                End If\r\n            Next intCol\r\n            lngPos = lngPos + 32\r\n            If lngPos < lngLen Then .Add \" ,\", vbCrLf\r\n        Loop\r\n\r\n        ' Add section closing\r\n        .Add vbCrLf\r\n        .Add Space$(intIndent), \"End\"\r\n\r\n        ' Return blob string\r\n        GetBlobFromString = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : NullPad\r\n' Author    : Adam Waller\r\n' Date      : 10/30/2020\r\n' Purpose   : Like the Space$() function, but uses vbNullChar instead.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function NullPad(lngNumber As Long) As String\r\n    NullPad = Replace$(Space$(lngNumber), \" \", vbNullChar)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RTrimNulls\r\n' Author    : Adam Waller\r\n' Date      : 10/30/2020\r\n' Purpose   : Trims the null characters off the right end of a string, leaving the\r\n'           : specified null characters at the end. (Used for variable length structures)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RTrimNulls(strData As String, lngLeaveCount As Long) As String\r\n\r\n    Dim lngCnt As Long\r\n    Dim strTrimmed As String\r\n\r\n    ' Walk backwards through the string, looking for the first non-null character.\r\n    If InStr(1, strData, vbNullChar) > 0 Then\r\n        For lngCnt = Len(strData) To 1 Step -1\r\n            If Mid$(strData, lngCnt, 1) <> vbNullChar Then\r\n                ' Found a non-null character.\r\n                strTrimmed = Left$(strData, lngCnt) & NullPad(lngLeaveCount)\r\n                Exit For\r\n            End If\r\n        Next lngCnt\r\n    End If\r\n\r\n    ' Return result\r\n    If strTrimmed = vbNullString Then\r\n        ' If no nulls found, then return original data.\r\n        RTrimNulls = strData\r\n    Else\r\n        RTrimNulls = strTrimmed\r\n    End If\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsDotEnv.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsDotEnv\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsDotEnv\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Simple wrapper class for working with .env files\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' Dictionary of lines in the .env file\r\nPublic Lines As Dictionary\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadFromFile\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Load .env file contents into dictionary of lines.\r\n'           : (Comments will have incremented keys representing the current line number\r\n'           : like: \"COMMENT_001 = # My Comment\")\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function LoadFromFile(strFilePath As String)\r\n\r\n    Dim varLines As Variant\r\n    Dim strLine As String\r\n    Dim lngLine As Long\r\n    Dim lngPos As Long\r\n    Dim strKey As String\r\n    Dim strValue As String\r\n\r\n    ' Reset Lines dictionary\r\n    Class_Initialize\r\n\r\n    ' Attempt to read file to array of lines\r\n    varLines = Split(ReadFile(strFilePath), vbCrLf)\r\n    For lngLine = 0 To UBound(varLines)\r\n        strLine = varLines(lngLine)\r\n        If (Left(Trim(strLine), 1) = \"#\") _\r\n            Or (Trim(strLine) = vbNullString) Then\r\n            ' Add comment line or blank line\r\n            strKey = \"COMMENT_\" & Format(lngLine, \"###\")\r\n            Lines(strKey) = strLine\r\n        Else\r\n            ' Find first equals sign\r\n            lngPos = InStr(2, strLine, \"=\")\r\n            If lngPos > 0 Then\r\n                strKey = Trim(Left(strLine, lngPos - 1))\r\n                strValue = Mid(strLine, lngPos + 1)\r\n                Lines(strKey) = strValue\r\n            Else\r\n                ' Neither comment or key/value pair. Discard line\r\n            End If\r\n        End If\r\n    Next lngLine\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveToFile\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Save lines dictionary to file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function SaveToFile(strFilePath As String)\r\n\r\n    Dim varKey As Variant\r\n    Dim strKey As String\r\n\r\n    ' Rebuild content in .env file format\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        For Each varKey In Lines.Keys\r\n            strKey = varKey\r\n            If strKey Like \"COMMENT_*\" Then\r\n                ' Comment line\r\n                .Add Lines(strKey)\r\n            Else\r\n                ' Key=value pair\r\n                .Add strKey, \"=\", Lines(strKey)\r\n            End If\r\n        Next varKey\r\n\r\n        ' Remove any trailing blank lines\r\n        Do While .RightStr(2) = vbCrLf\r\n            .Remove 2\r\n        Loop\r\n\r\n        ' Write to file\r\n        WriteFile .GetStr, strFilePath\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetVar\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Return the variable from the key value\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetVar(strKey As String, Optional blnUseEnviron As Boolean = True) As String\r\n    Dim strEnv As String\r\n    If blnUseEnviron Then strEnv = Environ(strKey)\r\n    If Len(strEnv) Then\r\n        ' Use environment variable as override\r\n        GetVar = strEnv\r\n    Else\r\n        If Lines.Exists(strKey) Then GetVar = Lines(strKey)\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetVar\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Save a key=value pair, updating if it already exists.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetVar(strKey As String, strValue As String)\r\n    Lines(strKey) = strValue\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergeIntoDictionary\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Merge the .env keys into the destination dictionary. (Excluding comments)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MergeIntoDictionary(ByRef dDestination As Dictionary, Optional blnUseEnviron As Boolean = True)\r\n    Dim varKey As Variant\r\n    For Each varKey In Lines.Keys\r\n        If varKey Like \"COMMENT_*\" Then\r\n            ' Skip comment lines\r\n        Else\r\n            ' Add or replace existing values\r\n            dDestination(varKey) = GetVar(CStr(varKey), blnUseEnviron)\r\n        End If\r\n    Next varKey\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 7/20/2023\r\n' Purpose   : Initialize the lines dictionary\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    Set Lines = New Dictionary\r\n    Lines.CompareMode = TextCompare ' Case insensitive for key lookups\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsGitIntegration.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsGitIntegration\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsGitIntegration\r\n' Author    : Adam Waller\r\n' Date      : 3/10/2023\r\n' Purpose   : Commands and functions relating specifically to Git repositories.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' Hash for revision we are diffing from.\r\nPublic FromRevision As String\r\n\r\n' Set this to true to output git commands and return to debug window\r\nPublic ShowDebug As Boolean\r\nPublic LogCommands As Boolean\r\n\r\n' Working folder for git commands. (Used when operating outside the database project)\r\nPublic WorkingFolder As String\r\n\r\n' Enum for commands we can run with Git.\r\nPrivate Enum eGitCommand\r\n    egcGetVersion\r\n    egcGetHeadCommitDate\r\n    egcGetCommittedFiles\r\n    egcGetAllChangedFiles\r\n    egcGetUntrackedFiles\r\n    egcGetHeadCommit\r\n    egcGetBranchName\r\n    egcGetReproPath\r\n    egcGetRevision\r\n    egcGetStatusPorcelain\r\n    egcIsInsideTree\r\n    ' Action commands\r\n    egcSetTaggedCommit\r\n    egcInitialize\r\n    egcAddAll\r\n    egcCommit\r\n    egcResetHard\r\n    egcMerge\r\n    egcMergeNoFastFwd\r\n    egcCheckoutBranch\r\n    egcCheckoutNewBranch\r\n    egcCheckoutHeadToCurrent\r\n    egcDeleteBranch\r\nEnd Enum\r\n\r\n\r\n' The structure of this dictionary is very similar to the VCS Index of components.\r\nPrivate m_dChangedItems As Dictionary\r\nPrivate m_strRepositoryRoot As String\r\n\r\n\r\n' Peforms operations related to interrogating the status of Git\r\n' Note: All of these operations make certain assumptions:\r\n' 1) The database is in the root of the git repository.\r\n' 2) Source code is in the source\\ directory.\r\n\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RunGitCommand\r\n' Author    : Adam Waller\r\n' Date      : 11/24/2020\r\n' Purpose   : Run a git command, and return the result.\r\n'           : (Define the specific git commands in this function)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RunGitCommand(intCmd As eGitCommand, Optional strArgument As String) As String\r\n\r\n    Dim strCmd As String\r\n    Dim strResult As String\r\n\r\n    ' Translate enum to command\r\n    Select Case intCmd\r\n        Case egcGetHeadCommitDate:      strCmd = \"git show -s --format=%ci HEAD\"\r\n        Case egcGetCommittedFiles:      strCmd = \"git diff --name-status {MyArg}..HEAD\"\r\n        Case egcGetUntrackedFiles:      strCmd = \"git ls-files . --exclude-standard --others\"\r\n        Case egcGetVersion:             strCmd = \"git version\"\r\n        Case egcSetTaggedCommit:        strCmd = \"git tag {MyArg} HEAD -f\"\r\n        Case egcGetAllChangedFiles:     strCmd = \"git diff --name-status {MyArg}\"\r\n        Case egcGetBranchName:          strCmd = \"git rev-parse --abbrev-ref HEAD\"\r\n        Case egcGetHeadCommit:          strCmd = \"git show -s --format=%h HEAD\"\r\n        Case egcGetReproPath:           strCmd = \"git rev-parse --show-toplevel\"\r\n        Case egcGetRevision:            strCmd = \"git rev-parse --verify {MyArg}\"\r\n        Case egcGetStatusPorcelain:     strCmd = \"git status --porcelain\"\r\n        Case egcInitialize:             strCmd = \"git init\"\r\n        Case egcAddAll:                 strCmd = \"git add --all\"\r\n        Case egcCommit:                 strCmd = \"git commit --all --message \"\"{MyArg}\"\"\"\r\n        Case egcResetHard:              strCmd = \"git reset --hard HEAD^\"\r\n        Case egcMerge:                  strCmd = \"git merge {MyArg}\"\r\n        Case egcMergeNoFastFwd:         strCmd = \"git merge --no-ff {MyArg}\"\r\n        Case egcCheckoutBranch:         strCmd = \"git checkout {MyArg}\"\r\n        Case egcCheckoutNewBranch:      strCmd = \"git checkout -b {MyArg}\"\r\n        Case egcCheckoutHeadToCurrent:  strCmd = \"git checkout HEAD~ .\"\r\n        Case egcDeleteBranch:           strCmd = \"git branch --delete {MyArg}\"\r\n        Case egcIsInsideTree:           strCmd = \"git rev-parse --is-inside-work-tree\"\r\n        Case Else\r\n            Log.Error eelError, \"Unrecognized Git Command Enum: \" & intCmd\r\n            Stop\r\n    End Select\r\n\r\n    ' Add argument, if supplied\r\n    strCmd = Replace(strCmd, \"{MyArg}\", strArgument)\r\n\r\n    ' Run command, and get result\r\n    Perf.OperationStart \"Git Command (id:\" & intCmd & \")\"\r\n    strResult = ShellRun(strCmd, intCmd)\r\n    Perf.OperationEnd\r\n\r\n    ' Trim any trailing vbLf\r\n    If Right$(strResult, 1) = vbLf Then strResult = Left$(strResult, Len(strResult) - 1)\r\n    RunGitCommand = strResult\r\n\r\n    ' Show debug info, if flag is on\r\n    With New clsConcat\r\n        .Add \"Sent Git Command: \", strCmd\r\n        If strResult <> vbNullString Then\r\n            .Add \"Returned:\"\r\n            .Add strResult\r\n        End If\r\n        If Me.LogCommands Then Log.Add .GetStr, False\r\n        If Me.ShowDebug Then Debug.Print .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n\r\n' Return the datestamp of the current head commit\r\nPublic Function GetHeadCommitDate() As Date\r\n\r\n    Dim strDate As String\r\n    Dim varParts As Variant\r\n\r\n    ' Returns something like \"2020-11-23 16:08:47 -0600\"\r\n    strDate = RunGitCommand(egcGetHeadCommitDate)\r\n\r\n    ' convert the result from ISO 8601 to Access,\r\n    ' trimming off the timezone at the end (should be local)\r\n    ' see StackOverflow #38751429\r\n    varParts = Split(strDate, \" -\")\r\n    If IsDate(varParts(0)) Then GetHeadCommitDate = CDate(varParts(0))\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetHeadCommitHash\r\n' Author    : Adam Waller\r\n' Date      : 11/24/2020\r\n' Purpose   : Return the 7-character hash of the head commit.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetHeadCommitHash() As String\r\n    GetHeadCommitHash = RunGitCommand(egcGetHeadCommit)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetRepositoryRoot\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2023\r\n' Purpose   : Returns the path to the root of the repository.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetRepositoryRoot(Optional blnFallBackToWorking As Boolean = True) As String\r\n\r\n    Static strLastFolder As String  ' Working folder\r\n    Static strLastRoot As String    ' Repository Root\r\n\r\n    Dim strWorking As String\r\n\r\n    ' Determine the current working folder\r\n    strWorking = GetWorkingFolder\r\n\r\n    ' Make sure git is actually installed\r\n    If Not Me.Installed Then\r\n        If blnFallBackToWorking Then GetRepositoryRoot = strWorking\r\n        Exit Function\r\n    End If\r\n\r\n    ' On first call, we will attempt to get the repository root from the working\r\n    ' folder, or the export folder if a working folder is not specified.\r\n    If strLastRoot = vbNullString Or (strLastFolder <> strWorking) Then\r\n        ' Use working folder, if specified, otherwise go with export folder.\r\n        If strLastFolder <> strWorking Then\r\n            ' We will verify the repository path on the next call.\r\n            strLastFolder = strWorking\r\n            strLastRoot = vbNullString\r\n            ' Recursively call this function to verify the path with git\r\n            GetRepositoryRoot = GetRepositoryRoot(blnFallBackToWorking)\r\n        Else\r\n            ' Run git command from last folder\r\n            strLastRoot = strLastFolder\r\n            ' Use Git to look up root folder in repository.\r\n            strLastRoot = Replace(RunGitCommand(egcGetReproPath), \"/\", PathSep) & PathSep\r\n            If strLastRoot = PathSep Then\r\n                If blnFallBackToWorking Then\r\n                    ' Might not be in a git repository. Fall back to working folder.\r\n                    GetRepositoryRoot = strWorking\r\n                    strLastRoot = strWorking\r\n                Else\r\n                    GetRepositoryRoot = vbNullString\r\n                End If\r\n            Else\r\n                ' Found the root folder. Return to caller.\r\n                GetRepositoryRoot = strLastRoot\r\n            End If\r\n        End If\r\n    Else\r\n        ' Return cached root folder\r\n        GetRepositoryRoot = strLastRoot\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetWorkingFolder\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2023\r\n' Purpose   : Return the working folder from either the specified folder, or fall back\r\n'           : to the export path.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetWorkingFolder() As String\r\n\r\n    Dim strWorking As String\r\n\r\n    ' Avoid calling Options if the working folder is already defined to prevent\r\n    ' a possible stack overflow. (That's why we don't use Nz2() here)\r\n    If Len(Me.WorkingFolder) Then\r\n        strWorking = Me.WorkingFolder\r\n    Else\r\n        strWorking = Options.GetExportFolder\r\n    End If\r\n\r\n    ' Return path in consistent format\r\n    GetWorkingFolder = StripSlash(strWorking) & PathSep\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Version\r\n' Author    : Adam Waller\r\n' Date      : 3/10/2023\r\n' Purpose   : Return git version (Cached between calls)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Version() As String\r\n    Static strVersion As String\r\n    If strVersion = vbNullString Then strVersion = Replace(RunGitCommand(egcGetVersion), \"git version \", vbNullString)\r\n    Version = strVersion\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsInsideRepository\r\n' Author    : Adam Waller\r\n' Date      : 11/6/2023\r\n' Purpose   : Returns true if the current working folder is inside a git repository.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IsInsideRepository() As Boolean\r\n    If Me.Installed Then\r\n        IsInsideRepository = (RunGitCommand(egcIsInsideTree) = \"true\")\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsCleanBranch\r\n' Author    : Adam Waller\r\n' Date      : 3/10/2023\r\n' Purpose   : Returns true if the branch has no changes or untracked files.\r\n'           : See discussion on StackOverflow on pros and cons of different approaches\r\n'           : for this. I went with simple for our purposes.\r\n'           : See: https://stackoverflow.com/questions/2657935\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IsCleanBranch() As Boolean\r\n    IsCleanBranch = (RunGitCommand(egcGetStatusPorcelain) = vbNullString)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BranchName\r\n' Author    : Adam Waller\r\n' Date      : 3/21/2023\r\n' Purpose   : Return the name of the current branch\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function BranchName() As String\r\n    BranchName = RunGitCommand(egcGetBranchName)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SplitFilesWithHistory\r\n' Author    : Adam Waller\r\n' Date      : 3/10/2023\r\n' Purpose   : Accepts two arrays of file paths. The first array represents the existing\r\n'           : files, and the second represents the new files that will take on the\r\n'           : content and history of the first files.\r\n'           : Based on https://devblogs.microsoft.com/oldnewthing/20190919-00/?p=102904\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SplitFilesWithHistory(strFilePathsArray() As String, strNewPaths() As String, _\r\n    strCommitMessage As String, Optional strRunInFolder As String = vbNullString)\r\n\r\n    Dim lngCnt As Long\r\n    Dim strOrig As String\r\n    Dim strNew As String\r\n    Dim strBranch As String\r\n    Dim blnLog As Boolean\r\n    Dim strBaseFolder As String\r\n\r\n    ' Save current value and turn on logging.\r\n    blnLog = Me.LogCommands\r\n    strBaseFolder = m_strRepositoryRoot\r\n    m_strRepositoryRoot = strRunInFolder\r\n    Me.LogCommands = True\r\n\r\n    ' We should be starting with a clean slate (no uncommitted changes)\r\n    If Not Me.IsCleanBranch Then\r\n        Log.Error eelCritical, \"Cannot split files in Git when changes are present in the branch\", _\r\n            ModuleName(Me) & \".SplitFilesWithHistory\"\r\n        GoTo CleanUp\r\n    End If\r\n\r\n    ' Initialize the repository just to make sure everthing is in order\r\n    RunGitCommand egcInitialize\r\n\r\n    ' Get the current branch name, so we can switch back to it later\r\n    strBranch = Me.BranchName\r\n\r\n    ' Create a new branch to use when splitting the files\r\n    RunGitCommand egcCheckoutNewBranch, \"split-files\"\r\n\r\n    ' We could use git to move the files individually, but this would be much slower.\r\n    ' For our purposes, the files should be pretty unique in content, so we should be\r\n    ' fine to move them in batches. (As passed to this function)\r\n    For lngCnt = 0 To UBound(strFilePathsArray)\r\n        strOrig = strFilePathsArray(lngCnt)\r\n        strNew = strNewPaths(lngCnt)\r\n        If FSO.FileExists(strOrig) Then\r\n            If FSO.FileExists(strNew) Then DeleteFile strNew\r\n            FSO.MoveFile strOrig, strNew\r\n        End If\r\n    Next lngCnt\r\n\r\n    ' Update the index, and commit the move\r\n    RunGitCommand egcAddAll\r\n    RunGitCommand egcCommit, \"Rename as new files\"\r\n\r\n    ' Restore the original files\r\n    ' (TODO: see if we can do this from a named branch)\r\n    RunGitCommand egcCheckoutHeadToCurrent\r\n\r\n    ' Commit these files (with history) to the temporary branch\r\n    RunGitCommand egcCommit, \"Restore original files\"\r\n\r\n    ' Move back to original branch\r\n    RunGitCommand egcCheckoutBranch, strBranch\r\n\r\n    ' Merge the temporary branch into the original branch\r\n    RunGitCommand egcMergeNoFastFwd, \"split-files\" & \" -m \"\"\" & strCommitMessage & \"\"\"\"\r\n\r\n    ' Delete the temporary branch now that we are finished using it\r\n    RunGitCommand egcDeleteBranch, \"split-files\"\r\n\r\n    ' Restore original logging\r\n    Me.LogCommands = blnLog\r\n\r\nCleanUp:\r\n\r\n    ' Restore base folder\r\n    m_strRepositoryRoot = strBaseFolder\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ShellRun\r\n' Author    : Adam Waller\r\n' Date      : 11/24/2020\r\n' Purpose   : Pass a git command to this function to return the result as a string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ShellRun(strCmd As String, intCmd As eGitCommand) As String\r\n\r\n    Dim oShell As WshShell\r\n    Dim strFile As String\r\n\r\n    ' Get path to temp file\r\n    strFile = GetTempFile\r\n\r\n    ' Build command line string\r\n    With New clsConcat\r\n        Select Case intCmd\r\n            Case egcGetVersion\r\n                ' Run independent of repository\r\n                .Add \"cmd.exe /c \", strCmd\r\n            Case Else\r\n                ' Open command prompt in repository folder\r\n                .Add \"cmd.exe /c cd \", GetRepositoryRoot\r\n                ' Run git command\r\n                .Add \" & \", strCmd\r\n        End Select\r\n        ' Output to temp file\r\n        .Add \" > \"\"\", strFile, \"\"\"\"\r\n        ' Execute command\r\n        Set oShell = New WshShell\r\n        oShell.Run .GetStr, WshHide, True\r\n        'Debug.Print .GetStr    ' To debug\r\n    End With\r\n\r\n    ' Read from temp file\r\n    ShellRun = ReadFile(strFile)\r\n\r\n    ' Remove temp file\r\n    FSO.DeleteFile strFile\r\n\r\nEnd Function\r\n\r\n\r\n'\r\n'' Returns a collcetion containing two lists:\r\n'' first, of all the objects to modify or re-import based on the state of the git repo\r\n'' second, of all the objects to delete based on the same\r\n'' if getUncommittedFiles is false, files list is all files between the current HEAD\r\n'' and the commit carrying the last-imported-commit tag that are in the\r\n'' /source directory. if it is true, file list includes any uncommitted changes\r\n'' Note: Last entries in file arrays will be empty.\r\n'Public Function GetSourceFilesSinceLastImport(getUncommittedFiles As Boolean) As Variant\r\n'    Dim FileListString As String\r\n'    Dim AllFilesArray As Variant\r\n'    Dim SourceFilesToImportCollection As Collection\r\n'    Dim SourceFilesToRemoveCollection As Collection\r\n'    Set SourceFilesToImportCollection = New Collection\r\n'    Set SourceFilesToRemoveCollection = New Collection\r\n'    Dim FileStatus As Variant\r\n'    Dim CommandToRun As String\r\n'    Dim File As Variant\r\n'    Dim Status As String\r\n'    Dim FileStatusSplit As Variant\r\n'    Dim ReturnArray(2) As Variant\r\n'\r\n'    If getUncommittedFiles = True Then\r\n'        CommandToRun = GetAllChangedFilesCommand\r\n'    Else\r\n'        CommandToRun = GetCommittedFilesCommand\r\n'    End If\r\n'\r\n'    ' get files already committed (and staged, if flag passed)\r\n'    FileListString = ShellRun(CommandToRun)\r\n'\r\n'    ' sanitize paths, determine the operation type, and add to relevant collection\r\n'    For Each FileStatus In Split(FileListString, vbLf)\r\n'        If FileStatus = \"\" Then Exit For\r\n'\r\n'        FileStatusSplit = Split(FileStatus, vbTab)\r\n'        Status = Left(FileStatusSplit(0), 1) ' only first character actually indicates status; the rest is \"score\"\r\n'        File = FileStatusSplit(1)\r\n'\r\n'        If File <> \"\" And File Like \"source/*\" Then\r\n'            File = Replace(File, \"/\", \"\\\")\r\n'\r\n'            ' overwrite/add modified, copied, added\r\n'            If Status = \"M\" Or Status = \"A\" Or Status = \"U\" Then\r\n'                SourceFilesToImportCollection.Add File\r\n'            End If\r\n'\r\n'            ' overwrite result of rename or copy\r\n'            If Status = \"R\" Or Status = \"C\" Then\r\n'                ' add the result to the collection of import files\r\n'                SourceFilesToImportCollection.Add Replace(FileStatusSplit(2), \"/\", \"\\\")\r\n'            End If\r\n'\r\n'            ' remove deleted objects and original renamed files\r\n'            If Status = \"D\" Or Status = \"R\" Then\r\n'                SourceFilesToRemoveCollection.Add File\r\n'            End If\r\n'        End If\r\n'    Next\r\n'\r\n'    ' get and add untracked files\r\n'    If getUncommittedFiles = True Then\r\n'        FileListString = ShellRun(GetUntrackedFilesCommand)\r\n'        For Each File In Split(FileListString, vbLf)\r\n'            If File <> \"\" And File Like \"source/*\" Then\r\n'                File = Replace(File, \"/\", \"\\\")\r\n'                SourceFilesToImportCollection.Add File\r\n'            End If\r\n'        Next\r\n'    End If\r\n'\r\n'    Set ReturnArray(0) = SourceFilesToImportCollection\r\n'    Set ReturnArray(1) = SourceFilesToRemoveCollection\r\n'    GetSourceFilesSinceLastImport = ReturnArray\r\n'End Function\r\n'\r\n'Public Sub SetLastImportedCommitToCurrent()\r\n'    ShellRun SetTaggedCommitCommand\r\n'End Sub\r\n\r\n\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GitInstalled\r\n' Author    : Adam Waller\r\n' Date      : 11/24/2020\r\n' Purpose   : Returns true if git is installed.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Installed() As Boolean\r\n    Installed = (Len(Me.Version))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetModifiedSourceFiles\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2020\r\n' Purpose   : Return the modified source file paths for this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetModifiedSourceFiles(cCategory As IDbComponent) As Collection\r\n\r\n    Dim varKey As Variant\r\n\r\n    ' Make sure the changes are loaded from Git\r\n    If m_dChangedItems Is Nothing Then Set m_dChangedItems = GetChangedFileIndex(Me.FromRevision)\r\n\r\n    ' Check for any matching changes.\r\n    Set GetModifiedSourceFiles = New Collection\r\n    With m_dChangedItems\r\n        If .Exists(cCategory.Category) Then\r\n            For Each varKey In .Item(cCategory.Category).Keys\r\n                ' Add source file\r\n                GetModifiedSourceFiles.Add CStr(varKey)\r\n            Next varKey\r\n        End If\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RevisionExists\r\n' Author    : Adam Waller\r\n' Date      : 1/19/2021\r\n' Purpose   : Returns true if the revision exists on Git.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function RevisionExists(strHash As String) As Boolean\r\n    RevisionExists = (RunGitCommand(egcGetRevision, strHash) <> vbNullString)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetChangedFileList\r\n' Author    : Adam Waller\r\n' Date      : 11/25/2020\r\n' Purpose   : Returns a collection of the files that have been changed. Only includes\r\n'           : source files used by VCS.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetChangedFileIndex(strFromCommit As String) As Dictionary\r\n\r\n    Dim varItems As Variant\r\n    Dim varFile As Variant\r\n    Dim strPath As String\r\n    Dim strBasePath As String\r\n    Dim varParts As Variant\r\n    Dim strExportFolder As String\r\n    Dim strCategory As String\r\n    Dim dIndex As Dictionary\r\n    Dim dFolders As Dictionary\r\n    Dim dCategory As Dictionary\r\n    Dim cComp As IDbComponent\r\n    Dim strSourceFile As String\r\n    Dim strFlag As String\r\n    Dim strRootPath As String\r\n    Dim strResponse As String\r\n\r\n    ' Get the base export folder\r\n    strExportFolder = Options.GetExportFolder\r\n    varParts = Split(strExportFolder, PathSep)\r\n    strBasePath = varParts(UBound(varParts) - 1)\r\n    strRootPath = GetRepositoryRoot\r\n\r\n    ' Get base folder list from component types.\r\n    ' (Used to organize the changed files by type)\r\n    Set dFolders = New Dictionary\r\n    For Each cComp In GetContainers\r\n        strCategory = StripSlash(Mid$(cComp.BaseFolder, Len(strRootPath) + 1))\r\n        If strCategory = strBasePath Then\r\n            ' Include file name in category\r\n            strCategory = Mid$(cComp.SourceFile, Len(strRootPath) + 1)\r\n        End If\r\n        ' Replace backslashes with forward slashes to match git output\r\n        strCategory = Replace(strCategory, PathSep, \"/\")\r\n        dFolders.Add strCategory, cComp.Category\r\n    Next cComp\r\n\r\n    ' Windows 10 can optionally support case-sensitive file names, but for\r\n    ' now we will go with case insensitive names for the purpose of the index.\r\n    Set dIndex = New Dictionary\r\n    dIndex.CompareMode = TextCompare\r\n\r\n    ' Return a list of changed and new files from git.\r\n    strResponse = RunGitCommand(egcGetAllChangedFiles, strFromCommit) & vbLf & _\r\n        RunGitCommand(egcGetUntrackedFiles)\r\n\r\n    ' Check for errors such as invalid commit\r\n    If InStr(1, strResponse, \": unknown revision\") > 0 Then\r\n        Log.Error eelError, \"Unknown git revision: \" & strFromCommit, \"clsGitIntegration.GetChangedFileIndex\"\r\n        Log.Spacer False\r\n        Log.Add strResponse, False\r\n        Log.Spacer\r\n    Else\r\n        ' Convert to list of items\r\n        varItems = Split(strResponse, vbLf)\r\n\r\n        ' Loop through list of changed files\r\n        For Each varFile In varItems\r\n\r\n            ' Check for flag from changed files.\r\n            If Mid(varFile, 2, 1) = vbTab Then\r\n                strFlag = Mid(varFile, 1, 1)\r\n                strPath = Mid(varFile, 3)\r\n            Else\r\n                strFlag = \"U\" ' Unversioned file.\r\n                strPath = varFile\r\n            End If\r\n\r\n            ' Skip any blank lines\r\n            If strPath <> vbNullString Then\r\n\r\n                ' Check for match on entire file name. (For single file items\r\n                ' in the root export folder.)\r\n                If dFolders.Exists(strPath) Then\r\n                    ' Use this component type.\r\n                    strCategory = dFolders(strPath)\r\n                Else\r\n                    ' Use the folder name to look up component type.\r\n                    strCategory = dNZ(dFolders, FSO.GetParentFolderName(strPath))\r\n                End If\r\n\r\n                ' Ignore files outside standard VCS source folders.\r\n                If strCategory <> vbNullString Then\r\n\r\n                    ' Add to index of changed files.\r\n                    With dIndex\r\n\r\n                        ' Add category if it does not exist.\r\n                        If Not .Exists(strCategory) Then\r\n                            Set dCategory = New Dictionary\r\n                            .Add strCategory, dCategory\r\n                        End If\r\n\r\n                        ' Build full path to source file, and add to index.\r\n                        strSourceFile = strRootPath & Replace(strPath, \"/\", PathSep)\r\n\r\n                        ' Add full file path to category, including flag with change type.\r\n                        .Item(strCategory).Add strSourceFile, strFlag\r\n                    End With\r\n                End If\r\n            End If\r\n        Next varFile\r\n\r\n    End If\r\n\r\n    ' Return dictionary of file paths.\r\n    Set GetChangedFileIndex = dIndex\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsJob.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsJob\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPublic KeyHash As String\r\nPublic Action As String\r\nPublic Start As Double\r\nPublic Timeout As Double\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsLblProg.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsLblProg\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsLblProg\r\n' Author    : Adam Waller\r\n' Date      : 5/20/2022\r\n' Purpose   : Display a progress bar using three labels on a form\r\n'---------------------------------------------------------------------------------------\r\nOption Explicit\r\n\r\n\r\n' Set the application name here to utilize the appropriate\r\n' object types in early binding.\r\n#Const APPLICATION_NAME = \"Microsoft Access\"\r\n\r\n' Color constants\r\nPrivate Const lngColorNavy As Long = 8388608    ' Default progress bar color (Navy Blue)\r\nPrivate Const lngColorWhite As Long = 16777215  ' Caption over blue background (White)\r\nPrivate Const lngColorBlack As Long = 0         ' Caption over grey background (Black)\r\nPrivate Const lngColorGrey As Long = 12566463   ' Back label color (Grey)\r\n\r\n' Public properties\r\nPublic Smooth As Boolean                ' Set to true for smooth updates < 1%\r\nPublic Max As Double                    ' Max total value of progress bar\r\n\r\n' General style of progress bar\r\nPublic Enum eProgressBarStyle\r\n    epbTraditional  ' Sunken style back label, inset bar\r\n    epbModernFlat   ' Flat, equally sized progress bar, no border\r\nEnd Enum\r\n\r\n' Private properties\r\nPrivate Type udtProg\r\n\r\n    ' Application specific properties\r\n    #If APPLICATION_NAME = \"Microsoft Access\" Then\r\n        ' Use Access specific controls/sizing\r\n        lblBack As Access.Label         ' Existing label for back\r\n        lblFront As Access.Label        ' Label for moving bar\r\n        lblCaption As Access.Label      ' Progress bar caption\r\n    #Else\r\n        ' Generic VBA objects\r\n        lblBack As MSForms.Label        ' Existing label for back\r\n        lblFront As MSForms.Label       ' Label created for front\r\n        lblCaption As MSForms.Label     ' Label created for caption\r\n    #End If\r\n\r\n    ' Shared private properties\r\n    dblValue As Double                  ' Current value of progress bar\r\n    dblDisplayed As Double              ' Displayed value\r\n    dblFullWidth As Double              ' Width of front label at 100%\r\n    sngOffset As Single                 ' Offset for front label\r\n    blnCaptionVisible As Boolean        ' Show/hide percent complete caption\r\n    objParent As Object                 ' Parent object of back label (in MSForms 2.0)\r\n    dteStartTime As Date                ' Time of first value change\r\n    sngRepaintInterval As Single        ' Seconds between updates\r\nEnd Type\r\nPrivate this As udtProg\r\n\r\n\r\n' Fires when the value of the progress bar changes.\r\n' (This can be used to set a custom caption)\r\nPublic Event ProgressChange(dblValue As Double, intPercent As Integer)\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Initialize\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Initialize the progress bar before using it. Include a caption label\r\n'           : if you want to display the percentage complete after updates.\r\n'---------------------------------------------------------------------------------------\r\n'\r\n#If APPLICATION_NAME = \"Microsoft Access\" Then\r\n    Public Sub Initialize(BackLabel As Label _\r\n                        , FrontLabel As Label _\r\n                        , Optional CaptionLabel As Label _\r\n                        , Optional BarStyle As eProgressBarStyle = epbTraditional)\r\n#Else\r\n    Public Sub Initialize(BackLabel As MSForms.Label _\r\n                        , Optional FrontLabel As MSForms.Label _\r\n                        , Optional CaptionLabel As MSForms.Label _\r\n                        , Optional BarStyle As eProgressBarStyle = epbTraditional)\r\n#End If\r\n\r\n    Set this.lblBack = BackLabel\r\n\r\n    #If APPLICATION_NAME = \"Microsoft Access\" Then\r\n        this.sngOffset = 15\r\n        ' Verify front label (not optional for Microsoft Access)\r\n        If FrontLabel Is Nothing Then\r\n            MsgBox \"Missing required front label for progress bar\" & vbCrLf & _\r\n                \"This parameter is required when using Microsoft Access.\", vbExclamation\r\n            Exit Sub\r\n        End If\r\n        ' Use existing controls\r\n        Set this.lblFront = FrontLabel\r\n        Set this.lblCaption = CaptionLabel\r\n    #Else\r\n        this.sngOffset = 1.5\r\n        ' Create front controls dynamically\r\n        Set this.objParent = GetParentFormName(BackLabel)\r\n        Set this.lblFront = this.objParent.Controls.Add(\"forms.label.1\", \"\", False)\r\n        Set this.lblCaption = this.objParent.Controls.Add(\"forms.label.1\", \"\", False)\r\n        ' Refresh display of parent form\r\n        this.objParent.Repaint\r\n    #End If\r\n\r\n    ' Set style-specific properties\r\n    If BarStyle = epbTraditional Then\r\n        this.lblBack.SpecialEffect = 2\r\n    ElseIf BarStyle = epbModernFlat Then\r\n        this.sngOffset = 0\r\n        this.lblBack.SpecialEffect = 0\r\n        this.lblBack.BackStyle = 1\r\n        this.lblBack.BackColor = lngColorGrey\r\n    End If\r\n\r\n    ' Properties for back label\r\n    SetVisibility this.lblBack, True\r\n\r\n    ' Set properties for front label\r\n    With this.lblFront\r\n        this.dblFullWidth = this.lblBack.Width - (this.sngOffset * 2)\r\n        .Left = this.lblBack.Left + this.sngOffset\r\n        .Top = this.lblBack.Top + this.sngOffset\r\n        .Width = 0\r\n        .Height = this.lblBack.Height - (this.sngOffset * 2)\r\n        .Caption = vbNullString\r\n        .BackColor = lngColorNavy\r\n        .BackStyle = 1      ' Opaque\r\n        .SpecialEffect = 0  ' Flat\r\n    End With\r\n\r\n    ' Passing in a caption label is optional\r\n    If Not this.lblCaption Is Nothing Then\r\n        ' set properties for caption label\r\n        this.blnCaptionVisible = True ' Turn this on as needed.\r\n        With this.lblCaption\r\n            .Top = this.lblBack.Top + (this.sngOffset * 1.33)\r\n            .Left = this.lblBack.Left + (this.sngOffset * 2)\r\n            .Width = this.lblBack.Width - (this.sngOffset * 4)\r\n            .TextAlign = 2 'fmTextAlignCenter\r\n            .BackStyle = 0 ' fmBackStyleTransparent\r\n            .Caption = \"0%\"\r\n            .ForeColor = lngColorBlack\r\n        End With\r\n    End If\r\n\r\n    ' Set default repaint interval to once every second\r\n    this.sngRepaintInterval = 1\r\n\r\n    ' Update the display immediately\r\n    UpdateDisplay True\r\n\r\nEnd Sub\r\n\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BarColor & BackColor\r\n' Author    : Adam Waller\r\n' Date      : 5/31/2022\r\n' Purpose   : Publicly expose some properties for ease of customization\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get BarColor() As Long\r\n    BarColor = this.lblFront.BackColor\r\nEnd Property\r\nPublic Property Let BarColor(NewVal As Long)\r\n    this.lblFront.BackColor = NewVal\r\nEnd Property\r\nPublic Property Get BackColor() As Long\r\n    BarColor = this.lblBack.BackColor\r\nEnd Property\r\nPublic Property Let BackColor(NewVal As Long)\r\n    this.lblBack.BackColor = NewVal\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CaptionVisible\r\n' Author    : Adam Waller\r\n' Date      : 5/31/2022\r\n' Purpose   : Expose properties for caption visibility\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get CaptionVisible() As Boolean\r\n    CaptionVisible = this.blnCaptionVisible\r\nEnd Property\r\nPublic Property Let CaptionVisible(NewVal As Boolean)\r\n    With this\r\n        If .blnCaptionVisible <> NewVal Then\r\n            .blnCaptionVisible = NewVal\r\n            If Not .lblCaption Is Nothing Then\r\n                SetVisibility .lblCaption, .blnCaptionVisible\r\n            Else\r\n                ' If the control isn't present, it's never visible.\r\n                .blnCaptionVisible = False\r\n            End If\r\n        End If\r\n    End With\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetRepaintInterval\r\n' Author    : Adam Waller\r\n' Date      : 6/3/2022\r\n' Purpose   : Set how frequently to repaint the screen (DoEvents)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function SetRepaintInterval(sngSeconds As Single)\r\n    this.sngRepaintInterval = sngSeconds\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Value\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Get the current value of the progress bar.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Value() As Double\r\n    Value = this.dblValue\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Value\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Set the value of the progress bar, adjusting to fit within Max\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Let Value(ByVal dblValue As Double)\r\n\r\n    Dim dblChange As Double\r\n\r\n    ' Don't allow value to exceed maximum value\r\n    If dblValue > Me.Max Then\r\n        this.dblValue = Me.Max\r\n    ElseIf dblValue < 0 Then\r\n        this.dblValue = 0\r\n    Else\r\n        ' Update value property\r\n        this.dblValue = dblValue\r\n    End If\r\n\r\n    ' Record start time on first change\r\n    If this.dteStartTime = 0 Then this.dteStartTime = Now\r\n\r\n    ' Measure change from currently displayed value\r\n    dblChange = Abs(this.dblValue - this.dblDisplayed)\r\n\r\n    ' Set value and update display if needed.\r\n    If dblChange > 0 And Me.Max > 0 Then\r\n        ' See if we need to update the display\r\n        ' (Normally updated every 1% or each increment if Smooth = True)\r\n        If Me.Smooth Or ((dblChange / Me.Max) > 0.01) Then UpdateDisplay\r\n    End If\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Increment\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Increment the progress bar by 1, or by a custom amount, if desired.\r\n'           : Normally used without arguments.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Increment(Optional dblAmount As Double = 1)\r\n    Me.Value = Me.Value + dblAmount\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : EstCompletionTime\r\n' Author    : Adam Waller\r\n' Date      : 4/21/2022\r\n' Purpose   : Return an estimated completion time based on the start date and remaining\r\n'           : segments. (Simple calculation assuming even increment intervals.)\r\n'           : Note that the START time is set at the COMPLETION of the first segment,\r\n'           : so the first segment is not included in the time calculation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get EstCompletionTime() As Date\r\n\r\n    Dim dblSeconds As Double\r\n    Dim dblRemaining As Double\r\n\r\n    If this.dteStartTime = 0 Then\r\n        EstCompletionTime = Now\r\n    Else\r\n        With this\r\n            ' Get elapsed seconds\r\n            dblSeconds = DateDiff(\"s\", .dteStartTime, Now)\r\n            ' Calculate remaining seconds\r\n            If .dblValue > 1 Then dblRemaining = (dblSeconds / (.dblValue - 1)) * (Me.Max - .dblValue)\r\n            ' Convert to completion time\r\n            EstCompletionTime = DateAdd(\"s\", dblRemaining, Now)\r\n        End With\r\n    End If\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : EstRemainingTime\r\n' Author    : Adam Waller\r\n' Date      : 4/21/2022\r\n' Purpose   : Return a string value with the estimated hours/minutes/seconds remaining.\r\n'           : Example return values: \"73 seconds\", \"41 minutes\", \"80 hours\"\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get EstRemainingTime() As String\r\n\r\n    Dim dblSeconds As Double\r\n\r\n    dblSeconds = DateDiff(\"s\", Now, Me.EstCompletionTime)\r\n    If dblSeconds < 120 Then ' Less than two minutes\r\n        EstRemainingTime = dblSeconds & \" seconds\"\r\n    ElseIf dblSeconds < (60 * 120) Then ' Less than 2 hours\r\n        EstRemainingTime = dblSeconds \\ 60 & \" minutes\"\r\n    Else\r\n        EstRemainingTime = dblSeconds \\ 3600 & \" hours\"\r\n    End If\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Clear\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Remove or hide front labels. (Will need to initialize again after this.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Clear()\r\n    Class_Terminate\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Reset\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Reset value back to zero. (Without changing Max)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Reset()\r\n    this.dteStartTime = 0\r\n    Me.Value = 0\r\n    UpdateDisplay True\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Hide\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Hide the visible elements of the progress bar (but retain values)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Hide()\r\n    SetVisibility this.lblBack, False\r\n    SetVisibility this.lblFront, False\r\n    SetVisibility this.lblCaption, False\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Update\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Update the display with an optional force parameter. (Normally updates\r\n'           : every second to prevent a performance hit with frequent calls to the\r\n'           : DoEvents function.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub UpdateDisplay(Optional blnForceUpdate As Boolean)\r\n\r\n    Dim intPercent As Integer\r\n    Dim dblWidth As Double\r\n\r\n    Static sngLastUpdate As Single\r\n\r\n    ' Set size and caption\r\n    With this\r\n        If Me.Max <= 0 Then\r\n            intPercent = 0\r\n            dblWidth = 0\r\n        ElseIf .dblValue >= Me.Max Then\r\n            intPercent = 100\r\n            .dblValue = Me.Max\r\n            dblWidth = .dblFullWidth\r\n        Else\r\n            intPercent = .dblValue * (100 / Me.Max)\r\n            dblWidth = .dblValue * (.dblFullWidth / Me.Max)\r\n        End If\r\n\r\n        ' See if we have a label assigned\r\n        If .lblFront Is Nothing Then\r\n            #If APPLICATION_NAME = \"Microsoft Access\" Then\r\n                ' Use system progress meter at bottom right of\r\n                ' the Microsoft Access application window\r\n                SysCmd acSysCmdInitMeter, \"Working...\", 100\r\n                SysCmd acSysCmdUpdateMeter, intPercent\r\n            #End If\r\n        Else\r\n            ' Adjust width of label to show progress\r\n            .lblFront.Width = dblWidth\r\n        End If\r\n\r\n        ' Save currently displayed value\r\n        .dblDisplayed = .dblValue\r\n\r\n        ' Ensure controls are visible\r\n        SetVisibility .lblBack, True\r\n        SetVisibility .lblFront, True\r\n\r\n        ' Set caption if we are using a caption\r\n        If Not .lblCaption Is Nothing Then\r\n            .lblCaption.Caption = intPercent & \"%\"\r\n            SetVisibility .lblCaption, .blnCaptionVisible\r\n            ' Verify caption color (black till 50%, then white)\r\n            VerifyForeColor .lblCaption, IIf(intPercent > 50, lngColorWhite, lngColorBlack)\r\n        End If\r\n    End With\r\n\r\n    ' Use DoEvents to repaint display\r\n    If Me.Smooth Then\r\n        ' Always update display\r\n        RaiseEvent ProgressChange(this.dblValue, intPercent)\r\n        DoEvents\r\n    ElseIf (Abs(sngLastUpdate - Timer) > this.sngRepaintInterval) Or blnForceUpdate Then\r\n        ' Update every second (or other specified interval),\r\n        ' or when forcing an immediate update\r\n        RaiseEvent ProgressChange(this.dblValue, intPercent)\r\n        DoEvents\r\n        sngLastUpdate = Timer\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetParentFormName\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Return the name of the parent form\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetParentFormName(ctlControl As Control) As String\r\n\r\n    ' returns the name of the parent form\r\n    Dim objParent As Object\r\n    Dim lngRunCount As Long\r\n\r\n    ' This is only used in other VBA applications, not Microsoft Access\r\n    #If APPLICATION_NAME = \"Microsoft Access\" Then\r\n        Exit Function\r\n    #End If\r\n\r\n    ' Walk up parent controls to find parent form.\r\n    Set objParent = ctlControl\r\n    Do While Not TypeName(objParent) = \"UserForm\" And lngRunCount < 100\r\n        lngRunCount = lngRunCount + 1\r\n        Set objParent = objParent.Parent\r\n    Loop\r\n\r\n    ' Now we should have the parent form\r\n    GetParentFormName = objParent.Name\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetVisibility\r\n' Author    : Adam Waller\r\n' Date      : 5/20/2022\r\n' Purpose   : Set the visibility of the object if it exists and does not match the\r\n'           : desired visibility setting.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SetVisibility(ctlObject As Control, blnVisible As Boolean)\r\n    If Not ctlObject Is Nothing Then\r\n        If ctlObject.Visible <> blnVisible Then ctlObject.Visible = blnVisible\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyForeColor\r\n' Author    : Adam Waller\r\n' Date      : 6/8/2022\r\n' Purpose   : Sets the ForeColor property to match the desired value.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub VerifyForeColor(ctlLabel As Control, lngForeColor As Long)\r\n    If ctlLabel.ForeColor <> lngForeColor Then ctlLabel.ForeColor = lngForeColor\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Terminate\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Remove temporary controls, if applicable\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Terminate()\r\n\r\n    ' Ignore any errors if object references are no longer valid\r\n    On Error Resume Next\r\n    #If APPLICATION_NAME = \"Microsoft Access\" Then\r\n        ' Hide front controls\r\n        Hide\r\n        ' Remove any progress meter if not using labels\r\n        If this.lblBack Is Nothing Then SysCmd acSysCmdRemoveMeter\r\n    #Else\r\n        ' Remove front controls\r\n        SetVisibility this.lblBack, False\r\n        this.objParent.Controls.Remove (this.lblFront.Name)\r\n        this.objParent.Controls.Remove (this.lblCaption.Name)\r\n    #End If\r\n\r\n    ' Clear any remaining object references\r\n    Set this.lblBack = Nothing\r\n    Set this.lblFront = Nothing\r\n    Set this.lblCaption = Nothing\r\n    Set this.objParent = Nothing\r\n\r\n    ' Clear any VBA errors\r\n    If Err.Number <> 0 Then Err.Clear\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsLog.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsLog\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPublic PadLength As Integer\r\nPublic ErrorLevel As eErrorLevel\r\nPublic OperationType As eOperationType\r\nPublic SourcePath As String\r\n\r\n' Set this to true when logging an operation such as an export or build\r\n' then set back to false after writing the log file. This affects\r\n' how error messages are reported to the user outside of operations.\r\nPublic Active As Boolean\r\n\r\nPrivate Const cstrSpacer As String = \"-------------------------------------\"\r\n\r\nPrivate m_Log As clsConcat      ' Log file output\r\nPrivate m_Console As clsConcat  ' Console output\r\nPrivate m_RichText As TextBox   ' Text box to display HTML\r\nPrivate m_Prog As clsLblProg    ' Progress bar\r\nPrivate m_blnProgressActive As Boolean\r\nPrivate m_sngLastUpdate As Single\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Clear\r\n' Author    : Adam Waller\r\n' Date      : 4/16/2020\r\n' Purpose   : Clear the log buffers and release object references\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Clear()\r\n    Class_Initialize\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Spacer\r\n' Author    : Adam Waller\r\n' Date      : 4/28/2020\r\n' Purpose   : Add a spacer to the log\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Spacer(Optional blnPrint As Boolean = True)\r\n    Me.Add cstrSpacer, blnPrint\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Add\r\n' Author    : Adam Waller\r\n' Date      : 1/18/2019\r\n' Purpose   : Add a log file entry.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Add(strText As String, Optional blnPrint As Boolean = True, _\r\n    Optional blnNextOutputOnNewLine As Boolean = True, _\r\n    Optional strColor As String = vbNullString, _\r\n    Optional blnBold As Boolean = False, _\r\n    Optional blnItalic As Boolean = False)\r\n\r\n    ' Add to log file output\r\n    m_Log.Add strText, vbCrLf\r\n\r\n    ' See if we want to print the output of this message.\r\n    If blnPrint Then\r\n        ' Remove existing progress indicator if in use.\r\n        If m_blnProgressActive Then\r\n            m_blnProgressActive = False\r\n            m_Prog.Hide\r\n        End If\r\n\r\n        ' Build HTML output for console\r\n        With m_Console\r\n\r\n            ' Opening tags\r\n            If blnBold Then .Add \"<strong>\"\r\n            If blnItalic Then .Add \"<i>\"\r\n            If strColor <> vbNullString Then .Add \"<font color=\", strColor, \">\"\r\n\r\n            ' Content\r\n            .Add MultiReplace(strText, _\r\n                \" \", \"&nbsp;\", _\r\n                vbCrLf, \"<br>\")\r\n\r\n            ' Closing tags\r\n            If strColor <> vbNullString Then .Add \"</font>\"\r\n            If blnItalic Then .Add \"</i>\"\r\n            If blnBold Then .Add \"</strong>\"\r\n\r\n            ' Add line break for HTML\r\n            If blnNextOutputOnNewLine Then m_Console.Add \"<br>\"\r\n        End With\r\n\r\n        ' Run debug output\r\n        If m_RichText Is Nothing Then\r\n            ' Only print debug output if not running from the GUI.\r\n            If blnNextOutputOnNewLine Then\r\n                ' Create new line\r\n                Debug.Print strText\r\n            Else\r\n                ' Continue next printout on this line.\r\n                Debug.Print strText;\r\n            End If\r\n        End If\r\n\r\n        ' Allow an update to the screen every second.\r\n        ' (This keeps the aplication from an apparent hang while\r\n        '  running intensive export processes.)\r\n        If m_sngLastUpdate + 1 < Timer Then\r\n            Me.Flush\r\n            m_sngLastUpdate = Timer\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Flush\r\n' Author    : Adam Waller\r\n' Date      : 4/29/2020\r\n' Purpose   : Flushes the buffer to the console\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Flush()\r\n\r\n    ' See if the GUI form is loaded.\r\n    Perf.OperationStart \"Console Updates\"\r\n    If Not m_RichText Is Nothing Then\r\n        With m_RichText\r\n            m_blnProgressActive = False\r\n            If Not m_Prog Is Nothing Then m_Prog.Hide\r\n            ' Set value, not text to avoid errors with large text strings.\r\n            Echo False\r\n            '.SelStart = Len(.Text & vbNullString)\r\n            ' Show the last 20K characters so\r\n            ' we don't hit the Integer limit\r\n            ' on the SelStart property.\r\n            .Value = m_Console.RightStr(20000)\r\n            If VerifyFocus(m_RichText) Then .SelStart = 20000\r\n            Echo True\r\n            'Form_frmVCSMain.Repaint\r\n        End With\r\n    Else\r\n        ' Update display in immediate window if not using console\r\n        DoEvents\r\n    End If\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Error\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Log an error, and update error level if needed. Reads Err object values.\r\n'           : A critical error will also present a message box with the details.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Error(eLevel As eErrorLevel, strDescription As String, Optional strSource As String, Optional blnIncludeErrorMessage As Boolean = False)\r\n\r\n    Dim strPrefix As String\r\n    Dim strDisplay As String\r\n\r\n    Select Case eLevel\r\n        Case eelWarning:    strPrefix = \"WARNING: \"\r\n        Case eelError:      strPrefix = \"ERROR: \"\r\n        Case eelCritical:   strPrefix = \"CRITICAL: \"\r\n    End Select\r\n\r\n    ' Build the error message string.\r\n    With New clsConcat\r\n\r\n        ' Sometimes the error description is sufficient for the description\r\n        If strDescription = vbNullString And Err.Number <> 0 Then\r\n            strDisplay = strPrefix & Err.Number & \" - \" & Err.Description\r\n        ElseIf blnIncludeErrorMessage Then\r\n            strDisplay = strPrefix & strDescription & \"; Error \" & Err.Number & \" - \" & Err.Description\r\n        Else\r\n            strDisplay = strPrefix & strDescription\r\n        End If\r\n\r\n        ' Log all errors, and display on the output screen anything higher than a warning\r\n        Me.Add vbNullString, (eLevel > eelWarning)\r\n        Me.Spacer (eLevel > eelWarning)\r\n        Me.Add strDisplay, (eLevel > eelWarning), , \"red\"\r\n\r\n        ' Log the additional error and source details to the log file\r\n        If Err Then .Add \"Error \", Err.Number, \": \", Err.Description, \" \"\r\n        If strSource <> vbNullString Then .Add \"Source: \", strSource\r\n        Me.Add .GetStr, False\r\n        Me.Spacer (eLevel > eelWarning)\r\n        Me.Flush ' Force a screen update.\r\n        ' See if we are actively logging an operation\r\n        If Log.Active Then\r\n            ' Show message box for fatal error.\r\n            If eLevel = eelCritical Then\r\n                MsgBox2 \"Unable to Continue\", .GetStr, _\r\n                    \"Please review the log file for additional details.\", vbCritical\r\n            End If\r\n        Else\r\n            ' Show message on any error level when we are not logging to a file.\r\n            Select Case eLevel\r\n                Case eelNoError:    ' Do nothing\r\n                Case eelWarning:    MsgBox2 \"Warning\", strDisplay, .GetStr, vbInformation\r\n                Case eelError:      MsgBox2 \"Error\", strDisplay, .GetStr, vbExclamation\r\n                Case eelCritical:   MsgBox2 \"Critical\", strDisplay, .GetStr, vbCritical\r\n            End Select\r\n        End If\r\n    End With\r\n\r\n    ' Update error level if higher.\r\n    If Me.ErrorLevel < eLevel Then Me.ErrorLevel = eLevel\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetConsole\r\n' Author    : Adam Waller\r\n' Date      : 4/28/2020\r\n' Purpose   : Set a reference to the rich text box for output display, and the\r\n'           : associated progress bar for logging progress.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetConsole(txtRichText As TextBox, cProg As clsLblProg)\r\n    Set m_Prog = cProg\r\n    Set m_RichText = txtRichText\r\n    If Not m_RichText Is Nothing Then\r\n        m_RichText.AllowAutoCorrect = False\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReleaseConsole\r\n' Author    : Adam Waller\r\n' Date      : 5/5/2022\r\n' Purpose   : Releases object references to the console form that were created with\r\n'           : the SetConsole sub.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ReleaseConsole()\r\n    Set m_RichText = Nothing\r\n    Set m_Prog = Nothing\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ProgressBar\r\n' Author    : Adam Waller\r\n' Date      : 11/6/2020\r\n' Purpose   : Pass the Progress Bar reference to this class.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Set ProgressBar(cProg As clsLblProg)\r\n    Set m_Prog = cProg\r\nEnd Property\r\nPublic Property Get ProgressBar() As clsLblProg\r\n    Set ProgressBar = m_Prog\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ProgMax\r\n' Author    : Adam Waller\r\n' Date      : 11/6/2020\r\n' Purpose   : Wrapper to set max value for progress bar.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Let ProgMax(lngMaxValue As Long)\r\n    If Not m_Prog Is Nothing Then m_Prog.Max = lngMaxValue\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveFile\r\n' Author    : Adam Waller\r\n' Date      : 1/18/2019\r\n' Purpose   : Saves the current log data to a file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SaveFile(Optional strAlternatePath As String)\r\n    If Len(strAlternatePath) Then VerifyPath strAlternatePath\r\n    WriteFile m_Log.GetStr, Nz2(strAlternatePath, Me.LogFilePath)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LogFilePath\r\n' Author    : Adam Waller\r\n' Date      : 5/13/2023\r\n' Purpose   : Return the (default) full path to the current log file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get LogFilePath() As String\r\n    Dim strFile As String\r\n    Select Case Me.OperationType\r\n        Case eotExport: strFile = \"Export.log\"\r\n        Case eotBuild:  strFile = \"Build.log\"\r\n        Case eotMerge:  strFile = \"Merge.log\"\r\n        Case Else:      strFile = \"Other.log\"\r\n    End Select\r\n    LogFilePath = FSO.BuildPath(Me.SourcePath, strFile)\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 4/28/2020\r\n' Purpose   : Set initial options\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    Set m_Console = New clsConcat\r\n    Set m_Log = New clsConcat\r\n    m_blnProgressActive = False\r\n    m_sngLastUpdate = 0\r\n    Me.PadLength = 30\r\n    Me.ErrorLevel = eelNoError\r\n    Me.Active = False\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PadRight\r\n' Author    : Adam Waller\r\n' Date      : 1/25/2019\r\n' Purpose   : Pad a string on the right to make it `count` characters long.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub PadRight(strText As String, Optional blnPrint As Boolean = True, Optional blnNextOutputOnNewLine As Boolean = False, Optional ByVal intCharacters As Integer)\r\n    If intCharacters = 0 Then intCharacters = Me.PadLength\r\n    If Len(strText) < intCharacters Then\r\n        Me.Add strText & Space$(intCharacters - Len(strText)), blnPrint, blnNextOutputOnNewLine\r\n    Else\r\n        Me.Add strText, blnPrint, blnNextOutputOnNewLine\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Increment\r\n' Author    : Adam Waller\r\n' Date      : 4/28/2020\r\n' Purpose   : Increment the progress bar/output display\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Increment()\r\n\r\n    ' Ignore if we are not using the form\r\n    If m_Prog Is Nothing Then Exit Sub\r\n    ' Check the current status.\r\n    Perf.OperationStart \"Increment Progress\"\r\n    If Not m_blnProgressActive Then\r\n        ' Flush any pending output\r\n        With m_RichText\r\n            Echo False\r\n            ' Show the last 20K characters so\r\n            ' we don't hit the Integer limit\r\n            ' on the SelStart property.\r\n            .Value = m_Console.RightStr(20000)\r\n            If VerifyFocus(m_RichText) Then .SelStart = 20000\r\n            Echo True\r\n        End With\r\n    End If\r\n    Me.ProgressBar.Increment\r\n\r\n    ' Status is now active\r\n    m_blnProgressActive = True\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsOptions.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsOptions\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = True\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' Note, this class could be described as Options, Settings or Preferences. After some\r\n' deliberation and review, I chose to go with Options because it is most widely used\r\n' in Microsoft products (Office, Visual Studio, VBA IDE, etc...)\r\n' Further reading: https://english.stackexchange.com/questions/59058/\r\n\r\nPrivate Const cstrOptionsFilename As String = \"vcs-options.json\"\r\nPrivate Const cstrSourcePathProperty As String = \"VCS Source Path\"\r\nPrivate Const cstrBuildPathProperty As String = \"VCS Build Path\"\r\n\r\n' Options\r\nPublic ExportFolder As String\r\nPublic ShowDebug As Boolean\r\nPublic UseFastSave As Boolean\r\nPublic UseMergeBuild As Boolean\r\nPublic UseGitIntegration As Boolean\r\nPublic GitSettings As Dictionary\r\nPublic SavePrintVars As Boolean\r\nPublic ExportPrintSettings As Dictionary\r\nPublic SaveQuerySQL As Boolean\r\nPublic FormatSQL As Boolean\r\nPublic ForceImportOriginalQuerySQL As Boolean\r\nPublic SaveTableSQL As Boolean\r\nPublic SplitLayoutFromVBA As Boolean\r\nPublic StripPublishOption As Boolean\r\nPublic SanitizeColors As eSanitizeLevel\r\nPublic SanitizeLevel As eSanitizeLevel\r\nPublic ExtractThemeFiles As Boolean\r\nPublic TablesToExportData As Dictionary\r\nPublic SchemaExports As Dictionary\r\nPublic RunBeforeExport As String\r\nPublic RunAfterExport As String\r\nPublic RunBeforeBuild As String\r\nPublic RunAfterBuild As String\r\nPublic RunBeforeMerge As String\r\nPublic RunAfterMerge As String\r\nPublic ShowVCSLegacy As Boolean\r\nPublic HashAlgorithm As String\r\nPublic UseShortHash As Boolean\r\nPublic BreakOnError As Boolean\r\nPublic PreserveRubberDuckID As Boolean\r\n\r\n' Enum to compare loaded version to installed version.\r\nPublic Enum eVersionCompare\r\n    evcUnknown\r\n    evcOlderVersion\r\n    evcSameVersion\r\n    evcNewerVersion\r\nEnd Enum\r\n\r\n' Private collections for options and enum values.\r\nPrivate m_colOptions As Collection\r\nPrivate m_dEnum As Dictionary\r\nPrivate m_strOptionsFilePath As String\r\nPrivate m_strLoadedVersion As String\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadDefaults\r\n' Author    : Adam Waller\r\n' Date      : 2/12/2020\r\n' Purpose   : Loads the default values. Define system defaults here.\r\n'           : (Some values not defined here when they initialize to the default state.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadDefaults()\r\n\r\n    With Me\r\n        ' Top level settings\r\n        .ExportFolder = vbNullString\r\n        .ShowDebug = False\r\n        .UseFastSave = True\r\n        .UseMergeBuild = False\r\n        .UseGitIntegration = False\r\n        .SavePrintVars = True\r\n        .SaveQuerySQL = True\r\n        .FormatSQL = True\r\n        .ForceImportOriginalQuerySQL = False\r\n        .SaveTableSQL = True\r\n        .SplitLayoutFromVBA = True\r\n        .StripPublishOption = True\r\n        .SanitizeLevel = eslStandard\r\n        .SanitizeColors = eslMinimal\r\n        .ShowVCSLegacy = True\r\n        .HashAlgorithm = DefaultHashAlgorithm\r\n        .UseShortHash = True\r\n\r\n        ' Table data export\r\n        Set .TablesToExportData = New Dictionary\r\n        ' Set CompareMode to textual comparison\r\n        .TablesToExportData.CompareMode = vbTextCompare\r\n        ' Save specific tables by default\r\n        AddTableToExportData \"USysRegInfo\", etdTabDelimited\r\n        AddTableToExportData \"USysRibbons\", etdTabDelimited\r\n\r\n        ' External Database Schemas\r\n        Set .SchemaExports = New Dictionary\r\n        .SchemaExports.CompareMode = TextCompare\r\n\r\n        ' Print settings to export\r\n        Set .ExportPrintSettings = New Dictionary\r\n        With .ExportPrintSettings\r\n            .CompareMode = TextCompare\r\n            .Add \"Orientation\", True\r\n            .Add \"PaperSize\", True\r\n            .Add \"Duplex\", False\r\n            .Add \"PrintQuality\", False\r\n            .Add \"DisplayFrequency\", False\r\n            .Add \"Collate\", False\r\n            .Add \"Resolution\", False\r\n            .Add \"DisplayFlags\", False\r\n            .Add \"Color\", False\r\n            .Add \"Copies\", False\r\n            .Add \"ICMMethod\", False\r\n            .Add \"DefaultSource\", False\r\n            .Add \"Scale\", False\r\n            .Add \"ICMIntent\", False\r\n            .Add \"FormName\", False\r\n            .Add \"PaperLength\", False\r\n            .Add \"DitherType\", False\r\n            .Add \"MediaType\", False\r\n            .Add \"PaperWidth\", False\r\n            .Add \"TTOption\", False\r\n        End With\r\n\r\n        ' Git integration settings\r\n        Set .GitSettings = New Dictionary\r\n        With .GitSettings\r\n            .CompareMode = TextCompare\r\n            .Add \"MergeUntrackedFiles\", True\r\n            .Add \"ImportTableData\", False\r\n            .Add \"MergeQuerySQL\", False\r\n            .Add \"MergeConflicts\", \"Cancel Merge\"\r\n            .Add \"RunBeforeMerge\", vbNullString\r\n            .Add \"RunAfterMerge\", vbNullString\r\n            .Add \"InspectSharedImages\", False\r\n            .Add \"InspectThemeFiles\", False\r\n        End With\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddTableToExportData\r\n' Author    : Adam Waller\r\n' Date      : 4/17/2020\r\n' Purpose   : Add a table to the list of saved tables\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub AddTableToExportData(strName As String, intExportFormat As eTableDataExportFormat)\r\n\r\n    Dim strFormat(etdTabDelimited To etdXML) As String\r\n    Dim dTable As Dictionary\r\n\r\n    Set dTable = New Dictionary\r\n\r\n    strFormat(etdTabDelimited) = \"TabDelimited\"\r\n    strFormat(etdXML) = \"XMLFormat\"\r\n    With Me.TablesToExportData\r\n        Set .Item(strName) = dTable\r\n        .Item(strName)(\"Format\") = GetTableExportFormatName(intExportFormat)\r\n        ' Could add ExcludeColumns here later...\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveOptionsToFile\r\n' Author    : Adam Waller\r\n' Date      : 2/12/2020\r\n' Purpose   : Save the loaded Options to a file in JSON format\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SaveOptionsToFile(strPath As String)\r\n    WriteFile modJsonConverter.ConvertToJson(SerializeOptions, JSON_WHITESPACE) & vbCrLf, strPath\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveOptionsAsDefault\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Save these options as default for projects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SaveOptionsAsDefault()\r\n    Me.SaveOptionsToFile GetDefaultOptionsFilePath\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveOptionsForProject\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Save these options for this particular project.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SaveOptionsForProject()\r\n    ' Save source path option in current database.\r\n    SavedSourcePath = Me.ExportFolder\r\n    ' Set the build path, if needed.\r\n    SetBuildPath\r\n    ' Save options to the export folder location\r\n    Me.SaveOptionsToFile Me.GetExportFolder & cstrOptionsFilename\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadOptionsFromFile\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Load matching options from json file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadOptionsFromFile(strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n    Dim dOptions As Dictionary\r\n    Dim varOption As Variant\r\n    Dim strKey As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Save file path, in case we need to use it to determine\r\n    ' the export folder location with no database open.\r\n    m_strOptionsFilePath = strFile\r\n\r\n    ' Read in the options from the json file.\r\n    Set dFile = ReadJsonFile(strFile)\r\n    If Not dFile Is Nothing Then\r\n        ' Get version of loaded file\r\n        m_strLoadedVersion = dNZ(dFile, \"Info\\AddinVersion\")\r\n        If dFile.Exists(\"Options\") Then\r\n            Set dOptions = dFile(\"Options\")\r\n            ' Perform any relevant option upgrades\r\n            Upgrade dOptions\r\n            ' Attempt to set any matching options in this class.\r\n            For Each varOption In m_colOptions\r\n                strKey = CStr(varOption)\r\n                If dOptions.Exists(strKey) Then\r\n                    ' Set class property with value read from file.\r\n                    Select Case strKey\r\n                        Case \"ExportPrintSettings\"\r\n                            Set Me.ExportPrintSettings = dOptions(strKey)\r\n                        Case \"TablesToExportData\"\r\n                            Set Me.TablesToExportData = CloneDictionary(dOptions(strKey), ecmTextCompare)\r\n                        Case \"SchemaExports\"\r\n                            Set Me.SchemaExports = CloneDictionary(dOptions(strKey), ecmTextCompare)\r\n                        Case Else\r\n                            ' Regular top-level properties\r\n                            CallByName Me, strKey, VbLet, dOptions(strKey)\r\n                    End Select\r\n                End If\r\n            Next varOption\r\n        End If\r\n    End If\r\n\r\n    ' Set the build path property when loading options\r\n    SetBuildPath\r\n\r\n    CatchAny eelError, \"Loading options from \" & strFile, ModuleName(Me) & \".LoadOptionsFromFile\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetLoadedVersion\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2023\r\n' Purpose   : Return the VCS version that was loaded for this project.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetLoadedVersion() As String\r\n    GetLoadedVersion = m_strLoadedVersion\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadedVersion\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2023\r\n' Purpose   : Compares the loaded version to the currently installed version of the\r\n'           : add-in.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function CompareLoadedVersion() As eVersionCompare\r\n\r\n    Dim strLoaded As String\r\n    Dim strInstalled As String\r\n\r\n    ' Build comparison strings\r\n    strLoaded = BuildVersionCompareString(m_strLoadedVersion)\r\n    strInstalled = BuildVersionCompareString(GetVCSVersion)\r\n\r\n    If Len(strLoaded) And Len(strInstalled) Then\r\n        If strLoaded < strInstalled Then CompareLoadedVersion = evcOlderVersion\r\n        If strLoaded = strInstalled Then CompareLoadedVersion = evcSameVersion\r\n        If strLoaded > strInstalled Then CompareLoadedVersion = evcNewerVersion\r\n    Else\r\n        CompareLoadedVersion = evcUnknown\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildVersionCompareString\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2023\r\n' Purpose   : Reformat the string to a padded string that can be used to compare\r\n'           : versions.  I.e. \"4.1.345\" becomes \"000040000100345\"\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function BuildVersionCompareString(strVersion As String) As String\r\n\r\n    Dim varParts As Variant\r\n\r\n    ' Build version strings to compare\r\n    varParts = Split(strVersion, \".\")\r\n    If UBound(varParts) = 2 Then\r\n        BuildVersionCompareString = _\r\n            Format(varParts(0), \"00000\") & _\r\n            Format(varParts(1), \"00000\") & _\r\n            Format(varParts(2), \"00000\")\r\n    Else\r\n        ' Unable to parse version\r\n        BuildVersionCompareString = strVersion\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Upgrade\r\n' Author    : Adam Waller\r\n' Date      : 11/21/2023\r\n' Purpose   :\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Upgrade(ByRef dOptions As Dictionary)\r\n\r\n    Dim varKey As Variant\r\n    Dim dItem As Dictionary\r\n    Dim varItems As Variant\r\n    Dim colItems As Collection\r\n    Dim varLine As Variant\r\n\r\n    ' 6/16/2021\r\n    ' Aggressive sanitize to sanitize levels\r\n    If dOptions.Exists(\"AggressiveSanitize\") Then\r\n        If Not dOptions.Exists(\"SanitizeLevel\") Then\r\n            ' Check for non-default level\r\n            If Not dOptions(\"AggressiveSanitize\") Then\r\n                ' Set to basic level\r\n                dOptions.Add \"SanitizeLevel\", eslMinimal\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' 11/3/2023\r\n    ' Check option to split VBA from object layout\r\n    If Not dOptions.Exists(\"SplitLayoutFromVBA\") Then\r\n        ' The existing options file does not have this option defined.\r\n        ' See if we have any source files from previous exports.\r\n        If HasUnifiedLayoutFilesInGit(Me.GetExportFolder) Then\r\n            ' Set the option as false by default, and let the user\r\n            ' turn it on explicitly for this project.\r\n            ' (That way they are not forced to make a decision immediately)\r\n            Me.SplitLayoutFromVBA = False\r\n        Else\r\n            ' If we already have split files, or if this project is\r\n            ' being exported for the first time, leave the option at\r\n            ' the default setting.\r\n        End If\r\n    End If\r\n\r\n    ' 1/9/2024 (4.0.31)\r\n    ' Use collection to store schema filter entries\r\n    If dOptions.Exists(\"SchemaExports\") Then\r\n        If dOptions(\"SchemaExports\").Count > 0 Then\r\n            For Each varKey In dOptions(\"SchemaExports\").Keys\r\n                Set dItem = dOptions(\"SchemaExports\").Item(varKey)\r\n                If dItem.Exists(\"Filter\") Then\r\n                    ' If this is a string value, upgrade it to a collection\r\n                    If Not IsObject(dItem(\"Filter\")) Then\r\n                        Set colItems = New Collection\r\n                        varItems = Split(dItem(\"Filter\"), \"\\r\\n\")\r\n                        Set dItem(\"Filter\") = New Collection\r\n                        ' Build collection items\r\n                        For Each varLine In varItems\r\n                            colItems.Add CStr(varLine)\r\n                        Next varLine\r\n                        Set dItem(\"Filter\") = colItems\r\n                    End If\r\n                End If\r\n            Next varKey\r\n        End If\r\n    End If\r\n\r\n    ' 2/8/2025 (4.0.39)\r\n    ' Remove UI option for SanitizeLevel = None (#554)\r\n    If dOptions.Exists(\"SanitizeLevel\") Then\r\n        If dOptions(\"SanitizeLevel\") = eSanitizeLevel.eslNone Then\r\n            dOptions(\"SanitizeLevel\") = eSanitizeLevel.eslMinimal\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasUnifiedLayoutFilesInGit\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2023\r\n' Purpose   : Returns true if the current project seems to have existing form or report\r\n'           : source files AND appears to be in a .git repository.\r\n'           : (This function is used when determining the default for splitting VBA\r\n'           :  from layout files in new projects.)\r\n'           : For performance reasons this is not a fully comprehensive check of every\r\n'           : possible source file, but should be a pretty good indication of whether\r\n'           : existing source files need to be split in git to preserve the history in\r\n'           : both source files.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function HasUnifiedLayoutFilesInGit(strExportPath As String) As Boolean\r\n\r\n    Dim blnHasFiles As Boolean\r\n\r\n    ' See if we have any \".bas\" files, but no corresponding \".cls\" files in the\r\n    ' forms and reports export folders.\r\n    ' Hard-coding the folder names to avoid calling options.\r\n    If GetFileList(BuildPath2(strExportPath, \"forms\"), \"*.bas\").Count > 0 Then\r\n        blnHasFiles = (GetFileList(BuildPath2(strExportPath, \"forms\"), \"*.cls\").Count = 0)\r\n    ElseIf GetFileList(BuildPath2(strExportPath, \"reports\"), \"*.bas\").Count > 0 Then\r\n        blnHasFiles = (GetFileList(BuildPath2(strExportPath, \"reports\"), \"*.cls\").Count = 0)\r\n    End If\r\n\r\n    If blnHasFiles Then\r\n        ' Check to see if this folder is in a git repository\r\n        If Git.Installed Then\r\n            ' Check export path\r\n            Git.WorkingFolder = strExportPath\r\n            HasUnifiedLayoutFilesInGit = Git.IsInsideRepository\r\n        End If\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadProjectOptions\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Loads the project options from a saved file. (If any)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadProjectOptions()\r\n\r\n    Dim strSaved As String\r\n\r\n    ' We can only load the options for the current project if we\r\n    ' have a database file open.\r\n    If Not DatabaseFileOpen Then Exit Sub\r\n\r\n    ' Get saved path from database (if defined)\r\n    strSaved = SavedSourcePath\r\n\r\n    ' Attempt to load the project options file.\r\n    If strSaved <> vbNullString Then Me.ExportFolder = strSaved\r\n    LoadOptionsFromFile Me.GetExportFolder & cstrOptionsFilename\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadDefaultOptions\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Load in the default options from the saved json file in the addin path.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadDefaultOptions()\r\n    LoadOptionsFromFile GetDefaultOptionsFilePath\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDefaultOptionsFilePath\r\n' Author    : Adam Waller\r\n' Date      : 5/7/2021\r\n' Purpose   : Return the full path of the default options file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetDefaultOptionsFilePath() As String\r\n    GetDefaultOptionsFilePath = FSO.BuildPath(CodeProject.Path, cstrOptionsFilename)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PrintOptionsToDebugWindow\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Output the current options.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub PrintOptionsToDebugWindow()\r\n    Debug.Print modJsonConverter.ConvertToJson(SerializeOptions, JSON_WHITESPACE)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetExportFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2020\r\n' Purpose   : Returns the actual export folder path from the ExportFolder option.\r\n'           : NOTE: We cannot return a relative export folder path without an open\r\n'           : database file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetExportFolder() As String\r\n\r\n    Dim strFullPath As String\r\n\r\n    If Me.ExportFolder = vbNullString Then\r\n        If DatabaseFileOpen Then\r\n            ' Build default path using project file name\r\n            strFullPath = CurrentProject.FullName & \".src\" & PathSep\r\n        Else\r\n            ' Check options file path\r\n            If Len(m_strOptionsFilePath) Then\r\n                strFullPath = FSO.GetParentFolderName(m_strOptionsFilePath)\r\n            End If\r\n        End If\r\n    Else\r\n        If Left$(Me.ExportFolder, 2) = PathSep & PathSep Then\r\n            ' UNC path\r\n            strFullPath = Me.ExportFolder\r\n        ElseIf Left$(Me.ExportFolder, 1) = PathSep Then\r\n            ' Relative path (from database file location)\r\n            strFullPath = CurrentProject.Path & Me.ExportFolder\r\n        Else\r\n            ' Other absolute path (i.e. c:\\myfiles\\)\r\n            strFullPath = Me.ExportFolder\r\n        End If\r\n        ' Environment variable replacements\r\n        If InStr(1, strFullPath, \"%\") > 0 Then\r\n            strFullPath = ExpandEnvironmentVariables(strFullPath)\r\n            ' Other placeholder replacements\r\n            If InStr(1, strFullPath, \"%dbName%\", vbTextCompare) > 0 Then\r\n                strFullPath = Replace(strFullPath, \"%dbName%\", CurrentProject.Name, , , vbTextCompare)\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' Check to make sure we have built a valid path.\r\n    Select Case strFullPath\r\n        Case PathSep & PathSep, PathSep, \".src\" & PathSep, vbNullString\r\n            ' Invalid paths\r\n            MsgBox2 \"Cannot determine export path\", _\r\n                \"A database file must be open to return a relative export path.\", _\r\n                \"This is probably due to a problem in the build sequence logic.\", vbExclamation\r\n            Log.Add \"ERROR: Cannot build relative export folder path without an open database file.\"\r\n            GetExportFolder = vbNullString\r\n        Case Else\r\n            ' Return export path with a trailing slash\r\n            GetExportFolder = StripSlash(strFullPath) & PathSep\r\n    End Select\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SerializeOptions\r\n' Author    : Adam Waller\r\n' Date      : 2/12/2020\r\n' Purpose   : Serializes Options into a dictionary array for saving to file as JSON.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SerializeOptions() As Dictionary\r\n\r\n    Dim dOptions As Dictionary\r\n    Dim dInfo As Dictionary\r\n    Dim dWrapper As Dictionary\r\n    Dim varOption As Variant\r\n    Dim strBit As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    Set dOptions = New Dictionary\r\n    Set dInfo = New Dictionary\r\n    Set dWrapper = New Dictionary\r\n\r\n    ' Add some header information (For debugging or upgrading)\r\n    #If Win64 Then\r\n        strBit = \" 64-bit\"\r\n    #Else\r\n        strBit = \" 32-bit\"\r\n    #End If\r\n    dInfo.Add \"AddinVersion\", AppVersion\r\n    dInfo.Add \"AccessVersion\", Application.Version & strBit\r\n\r\n    ' Loop through options\r\n    For Each varOption In m_colOptions\r\n        ' Simulate reflection to serialize properties.\r\n        dOptions.Add CStr(varOption), CallByName(Me, CStr(varOption), VbGet)\r\n    Next varOption\r\n\r\n    'Set SerializeOptions = new Dictionary\r\n    Set dWrapper(\"Info\") = dInfo\r\n    Set dWrapper(\"Options\") = dOptions\r\n    Set SerializeOptions = dWrapper\r\n\r\n    CatchAny eelError, \"Serializing options\", ModuleName(Me) & \".SerializeOptions\"\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetHash\r\n' Author    : Adam Waller\r\n' Date      : 2/16/2021\r\n' Purpose   : Return a hash of the current options. Used to detect if options have\r\n'           : changed, which may require a full export to reflect the change.\r\n'           : Note that this only checks a hash of the options that affect general\r\n'           : exports, not changes to external database connections.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetHash() As String\r\n    Dim dExportOptions As Dictionary\r\n    Set dExportOptions = CloneDictionary(SerializeOptions)\r\n    With dExportOptions(\"Options\")\r\n        ' Remove options not related to exports\r\n        If .Exists(\"SchemaExports\") Then .Remove (\"SchemaExports\")\r\n    End With\r\n    GetHash = GetDictionaryHash(dExportOptions)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSanitizeLevelName\r\n' Author    : Hecon5 (as adapted from Adam Waller's below)\r\n' Date      : 6/09/2021\r\n' Purpose   : Return the name used to read and write to the JSON options files.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSanitizeLevelName(intSanitizeLevel As eSanitizeLevel) As String\r\n    Select Case intSanitizeLevel\r\n        Case eslNone:           GetSanitizeLevelName = \"None (Off)\"\r\n        Case eslMinimal:        GetSanitizeLevelName = \"Minimal\"\r\n        Case eslStandard:       GetSanitizeLevelName = \"Standard\"\r\n        Case eslExtended:       GetSanitizeLevelName = \"Extended\"\r\n        Case Else:              GetSanitizeLevelName = vbNullString\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSanitizeLevel\r\n' Author    : Hecon5 (as adapted from Adam Waller's below)\r\n' Date      : 4/17/2020\r\n' Purpose   : Translate the table export format key to the corresponding enum value.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSanitizeLevel(strKey As String) As eSanitizeLevel\r\n    Dim intSanitizeLevel As eSanitizeLevel\r\n    Dim strName As String\r\n    For intSanitizeLevel = eslNone To eSanitizeLevel.[_Last]\r\n        strName = Me.GetSanitizeLevelName(intSanitizeLevel)\r\n        If strName = strKey Then\r\n            GetSanitizeLevel = intSanitizeLevel\r\n            Exit For\r\n        End If\r\n    Next intSanitizeLevel\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTableExportFormatName\r\n' Author    : Adam Waller\r\n' Date      : 4/17/2020\r\n' Purpose   : Return the name used to read and write to the JSON options files.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetTableExportFormatName(intFormat As eTableDataExportFormat) As String\r\n    Select Case intFormat\r\n        Case etdTabDelimited:   GetTableExportFormatName = \"Tab Delimited\"\r\n        Case etdXML:            GetTableExportFormatName = \"XML Format\"\r\n        Case Else:              GetTableExportFormatName = vbNullString\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTableExportFormat\r\n' Author    : Adam Waller\r\n' Date      : 4/17/2020\r\n' Purpose   : Translate the table export format key to the corresponding enum value.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetTableExportFormat(strKey As String) As eTableDataExportFormat\r\n    Dim intFormat As eTableDataExportFormat\r\n    Dim strName As String\r\n    For intFormat = etdNoData To eTableDataExportFormat.[_Last] - 1\r\n        strName = Me.GetTableExportFormatName(intFormat)\r\n        If strName = strKey Then\r\n            GetTableExportFormat = intFormat\r\n            Exit For\r\n        End If\r\n    Next intFormat\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 2/12/2020\r\n' Purpose   : Load default values when creating class\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n\r\n    ' Initialize the options collection\r\n    Set m_colOptions = New Collection\r\n\r\n    ' Load enum values\r\n    Set m_dEnum = New Dictionary\r\n\r\n    ' Load list of property names for reflection type behavior.\r\n    With m_colOptions\r\n        .Add \"ExportFolder\"\r\n        .Add \"ShowDebug\"\r\n        .Add \"UseFastSave\"\r\n        .Add \"UseMergeBuild\"\r\n        .Add \"UseGitIntegration\"\r\n        .Add \"SavePrintVars\"\r\n        .Add \"ExportPrintSettings\"\r\n        .Add \"SaveQuerySQL\"\r\n        .Add \"FormatSQL\"\r\n        .Add \"ForceImportOriginalQuerySQL\"\r\n        .Add \"SaveTableSQL\"\r\n        .Add \"SplitLayoutFromVBA\"\r\n        .Add \"StripPublishOption\"\r\n        .Add \"SanitizeColors\"\r\n        .Add \"SanitizeLevel\"\r\n        .Add \"ExtractThemeFiles\"\r\n        .Add \"TablesToExportData\"\r\n        .Add \"SchemaExports\"\r\n        .Add \"RunBeforeExport\"\r\n        .Add \"RunAfterExport\"\r\n        .Add \"RunBeforeBuild\"\r\n        .Add \"RunAfterBuild\"\r\n        .Add \"RunBeforeMerge\"\r\n        .Add \"RunAfterMerge\"\r\n        .Add \"ShowVCSLegacy\"\r\n        .Add \"HashAlgorithm\"\r\n        .Add \"UseShortHash\"\r\n        .Add \"BreakOnError\"\r\n        .Add \"PreserveRubberDuckID\"\r\n    End With\r\n\r\n    ' Load default values\r\n    Me.LoadDefaults\r\n\r\n    ' Load saved defaults\r\n    LoadDefaultOptions\r\n\r\n    ' Other run-time options\r\n    JsonOptions.AllowUnicodeChars = True\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetBuildPath\r\n' Author    : Adam Waller\r\n' Date      : 10/28/2021\r\n' Purpose   : Set a build path property value if the export folder is not relative\r\n'           : to the current database path.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SetBuildPath()\r\n\r\n    Dim prp As AccessObjectProperty\r\n    Dim proj As CurrentProject\r\n\r\n    Set proj = CurrentProject\r\n    Set prp = GetPropertyByName(cstrBuildPathProperty)\r\n\r\n    ' This should only be done on an open database/project\r\n    If DatabaseFileOpen Then\r\n        ' Check the export folder option\r\n        If Me.ExportFolder = vbNullString Then\r\n            ' When using the default of a blank export path, we don't need\r\n            ' to store the build folder location. (It will be different on\r\n            ' different computers, and not needed for a relative export path.)\r\n            If Not prp Is Nothing Then proj.Properties.Remove prp.Name\r\n        Else\r\n            If prp Is Nothing Then\r\n                ' Create the property\r\n                proj.Properties.Add cstrBuildPathProperty, proj.Path\r\n            Else\r\n                ' Update the value.\r\n                If prp.Value <> proj.Path Then prp.Value = proj.Path\r\n            End If\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SavedSourcePath\r\n' Author    : Adam Waller\r\n' Date      : 7/13/2020\r\n' Purpose   : Get any saved path for VCS source files. (In case we are using a\r\n'           : different location for the files.) This is stored as a property\r\n'           : under the currentproject. (Works for both ADP and MDB)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get SavedSourcePath() As String\r\n    Dim prp As AccessObjectProperty\r\n    Set prp = GetPropertyByName(cstrSourcePathProperty)\r\n    If Not prp Is Nothing Then SavedSourcePath = prp.Value\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SavedSourcePath\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2020\r\n' Purpose   : Save the source path as a property in the current database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Let SavedSourcePath(strPath As String)\r\n\r\n    Dim prp As AccessObjectProperty\r\n    Dim proj As CurrentProject\r\n\r\n    Set proj = CurrentProject\r\n    Set prp = GetPropertyByName(cstrSourcePathProperty)\r\n\r\n    If strPath = vbNullString Then\r\n        ' Remove the property when no longer used.\r\n        If Not prp Is Nothing Then proj.Properties.Remove prp.Name\r\n    Else\r\n        If prp Is Nothing Then\r\n            ' Create the property\r\n            proj.Properties.Add cstrSourcePathProperty, strPath\r\n        Else\r\n            ' Update the value.\r\n            prp.Value = strPath\r\n        End If\r\n    End If\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetPropertyByName\r\n' Author    : Adam Waller\r\n' Date      : 10/28/2021\r\n' Purpose   : Return a project property\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetPropertyByName(strName As String) As AccessObjectProperty\r\n\r\n    Dim prp As AccessObjectProperty\r\n    Dim proj As CurrentProject\r\n\r\n    If DatabaseFileOpen Then\r\n        Set proj = CurrentProject\r\n        For Each prp In proj.Properties\r\n            If prp.Name = strName Then\r\n                Set GetPropertyByName = prp\r\n                Exit For\r\n            End If\r\n        Next prp\r\n    End If\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsPerformance.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsPerformance\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsPerformance\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Measure the performance of the export/import process. Since different\r\n'           : users have different needs and work with sometimes very different\r\n'           : databases, this tool will help identify potential bottlenecks in the\r\n'           : performance of the add-in in real-life scenarios. The results are\r\n'           : typically added to the log files.\r\n'           : Note: This class has been updated to use API calls for timing to the\r\n'           : microsecond level. For additional details, see the following link:\r\n'           : http://www.mendipdatasystems.co.uk/timer-comparison-tests/4594552971\r\n'---------------------------------------------------------------------------------------\r\n\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' Enable or disable timing operations\r\nPublic Enabled As Boolean\r\n\r\n' Use type for private class variables\r\nPrivate Type udtPerformance\r\n    Overall As clsPerformanceItem\r\n    CategoryName As String\r\n    Categories As Dictionary\r\n    OperationName As String\r\n    Operations As Dictionary\r\n    Frequency As Currency\r\n    intDigitsAfterDecimal As Integer\r\n    ' Manage a type of call stack to track nested operations.\r\n    ' When an operation finishes, it goes back to timing the\r\n    ' previous operation.\r\n    CallStackItems As Collection\r\nEnd Type\r\nPrivate this As udtPerformance\r\n\r\n' API calls to get more precise time than Timer function\r\nPrivate Declare PtrSafe Function GetFrequencyAPI Lib \"kernel32\" Alias \"QueryPerformanceFrequency\" (ByRef Frequency As Currency) As Long\r\nPrivate Declare PtrSafe Function GetTimeAPI Lib \"kernel32\" Alias \"QueryPerformanceCounter\" (ByRef Counter As Currency) As Long\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StartTiming\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Start the overall timing.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub StartTiming()\r\n    If Not Me.Enabled Then Exit Sub\r\n    Reset\r\n    Set this.Overall = New clsPerformanceItem\r\n    this.Overall.Start = MicroTimer\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CallStack\r\n' Author    : hecon5\r\n' Date      : 01/26/2022; 04/28/2022\r\n' Purpose   : Return the call stack in operation.\r\n'           : Adds major element \"Component\"(now \"Category\") if in use, and the trace of all minor\r\n'           : elements (Operations) affixed with a period. This facilitates use of\r\n'           : performance and because the VBA error may be returned before you expect\r\n'           : (or after...VBA can be fun!).\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get CallStack() As String\r\n\r\n    Dim lngCallStackPosition As Long\r\n\r\n    If Not Me.Enabled Then Exit Property\r\n\r\n    ' Build out breadcrumb type call stack string\r\n    With New clsConcat\r\n        .AppendOnAdd = \".\"\r\n        If this.CategoryName <> vbNullString Then .Add this.CategoryName\r\n        If Not this.CallStackItems Is Nothing Then\r\n            For lngCallStackPosition = 1 To this.CallStackItems.Count\r\n                .Add CStr(this.CallStackItems(lngCallStackPosition))\r\n            Next lngCallStackPosition\r\n        End If\r\n        If this.OperationName <> vbNullString Then .Add this.OperationName\r\n        If .Length > 0 Then .Remove 1 ' Remove trailing delimiter only if there's something to remove.\r\n        CallStack = .GetStr\r\n    End With\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CurrentCategoryName\r\n' Author    : hecon5\r\n' Date      : 10/3/2023\r\n' Purpose   : Return the current category name.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get CurrentCategoryName() As String\r\n    CurrentCategoryName = this.CategoryName\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CategoryStart\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Start timing a component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CategoryStart(strName As String)\r\n    If Not Me.Enabled Then Exit Sub\r\n    If this.Categories Is Nothing Then Set this.Categories = New Dictionary\r\n    If this.CategoryName <> vbNullString Then CategoryEnd\r\n    StartTimer this.Categories, strName\r\n    this.CategoryName = strName\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CategoryEnd\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : End the timing of the active component\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CategoryEnd(Optional lngCount As Long = 1)\r\n    If Not Me.Enabled Then Exit Sub\r\n    If this.CategoryName <> vbNullString And Not this.Categories Is Nothing Then\r\n        LapTimer this.Categories(this.CategoryName), lngCount\r\n        this.CategoryName = vbNullString\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OperationStart\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Start timing a named operation. (i.e. Sanitize Files)\r\n'           : Note: This does a type of \"call stack\" function, where nested operations\r\n'           : are recorded exclusive of the parent operations.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub OperationStart(strName As String)\r\n\r\n    If Not Me.Enabled Then Exit Sub\r\n\r\n    ' Ensure that we have created the operations dictionary\r\n    If this.Operations Is Nothing Then Set this.Operations = New Dictionary\r\n\r\n    ' See if we are already timing something\r\n    If this.OperationName <> vbNullString Then\r\n\r\n        ' We are already timing something else right now.\r\n        ' Save the current process to the call stack before switching\r\n        ' to the new operation.\r\n        LapTimer this.Operations(this.OperationName), 0\r\n        If this.CallStackItems Is Nothing Then Set this.CallStackItems = New Collection\r\n        With this.CallStackItems\r\n            ' Safety check!\r\n            If .Count < 100 Then .Add this.OperationName\r\n        End With\r\n    End If\r\n\r\n    ' Start the timer for this operation.\r\n    StartTimer this.Operations, strName\r\n    this.OperationName = strName\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OperationEnd\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Stop the timing of the active operation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub OperationEnd(Optional lngCount As Long = 1)\r\n\r\n    Dim strLastOperation As String\r\n\r\n    If Not Me.Enabled Then Exit Sub\r\n\r\n    ' Verify that we are timing something, and record the elapsed time.\r\n    If this.OperationName <> vbNullString And Not this.Operations Is Nothing Then\r\n\r\n        ' Record the elapsed time.\r\n        LapTimer this.Operations(this.OperationName), lngCount\r\n\r\n        ' Check the call stack to see if we need to move back to the previous process.\r\n        If Not this.CallStackItems Is Nothing Then\r\n            With this.CallStackItems\r\n                If .Count > 0 Then\r\n                    ' Resume previous activity\r\n                    strLastOperation = .Item(.Count)\r\n                    this.OperationName = vbNullString\r\n                    OperationStart strLastOperation\r\n                    ' Remove last item from call stack\r\n                    .Remove .Count\r\n                Else\r\n                    ' No longer timing any operations.\r\n                    this.OperationName = vbNullString\r\n                End If\r\n            End With\r\n        Else\r\n            this.OperationName = vbNullString\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CurrentOperationName\r\n' Author    : hecon5\r\n' Date      : 10/3/2023\r\n' Purpose   : Return the current operation's name.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get CurrentOperationName() As String\r\n    CurrentOperationName = this.OperationName\r\nEnd Property\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DigitsAfterDecimal\r\n' Author    : Eugen Albiker\r\n' Date      : 16/3/2021\r\n' Purpose   : Set Number of Digits after Decimal for the Elapsed Time\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get DigitsAfterDecimal() As Integer\r\n    DigitsAfterDecimal = this.intDigitsAfterDecimal\r\nEnd Property\r\n\r\nPublic Property Let DigitsAfterDecimal(intDigitsAfterDecimal As Integer)\r\n    If intDigitsAfterDecimal > 4 Then intDigitsAfterDecimal = 4\r\n    this.intDigitsAfterDecimal = intDigitsAfterDecimal\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : EndTiming\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : End the overall timing, adding to total. (Allows you to start and stop\r\n'           : during the instance of the class.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub EndTiming()\r\n    If Not Me.Enabled Then Exit Sub\r\n    LapTimer this.Overall, 1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MicroTimer\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Return time in seconds with microsecond precision\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function MicroTimer() As Currency\r\n\r\n    Dim curTime As Currency\r\n\r\n    ' Call API to get current time\r\n    GetTimeAPI curTime\r\n\r\n    ' Convert to seconds\r\n    MicroTimer = (curTime / this.Frequency)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StartTimer\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Add the item if it doesn't exist, then set the start time.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub StartTimer(dItems As Dictionary, strName As String)\r\n\r\n    Dim cItem As clsPerformanceItem\r\n\r\n    If Not dItems.Exists(strName) Then\r\n        Set cItem = New clsPerformanceItem\r\n        dItems.Add strName, cItem\r\n    End If\r\n    dItems(strName).Start = MicroTimer\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LapTimer\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Adds the elapsed time to the timer.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub LapTimer(cItem As clsPerformanceItem, lngCount As Long)\r\n    If Not cItem Is Nothing Then\r\n        With cItem\r\n            If .Start > 0 Then\r\n                .Total = .Total + GetElapsed(.Start)\r\n                .Start = 0\r\n                .Count = .Count + lngCount\r\n            End If\r\n        End With\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PauseTiming\r\n' Author    : Adam Waller\r\n' Date      : 10/20/2021\r\n' Purpose   : Lap all timers to capture elapsed time.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub PauseTiming()\r\n\r\n    If Not Me.Enabled Then Exit Sub\r\n\r\n    ' Lap overall time\r\n    LapTimer this.Overall, 0\r\n\r\n    ' Lap any active timing operation without incrementing\r\n    If this.OperationName <> vbNullString And Not this.Operations Is Nothing Then\r\n        LapTimer this.Operations(this.OperationName), 0\r\n    End If\r\n\r\n    If this.CategoryName <> vbNullString And Not this.Categories Is Nothing Then\r\n        LapTimer this.Categories(this.CategoryName), 0\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ResumeTiming\r\n' Author    : Adam Waller\r\n' Date      : 10/20/2021\r\n' Purpose   : Resume the timing of all timers by resetting the start time.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ResumeTiming()\r\n\r\n    If Not Me.Enabled Then Exit Sub\r\n\r\n    ' Resume overall time\r\n    If Not this.Overall Is Nothing Then this.Overall.Start = MicroTimer\r\n\r\n    ' Resume current operation\r\n    If this.OperationName <> vbNullString Then StartTimer this.Operations, this.OperationName\r\n\r\n    ' Resume current Category\r\n    If this.CategoryName <> vbNullString Then StartTimer this.Categories, this.CategoryName\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetElapsed\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Add current timer to sngStart to get elapsed seconds.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetElapsed(curStart As Currency) As Currency\r\n\r\n    Dim curNow As Currency\r\n\r\n    ' Only return a value if we have a starting time.\r\n    If curStart > 0 Then\r\n        curNow = MicroTimer\r\n        If curStart <= curNow Then\r\n            GetElapsed = curNow - curStart\r\n        Else\r\n            ' Just in case someone was up really late, and crossed midnight...\r\n            GetElapsed = curStart + ((24# * 60 * 60) - curStart)\r\n        End If\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TotalTime\r\n' Author    : Adam Waller\r\n' Date      : 4/29/2021\r\n' Purpose   : Return total time from start to end, or to now if end not specified.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get TotalTime() As Currency\r\n    If Not Me.Enabled Then Exit Function\r\n    If this.Overall Is Nothing Then\r\n        TotalTime = 0\r\n    Else\r\n        LapTimer this.Overall, 0\r\n        TotalTime = this.Overall.Total\r\n    End If\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetReports\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Return report text\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetReports() As String\r\n\r\n    Const cstrTitle As String = \"PERFORMANCE REPORTS\"\r\n\r\n    Dim varKey As Variant\r\n    Dim curTotal As Currency\r\n    Dim dblCount As Double\r\n    Dim lngCol(0 To 2) As Long\r\n    Dim strSpacer As String\r\n\r\n    If Not Me.Enabled Then Exit Function\r\n\r\n    ' Set up column sizes\r\n    lngCol(0) = 30\r\n    lngCol(1) = 10\r\n    lngCol(2) = 10\r\n    strSpacer = Space(lngCol(0) + lngCol(1) + lngCol(2))\r\n    strSpacer = Replace(strSpacer, \" \", \"-\")\r\n\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        .Add strSpacer\r\n        .Add Space((Len(strSpacer) - Len(cstrTitle)) / 2) & cstrTitle\r\n\r\n        ' Table for object types\r\n        If Not this.Categories Is Nothing Then\r\n            Set this.Categories = SortItemsByTime(this.Categories)\r\n            .Add strSpacer\r\n            .Add ListResult(\"Category\", \"Count\", \"Seconds\", lngCol), vbCrLf, strSpacer\r\n            For Each varKey In this.Categories.Keys\r\n                .Add ListResult(CStr(varKey), CStr(this.Categories(varKey).Count), _\r\n                    Format$(this.Categories(varKey).Total, \"0.\" & String$(this.intDigitsAfterDecimal, \"0\")), lngCol)\r\n                ' Add to totals\r\n                dblCount = dblCount + this.Categories(varKey).Count\r\n                curTotal = curTotal + this.Categories(varKey).Total\r\n            Next varKey\r\n            .Add strSpacer\r\n            .Add ListResult(\"TOTALS:\", CStr(dblCount), _\r\n                Format$(curTotal, \"0.\" & String$(this.intDigitsAfterDecimal, \"0\")), lngCol)\r\n            .Add strSpacer\r\n            .Add vbNullString\r\n        End If\r\n\r\n        ' Table for operations\r\n        If Not this.Operations Is Nothing Then\r\n            Set this.Operations = SortItemsByTime(this.Operations)\r\n            curTotal = 0\r\n            .Add strSpacer\r\n            .Add ListResult(\"Operations\", \"Count\", \"Seconds\", lngCol), vbCrLf, strSpacer\r\n            For Each varKey In this.Operations.Keys\r\n                .Add ListResult(CStr(varKey), CStr(this.Operations(varKey).Count), _\r\n                    Format$(this.Operations(varKey).Total, \"0.\" & String$(this.intDigitsAfterDecimal, \"0\")), lngCol)\r\n                curTotal = curTotal + this.Operations(varKey).Total\r\n            Next varKey\r\n            .Add strSpacer\r\n            If Not this.Overall Is Nothing Then\r\n                .Add ListResult(\"Other Operations\", vbNullString, _\r\n                    Format$(this.Overall.Total - curTotal, \"0.\" & String$(this.intDigitsAfterDecimal, \"0\")), lngCol)\r\n                .Add strSpacer\r\n            End If\r\n            .Add vbNullString\r\n        End If\r\n\r\n        ' Check for unfinished operations\r\n        If Not this.CallStackItems Is Nothing Then\r\n            If this.CallStackItems.Count > 0 Then\r\n                .Add vbNullString\r\n                .Add \"WARNING: The performance monitoring for operations still\"\r\n                .Add \"had items in the call stack. This typically happens when\"\r\n                .Add \"performance logging is started for an operation, but not\"\r\n                .Add \"closed at the conclusion of the operation.\"\r\n                .Add \"The call stack currently contains the following \", this.CallStackItems.Count, \" items:\"\r\n                For Each varKey In this.CallStackItems\r\n                    .Add \" - \", CStr(varKey)\r\n                Next varKey\r\n            End If\r\n        End If\r\n\r\n        ' Return report section\r\n        GetReports = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ListResult\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : List the result of a test in a fixed width format. The result strings\r\n'           : are positioned at the number of characters specified. If the heading size\r\n'           : exceeds the width of the column, the text will be wrapped.\r\n'           : I.e:\r\n'           : MyFancyTest      23     2.45\r\n'           : My very long nam\r\n'           : e that I probabl\r\n'           : y should condens\r\n'           : e                12     3.23\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ListResult(strHeading As String, _\r\n                            strResult1 As String, _\r\n                            strResult2 As String, _\r\n                            lngCol() As Long) As String\r\n\r\n    Dim strRowHeading As String\r\n    Dim lngPos As Long\r\n    Dim intMax As Integer\r\n\r\n    ' Wrap at one character less than the column width\r\n    intMax = lngCol(0) - 1\r\n\r\n    ' Use concatenation class in case we need to deal with line wrapping\r\n    With New clsConcat\r\n\r\n        ' Check for size overflow on heading. (Wrap on multiple lines)\r\n        strRowHeading = strHeading\r\n        If Len(strRowHeading) > intMax Then\r\n            lngPos = 1\r\n            Do While lngPos + intMax <= Len(strHeading)\r\n                ' Add segment and linebreak\r\n                .Add Mid$(strHeading, lngPos, intMax), \" \", vbCrLf\r\n                lngPos = lngPos + intMax\r\n            Loop\r\n            ' Get last heading line to use with results\r\n            strRowHeading = Mid$(strHeading, lngPos)\r\n        End If\r\n\r\n        ' Display heading and amounts\r\n        .Add PadRight(strRowHeading, lngCol(0))\r\n        .Add PadRight(strResult1, lngCol(1))\r\n        .Add strResult2\r\n        ListResult = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PadRight\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Pads a string\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function PadRight(strText As String, _\r\n                        lngLen As Long, _\r\n                        Optional lngMinTrailingSpaces As Long = 1) As String\r\n\r\n    Dim strResult As String\r\n    Dim strTrimmed As String\r\n\r\n    strResult = Space$(lngLen)\r\n    strTrimmed = Left$(strText, lngLen - lngMinTrailingSpaces)\r\n\r\n    ' Use mid function to write over existing string of spaces.\r\n    Mid$(strResult, 1, Len(strTrimmed)) = strTrimmed\r\n    PadRight = strResult\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SortItemsByTime\r\n' Author    : Adam Waller\r\n' Date      : 4/4/2023\r\n' Purpose   : Sort the items by total time in descending order to put the slowest\r\n'           : items at the top of the list.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SortItemsByTime(dItems As Dictionary) As Dictionary\r\n\r\n    Dim varItems() As Variant\r\n    Dim varKey As Variant\r\n    Dim lngCnt As Long\r\n    Dim strRecord As String\r\n    Dim cItem As clsPerformanceItem\r\n    Dim dSorted As Dictionary\r\n\r\n    ' Create an array to build our items\r\n    ReDim varItems(0 To dItems.Count - 1)\r\n\r\n    ' The idea here is that we want to build a fixed width record with the time on\r\n    ' the left, and the key on the right. We will then sort this list of \"records\"\r\n    ' to get them sorted by time. However, since the sorting is in ascending order,\r\n    ' we will loop backwards through the resulting array to rebuild a dictionary\r\n    ' of items in descending order. We will split out the key and use this to get\r\n    ' a reference back to the original item in the source dictionary.\r\n\r\n    ' Build our list of records\r\n    For Each varKey In dItems.Keys\r\n        ' Create a record like this: \"00062840.170000|Export Form Objects\"\r\n        strRecord = Format$(dItems(varKey).Total, \"00000000.000000\") & \"|\" & CStr(varKey)\r\n        ' Add to array.\r\n        varItems(lngCnt) = strRecord\r\n        ' Increment counter for array\r\n        lngCnt = lngCnt + 1\r\n    Next varKey\r\n\r\n    ' Sort the array in ascending order\r\n    QuickSort varItems\r\n\r\n    ' Now, build a new dictionary with the sorted items\r\n    ' (We are walking backwards through the array to flip the sort to descending)\r\n    Set dSorted = New Dictionary\r\n    For lngCnt = dItems.Count - 1 To 0 Step -1\r\n        ' Parse key from record (text after first pipe character)\r\n        strRecord = varItems(lngCnt)\r\n        varKey = Mid$(strRecord, InStr(1, strRecord, \"|\") + 1)\r\n        ' Reference performance item class\r\n        Set cItem = dItems(varKey)\r\n        ' Add to dictionary of resorted items\r\n        dSorted.Add varKey, cItem\r\n    Next lngCnt\r\n\r\n    ' Return the sorted dictionary\r\n    Set SortItemsByTime = dSorted\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Reset\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Reset all class values to start timing again.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Reset()\r\n    Set this.Overall = Nothing\r\n    Set this.CallStackItems = Nothing\r\n    this.CategoryName = vbNullString\r\n    Set this.Categories = Nothing\r\n    this.OperationName = vbNullString\r\n    Set this.Operations = Nothing\r\n    Me.Enabled = True\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 11/5/2020\r\n' Purpose   : Initialize objects for timing.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    ' this.curFrequency need only be queried once\r\n    ' https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency\r\n    GetFrequencyAPI this.Frequency\r\n    this.intDigitsAfterDecimal = 2\r\n    Me.Enabled = True\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsPerformanceItem.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsPerformanceItem\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = True\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' See usage in clsPerformance\r\nPublic Start As Currency\r\nPublic Total As Currency\r\nPublic Count As Double\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsSchemaMsSql.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsSchemaMsSql\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbSchema class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' How many times to retry connecting to ADODB.\r\nPrivate Const ADODB_MAX_RETRIES As Long = 5\r\n\r\n' Status of GetDDL stored procedure\r\nPrivate Enum eSpStatus\r\n    essUnknown\r\n    essUnavailable\r\n    essInstalled\r\nEnd Enum\r\n\r\n' Handle local variables\r\nPrivate Type udtThis\r\n    blnInitialized As Boolean\r\n    strName As String\r\n    strBaseFolder As String\r\n    strConnect As String\r\n    blnUtcTime As Boolean\r\n    strUserID As String\r\n    strPassword As String\r\n    blnAttemptConvert As Boolean\r\n    colFilters As Collection\r\nEnd Type\r\nPrivate this As udtThis\r\n\r\n' Dictionaries representing modified items, and all items\r\nPrivate m_Files As Dictionary\r\nPrivate m_AllItems As Dictionary\r\nPrivate m_ModifiedItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the server classes consistent in how they are used in the export\r\n' process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbSchema\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_Export\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : Export DDL representations of the external database objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbSchema_Export(blnFullExport As Boolean _\r\n                            , Optional strAlternatePath As String)\r\n\r\n    Dim conn As ADODB.Connection\r\n    Dim strItem As String\r\n    Dim dItem As Dictionary\r\n    Dim varItem As Variant\r\n    Dim dblStart As Double\r\n    Dim strPath As String\r\n    Dim blnChanges As Boolean\r\n    Dim dFolders As Dictionary\r\n\r\n    ' Make sure we initialize before running the export\r\n    If Not this.blnInitialized Then Exit Sub\r\n\r\n    ' Make sure we have already performed a scan of the database objects\r\n    If m_Files Is Nothing Then ScanFiles\r\n    If m_AllItems Is Nothing Then ScanDatabaseObjects\r\n\r\n    ' Bail out if critical Error\r\n    If Log.ErrorLevel >= eelError Then Exit Sub\r\n\r\n    ' If there are no new changes found, we may not need to export anything\r\n    If (m_ModifiedItems.Count = 0) And (m_Files.Count = m_AllItems.Count) Then\r\n        ' Database matches the current set of files\r\n    Else\r\n        blnChanges = True\r\n        If m_ModifiedItems.Count = 0 Then\r\n            Log.Add \"     Verifying files...\", , , , , True\r\n        Else\r\n            Log.Add \"     Exporting \" & m_ModifiedItems.Count & \" objects...\", , , , , True\r\n            Log.ProgMax = m_ModifiedItems.Count\r\n            Log.Flush\r\n        End If\r\n\r\n        ' Open database connection\r\n        Set conn = GetNewOpenConnection\r\n        If conn Is Nothing Then Exit Sub\r\n\r\n        ' --------------------------------------------------\r\n        ' FIRST PASS - Export changed/new database objects\r\n        ' --------------------------------------------------\r\n        For Each varItem In m_ModifiedItems.Keys\r\n\r\n            ' Time the export of each item\r\n            dblStart = Perf.MicroTimer\r\n            Set dItem = m_ModifiedItems(varItem)\r\n            strItem = varItem\r\n            ExportObject dItem(\"type_desc\"), dItem(\"schema\"), dItem(\"name\"), dItem(\"last_modified\"), this.strBaseFolder & varItem, conn\r\n            Log.Add \"    Exported \" & varItem & \" in \" & Round(Perf.MicroTimer - dblStart, 2) & \" seconds.\", Options.ShowDebug\r\n            Log.Increment\r\n            ' Check for canceled operation\r\n            If Log.ErrorLevel = eelCritical Then Exit For\r\n        Next varItem\r\n\r\n        ' Close database connection\r\n        conn.Close\r\n        Set conn = Nothing\r\n    End If\r\n\r\n    ' --------------------------------------------------\r\n    ' SECOND PASS - Remove orphaned files\r\n    ' --------------------------------------------------\r\n    Perf.OperationStart \"Clear Orphaned Schema Files\"\r\n    For Each varItem In m_Files\r\n        If Not m_AllItems.Exists(varItem) Then\r\n            strPath = this.strBaseFolder & varItem\r\n            If FSO.FileExists(strPath) Then\r\n                Log.Add \" - Removed orphaned file: \" & varItem, False\r\n                DeleteFile strPath\r\n                blnChanges = True\r\n            End If\r\n        End If\r\n    Next varItem\r\n    Perf.OperationEnd\r\n\r\n    ' --------------------------------------------------\r\n    ' THIRD PASS - Remove empty source folders\r\n    ' --------------------------------------------------\r\n    If blnChanges Then\r\n        Set dFolders = GetBaseFolders\r\n        For Each varItem In dFolders\r\n            strPath = this.strBaseFolder & varItem\r\n            If FSO.FolderExists(strPath) Then\r\n                If FSO.GetFolder(strPath).Files.Count = 0 Then\r\n                    ' Remove empty component subfolders\r\n                    FSO.DeleteFolder strPath\r\n                End If\r\n            End If\r\n        Next varItem\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportObject\r\n' Author    : Adam Waller\r\n' Date      : 7/18/2023\r\n' Purpose   : Export the object definition to a file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ExportObject(strType _\r\n                            , strSchema As String _\r\n                            , strName As String _\r\n                            , dteModified As Date _\r\n                            , strPath As String _\r\n                            , ByRef oConn As ADODB.Connection) As String\r\n\r\n    Dim strSqlDef\r\n    Dim strDefinition As String\r\n    Dim rst As ADODB.Recordset\r\n    Dim rst2 As ADODB.Recordset\r\n    Dim strFullName As String\r\n    Dim cmd As ADODB.Command\r\n    Dim strCmdTemplate As String\r\n\r\n    ' Attempt to use the sp_GetDDL SP if possible\r\n    If CanUseGetDDL Then\r\n        ' Prepare template statement for sp_GetDDL to work around VARCHAR(MAX) issue\r\n        ' with many SQL Server ODBC drivers.\r\n        strCmdTemplate = _\r\n            \"DECLARE @table TABLE (item text) \" & _\r\n            \"INSERT INTO @table exec sp_GetDDL N'{name}' \" & _\r\n            \"SELECT * FROM @table\"\r\n    Else\r\n        ' Fall back to built-in SQL statements\r\n        strCmdTemplate = _\r\n            \"DECLARE @table TABLE (item text) \" & _\r\n            \"INSERT INTO @table SELECT object_definition (OBJECT_ID(N'{name}')) \" & _\r\n            \"SELECT * FROM @table\"\r\n\r\n        'strCmdTemplate = \"SELECT object_definition (OBJECT_ID(N'{name}'))\"\r\n        'strCmdTemplate = \"test\"\r\n    End If\r\n\r\n    ' Build full name of SQL object\r\n    strFullName = \"[\" & strSchema & \"].[\" & strName & \"]\"\r\n\r\n    ' Determine how to export this type of object\r\n    Select Case strType\r\n        Case \"USER_TABLE\", \"VIEW\", \"SYNONYM\", \"SQL_STORED_PROCEDURE\", _\r\n            \"SQL_SCALAR_FUNCTION\", \"SQL_INLINE_TABLE_VALUED_FUNCTION\", \"SQL_TABLE_VALUED_FUNCTION\"\r\n            strSqlDef = Replace(strCmdTemplate, \"{name}\", strFullName)\r\n\r\n        Case \"TYPE_TABLE\", \"SEQUENCE_OBJECT\", \"SERVICE_QUEUE\", \"SYSTEM_TABLE\", \"INTERNAL_TABLE\"\r\n            ' Unsupported non-dependent objects\r\n\r\n    End Select\r\n\r\n    ' Sanity check\r\n    If Len(strSqlDef) Then\r\n        If CanUseGetDDL Then\r\n            Perf.OperationStart \"Run sp_GetDDL on \" & strType\r\n        Else\r\n            Perf.OperationStart \"Get DDL for \" & strType\r\n        End If\r\n        Set cmd = New ADODB.Command\r\n        With cmd\r\n            Set .ActiveConnection = oConn\r\n            .CommandText = strSqlDef\r\n            Set rst = .Execute\r\n        End With\r\n\r\n        ' Get secondary recordset with object definition record\r\n        If strType = \"USER_TABLE\" And Not CanUseGetDDL Then\r\n            strDefinition = GetTableDefFallback(strFullName, oConn)\r\n        Else\r\n            Set rst2 = rst.NextRecordset\r\n            With rst2\r\n                If Not .EOF Then strDefinition = Nz(.Fields(0))\r\n                .Close\r\n            End With\r\n        End If\r\n\r\n        ' Write object definition to file\r\n        If strDefinition = vbNullString Then\r\n            If FSO.FileExists(strPath) Then DeleteFile strPath\r\n        Else\r\n            ' Export to file, and set modified date to match SQL object\r\n            WriteFile strDefinition, strPath\r\n            SetFileDate strPath, dteModified, Not this.blnUtcTime\r\n        End If\r\n\r\n        Perf.OperationEnd\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CanUseGetDDL\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2023\r\n' Purpose   : Returns true if we can use the system SP `sp_GetDDL`\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function CanUseGetDDL() As Boolean\r\n\r\n    Static intUseSP As eSpStatus\r\n\r\n    Dim conn As ADODB.Connection\r\n    Dim cmd As ADODB.Command\r\n    Dim rst As ADODB.Recordset\r\n\r\n    ' Cache whether or not sp_GetDDL exists\r\n    If intUseSP = essUnknown Then\r\n\r\n        ' Open connection, if not provided\r\n        Set conn = GetNewOpenConnection\r\n        If conn Is Nothing Then Exit Function\r\n\r\n        Set cmd = New ADODB.Command\r\n        With cmd\r\n            ' Check in master database (System SP)\r\n            .CommandText = \"select OBJECT_ID('master.dbo.sp_GetDDL')\"\r\n            Set .ActiveConnection = conn\r\n            Set rst = .Execute\r\n            If Nz(rst.Fields(0).Value, 0) = 0 Then\r\n                ' Nothing found on the master DB. Try this db.\r\n                .CommandText = \"select OBJECT_ID('sp_GetDDL')\"\r\n                Set rst = .Execute\r\n                If Nz(rst.Fields(0).Value, 0) = 0 Then\r\n                    ' Still not available\r\n                    intUseSP = essUnavailable\r\n                Else\r\n                    ' Works on the local database\r\n                    intUseSP = essInstalled\r\n                End If\r\n            Else\r\n                ' Found an object ID. Should be available\r\n                intUseSP = essInstalled\r\n            End If\r\n        End With\r\n\r\n        ' Close connection\r\n        conn.Close\r\n\r\n        ' Add log entries if the tool is not available\r\n        If intUseSP = essUnavailable Then\r\n            Log.Add \"   Note: sp_GetDDL was not available for generating object definitions. Using built-in SQL functions instead.\", False\r\n            Log.Add \"   This system stored procedure can be found at: http://www.stormrage.com/2022/03/16/sp_getddla-and-sp_getddl/\", False\r\n            Log.Add \"   or at https://web.archive.org/web/20230928112439/https://stormrage.com/SQLStuff/sp_GetDDL_Latest.txt\", False\r\n        End If\r\n    End If\r\n\r\n    ' Return current status\r\n    CanUseGetDDL = (intUseSP = essInstalled)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTableDefFallback\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2023\r\n' Purpose   : Return a simplified fall-back version of a table definition without using\r\n'           : sp_GetDDL.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetTableDefFallback(strTable As String _\r\n                                    , oConn As ADODB.Connection) As String\r\n\r\n    Dim strSql As String\r\n    Dim rst As ADODB.Recordset\r\n    Dim intRst As Integer\r\n    Dim fld As ADODB.Field\r\n    Dim colText As New clsConcat\r\n\r\n    ' Initialize counter\r\n    intRst = 2\r\n\r\n    ' Get initial table information\r\n    strSql = \"exec sp_help N'\" & strTable & \"'\"\r\n    '@Ignore SetAssignmentWithIncompatibleObjectType\r\n    Set rst = oConn.Execute(strSql)\r\n    colText.Add \"-- sp_help Recordset 1\" & vbCrLf & vbCrLf\r\n    For Each fld In rst.Fields\r\n        colText.Add fld.Name\r\n        colText.Add vbTab\r\n    Next fld\r\n    colText.Add vbCrLf\r\n    colText.Add rst.GetString(, , vbTab, vbCrLf)\r\n\r\n    ' Loop through additional recordsets for columns, keys and other data\r\n    Do\r\n        Set rst = rst.NextRecordset\r\n        If rst Is Nothing Then Exit Do\r\n        If rst.State = adStateClosed Then Exit Do\r\n\r\n        colText.Add vbCrLf & vbCrLf & \"-- sp_help Recordset \" & intRst & vbCrLf & vbCrLf\r\n        For Each fld In rst.Fields\r\n            colText.Add fld.Name\r\n            colText.Add vbTab\r\n        Next fld\r\n        If Not rst.EOF Then\r\n            colText.Add vbCrLf\r\n            colText.Add rst.GetString(, , vbTab, vbCrLf)\r\n        End If\r\n\r\n        intRst = intRst + 1\r\n    Loop\r\n\r\n    ' Clear references\r\n    Set fld = Nothing\r\n    Set rst = Nothing\r\n\r\n    ' Return SQL content\r\n    GetTableDefFallback = colText.GetStr\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ScanDatabaseObjects\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Scan the database objects for any changed items\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ScanDatabaseObjects()\r\n\r\n    Dim rstObjects As ADODB.Recordset\r\n    Dim conn As ADODB.Connection\r\n    Dim strItem As String\r\n    Dim strSchema As String\r\n    Dim strPath As String\r\n    Dim blnModified As Boolean\r\n    Dim dItem As Dictionary\r\n    Dim lngCount As Long\r\n\r\n    ' Clear module level objects\r\n    Set m_AllItems = Nothing\r\n    Set m_ModifiedItems = Nothing\r\n\r\n    ' Make sure we initialize before running the scan\r\n    If Not this.blnInitialized Then Exit Sub\r\n\r\n    ' Initialize dictionaries\r\n    Set m_AllItems = New Dictionary\r\n    Set m_ModifiedItems = New Dictionary\r\n\r\n    ' Open database connection\r\n    Set conn = GetNewOpenConnection\r\n    If conn Is Nothing Then Exit Sub\r\n\r\n    ' Return list of objects from the server\r\n    Perf.OperationStart \"Retrieve SQL Objects\"\r\n    Set rstObjects = conn.Execute(CodeDb.QueryDefs(\"qryMsSqlServerObjects\").SQL)\r\n    Perf.OperationEnd\r\n\r\n    ' Loop through objects, building dictionary of items that match our filter.\r\n    Perf.OperationStart \"Loop through SQL objects\"\r\n    With rstObjects\r\n        Do While Not .EOF\r\n\r\n            ' Build item path and full path to source file\r\n            strSchema = Nz(!schema, \"dbo\") & \".\"\r\n            strItem = Nz(!Folder) & PathSep & GetSafeFileName(strSchema & Nz(!Name)) & \".sql\"\r\n            strPath = this.strBaseFolder & strItem\r\n\r\n            ' See if we pass the filter\r\n            If PassesSchemaFilter(strItem, this.colFilters) Then\r\n\r\n                ' Add all objects to full collection\r\n                m_AllItems.Add strItem, Nz(!last_modified)\r\n\r\n                ' Check for modification/new item\r\n                If m_Files.Exists(strItem) Then\r\n                    ' Flag as modified if the dates don't match\r\n                    blnModified = (Nz(!last_modified) <> CStr(m_Files(strItem)))\r\n                Else\r\n                    ' File does not yet exist\r\n                    blnModified = True\r\n                End If\r\n\r\n                ' Build dictionary of modified objects\r\n                If blnModified Then\r\n                    Set dItem = New Dictionary\r\n                    dItem(\"type_desc\") = Nz(!type_desc)\r\n                    dItem(\"schema\") = Nz(!schema)\r\n                    dItem(\"name\") = Nz(!Name)\r\n                    dItem(\"last_modified\") = CDate(!last_modified)\r\n                    m_ModifiedItems.Add strItem, dItem\r\n                End If\r\n            End If\r\n\r\n            ' Move to next object\r\n            lngCount = lngCount + 1\r\n            .MoveNext\r\n        Loop\r\n        .Close\r\n    End With\r\n    Perf.OperationEnd lngCount\r\n\r\n    ' Close connection\r\n    conn.Close\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetNewOpenConnection\r\n' Author    : Adam Waller, hecon5\r\n' Date      : 8/3/2023\r\n' Purpose   : Return a new, open ADODB connection\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetNewOpenConnection() As ADODB.Connection\r\n\r\n    Static lngFailCount As Long\r\n\r\n    Dim FunctionName As String\r\n    Dim oConn As ADODB.Connection\r\n\r\n    FunctionName = ModuleName(Me) & \".GetNewOpenConnection\"\r\n\r\n    LogUnhandledErrors FunctionName\r\n    On Error Resume Next\r\n    Perf.OperationStart FunctionName\r\n\r\nRebuild_Connection:\r\n    Set oConn = New ADODB.Connection\r\n\r\nRetry_Connection:\r\n    With oConn\r\n        If this.blnAttemptConvert Then\r\n            .Provider = \"MSDASQL\"\r\n\r\n            If Len(this.strUserID) Then\r\n                .Open \"Persist Security Info=False; Extended Properties=\" & GetADOConnectionString(this.strConnect), this.strUserID, this.strPassword\r\n\r\n            Else\r\n                .Provider = \"MSDASQL\"\r\n                .ConnectionString = \"Persist Security Info=False; Extended Properties=\" & GetADOConnectionString(this.strConnect)\r\n                .Open\r\n            End If\r\n\r\n        Else\r\n            .Open this.strConnect\r\n        End If\r\n\r\n        CatchAny eelError, \"Error Connecting to \" & this.strName, FunctionName\r\n        If (.State And adStateOpen) <> adStateOpen Then\r\n\r\n            If lngFailCount <= ADODB_MAX_RETRIES Then\r\n                lngFailCount = lngFailCount + 1\r\n                GoTo Retry_Connection\r\n            Else\r\n                Log.Error eelError, \"Could not establish connection to \" & this.strName, FunctionName\r\n                Perf.OperationEnd ' Close out the Function operation.\r\n                Exit Function\r\n            End If\r\n        End If\r\n    End With\r\n\r\n    If lngFailCount <= ADODB_MAX_RETRIES And (oConn.State And adStateOpen) <> adStateOpen Then GoTo Rebuild_Connection\r\n\r\n    ' Check for any connection error\r\n    If CatchAny(eelError, \"Unable to connect to \" & this.strName, FunctionName) Then\r\n        Log.Add \"Connection string: \" & this.strConnect, False\r\n\r\n    Else\r\n        ' Return open connection\r\n        Set GetNewOpenConnection = oConn\r\n    End If\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetADOConnectionString\r\n' Author    : hecon5\r\n' Date      : 10/17/2024\r\n' Purpose   : Returns with an ADODB connection string based on existing ODBC Connection.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetADOConnectionString(ByRef ODBCConnString As String) As String\r\n\r\n    Dim FunctionName As String\r\n    Dim currConnString As String\r\n    Dim ConnectionProperties() As String\r\n    Dim PropertyCount As Long\r\n    Dim strProperty As String\r\n    Dim ConStrParsed As New clsConnectionODBC\r\n\r\n    If Not this.blnAttemptConvert Then\r\n        GetADOConnectionString = ODBCConnString\r\n        Exit Function\r\n    End If\r\n\r\n    FunctionName = ModuleName(Me) & \".GetADOConnectionString\"\r\n    Perf.OperationStart FunctionName\r\n\r\n    ConStrParsed.ParseOdbcConnectionString ODBCConnString\r\n\r\n    currConnString = ConStrParsed.OriginalConnectionString\r\n    ConnectionProperties = Split(currConnString, \";\")\r\n\r\n    With New clsConcat\r\n        .AppendOnAdd = \";\"\r\n\r\n        For PropertyCount = 0 To UBound(ConnectionProperties)\r\n            strProperty = ConnectionProperties(PropertyCount)\r\n            Select Case True ' This looks odd, yes, but it really just means we can use the comma delimited feature of \"case\"\r\n                Case StartsWith(strProperty, \"ODBC\", vbTextCompare), _\r\n                     Len(strProperty) < 1\r\n                    ' Don't include these; they aren't needed (or are specified elsewhere).\r\n\r\n                Case Else\r\n                    .Add strProperty\r\n            End Select\r\n        Next PropertyCount\r\n\r\n        ' These properties should go at the end.\r\n        ' N/A\r\n\r\n        Log.Add FunctionName & \"ConnectionString: \" & .GetStr, False\r\n        GetADOConnectionString = .GetStr\r\n    End With\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ScanFiles\r\n' Author    : Adam Waller\r\n' Date      : 7/28/2023\r\n' Purpose   : Scan the files to get a list of source files that should match the current\r\n'           : database objects. For performance reasons, we won't apply the filter here\r\n'           : but will check the filter later when removing orphaned objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ScanFiles()\r\n\r\n    Dim oFld As Scripting.Folder\r\n    Dim dBaseFolders As Dictionary\r\n    Dim dFiles As Dictionary\r\n    Dim varKey As Variant\r\n    Dim strFolder As String\r\n\r\n    ' Reset module-level dictionary\r\n    Set m_Files = New Dictionary\r\n\r\n    ' Build a collection of subfolders and files with modified dates\r\n    ' (Using the Windows API for faster scanning and more accurate dates)\r\n    Set dBaseFolders = GetBaseFolders\r\n    If FSO.FolderExists(this.strBaseFolder) Then\r\n        For Each oFld In FSO.GetFolder(this.strBaseFolder).SubFolders\r\n            strFolder = oFld.Name\r\n            If dBaseFolders.Exists(strFolder) Then\r\n                ' Get dictionary of files with modified dates\r\n                Set dFiles = GetFileList(oFld.Path, \"*.sql\", Not this.blnUtcTime)\r\n                ' Loop through files, adding to index\r\n                For Each varKey In dFiles.Keys\r\n                    ' Add each file with a key that matches the database object, and the\r\n                    ' file modified date as the value for each item.\r\n                    m_Files.Add strFolder & \"\\\" & CStr(varKey), dFiles(varKey)\r\n                Next varKey\r\n            End If\r\n        Next oFld\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetBaseFolders\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2023\r\n' Purpose   : Return a dictionary of base folders used for component types\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetBaseFolders() As Dictionary\r\n\r\n    Set GetBaseFolders = New Dictionary\r\n    With GetBaseFolders\r\n        .CompareMode = TextCompare\r\n        .Add \"views\", Null\r\n        .Add \"tables\", Null\r\n        .Add \"procedures\", Null\r\n        .Add \"functions\", Null\r\n        .Add \"types\", Null\r\n        .Add \"sequences\", Null\r\n        .Add \"synonymns\", Null\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_GetChangeCount\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Return count of modified objects\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbSchema_ObjectCount(blnModifiedOnly As Boolean) As Long\r\n\r\n    If m_Files Is Nothing Then ScanFiles\r\n    If m_AllItems Is Nothing Then ScanDatabaseObjects\r\n    If m_AllItems Is Nothing Then Exit Function\r\n    IDbSchema_ObjectCount = IIf(blnModifiedOnly, m_ModifiedItems.Count, m_AllItems.Count)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 7/18/2023\r\n' Purpose   : Initialize the database schema\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbSchema_Initialize(dInstance As Scripting.IDictionary)\r\n\r\n    Dim colRules As Collection\r\n\r\n    ' Map filter to collection\r\n    If dInstance.Exists(\"Filter\") Then\r\n        ' Create collection of rules (we will skip comments and blank lines later)\r\n        Set colRules = dInstance(\"Filter\")\r\n    Else\r\n        ' No rules defined\r\n        Set colRules = New Collection\r\n    End If\r\n\r\n    ' Set class values\r\n    With this\r\n        .strName = dNZ(dInstance, \"Name\")\r\n        .strConnect = dNZ(dInstance, \"Connect\")\r\n        .strBaseFolder = Options.GetExportFolder & \"databases\\\" & GetSafeFileName(.strName) & PathSep\r\n        Set .colFilters = colRules\r\n        If dInstance.Exists(\"UtcDateTime\") Then .blnUtcTime = dInstance(\"UtcDateTime\")\r\n        If dInstance.Exists(\"AttemptConvert\") Then .blnAttemptConvert = dInstance(\"AttemptConvert\")\r\n        .blnInitialized = (Len(.strConnect))\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 8/2/2023\r\n' Purpose   : Return base folder for this schema export\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbSchema_BaseFolder() As String\r\n    IDbSchema_BaseFolder = this.strBaseFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_Name\r\n' Author    : Adam Waller\r\n' Date      : 8/2/2023\r\n' Purpose   : Return the name of this schema\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbSchema_Name() As String\r\n    IDbSchema_Name = this.strName\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_ServerType\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : Return server type\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbSchema_ServerType() As eDatabaseServerType\r\n    IDbSchema_ServerType = estMsSql\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_TypeDescription\r\n' Author    : Adam Waller\r\n' Date      : 8/2/2023\r\n' Purpose   : Return type description\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbSchema_TypeDescription() As Variant\r\n    IDbSchema_TypeDescription = \"Microsoft SQL Server\"\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsSchemaMySql.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsSchemaMySql\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : This class extends the IDbSchema class to perform the specific\r\n'           : operations required by this particular object type.\r\n'           : (I.e. The specific way you export or import this component.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' Handle local variables\r\nPrivate Type udtThis\r\n    blnInitialized As Boolean\r\n    strName As String\r\n    strBaseFolder As String\r\n    strConnect As String\r\n    blnUtcTime As Boolean\r\n    strUserID As String\r\n    strPassword As String\r\n    colFilters As Collection\r\nEnd Type\r\nPrivate this As udtThis\r\n\r\n' Dictionaries representing modified items, and all items\r\nPrivate m_Files As Dictionary\r\nPrivate m_AllItems As Dictionary\r\nPrivate m_ModifiedItems As Dictionary\r\n\r\n' This requires us to use all the public methods and properties of the implemented class\r\n' which keeps all the server classes consistent in how they are used in the export\r\n' process. The implemented functions should be kept private as they are called\r\n' from the implementing class, not this class.\r\nImplements IDbSchema\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_Export\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : Export DDL representations of the external database objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbSchema_Export(blnFullExport As Boolean, Optional strAlternatePath As String)\r\n\r\n    Dim conn As ADODB.Connection\r\n    Dim strItem As String\r\n    Dim dItem As Dictionary\r\n    Dim varItem As Variant\r\n    Dim dblStart As Double\r\n    Dim strPath As String\r\n    Dim blnChanges As Boolean\r\n    Dim dFolders As Dictionary\r\n\r\n    ' Make sure we initialize before running the export\r\n    If Not this.blnInitialized Then Exit Sub\r\n\r\n    ' Make sure we have already performed a scan of the database objects\r\n    If m_Files Is Nothing Then ScanFiles\r\n    If m_AllItems Is Nothing Then ScanDatabaseObjects\r\n\r\n    ' If there are no new changes found, we may not need to export anything\r\n    If (m_ModifiedItems.Count = 0) And (m_Files.Count = m_AllItems.Count) Then\r\n        ' Database matches the current set of files\r\n    Else\r\n        blnChanges = True\r\n        If m_ModifiedItems.Count = 0 Then\r\n            Log.Add \"     Verifying files...\", , , , , True\r\n        Else\r\n            Log.Add \"     Exporting \" & m_ModifiedItems.Count & \" objects...\", , , , , True\r\n            Log.ProgMax = m_ModifiedItems.Count\r\n            Log.Flush\r\n        End If\r\n\r\n        ' Open database connection\r\n        Set conn = GetNewOpenConnection\r\n        If conn Is Nothing Then Exit Sub\r\n\r\n        ' --------------------------------------------------\r\n        ' FIRST PASS - Export changed/new database objects\r\n        ' --------------------------------------------------\r\n        For Each varItem In m_ModifiedItems.Keys\r\n\r\n            ' Time the export of each item\r\n            dblStart = Perf.MicroTimer\r\n            Set dItem = m_ModifiedItems(varItem)\r\n            strItem = varItem\r\n            ExportObject dItem(\"folder\"), dItem(\"schema\"), dItem(\"name\"), dItem(\"last_modified\"), dItem(\"hash\"), CStr(varItem), conn\r\n            Log.Add \"    Exported \" & varItem & \" in \" & Round(Perf.MicroTimer - dblStart, 2) & \" seconds.\", Options.ShowDebug\r\n            Log.Increment\r\n            ' Check for canceled operation\r\n            If Log.ErrorLevel = eelCritical Then Exit For\r\n        Next varItem\r\n\r\n        ' Close database connection\r\n        conn.Close\r\n        Set conn = Nothing\r\n    End If\r\n\r\n    ' --------------------------------------------------\r\n    ' SECOND PASS - Remove orphaned files\r\n    ' --------------------------------------------------\r\n    Perf.OperationStart \"Clear Orphaned Schema Files\"\r\n    For Each varItem In m_Files\r\n        If Not m_AllItems.Exists(varItem) Then\r\n            strPath = this.strBaseFolder & varItem\r\n            If FSO.FileExists(strPath) Then\r\n                Log.Add \" - Removed orphaned file: \" & varItem, False\r\n                DeleteFile strPath\r\n                blnChanges = True\r\n            End If\r\n        End If\r\n    Next varItem\r\n    Perf.OperationEnd\r\n\r\n    ' --------------------------------------------------\r\n    ' THIRD PASS - Remove empty source folders\r\n    ' --------------------------------------------------\r\n    If blnChanges Then\r\n        Set dFolders = GetBaseFolders\r\n        For Each varItem In dFolders\r\n            strPath = this.strBaseFolder & varItem\r\n            If FSO.FolderExists(strPath) Then\r\n                If FSO.GetFolder(strPath).Files.Count = 0 Then\r\n                    ' Remove empty component subfolders\r\n                    FSO.DeleteFolder strPath\r\n                End If\r\n            End If\r\n        Next varItem\r\n    End If\r\n\r\n    ' Save updated index\r\n    SaveUpdatedIndex\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportObject\r\n' Author    : Adam Waller\r\n' Date      : 7/18/2023\r\n' Purpose   : Export the object definition to a file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ExportObject(strType, strSchema As String, strName As String, dteModified As Date, strHash As String, strFile As String, ByRef oConn As ADODB.Connection) As String\r\n\r\n    Dim strSqlDef\r\n    Dim strDefinition As String\r\n    Dim rst As ADODB.Recordset\r\n    Dim strFullName As String\r\n    Dim cmd As ADODB.Command\r\n    Dim intField As Integer\r\n    Dim strPath As String\r\n\r\n    ' Build full name of SQL object\r\n    strFullName = \"`\" & strSchema & \"`.`\" & strName & \"`\"\r\n\r\n    ' Determine how to export this type of object\r\n    Select Case strType\r\n        Case \"tables\":      strSqlDef = \"show create table \" & strFullName\r\n        Case \"views\":       strSqlDef = \"show create view \" & strFullName\r\n        Case \"procedures\":  strSqlDef = \"show create procedure \" & strFullName\r\n        Case \"functions\":   strSqlDef = \"show create function \" & strFullName\r\n        Case \"triggers\":    strSqlDef = \"show create trigger \" & strFullName\r\n        Case Else\r\n            ' Unsupported type\r\n            Log.Error eelError, \"Unsupported object type: \" & strType, ModuleName(Me) & \".ExportObject\"\r\n            Exit Function\r\n    End Select\r\n\r\n    ' Sanity check\r\n    If Len(strSqlDef) Then\r\n        Perf.OperationStart \"Get DDL for \" & strType\r\n        Set cmd = New ADODB.Command\r\n        With cmd\r\n            Set .ActiveConnection = oConn\r\n            .CommandText = strSqlDef\r\n            Set rst = .Execute\r\n        End With\r\n\r\n        ' Look up definition from recordset\r\n        With rst\r\n            If Not .EOF Then\r\n                ' Definition might be in second or third column\r\n                For intField = 1 To 2\r\n                    If StartsWith(.Fields(intField).Name, \"Create \") Then\r\n                        strDefinition = Nz(.Fields(intField))\r\n                        Exit For\r\n                    End If\r\n                Next intField\r\n            End If\r\n            .Close\r\n        End With\r\n\r\n        ' Write object definition to file\r\n        strPath = this.strBaseFolder & strFile\r\n        If strDefinition = vbNullString Then\r\n            If FSO.FileExists(strPath) Then DeleteFile strPath\r\n        Else\r\n            ' Export to file\r\n            WriteFile strDefinition, strPath\r\n            If strType = \"views\" Then\r\n                ' Use hash index\r\n                m_AllItems(strFile) = strHash\r\n            Else\r\n                ' Set file modified date to match SQL object\r\n                SetFileDate strPath, dteModified, Not this.blnUtcTime\r\n            End If\r\n        End If\r\n\r\n        Perf.OperationEnd\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ScanDatabaseObjects\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Scan the database objects for any changed items\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ScanDatabaseObjects()\r\n\r\n    Dim rstObjects As ADODB.Recordset\r\n    Dim conn As ADODB.Connection\r\n    Dim strItem As String\r\n    Dim strSchema As String\r\n    Dim strPath As String\r\n    Dim blnModified As Boolean\r\n    Dim dItem As Dictionary\r\n    Dim lngCount As Long\r\n    Dim strHash As String\r\n\r\n    ' Clear module level objects\r\n    Set m_AllItems = Nothing\r\n    Set m_ModifiedItems = Nothing\r\n\r\n    ' Make sure we initialize before running the scan\r\n    If Not this.blnInitialized Then Exit Sub\r\n\r\n    ' Open database connection\r\n    Set conn = GetNewOpenConnection\r\n    If conn Is Nothing Then Exit Sub\r\n\r\n    ' Return list of objects from the server\r\n    Perf.OperationStart \"Retrieve SQL Objects\"\r\n    Set rstObjects = conn.Execute(CodeDb.QueryDefs(\"qryMySqlServerObjects\").SQL)\r\n    Perf.OperationEnd\r\n\r\n    ' Initialize dictionaries\r\n    Set m_AllItems = New Dictionary\r\n    Set m_ModifiedItems = New Dictionary\r\n\r\n    ' Loop through objects, building dictionary of items that match our filter.\r\n    Perf.OperationStart \"Loop through MySQL objects\"\r\n    With rstObjects\r\n        Do While Not .EOF\r\n\r\n            ' Build item path and full path to source file\r\n            strSchema = Nz(!schema, \"schema\") & \".\"\r\n            strItem = Nz(!Folder) & PathSep & GetSafeFileName(strSchema & Nz(!Name)) & \".sql\"\r\n            strPath = this.strBaseFolder & strItem\r\n            strHash = vbNullString\r\n\r\n            ' See if we pass the filter\r\n            If PassesSchemaFilter(strItem, this.colFilters) Then\r\n\r\n                ' Use modified date to match file, if possible.\r\n                ' Some objects don't store a modified date, so use a hash for those.\r\n                If Nz(!last_modified) = vbNullString Then\r\n                    ' Create hash from definition\r\n                    strHash = \"hash:\" & GetStringHash(Nz(!definition))\r\n                End If\r\n\r\n                ' Check for modification/new item\r\n                If m_Files.Exists(strItem) Then\r\n                    ' Flag as modified if the dates or hash don't match\r\n                    blnModified = (Nz2(strHash, Nz(!last_modified)) <> CStr(m_Files(strItem)))\r\n                Else\r\n                    ' File does not yet exist\r\n                    blnModified = True\r\n                End If\r\n\r\n                ' Add all objects to full collection\r\n                m_AllItems.Add strItem, Nz2(strHash, Nz(!last_modified))\r\n\r\n                ' Build dictionary of modified objects\r\n                If blnModified Then\r\n                    Set dItem = New Dictionary\r\n                    dItem(\"folder\") = Nz(!Folder)\r\n                    dItem(\"schema\") = Nz(!schema)\r\n                    dItem(\"name\") = Nz(!Name)\r\n                    dItem(\"hash\") = strHash\r\n                    dItem(\"last_modified\") = CDate(Nz(!last_modified, 0))\r\n                    m_ModifiedItems.Add strItem, dItem\r\n                End If\r\n            End If\r\n\r\n            ' Move to next object\r\n            lngCount = lngCount + 1\r\n            .MoveNext\r\n        Loop\r\n        .Close\r\n    End With\r\n    Perf.OperationEnd lngCount\r\n\r\n    ' Close connection\r\n    conn.Close\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveUpdatedIndex\r\n' Author    : Adam Waller\r\n' Date      : 8/7/2023\r\n' Purpose   : Save an updated version of the index (after scanning for changes and\r\n'           : exporting any changed objects)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SaveUpdatedIndex()\r\n\r\n    Dim varKey As Variant\r\n    Dim strValue As String\r\n    Dim dIndex As Dictionary\r\n    Dim strPath As String\r\n\r\n    ' Only run this if we actually have an index to process\r\n    If m_AllItems Is Nothing Then Exit Function\r\n\r\n    ' Loop through all items, building a dictionary of hashed items.\r\n    ' (These items don't have modified dates in MySQL, and must be tracked via hash.)\r\n    Set dIndex = New Dictionary\r\n    For Each varKey In m_AllItems.Keys\r\n        strValue = m_AllItems(varKey)\r\n        If StartsWith(strValue, \"hash:\") Then\r\n            ' Add to index\r\n            dIndex(varKey) = strValue\r\n        End If\r\n    Next varKey\r\n\r\n    ' Update the saved index file\r\n    strPath = this.strBaseFolder & \"vcs-index.json\"\r\n    If dIndex.Count = 0 Then\r\n        ' Remove index when no longer needed.\r\n        If FSO.FileExists(strPath) Then DeleteFile strPath\r\n    Else\r\n        ' Save the rebuilt index\r\n        WriteFile BuildJsonFile(TypeName(Me), dIndex, \"Version Control System Schema Index for MySQL\"), strPath\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetNewOpenConnection\r\n' Author    : Adam Waller\r\n' Date      : 8/3/2023\r\n' Purpose   : Return a new, open ADODB connection\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetNewOpenConnection() As ADODB.Connection\r\n\r\n    Dim oConn As ADODB.Connection\r\n\r\n    Set oConn = New ADODB.Connection\r\n    With oConn\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        If Len(this.strUserID) Then\r\n            .Open this.strConnect, this.strUserID, this.strPassword\r\n        Else\r\n            .Open this.strConnect\r\n        End If\r\n    End With\r\n\r\n    ' Check for any connection error\r\n    If CatchAny(eelError, \"Unable to connect to \" & this.strName, ModuleName(Me)) Then\r\n        Log.Add \"Connection string: \" & this.strConnect, False\r\n    Else\r\n        ' Return open connection\r\n        Set GetNewOpenConnection = oConn\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ScanFiles\r\n' Author    : Adam Waller\r\n' Date      : 7/28/2023\r\n' Purpose   : Scan the files to get a list of source files that should match the current\r\n'           : database objects. For performance reasons, we won't apply the filter here\r\n'           : but will check the filter later when removing orphaned objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ScanFiles()\r\n\r\n    Dim oFld As Scripting.Folder\r\n    Dim dBaseFolders As Dictionary\r\n    Dim dFiles As Dictionary\r\n    Dim varKey As Variant\r\n    Dim strKey As String\r\n    Dim strValue As String\r\n    Dim strFolder As String\r\n    Dim dFile As Dictionary\r\n    Dim dIndex As Dictionary\r\n    Dim strPath As String\r\n\r\n    ' Reset module-level dictionary\r\n    Set m_Files = New Dictionary\r\n\r\n    ' Load any existing index file of hashes for object that don't store modified dates.\r\n    strPath = this.strBaseFolder & \"vcs-index.json\"\r\n    If FSO.FileExists(strPath) Then\r\n        Set dFile = ReadJsonFile(strPath)\r\n        If Not dFile Is Nothing Then Set dIndex = dFile(\"Items\")\r\n    End If\r\n    If dIndex Is Nothing Then Set dIndex = New Dictionary\r\n\r\n    ' Build a collection of subfolders and files with modified dates\r\n    ' (Using the Windows API for faster scanning and more accurate dates)\r\n    Set dBaseFolders = GetBaseFolders\r\n    If FSO.FolderExists(this.strBaseFolder) Then\r\n        For Each oFld In FSO.GetFolder(this.strBaseFolder).SubFolders\r\n            strFolder = oFld.Name\r\n            If dBaseFolders.Exists(strFolder) Then\r\n                ' Get dictionary of files with modified dates\r\n                Set dFiles = GetFileList(oFld.Path, \"*.sql\", Not this.blnUtcTime)\r\n                ' Loop through files, adding to index\r\n                For Each varKey In dFiles.Keys\r\n                    strKey = strFolder & \"\\\" & varKey\r\n                    ' For most objects, the key value will be the modified date.\r\n                    ' For objects (like views) that don't have a modified date, look up\r\n                    ' the hash from the index. (Or fall back to the file date, if no\r\n                    ' index entry is found.)\r\n                    If dIndex.Exists(strKey) Then\r\n                        strValue = dIndex(strKey)\r\n                    Else\r\n                        ' Use modified date from file\r\n                        strValue = dFiles(varKey)\r\n                    End If\r\n                    ' Add each file with a key that matches the database object, and the\r\n                    ' file modified date as the value for each item.\r\n                    m_Files.Add strKey, strValue\r\n                Next varKey\r\n            End If\r\n        Next oFld\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetBaseFolders\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2023\r\n' Purpose   : Return a dictionary of base folders used for component types\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetBaseFolders() As Dictionary\r\n    Set GetBaseFolders = New Dictionary\r\n    With GetBaseFolders\r\n        .CompareMode = TextCompare\r\n        .Add \"tables\", Null\r\n        .Add \"views\", Null\r\n        .Add \"procedures\", Null\r\n        .Add \"functions\", Null\r\n        .Add \"triggers\", Null\r\n        '.Add \"indexes\", Null\r\n    End With\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_GetChangeCount\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Return count of modified objects\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IDbSchema_ObjectCount(blnModifiedOnly As Boolean) As Long\r\n    If m_Files Is Nothing Then ScanFiles\r\n    If m_AllItems Is Nothing Then ScanDatabaseObjects\r\n    If m_AllItems Is Nothing Then Exit Function\r\n    IDbSchema_ObjectCount = IIf(blnModifiedOnly, m_ModifiedItems.Count, m_AllItems.Count)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 7/18/2023\r\n' Purpose   : Initialize the database schema\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IDbSchema_Initialize(dInstance As Scripting.IDictionary)\r\n\r\n    Dim colRules As Collection\r\n\r\n    ' Map filter to collection\r\n    If dInstance.Exists(\"Filter\") Then\r\n        ' Create collection of rules (we will skip comments and blank lines later)\r\n        Set colRules = dInstance(\"Filter\")\r\n    Else\r\n        ' No rules defined\r\n        Set colRules = New Collection\r\n    End If\r\n\r\n    ' Set class values\r\n    With this\r\n        .strName = dNZ(dInstance, \"Name\")\r\n        .strConnect = dNZ(dInstance, \"Connect\")\r\n        .strBaseFolder = Options.GetExportFolder & \"databases\\\" & GetSafeFileName(.strName) & PathSep\r\n        Set .colFilters = colRules\r\n        If dInstance.Exists(\"UtcDateTime\") Then .blnUtcTime = dInstance(\"UtcDateTime\")\r\n        .blnInitialized = (Len(.strConnect))\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_BaseFolder\r\n' Author    : Adam Waller\r\n' Date      : 8/2/2023\r\n' Purpose   : Return base folder for this schema export\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbSchema_BaseFolder() As String\r\n    IDbSchema_BaseFolder = this.strBaseFolder\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_Name\r\n' Author    : Adam Waller\r\n' Date      : 8/2/2023\r\n' Purpose   : Return the name of this schema\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbSchema_Name() As String\r\n    IDbSchema_Name = this.strName\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_ServerType\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2023\r\n' Purpose   : Return server type\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbSchema_ServerType() As eDatabaseServerType\r\n    IDbSchema_ServerType = estMySql\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IDbSchema_TypeDescription\r\n' Author    : Adam Waller\r\n' Date      : 8/2/2023\r\n' Purpose   : Return type description\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Property Get IDbSchema_TypeDescription() As Variant\r\n    IDbSchema_TypeDescription = \"MySQL Server\"\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsSourceParser.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsSourceParser\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsSourceParser\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Functions to sanitize files to remove non-essential metadata\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPublic ObjectName As String\r\n\r\nPublic Enum eContentType\r\n    ectObjectDefinition\r\n    ectXML\r\n    ectVBA\r\nEnd Enum\r\n\r\n' Private type to handle internal variables\r\nPrivate Type udtThis\r\n    lngSkipLines() As Long  ' Array of lines to skip\r\n    lngSkipIndex As Long\r\n    intObjectType As eDatabaseComponentType\r\n    strFilePath As String   ' Path to loaded file\r\n    blnOutputModified As Boolean\r\n    strInput As String\r\n    strOutput As String\r\n    strVBA As String\r\n    colBlocks As Collection\r\nEnd Type\r\nPrivate this As udtThis\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadFile\r\n' Author    : Adam Waller\r\n' Date      : 10/25/2023\r\n' Purpose   : Load a source file to sanitize\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function LoadSourceFile(strPath As String, intType As eDatabaseComponentType)\r\n    ResetContent\r\n    this.intObjectType = intType\r\n    this.strInput = ReadSourceFile(strPath)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadString\r\n' Author    : Adam Waller\r\n' Date      : 10/27/2023\r\n' Purpose   : Load input from a string\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function LoadString(ByVal strContent As String)\r\n    ResetContent\r\n    this.strInput = strContent\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetOutput\r\n' Author    : Adam Waller\r\n' Date      : 11/8/2023\r\n' Purpose   : Wrapper to return output string\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetOutput() As String\r\n    GetOutput = this.strOutput\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OutputModified\r\n' Author    : Adam Waller\r\n' Date      : 11/8/2023\r\n' Purpose   : Return true if the output has been modified\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get OutputModified() As Boolean\r\n    OutputModified = this.blnOutputModified\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ResetContent\r\n' Author    : Adam Waller\r\n' Date      : 10/31/2023\r\n' Purpose   : Resets the local variables when data is loaded from a new source.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ResetContent()\r\n    With this\r\n        .strFilePath = vbNullString\r\n        .strInput = vbNullString\r\n        .strOutput = vbNullString\r\n        .strVBA = vbNullString\r\n        .blnOutputModified = False\r\n    End With\r\n    Me.ObjectName = vbNullString\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Sanitize\r\n' Author    : Adam Waller\r\n' Date      : 10/25/2023\r\n' Purpose   : Public wrapper for sanitize functions\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Sanitize(intContentType As eContentType) As String\r\n    Select Case intContentType\r\n        Case ectObjectDefinition:   SanitizeObject\r\n        Case ectVBA:                SanitizeVBA\r\n        Case ectXML:                SanitizeXML\r\n    End Select\r\n    Sanitize = this.strOutput\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergeVBA\r\n' Author    : Adam Waller\r\n' Date      : 10/27/2023\r\n' Purpose   : Merge VBA into the output content.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MergeVBA(strVbaCode As String)\r\n\r\n    Dim varLines As Variant\r\n    Dim lngLine As Long\r\n\r\n    ' Make sure we have some output\r\n    If Len(this.strOutput) = 0 Then this.strOutput = this.strInput\r\n\r\n    ' Rebuild output using provided VBA code\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n\r\n        ' Load in existing sanitized content\r\n        varLines = Split(this.strOutput, vbCrLf)\r\n        For lngLine = 0 To UBound(varLines)\r\n            ' Note that the same heading name is used in both forms and reports\r\n            If varLines(lngLine) = \"CodeBehindForm\" Then\r\n                ' Allow merge of empty string to remove VBA code module\r\n                If Len(strVbaCode) Then .Add CStr(varLines(lngLine))\r\n                Exit For\r\n            Else\r\n                ' Add all other lines\r\n                .Add CStr(varLines(lngLine))\r\n            End If\r\n        Next lngLine\r\n\r\n        ' Add the VBA code here, and remove extra vbCrLf\r\n        .Add strVbaCode\r\n        .Remove 2\r\n\r\n        ' Update output with combined content\r\n        this.strOutput = .GetStr\r\n        this.strVBA = strVbaCode\r\n        this.blnOutputModified = True\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergePrintSettings\r\n' Author    : Adam Waller\r\n' Date      : 11/8/2023\r\n' Purpose   : Merge print settings into the current source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MergePrintSettings(strJson As String)\r\n\r\n    Dim dSettings As Dictionary\r\n\r\n    ' Make sure we have some output\r\n    If Len(this.strOutput) = 0 Then this.strOutput = this.strInput\r\n\r\n    ' Don't try to parse an empty string\r\n    If strJson = vbNullString Then Exit Sub\r\n\r\n    ' Read settings from JSON\r\n    Set dSettings = ParseJson(strJson)\r\n    If dSettings.Exists(\"Items\") Then\r\n        With New clsDevMode\r\n            ' Load default printer settings, then overlay\r\n            ' settings saved with report.\r\n            .ApplySettings dSettings(\"Items\")\r\n            ' Write the printer settings to the output content\r\n            this.strOutput = .AddToExportFile(this.strOutput)\r\n            this.blnOutputModified = True\r\n        End With\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveObjectVBA\r\n' Author    : Adam Waller\r\n' Date      : 10/31/2023\r\n' Purpose   : Return the extracted VBA code\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetObjectVBA() As String\r\n    GetObjectVBA = this.strVBA\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Hash\r\n' Author    : Adam Waller\r\n' Date      : 10/25/2023\r\n' Purpose   : Return a hash of the sanitized content\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Hash(Optional blnWithBom As Boolean = True) As String\r\n    Hash = GetStringHash(this.strOutput, blnWithBom)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SanitizeFile\r\n' Author    : Adam Waller\r\n' Date      : 11/4/2020\r\n' Purpose   : Rewritten version of sanitize function. Returns hash of content as well\r\n'           : as saving to the specified path.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SanitizeObject() As String 'strPath As String, blnReturnHash As Boolean, Optional strObjectName As String) As String\r\n\r\n    Dim varLines As Variant\r\n    Dim lngLine As Long\r\n    Dim strLine As String\r\n    Dim strTLine As String\r\n    Dim blnInsideIgnoredBlock As Boolean\r\n    Dim intIndent As Integer\r\n    Dim blnIsReport As Boolean\r\n    Dim blnIsPassThroughQuery As Boolean\r\n    Dim curStart As Currency\r\n    Dim cVBA As clsConcat\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' If not sanitizing, then return output\r\n    If Options.SanitizeLevel = eslNone Then\r\n        this.strOutput = this.strInput\r\n        SanitizeObject = this.strOutput\r\n        Exit Function\r\n    End If\r\n\r\n    Perf.OperationStart \"Sanitize File\"\r\n    varLines = Split(this.strInput, vbCrLf)\r\n\r\n    ' Set up index of lines to skip\r\n    ReDim this.lngSkipLines(0 To UBound(varLines)) As Long\r\n    this.lngSkipIndex = 0\r\n    Set this.colBlocks = New Collection\r\n\r\n    ' Initialize concatenation class to include line breaks\r\n    ' after each line that we add when building new file text.\r\n    curStart = Perf.MicroTimer\r\n\r\n    ' Using a do loop since we may adjust the line counter\r\n    ' during a loop iteration.\r\n    Do While lngLine <= UBound(varLines)\r\n\r\n        ' Get unmodified and trimmed line\r\n        strLine = varLines(lngLine)\r\n        strTLine = Trim$(strLine)\r\n\r\n        ' Improve performance by reducing comparisons\r\n        If Len(strTLine) > 3 And blnInsideIgnoredBlock Then\r\n            SkipLine lngLine\r\n        ElseIf Len(strTLine) > 60 And StartsWith(strTLine, \"0x\") Then\r\n            ' Add binary data line. No need to test this line further.\r\n        Else\r\n            ' Run the rest of the tests\r\n            Select Case strTLine\r\n\r\n                ' File version\r\n                Case \"Version =21\"\r\n                    ' Change version down to 20 to allow import into Access 2010.\r\n                    ' (Haven't seen any significant issues with this.)\r\n                    varLines(lngLine) = \"Version =20\"\r\n\r\n                ' Print settings blocks to ignore\r\n                Case \"PrtMip = Begin\", _\r\n                    \"PrtDevMode = Begin\", _\r\n                    \"PrtDevModeW = Begin\", _\r\n                    \"PrtDevNames = Begin\", _\r\n                    \"PrtDevNamesW = Begin\"\r\n                    ' Set flag to ignore lines inside this block.\r\n                    blnInsideIgnoredBlock = True\r\n                    SkipLine lngLine\r\n\r\n                ' Aggressive sanitize blocks\r\n                Case \"GUID = Begin\", _\r\n                    \"NameMap = Begin\", _\r\n                    \"dbLongBinary \"\"DOL\"\" = Begin\", _\r\n                    \"dbBinary \"\"GUID\"\" = Begin\"\r\n                    If Options.SanitizeLevel >= eslStandard Then\r\n                        blnInsideIgnoredBlock = True\r\n                        SkipLine lngLine\r\n                    End If\r\n\r\n                ' Single lines to ignore (#249)\r\n                Case \"NoSaveCTIWhenDisabled =1\", _\r\n                    \"AllowPivotTableView =0\", _\r\n                    \"AllowPivotChartView =0\"\r\n                    SkipLine lngLine\r\n\r\n                ' Publish option (used in Queries)\r\n                Case \"dbByte \"\"PublishToWeb\"\" =\"\"1\"\"\", _\r\n                    \"PublishOption =1\"\r\n                    If Options.StripPublishOption Then SkipLine lngLine\r\n\r\n                ' End of block section\r\n                Case \"End\"\r\n                    If blnInsideIgnoredBlock Then\r\n                        ' Reached the end of the ignored block.\r\n                        blnInsideIgnoredBlock = False\r\n                        SkipLine lngLine\r\n                    Else\r\n                        ' Check for theme color index\r\n                        CloseBlock\r\n                    End If\r\n\r\n                ' See if this file is from a report object\r\n                Case \"Begin Report\"\r\n                    ' Turn flag on to ignore Right and Bottom lines\r\n                    blnIsReport = True\r\n                    BeginBlock\r\n\r\n                ' Beginning of main section\r\n                Case \"Begin\"\r\n                    If blnIsPassThroughQuery Then\r\n                        ' Ignore remaining content. (See Issue #182)\r\n                        Do While lngLine < UBound(varLines)\r\n                            SkipLine lngLine, eslStandard\r\n                            lngLine = lngLine + 1\r\n                        Loop\r\n                        Exit Do\r\n                    ElseIf IsQueryNameAliasBlock(lngLine, varLines) Then\r\n                        SkipLine lngLine, eslStandard\r\n                        intIndent = GetIndent(strLine)\r\n                        ' Skip following lines till we return to original indent level\r\n                        Do\r\n                            SkipLine lngLine + 1, eslStandard\r\n                            lngLine = lngLine + 1\r\n                            If GetIndent(varLines(lngLine)) = intIndent Then Exit Do\r\n                            ' Sanity check for malformed file\r\n                            If lngLine > UBound(varLines) Then Exit Do\r\n                        Loop\r\n                    Else\r\n                        BeginBlock\r\n                    End If\r\n\r\n                ' Code section behind form or report object\r\n                Case \"CodeBehindForm\"\r\n                    If Options.SplitLayoutFromVBA Then\r\n                        ' Remove the VBA code from the layout file, but add a placeholder\r\n                        ' comment just in case the user wonders what happened to the VBA\r\n                        ' source code.\r\n                        lngLine = lngLine + 1\r\n                        Set cVBA = New clsConcat\r\n                        cVBA.AppendOnAdd = vbCrLf\r\n                        cVBA.Add CStr(varLines(lngLine))\r\n                        varLines(lngLine) = \"' See \"\"\" & Nz2(Me.ObjectName, \"FileName\") & \".cls\"\"\"\r\n                        ' Skip remaining lines\r\n                        Do While lngLine < UBound(varLines)\r\n                            lngLine = lngLine + 1\r\n                            cVBA.Add CStr(varLines(lngLine))\r\n                            SkipLine lngLine, eslStandard\r\n                        Loop\r\n                        Exit Do\r\n                    Else\r\n                        ' Apply sanitize rules to VBA code\r\n                        SanitizeCodeLines lngLine, varLines\r\n                        ' Keep everything from this point on\r\n                        Exit Do\r\n                    End If\r\n\r\n                Case Else\r\n                    If blnInsideIgnoredBlock Then\r\n                        ' Skip content inside ignored blocks.\r\n                        SkipLine lngLine\r\n                    ElseIf StartsWith(strTLine, \"Checksum =\") Then\r\n                        ' Ignore Checksum lines, since they will change.\r\n                        SkipLine lngLine, eslMinimal\r\n                    ElseIf StartsWith(strTLine, \"ColumnInfo =\") _\r\n                        Or StartsWith(strTLine, \"BaseInfo =\") Then\r\n                        ' [ColumnInfo] contains some cached info from the record source\r\n                        ' and will be regenerated when the form is imported. See #412\r\n                        ' [BaseInfo] is used with combo boxes, similar to RowSource.\r\n                        ' Since the value could span multiple lines, we need to\r\n                        ' check the indent level of the following lines to see how\r\n                        ' many lines to skip.\r\n                        SkipLine lngLine, eslStandard\r\n                        intIndent = GetIndent(strLine)\r\n                        ' Preview the next line, and check the indent level\r\n                        Do While GetIndent(varLines(lngLine + 1)) > intIndent\r\n                            ' Skip previewed line and move to next line\r\n                            SkipLine lngLine + 1, eslStandard\r\n                            lngLine = lngLine + 1\r\n                        Loop\r\n                    ElseIf blnIsReport And StartsWith(strLine, \"    Right =\") Then\r\n                        ' Ignore this line. (Not important, and frequently changes.)\r\n                        SkipLine lngLine, eslStandard\r\n                    ElseIf blnIsReport And StartsWith(strLine, \"    Bottom =\") Then\r\n                        ' Turn flag back off now that we have ignored these two lines.\r\n                        SkipLine lngLine, eslStandard\r\n                        blnIsReport = False\r\n                    ElseIf StartsWith(strTLine, \"WebImagePadding\") Then\r\n                        ' These values tend to drift between builds. See #423\r\n                        SkipLine lngLine, eslStandard\r\n                    ElseIf StartsWith(strTLine, \"Begin \") Then\r\n                        ' Include block type name for controls\r\n                        BeginBlock Mid$(strTLine, 7)\r\n                    ElseIf EndsWith(strTLine, \" = Begin\") Then\r\n                        BeginBlock\r\n                    Else\r\n                        ' All other lines will be added.\r\n\r\n                        ' Check for color properties\r\n                        If InStr(1, strTLine, \" =\") > 1 Then CheckColorProperties strTLine, lngLine\r\n\r\n                        ' Check for pass-through query connection string\r\n                        If StartsWith(strLine, \"dbMemo \"\"Connect\"\" =\"\"\") Then\r\n                            ' Not just an empty value (See issue #337)\r\n                            If Len(strLine) > 20 Then\r\n                                blnIsPassThroughQuery = True\r\n                            End If\r\n                        End If\r\n                    End If\r\n\r\n            End Select\r\n        End If\r\n\r\n        ' Increment counter to next line\r\n        lngLine = lngLine + 1\r\n    Loop\r\n\r\n    ' Ensure that we correctly processed the nested block sequence.\r\n    If this.colBlocks.Count > 0 Then\r\n        Log.Error eelWarning, MultiReplace( _\r\n            \"Found ${BlockCount} unclosed blocks after sanitizing ${File}.\", _\r\n            \"${BlockCount}\", this.colBlocks.Count, _\r\n            \"${File}\", Nz2(Me.ObjectName, this.strFilePath)), _\r\n            ModuleName(Me) & \".SanitizeFile\"\r\n    End If\r\n\r\n    ' Prepare primary output\r\n    this.strOutput = BuildOutput(varLines)\r\n    SanitizeObject = this.strOutput\r\n\r\n    ' Prepare VBA output (if used)\r\n    If Not cVBA Is Nothing Then\r\n        ' Build sanitized VBA string\r\n        With New clsSourceParser\r\n            .LoadString cVBA.GetStr\r\n            this.strVBA = .Sanitize(ectVBA)\r\n        End With\r\n    End If\r\n\r\n    ' Log performance\r\n    Set this.colBlocks = Nothing\r\n    Perf.OperationEnd\r\n    Log.Add \"    Sanitized in \" & Format$(Perf.MicroTimer - curStart, \"0.000\") & \" seconds.\", Options.ShowDebug\r\n\r\n    ' Log any errors\r\n    CatchAny eelError, \"Error sanitizing \" & Nz2(Me.ObjectName, FSO.GetFileName(this.strFilePath)), ModuleName(Me) & \".SanitizeFile\"\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildOutput\r\n' Author    : Adam Waller\r\n' Date      : 6/4/2021\r\n' Purpose   : Splitting this out into its own sub to reduce complexity.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function BuildOutput(varLines As Variant) As String\r\n\r\n    Dim cData As clsConcat\r\n    Dim lngSkip As Long\r\n    Dim lngLine As Long\r\n\r\n    ' Check index of skipped lines\r\n    If this.lngSkipIndex = 0 Then\r\n        ' No lines to skip\r\n        ReDim this.lngSkipLines(0 To 0)\r\n        this.lngSkipLines(0) = UBound(varLines) + 1\r\n    Else\r\n        ' Trim and sort index array\r\n        ReDim Preserve this.lngSkipLines(0 To this.lngSkipIndex - 1)\r\n        QuickSort this.lngSkipLines\r\n    End If\r\n\r\n    ' Use concatenation class to maximize performance\r\n    Set cData = New clsConcat\r\n    With cData\r\n        .AppendOnAdd = vbCrLf\r\n\r\n        ' Loop through array of lines in source file\r\n        For lngLine = 0 To UBound(varLines)\r\n\r\n            ' Iterate the sorted skipped lines index to keep up with main loop\r\n            ' (Using parallel loops to optimize performance)\r\n            If this.lngSkipLines(lngSkip) < lngLine Then\r\n                If lngSkip < UBound(this.lngSkipLines) Then lngSkip = lngSkip + 1\r\n            End If\r\n\r\n            ' Add content, unless the line is flagged to skip\r\n            If this.lngSkipLines(lngSkip) <> lngLine Then .Add CStr(varLines(lngLine))\r\n\r\n        Next lngLine\r\n\r\n        ' Remove last vbcrlf\r\n        cData.Remove Len(vbCrLf)\r\n\r\n        ' Return assembled output\r\n        BuildOutput = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StripClassHeader\r\n' Author    : Adam Waller\r\n' Date      : 10/24/2023\r\n' Purpose   : Strip the class header section from the VBA content. (Remove the version\r\n'           : and VBE attributes lines that come before the actual VBA code.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function StripClassHeader(strContent As String, blnStripNameOnly As Boolean) As String\r\n\r\n    Dim lngLine As Long\r\n    Dim varLines As Variant\r\n    Dim strLine As String\r\n    Dim blnPastHeader As Boolean\r\n\r\n    ' Split code into lines\r\n    varLines = Split(strContent, vbCrLf)\r\n\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n\r\n        ' Skip the header information saved in the VBA class\r\n        For lngLine = 0 To UBound(varLines)\r\n            If Not blnPastHeader Then\r\n                strLine = varLines(lngLine)\r\n                If blnStripNameOnly Then\r\n                    If StartsWith(strLine, \"Attribute VB_Name = \") Then\r\n                        ' Just skip that line. Keep everything else.\r\n                        blnPastHeader = True\r\n                    Else\r\n                        .Add CStr(varLines(lngLine))\r\n                    End If\r\n                Else\r\n                    Select Case True\r\n                        Case (strLine = \"VERSION 1.0 CLASS\")\r\n                        Case (strLine = \"BEGIN\")\r\n                        Case (strLine = \"  MultiUse = -1  'True\")\r\n                        Case (strLine = \"END\")\r\n                        Case StartsWith(strLine, \"Attribute VB_\")\r\n                        Case Else\r\n                            blnPastHeader = True\r\n                            .Add CStr(varLines(lngLine))\r\n                    End Select\r\n                End If\r\n            Else\r\n                ' Add remaining lines\r\n                .Add CStr(varLines(lngLine))\r\n            End If\r\n        Next lngLine\r\n\r\n        ' remove trailing CrLf and return result\r\n        .Remove 2\r\n        StripClassHeader = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SkipLine\r\n' Author    : Adam Waller\r\n' Date      : 6/4/2021\r\n' Purpose   : Skip this line in the final output file. Optionally include a minimum\r\n'           : sanitize level to skip this line.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SkipLine(lngLine As Long, Optional intMinSanitizeLevel As eSanitizeLevel)\r\n    If Options.SanitizeLevel >= intMinSanitizeLevel Then\r\n        this.lngSkipLines(this.lngSkipIndex) = lngLine\r\n        this.lngSkipIndex = this.lngSkipIndex + 1\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SanitizeCodeLines\r\n' Author    : Adam Waller\r\n' Date      : 7/11/2023\r\n' Purpose   : Perform any sanitizing of code lines.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SanitizeCodeLines(lngLineStart As Long, ByRef varLines As Variant)\r\n\r\n    Dim lngLine As Long\r\n\r\n    Perf.OperationStart \"Sanitize Code Lines\"\r\n    For lngLine = lngLineStart To UBound(varLines)\r\n        ' Check for lines that include only space padding\r\n        ' added by the IDE automatic indenting. (The padding is removed if you\r\n        ' comment out a block, then uncomment the same block, causing unwanted\r\n        ' noise in version control.)\r\n        If Len(varLines(lngLine)) > 0 Then\r\n            If Trim(varLines(lngLine)) = vbNullString Then\r\n                ' Remove the space padding\r\n                varLines(lngLine) = vbNullString\r\n            End If\r\n        End If\r\n    Next lngLine\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BeginBlock\r\n' Author    : Adam Waller\r\n' Date      : 6/4/2021\r\n' Purpose   : Add a dictionary object to represent the block\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub BeginBlock(Optional strType As String)\r\n    Dim dBlock As Dictionary\r\n    If this.colBlocks Is Nothing Then Set this.colBlocks = New Collection\r\n    Set dBlock = New Dictionary\r\n    If strType <> vbNullString Then dBlock.Add \"Type\", strType\r\n    this.colBlocks.Add dBlock\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CloseBlock\r\n' Author    : Adam Waller\r\n' Date      : 6/4/2021\r\n' Purpose   : Determine if the block used any theme-based dynamic colors that should\r\n'           : be skipped in the output file. (See issue #183)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub CloseBlock()\r\n\r\n    Dim varBase As Variant\r\n    Dim intCnt As Integer\r\n    Dim dBlock As Dictionary\r\n    Dim strKey As String\r\n\r\n    ' Skip if we are not using aggressive color sanitize\r\n    If Options.SanitizeColors <= eslNone Then Exit Sub\r\n\r\n    ' Bail out if we don't have a block to review\r\n    If this.colBlocks.Count = 0 Then Exit Sub\r\n    Set dBlock = this.colBlocks(this.colBlocks.Count)\r\n\r\n    ' Skip if we are not using themes for this control (UseTheme=0)\r\n    ' (Applies to \"CommandButton\", \"Tab\", \"ToggleButton\")\r\n    If dBlock.Exists(\"UseTheme\") Then\r\n        ' Remove this block\r\n        this.colBlocks.Remove this.colBlocks.Count\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Build array of base properties\r\n    varBase = Array(\"Back\", \"AlternateBack\", \"Border\", _\r\n            \"Fore\", \"Gridline\", \"HoverFore\", _\r\n            \"Hover\", \"PressedFore\", \"Pressed\", _\r\n            \"DatasheetFore\", \"DatasheetBack\", \"DatasheetGridlines\")\r\n\r\n    ' Loop through properties, checking for index\r\n    For intCnt = 0 To UBound(varBase)\r\n        strKey = varBase(intCnt) & \"ThemeColorIndex\"\r\n        If dBlock.Exists(strKey) Then\r\n            If dBlock(strKey) <> NO_THEME_INDEX Then\r\n                ' Check for corresponding color property\r\n                strKey = varBase(intCnt) & \"Color\"\r\n                If dBlock.Exists(strKey) Then\r\n                    ' Skip the dynamic color line\r\n                    SkipLine dBlock(strKey)\r\n                End If\r\n            End If\r\n        Else\r\n            Select Case dBlock(\"Type\")\r\n                Case \"Section\", \"FormHeader\", \"FormFooter\"\r\n                    ' Some controls like form sections don't use color values\r\n                    ' if a theme index is specified. If a color value exists,\r\n                    ' we should preserve it.\r\n                Case Else\r\n                    ' Most controls automatically use theme indexes\r\n                    ' unless otherwise specified.\r\n                    ' As discussed in #183, this can be affected by incomplete\r\n                    ' component definition blocks.\r\n                    If Options.SanitizeColors = eslExtended Then\r\n                        strKey = varBase(intCnt) & \"Color\"\r\n                        If dBlock.Exists(strKey) Then\r\n                            ' Skip the dynamic color line\r\n                            SkipLine dBlock(strKey)\r\n                        End If\r\n                    End If\r\n            End Select\r\n        End If\r\n    Next intCnt\r\n\r\n    ' Remove this block\r\n    this.colBlocks.Remove this.colBlocks.Count\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsQueryNameAliasBlock\r\n' Author    : Adam Waller\r\n' Date      : 3/25/2025\r\n' Purpose   : Determine if this is simply an alias of the field name with no other\r\n'           : properties. (These come and go depending on the internal compilation\r\n'           : of the query, causing unwanted noise in souce code.)\r\n'           : These blocks can be safely removed with no impact on the database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IsQueryNameAliasBlock(lngLine As Long, varLines As Variant) As Boolean\r\n\r\n    Dim lngNext As Long\r\n\r\n    ' This only applies to queries\r\n    If this.intObjectType <> edbQuery Then Exit Function\r\n\r\n    ' Make sure we have additional lines to check\r\n    If UBound(varLines) < lngLine + 2 Then Exit Function\r\n\r\n    ' Look at the next two lines to see if we are just dealing with an alias block\r\n    If StartsWith(Trim(varLines(lngLine + 1)), \"dbText \"\"Name\"\" =\") Then\r\n\r\n        ' Skip past any GUID lines\r\n        lngNext = lngLine + 2\r\n        If StartsWith(Trim(varLines(lngNext)), \"dbBinary \"\"GUID\"\" = Begin\") Then lngNext = lngNext + 3\r\n\r\n        ' See if the following line closes the block\r\n        If Trim(varLines(lngNext)) = \"End\" Then\r\n            ' Yes, this was simply a name alias block\r\n            IsQueryNameAliasBlock = True\r\n        End If\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckColorProperties\r\n' Author    : Adam Waller\r\n' Date      : 6/4/2021\r\n' Purpose   : Use an index to reference color properties so we can determine any lines\r\n'           : that we need to discard after finishing the block.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub CheckColorProperties(strTLine As String, lngLine As Long)\r\n\r\n    Dim dBlock As Dictionary\r\n    Dim varParts As Variant\r\n    Dim lngCnt As Long\r\n    Dim lngColor As Long\r\n\r\n    ' Skip if not using this option\r\n    If Options.SanitizeColors <= eslNone Then Exit Sub\r\n\r\n    ' Exit if we are not inside a block\r\n    If Not this.colBlocks Is Nothing Then lngCnt = this.colBlocks.Count\r\n    If lngCnt = 0 Then Exit Sub\r\n    Set dBlock = this.colBlocks(this.colBlocks.Count)\r\n\r\n    ' Split on property/value\r\n    varParts = Split(strTLine, \" =\")\r\n    Select Case varParts(0)\r\n\r\n        ' Theme color index properties\r\n        Case \"BackThemeColorIndex\", \"AlternateBackThemeColorIndex\", \"BorderThemeColorIndex\", _\r\n            \"ForeThemeColorIndex\", \"GridlineThemeColorIndex\", \"HoverForeThemeColorIndex\", _\r\n            \"HoverThemeColorIndex\", \"PressedForeThemeColorIndex\", \"PressedThemeColorIndex\", _\r\n            \"DatasheetBackThemeColorIndex\", \"DatasheetForeThemeColorIndex\", \"DatasheetGridlinesThemeColorIndex\"\r\n            ' Save to dictionary if using a theme index color\r\n            dBlock.Add varParts(0), varParts(1)\r\n\r\n        ' Matching color properties\r\n        Case \"BackColor\", \"AlternateBackColor\", \"BorderColor\", _\r\n            \"ForeColor\", \"GridlineColor\", \"HoverForeColor\", _\r\n            \"HoverColor\", \"PressedForeColor\", \"PressedColor\", _\r\n            \"DatasheetBackColor\", \"DatasheetForeColor\", \"DatasheetGridlinesColor\"\r\n\r\n            ' Check for system color constants\r\n            If IsNumeric(varParts(1)) Then lngColor = varParts(1)\r\n            If lngColor < 0 Then\r\n                ' Using a system color constant or other Access constant value.\r\n                ' https://stackoverflow.com/a/30396550/4121863\r\n                ' Leave this color value intact.\r\n            Else\r\n                ' Save line of color property\r\n                dBlock.Add varParts(0), lngLine\r\n            End If\r\n\r\n        Case \"UseTheme\"\r\n            ' You can tell certain controls to not use the theme. (Buttons, Tabs, Toggles)\r\n            If varParts(1) = 0 Then dBlock.Add varParts(0), 0\r\n\r\n        Case Else\r\n            ' Check for other related dynamic color properties/indexes\r\n            If StartsWith(strTLine, \"DatasheetGridlinesColor\") Then\r\n                ' May include the index number in the property name. (I.e. DatasheetGridlinesColor12 =0)\r\n                ' Convert to a more consistent identifier, using the index suffix as the value.\r\n                dBlock.Add \"DatasheetGridlinesThemeColorIndex\", Mid$(varParts(0), 24)\r\n            End If\r\n\r\n    End Select\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SanitizeVBA\r\n' Author    : Adam Waller\r\n' Date      : 7/12/2023\r\n' Purpose   : Standardizes blank line padding and trailing lines in VBA code content.\r\n'           : NOTE: This does not make any code changes, but only modifies padding\r\n'           : and blank lines to generate standardized output for version control.\r\n'           :\r\n'           : OPERATIONS:\r\n'           :   - Trim blank lines that only include space padding.\r\n'           :   - Remove extra trailing lines from the end of the module.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SanitizeVBA() As String\r\n\r\n    Dim lngLine As Long\r\n    Dim varLines As Variant\r\n    Dim lngLastLine As Long\r\n\r\n    ' Skip sanitizing if not using that option.\r\n    If Options.SanitizeLevel < eslStandard Then\r\n        this.strOutput = this.strInput\r\n        this.strVBA = this.strOutput\r\n        SanitizeVBA = this.strOutput\r\n        Exit Function\r\n    End If\r\n\r\n    Perf.OperationStart \"Sanitize VBA Code\"\r\n\r\n    ' Split code into lines\r\n    varLines = Split(this.strInput, vbCrLf)\r\n\r\n    ' Build sanitized content\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n\r\n        ' Find the last non-blank line\r\n        For lngLine = UBound(varLines) To 0 Step -1\r\n            If Len(Trim(varLines(lngLine))) > 0 Then\r\n                lngLastLine = lngLine\r\n                Exit For\r\n            End If\r\n        Next lngLine\r\n\r\n        ' Loop through lines\r\n        For lngLine = 0 To lngLastLine\r\n            .Add RTrim(varLines(lngLine))\r\n        Next lngLine\r\n\r\n        ' Return standardized code block\r\n        this.strOutput = .GetStr\r\n        this.strVBA = this.strOutput\r\n        SanitizeVBA = this.strOutput\r\n        Perf.OperationEnd\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SanitizeXML\r\n' Author    : Adam Waller\r\n' Date      : 4/29/2021\r\n' Purpose   : Remove non-essential data that changes every time the file is exported.\r\n'           : Optionally returns a hash of the file content. (To save reading the file\r\n'           : back again afterwards to compute the hash.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SanitizeXML() As String\r\n\r\n    Dim curStart As Currency\r\n    Dim cData As clsConcat\r\n    Dim strXML As String\r\n    Dim rxLine As VBScript_RegExp_55.RegExp\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    Set cData = New clsConcat\r\n    cData.AppendOnAdd = vbCrLf\r\n    Set rxLine = New VBScript_RegExp_55.RegExp\r\n\r\n    Perf.OperationStart \"Sanitize XML\"\r\n    curStart = Perf.MicroTimer\r\n    strXML = this.strInput\r\n\r\n    ' Exporting Table Def as XML does not properly encode ampersand character (See #314)\r\n    ' Most likely if any ampersands are encoded correctly, all of them will be.\r\n    With New VBScript_RegExp_55.RegExp\r\n        .Multiline = True\r\n        .Global = True\r\n        ' Match &amp; &quot; &gt; &lt; etc...\r\n        .Pattern = \"&[A-z]{2,6};\"\r\n        If Not .Test(strXML) Then\r\n            ' Properly encode any embedded ampersand characters to make valid XML\r\n            strXML = Replace(strXML, \"&\", \"&amp;\")\r\n        End If\r\n    End With\r\n\r\n    Static objXml As MSXML2.DOMDocument60\r\n    Dim objNode As MSXML2.IXMLDOMNode\r\n\r\n    If objXml Is Nothing Then\r\n        Set objXml = New MSXML2.DOMDocument60\r\n    End If\r\n\r\n    If objXml.LoadXML(strXML) = False Then\r\n        Log.Error eelError, _\r\n            \"Unable to parse the XML for file '\" & Nz2(this.strFilePath, Me.ObjectName) & _\r\n            \"'. This may be due to containing malformed XML. Check the source XML document for validity. \" & _\r\n            \"In some cases, this may be due to table data containing characters not allowed in XML documents.\", _\r\n            ModuleName(Me) & \".SanitizeXML\"\r\n        Exit Function\r\n    End If\r\n\r\n    ' Determine if it's a table data with schema\r\n    For Each objNode In objXml.SelectNodes(\"/root/dataroot\")\r\n        ' Remove the generated timestamp attribute to reduce noise\r\n        '   <dataroot xmlns:od=\"urn:schemas-microsoft-com:officedata\" generated=\"2020-04-27T10:28:32\">\r\n        '   <dataroot generated=\"2021-04-29T17:27:33\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n        objNode.Attributes.removeNamedItem \"generated\"\r\n\r\n        ' Determine whether the schema is required for import. If the schema contains elements:\r\n        '   <xsd:element od:expression ...>\r\n        '   <xsd:element od:jetType=\"complex\" ...>\r\n        '   <xsd:element od:jetType=\"oleobject\" ...>\r\n        ' Then the schema must be retained. Otherwise, discard the schema and retain only the data.\r\n        If objXml.SelectNodes(\"//*[(namespace-uri()='http://www.w3.org/2001/XMLSchema' and local-name()='element' and @*[namespace-uri()='urn:schemas-microsoft-com:officedata' and ((local-name()='jetType' and (string()='complex' or string()='oleobject')) or (local-name()='expression'))])]\").Length = 0 Then\r\n            objXml.replaceChild objXml.SelectSingleNode(\"/root/dataroot\"), objXml.SelectSingleNode(\"/root\")\r\n        End If\r\n    Next\r\n\r\n    ' Remove generated timestamp from tables exported without schema (such as linked tables)\r\n    For Each objNode In objXml.SelectNodes(\"/dataroot\")\r\n        objNode.Attributes.removeNamedItem \"generated\"\r\n    Next\r\n\r\n    ' Remove all nodes that are meaningless noise:\r\n    '   <od:tableProperty name=\"NameMap\" ...>\r\n    '   <od:tableProperty name=\"GUID\" ...>\r\n    '   <od:fieldProperty name=\"GUID\" ...>\r\n    For Each objNode In objXml.SelectNodes(\"//*[(namespace-uri()='urn:schemas-microsoft-com:officedata' and local-name()='tableProperty' and (@name='NameMap' or @name='GUID')) or (namespace-uri()='urn:schemas-microsoft-com:officedata' and local-name()='fieldProperty' and @name='GUID')]\")\r\n        objNode.ParentNode.RemoveChild objNode\r\n    Next\r\n\r\n    If Options.StripPublishOption Then\r\n        ' Remove all web publish options:\r\n        '   <od:tableProperty name=\"PublishToWeb\" ...>\r\n        For Each objNode In objXml.SelectNodes(\"//*[(namespace-uri()='urn:schemas-microsoft-com:officedata' and local-name()='tableProperty' and @name='PublishToWeb')]\")\r\n            objNode.ParentNode.RemoveChild objNode\r\n        Next\r\n    End If\r\n\r\n    Perf.OperationEnd\r\n\r\n    ' Check string length to determine which formatter to use.\r\n    ' (XSLT does a better job, but may fail on large sets of data)\r\n    If Len(strXML) < 30000 Then\r\n        ' Use XSLT to format the XML output.\r\n        this.strOutput = FormatXML(objXml)\r\n    Else\r\n        ' For very large XML strings like exporting data from large tables,\r\n        ' use an alternate simplified formatting function that avoids\r\n        ' the memory errors than can occur with XSLT.\r\n        this.strOutput = FormatXML2(objXml.XML)\r\n    End If\r\n\r\n    ' Save the output\r\n    SanitizeXML = this.strOutput\r\n\r\n    ' Show stats if debug turned on.\r\n    Log.Add \"    Sanitized in \" & Format$(Perf.MicroTimer - curStart, \"0.000\") & \" seconds.\", Options.ShowDebug\r\n\r\n    ' Log any errors\r\n    CatchAny eelError, \"Error sanitizing XML for \" & Nz2(FSO.GetFileName(this.strFilePath), Me.ObjectName), ModuleName(Me) & \".SanitizeXML\"\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetIndent\r\n' Author    : Adam Waller\r\n' Date      : 11/5/2020\r\n' Purpose   : Returns the number of spaces until the first non-space character.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetIndent(strLine As Variant) As Integer\r\n    Dim strChar As String\r\n    strChar = Left$(Trim(strLine), 1)\r\n    If strLine <> vbNullString Then GetIndent = InStr(1, strLine, strChar) - 1\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FormatXML\r\n' Author    : Adam Waller\r\n' Date      : 4/22/2021\r\n' Purpose   : Format XML content for consistent and readable output.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function FormatXML(objInput As MSXML2.DOMDocument60, _\r\n                            Optional blnOmitDeclaration As Boolean) As String\r\n\r\n    ' XSLT stylesheet that allow us to control indenting and also get a better indent result.\r\n    ' For testing and adjusting, you can use https://www.online-toolz.com/tools/xslt-validator-tester-online.php\r\n    'Const strIndentXslt As String = \"<xsl:stylesheet xmlns:xsl=\"\"http://www.w3.org/1999/XSL/Transform\"\" version=\"\"1.0\"\"><xsl:output method=\"\"xml\"\"/><xsl:template match=\"\"@*\"\"><xsl:copy/></xsl:template><xsl:template match=\"\"text()\"\"><xsl:value-of select=\"\"normalize-space(.)\"\"/></xsl:template><xsl:template match=\"\"*\"\"><xsl:param name=\"\"indent\"\" select=\"\"''\"\"/><xsl:text>&#xA;</xsl:text><xsl:value-of select=\"\"$indent\"\"/><xsl:copy><xsl:apply-templates select=\"\"@*|*|text()\"\"><xsl:with-param name=\"\"indent\"\" select=\"\"concat($indent, '  ')\"\"/></xsl:apply-templates></xsl:copy><xsl:if test=\"\"count(../*)&gt;0 and ../*[last()]=. and not(following-sibling::*)\"\"><xsl:text>&#xA;</xsl:text><xsl:value-of select=\"\"substring($indent,3)\"\"/></xsl:if></xsl:template></xsl:stylesheet>\"\r\n    Const strIndentXslt As String = \"<xsl:stylesheet xmlns:xsl=\"\"http://www.w3.org/1999/XSL/Transform\"\" version=\"\"1.0\"\"><xsl:output method=\"\"xml\"\"/><xsl:template match=\"\"@*\"\"><xsl:copy/></xsl:template><xsl:template match=\"\"*\"\"><xsl:param name=\"\"indent\"\" select=\"\"''\"\"/><xsl:text>&#xA;</xsl:text><xsl:value-of select=\"\"$indent\"\"/><xsl:copy><xsl:apply-templates select=\"\"@*|*|text()\"\"><xsl:with-param name=\"\"indent\"\" select=\"\"concat($indent, '  ')\"\"/></xsl:apply-templates></xsl:copy><xsl:if test=\"\"count(../*)&gt;0 and ../*[last()]=. and not(following-sibling::*)\"\"><xsl:text>&#xA;</xsl:text><xsl:value-of select=\"\"substring($indent,3)\"\"/></xsl:if></xsl:template></xsl:stylesheet>\"\r\n    ' This constant has the `omit-xml-declaration=\"yes\"` added to remove XML declarations.\r\n    'Const strIndentXsltNoDeclarations As String = \"<xsl:stylesheet xmlns:xsl=\"\"http://www.w3.org/1999/XSL/Transform\"\" version=\"\"1.0\"\"><xsl:output method=\"\"xml\"\" omit-xml-declaration=\"\"yes\"\"/><xsl:template match=\"\"@*\"\"><xsl:copy/></xsl:template><xsl:template match=\"\"text()\"\"><xsl:value-of select=\"\"normalize-space(.)\"\"/></xsl:template><xsl:template match=\"\"*\"\"><xsl:param name=\"\"indent\"\" select=\"\"''\"\"/><xsl:text>&#xA;</xsl:text><xsl:value-of select=\"\"$indent\"\"/><xsl:copy><xsl:apply-templates select=\"\"@*|*|text()\"\"><xsl:with-param name=\"\"indent\"\" select=\"\"concat($indent, '  ')\"\"/></xsl:apply-templates></xsl:copy><xsl:if test=\"\"count(../*)&gt;0 and ../*[last()]=. and not(following-sibling::*)\"\"><xsl:text>&#xA;</xsl:text><xsl:value-of select=\"\"substring($indent,3)\"\"/></xsl:if></xsl:template></xsl:stylesheet>\"\r\n    Const strIndentXsltNoDeclarations As String = \"<xsl:stylesheet xmlns:xsl=\"\"http://www.w3.org/1999/XSL/Transform\"\" version=\"\"1.0\"\"><xsl:output method=\"\"xml\"\" omit-xml-declaration=\"\"yes\"\"/><xsl:template match=\"\"@*\"\"><xsl:copy/></xsl:template><xsl:template match=\"\"*\"\"><xsl:param name=\"\"indent\"\" select=\"\"''\"\"/><xsl:text>&#xA;</xsl:text><xsl:value-of select=\"\"$indent\"\"/><xsl:copy><xsl:apply-templates select=\"\"@*|*|text()\"\"><xsl:with-param name=\"\"indent\"\" select=\"\"concat($indent, '  ')\"\"/></xsl:apply-templates></xsl:copy><xsl:if test=\"\"count(../*)&gt;0 and ../*[last()]=. and not(following-sibling::*)\"\"><xsl:text>&#xA;</xsl:text><xsl:value-of select=\"\"substring($indent,3)\"\"/></xsl:if></xsl:template></xsl:stylesheet>\"\r\n\r\n    Static objTransform As MSXML2.DOMDocument60\r\n    Static objTransformNoDeclaration As MSXML2.DOMDocument60\r\n\r\n    Dim strOutput As String\r\n\r\n    ' Skip processing if no content to format\r\n    If objInput.ChildNodes.Length = 0 Then Exit Function\r\n\r\n    Perf.OperationStart \"Format XML\"\r\n\r\n    ' Trap any errors with parsing or formatting the XML\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Transform the input; we don't want to use transformNodeToObject\r\n    ' because that would be defeated by MSXML reformatting when reading\r\n    ' from the XML property. We also cache the MSXML2.DOMDocument to\r\n    ' avoid paying the cost of loading the XSLT repeatedly.\r\n    If blnOmitDeclaration Then\r\n        If objTransformNoDeclaration Is Nothing Then\r\n            Set objTransformNoDeclaration = New MSXML2.DOMDocument60\r\n            objTransformNoDeclaration.LoadXML strIndentXsltNoDeclarations\r\n        End If\r\n        strOutput = objInput.transformNode(objTransformNoDeclaration)\r\n    Else\r\n        If objTransform Is Nothing Then\r\n            Set objTransform = New MSXML2.DOMDocument60\r\n            objTransform.LoadXML strIndentXslt\r\n        End If\r\n        strOutput = objInput.transformNode(objTransform)\r\n    End If\r\n\r\n    ' Check for any errors parsing the XML\r\n    If CatchAny(eelError, \"Error parsing XML content\", ModuleName(Me) & \".FormatXML\") Then\r\n        ' Fall back to input XML\r\n        strOutput = objInput.XML\r\n        ' Output XML to log file\r\n        Log.Spacer False\r\n        Log.Add objInput.XML, False\r\n        Log.Spacer False\r\n    End If\r\n\r\n    ' Return formatted output\r\n    Perf.OperationEnd\r\n    FormatXML = strOutput\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FormatXML2\r\n' Author    : Adam Waller\r\n' Date      : 12/12/2023\r\n' Purpose   : Alternate approach to formatting XML for use in large data sets where\r\n'           : the XSL transformation may encounter memory errors.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function FormatXML2(strInput As String) As String\r\n\r\n    Dim varLines As Variant\r\n    Dim varLine As Variant\r\n    Dim lngChar As Long\r\n\r\n    Perf.OperationStart \"Format XML (alt)\"\r\n\r\n    ' Split file into lines\r\n    varLines = Split(strInput, vbCrLf)\r\n\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n\r\n        ' Loop through lines\r\n        For Each varLine In varLines\r\n\r\n            ' Reset position\r\n            lngChar = 1\r\n\r\n            ' Loop through leading characters, watching for tabs.\r\n            Do While lngChar < Len(varLine)\r\n                ' See if this is\r\n                Select Case AscW(Mid(varLine, lngChar, 1))\r\n                    Case vbKeyTab   ' 9\r\n                        ' Increment tab counter\r\n                        lngChar = lngChar + 1\r\n                    Case Else\r\n                        ' Some other character.\r\n                        Exit Do\r\n                End Select\r\n            Loop\r\n\r\n            ' Add line with any indent\r\n            .Add Space$((lngChar - 1) * 2), Mid(varLine, lngChar)\r\n\r\n        Next varLine\r\n\r\n        ' Remove trailing vbCrLf and return result\r\n        .Remove 2\r\n        FormatXML2 = .GetStr\r\n\r\n    End With\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Terminate\r\n' Author    : Adam Waller\r\n' Date      : 10/25/2023\r\n' Purpose   : Release private objects when terminating\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Terminate()\r\n    Set this.colBlocks = Nothing\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsSqlFormatter.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsSqlFormatter\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsSqlFormatter\r\n' Author    : Adam Waller\r\n' Date      : 8/11/2023\r\n' Purpose   : Provide a \"simple\" implementation of SQL formatting for consistent output\r\n'           : in source code files. This is largely based on the doctrine/sql-formatter\r\n'           : project on GitHub. For my purposes I didn't need a huge multi-dialect\r\n'           : parser with lots of output options, but something that I could inlcude\r\n'           : as a single module in a project to parse SQL statements from Access\r\n'           : queries, as well as basic support for other common dialects like MSSQL\r\n'           : and MySQL.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' Lists Updated 8/11/2023 from doctrine/sql-formatter\r\nPrivate Const cstrReserved As String = _\r\n    \"|ACCESSIBLE|ACTION|AFTER|AGAINST|AGGREGATE|ALGORITHM|ALL|ALTER|ANALYSE|ANALYZE|AS|ASC|AUTOCOMMIT|AUTO_INCREMENT|BACKUP|BEGIN|BETWEEN|BINLOG|BOTH|CASCADE|CASE|CHANGE|CHANGED|CHARACTER SET|CHARSET|CHECK|CHECKSUM|COLLATE|COLLATION|COLUMN|COLUMNS|COMMENT|COMMIT|COMMITTED|COMPRESSED|CONCURRENT|CONSTRAINT|CONTAINS|CONVERT|CREATE|CROSS|CURRENT ROW|CURRENT_TIMESTAMP|DATABASE|DATABASES|DAY|DAY_HOUR|DAY_MINUTE|DAY_SECOND|DEFAULT|DEFINER|DELAYED|DELETE|DESC|DESCRIBE|DETERMINISTIC|DISTINCT|DISTINCTROW|DIV|DO|DUMPFILE|DUPLICATE|DYNAMIC|ELSE|ENCLOSED|END|ENGINE|ENGINE_TYPE|ENGINES|ESCAPE|ESCAPED|EVENTS|EXEC|EXECUTE|EXISTS|EXPLAIN|EXTENDED|FAST|FIELDS|FILE|FILTER|FIRST|FIXED|FLUSH|FOR|FORCE|FOLLOWING|FOREIGN|FULL|FULLTEXT|FUNCTION|GLOBAL|GRANT|GRANTS|GROUP|GROUPS|HEAP|HIGH_PRIORITY|HOSTS|HOUR|HOUR_MINUTE|HOUR_SECOND|IDENTIFIED|IF|IFNULL|IGNORE|IN|INDEX|INDEXES|INFILE|INSERT|INSERT_ID|INSERT_METHOD|INTERVAL|INTO|INVOKER|IS|ISOLATION|\" & _\r\n    \"KEY|KEYS|KILL|LAST_INSERT_ID|LEADING|LEVEL|LIKE|LINEAR|LINES|LOAD|LOCAL|LOCK|LOCKS|LOGS|LOW_PRIORITY|MARIA|MASTER|MASTER_CONNECT_RETRY|MASTER_HOST|MASTER_LOG_FILE|MATCH|MAX_CONNECTIONS_PER_HOUR|MAX_QUERIES_PER_HOUR|MAX_ROWS|MAX_UPDATES_PER_HOUR|MAX_USER_CONNECTIONS|MEDIUM|MERGE|MINUTE|MINUTE_SECOND|MIN_ROWS|MODE|MONTH|MRG_MYISAM|MYISAM|NAMES|NATURAL|NO OTHERS|NOT|NOW()|NULL|OFFSET|ON|OPEN|OPTIMIZE|OPTION|OPTIONALLY|ON UPDATE|ON DELETE|OUTFILE|OVER|PACK_KEYS|PAGE|PARTIAL|PARTITION|PARTITIONS|PASSWORD|PRECEDING|PRIMARY|PRIVILEGES|PROCEDURE|PROCESS|PROCESSLIST|PURGE|QUICK|RANGE|RAID0|RAID_CHUNKS|RAID_CHUNKSIZE|RAID_TYPE|READ|READ_ONLY|READ_WRITE|RECURSIVE|REFERENCES|REGEXP|RELOAD|RENAME|REPAIR|REPEATABLE|REPLACE|REPLICATION|RESET|RESTORE|RESTRICT|RETURN|RETURNS|REVOKE|RLIKE|ROLLBACK|ROW|ROWS|ROW_FORMAT|SECOND|SECURITY|SEPARATOR|SERIALIZABLE|SESSION|SHARE|SHOW|SHUTDOWN|SLAVE|SONAME|SOUNDS|SQL|SQL_AUTO_IS_NULL|SQL_BIG_RESULT|\" & _\r\n    \"SQL_BIG_SELECTS|SQL_BIG_TABLES|SQL_BUFFER_RESULT|SQL_CALC_FOUND_ROWS|SQL_LOG_BIN|SQL_LOG_OFF|SQL_LOG_UPDATE|SQL_LOW_PRIORITY_UPDATES|SQL_MAX_JOIN_SIZE|SQL_QUOTE_SHOW_CREATE|SQL_SAFE_UPDATES|SQL_SELECT_LIMIT|SQL_SLAVE_SKIP_COUNTER|SQL_SMALL_RESULT|SQL_WARNINGS|SQL_CACHE|SQL_NO_CACHE|START|STARTING|STATUS|STOP|STORAGE|STRAIGHT_JOIN|STRING|STRIPED|SUPER|TABLE|TABLES|TEMPORARY|TERMINATED|THEN|TIES|TO|TRAILING|TRANSACTIONAL|TRUE|TRUNCATE|TYPE|TYPES|UNBOUNDED|UNCOMMITTED|UNIQUE|UNLOCK|UNSIGNED|USAGE|USE|USING|VARIABLES|VIEW|WHEN|WITH|WORK|WRITE|YEAR_MONTH|\"\r\nPrivate Const cstrFunctions As String = _\r\n    \"|ABS|ACOS|ADDDATE|ADDTIME|AES_DECRYPT|AES_ENCRYPT|APPROX_COUNT_DISTINCT|AREA|ASBINARY|ASCII|ASIN|ASTEXT|ATAN|ATAN2|AVG|BDMPOLYFROMTEXT|BDMPOLYFROMWKB|BDPOLYFROMTEXT|BDPOLYFROMWKB|BENCHMARK|BIN|BIT_AND|BIT_COUNT|BIT_LENGTH|BIT_OR|BIT_XOR|BOUNDARY|BUFFER|CAST|CEIL|CEILING|CENTROID|CHAR|CHARACTER_LENGTH|CHARSET|CHAR_LENGTH|CHECKSUM_AGG|COALESCE|COERCIBILITY|COLLATION|COMPRESS|CONCAT|CONCAT_WS|CONNECTION_ID|CONTAINS|CONV|CONVERT|CONVERT_TZ|CONVEXHULL|COS|COT|COUNT|COUNT_BIG|CRC32|CROSSES|CUME_DIST|CURDATE|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURTIME|DATABASE|DATE|DATEDIFF|DATE_ADD|DATE_DIFF|DATE_FORMAT|DATE_SUB|DAY|DAYNAME|DAYOFMONTH|DAYOFWEEK|DAYOFYEAR|DECODE|DEFAULT|DEGREES|DENSE_RANK|DES_DECRYPT|DES_ENCRYPT|DIFFERENCE|DIMENSION|DISJOINT|DISTANCE|ELT|ENCODE|ENCRYPT|ENDPOINT|ENVELOPE|EQUALS|EXP|EXPORT_SET|EXTERIORRING|EXTRACT|EXTRACTVALUE|FIELD|FIND_IN_SET|FIRST_VALUE|FLOOR|FORMAT|FOUND_ROWS|FROM_DAYS|\" & _\r\n    \"FROM_UNIXTIME|GEOMCOLLFROMTEXT|GEOMCOLLFROMWKB|GEOMETRYCOLLECTION|GEOMETRYCOLLECTIONFROMTEXT|GEOMETRYCOLLECTIONFROMWKB|GEOMETRYFROMTEXT|GEOMETRYFROMWKB|GEOMETRYN|GEOMETRYTYPE|GEOMFROMTEXT|GEOMFROMWKB|GET_FORMAT|GET_LOCK|GLENGTH|GREATEST|GROUPING|GROUPING_ID|GROUP_CONCAT|GROUP_UNIQUE_USERS|HEX|HOUR|IF|IFNULL|INET_ATON|INET_NTOA|INSERT|INSTR|INTERIORRINGN|INTERSECTION|INTERSECTS|INTERVAL|ISCLOSED|ISEMPTY|ISNULL|ISRING|ISSIMPLE|IS_FREE_LOCK|IS_USED_LOCK|LAG|LAST_DAY|LAST_INSERT_ID|LAST_VALUE|LCASE|LEAD|LEAST|LEFT|LENGTH|LINEFROMTEXT|LINEFROMWKB|LINESTRING|LINESTRINGFROMTEXT|LINESTRINGFROMWKB|LISTAGG|LN|LOAD_FILE|LOCALTIME|LOCALTIMESTAMP|LOCATE|LOG|LOG10|LOG2|LOWER|LPAD|LTRIM|MAKEDATE|MAKETIME|MAKE_SET|MASTER_POS_WAIT|MAX|MBRCONTAINS|MBRDISJOINT|MBREQUAL|MBRINTERSECTS|MBROVERLAPS|MBRTOUCHES|MBRWITHIN|MD5|MICROSECOND|MID|MIN|MINUTE|MLINEFROMTEXT|MLINEFROMWKB|MOD|MONTH|MONTHNAME|MPOINTFROMTEXT|MPOINTFROMWKB|MPOLYFROMTEXT|\" & _\r\n    \"MPOLYFROMWKB|MULTILINESTRING|MULTILINESTRINGFROMTEXT|MULTILINESTRINGFROMWKB|MULTIPOINT|MULTIPOINTFROMTEXT|MULTIPOINTFROMWKB|MULTIPOLYGON|MULTIPOLYGONFROMTEXT|MULTIPOLYGONFROMWKB|NAME_CONST|NTH_VALUE|NTILE|NULLIF|NUMGEOMETRIES|NUMINTERIORRINGS|NUMPOINTS|OCT|OCTET_LENGTH|OLD_PASSWORD|ORD|OVERLAPS|PASSWORD|PERCENT_RANK|PERCENTILE_CONT|PERCENTILE_DISC|PERIOD_ADD|PERIOD_DIFF|PI|POINT|POINTFROMTEXT|POINTFROMWKB|POINTN|POINTONSURFACE|POLYFROMTEXT|POLYFROMWKB|POLYGON|POLYGONFROMTEXT|POLYGONFROMWKB|POSITION|POW|POWER|QUARTER|QUOTE|RADIANS|RAND|RANK|RELATED|RELEASE_LOCK|REPEAT|REPLACE|REVERSE|RIGHT|ROUND|ROW_COUNT|ROW_NUMBER|RPAD|RTRIM|SCHEMA|SECOND|SEC_TO_TIME|SESSION_USER|SHA|SHA1|SIGN|SIN|SLEEP|SOUNDEX|SPACE|SQRT|SRID|STARTPOINT|STD|STDEV|STDEVP|STDDEV|STDDEV_POP|STDDEV_SAMP|STRING_AGG|STRCMP|STR_TO_DATE|SUBDATE|SUBSTR|SUBSTRING|SUBSTRING_INDEX|SUBTIME|SUM|SYMDIFFERENCE|SYSDATE|SYSTEM_USER|TAN|TIME|TIMEDIFF|TIMESTAMP|TIMESTAMPADD|\" & _\r\n    \"TIMESTAMPDIFF|TIME_FORMAT|TIME_TO_SEC|TOUCHES|TO_DAYS|TRIM|TRUNCATE|UCASE|UNCOMPRESS|UNCOMPRESSED_LENGTH|UNHEX|UNIQUE_USERS|UNIX_TIMESTAMP|UPDATEXML|UPPER|USER|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|UUID|VAR|VARIANCE|VARP|VAR_POP|VAR_SAMP|VERSION|WEEK|WEEKDAY|WEEKOFYEAR|WITHIN|X|Y|YEAR|YEARWEEK|\"\r\nPrivate Const cstrReservedToplevel As String = \"|WITH|SELECT|FROM|WHERE|SET|ORDER BY|GROUP BY|LIMIT|DROP|VALUES|UPDATE|HAVING|ADD|CHANGE|MODIFY|ALTER TABLE|DELETE FROM|UNION ALL|UNION|EXCEPT|INTERSECT|PARTITION BY|ROWS|RANGE|GROUPS|WINDOW|\"\r\nPrivate Const cstrReservedNewline As String = \"|LEFT OUTER JOIN|RIGHT OUTER JOIN|LEFT JOIN|RIGHT JOIN|OUTER JOIN|INNER JOIN|JOIN|XOR|OR|AND|EXCLUDE|\"\r\nPrivate Const cstrBoundaries As String = \",;:)(.=<>+-*/!^%|&#\"\r\nPrivate Const cstrRegExSpecial As String = \".\\+*?[^]$(){}=!<>|:-#/\"\r\n\r\n' Reserved words in Microsoft Access SQL\r\n' https://support.microsoft.com/en-us/office/sql-reserved-words-b899948b-0e1c-4b56-9622-a03f8f07cfc8\r\nPrivate Const cstrReservedAccess As String = _\r\n    \"|ABSOLUTE|ADD|ADMINDB|ALL|Alphanumeric|ALTER|ALTER TABLE|And|ANY|ARE|AS|AS|ASC|ASSERTION|AUTHORIZATION|AUTOINCREMENT|Avg|BEGIN|Between|BINARY|BIT|BIT_LENGTH|BOOLEAN|BOTH|BY|BYTE|CASCADE|CATALOG|CHAR|CHARACTER|CHAR_LENGTH|CHARACTER_LENGTH|CHECK|CLOSE|CLUSTERED|COALESCE|COLLATE|COLLATION|COLUMN|COMMIT|COMP|COMPRESSION|CONNECT|CONNECTION|CONSTRAINT|CONSTRAINTS|CONTAINER|CONTAINS|CONVERT|Count|COUNTER|CREATE|CURRENCY|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DATE|DATETIME|DAY|DEC|DECIMAL|DECLARE|DELETE|DESC|DISALLOW|DISCONNECT|DISTINCT|DISTINCTROW|DOMAIN|DOUBLE|DROP|Eqv|EXCLUSIVECONNECT|EXEC|EXECUTE|EXISTS|EXTRACT|FETCH|FIRST|FLOAT|FLOAT8|FLOAT4|FOREIGN|FROM|GENERAL|GRANT|GROUP|GUID|HAVING|HOUR|IDENTITY|IEEEDOUBLE|IEEESINGLE|IGNORE|IMAGE|Imp|In|IN|INDEX|INDEXCREATEDB|INNER|INPUT|INSENSITIVE|INSERT|INSERT INTO|INT|INTEGER|INTEGER1|INTEGER2|INTEGER4|INTERVAL|INTO|Is|ISOLATION|JOIN|KEY|LANGUAGE|LAST|LEFT|\" & _\r\n    \"Level|Like|LOGICAL|LOGICAL1|LONG|LONGBINARY|LONGCHAR|LONGTEXT|LOWER|MATCH|Max|MEM|Min|MINUTE|Mod|MONEY|MONTH|NATIONAL|NCHAR|NONCLUSTERED|Not|NTEXT|NULL|NUMBER|NUMERIC|NVARCHAR|OCTET_LENGTH|OLEOBJECT|ON|OPEN|OPTION|Or|ORDER|Outer|OUTPUT|OWNERACCESS|PAD|PARAMETERS|PARTIAL|PASSWORD|PERCENT|PIVOT|POSITION|PRECISION|PREPARE|PRIMARY|PRIVILEGES|PROC|PROCEDURE|PUBLIC|REAL|REFERENCES|RESTRICT|REVOKE|RIGHT|ROLLBACK|SCHEMA|SECOND|SELECT|SELECTSCHEMA|SELECTSECURITY|SET|SHORT|SINGLE|SIZE|SMALLDATETIME|SMALLINT|SMALLMONEY|SOME|SPACE|SQL|SQLCODE|SQLERROR|SQLSTATE|StDev|StDevP|STRING|SUBSTRING|Sum|SYSNAME|SYSTEM_USER|TABLE|TableID|TEMPORARY|TEXT|TIME|TIMESTAMP|TIMEZONE_HOUR|TIMEZONE_MINUTE|TINYINT|TO|TOP|TRAILING|TRANSACTION|TRANSFORM|TRANSLATE|TRANSLATION|TRIM|UNION|UNIQUE|UNIQUEIDENTIFIER|UNKNOWN|UPDATE|UPDATEIDENTITY|UPDATEOWNER|UPDATESECURITY|UPPER|USAGE|USER|USING|VALUE|VALUES|Var|VARBINARY|VARCHAR|VarP|VARYING|VIEW|WHEN|WHENEVER|WHERE|WITH|WORK|Xor|YEAR|YESNO|ZONE|FALSE|TRUE|\"\r\n\r\n' SQL Dialects\r\nPublic Enum eSqlDialect\r\n    esdUnknown\r\n    esdAccess\r\n    esdMSSQL\r\n    esdMySQL\r\nEnd Enum\r\n\r\n' Types of lists\r\nPrivate Enum eListType\r\n    eltReserved\r\n    eltFunctions\r\n    eltReservedToplevel\r\n    eltReservedNewline\r\n    eltBoundaries\r\n    eltRegExSpecial\r\n    eltReservedAccess\r\nEnd Enum\r\n\r\n' Token types\r\nPrivate Enum eTokenTypes\r\n    ttUnknown\r\n    ttWhitespace\r\n    ttWord\r\n    ttQuote\r\n    ttBacktickQuote\r\n    ttReserved\r\n    ttReservedTopLevel\r\n    ttReservedNewline\r\n    ttBoundary\r\n    ttComment\r\n    ttBlockComment\r\n    ttNumber\r\n    ttError\r\n    ttVariable\r\nEnd Enum\r\n\r\nPrivate m_strSql As String\r\nPrivate m_colTokens As Collection\r\nPrivate m_lngPos As Long\r\nPrivate m_intDialect As eSqlDialect\r\nPrivate m_varWordCache(1 To 2) As Variant\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FormatSQL\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2020\r\n' Purpose   : This is the main function used outside the class for SQL formatting.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function FormatSQL(Optional strSql As String, Optional intDialect As eSqlDialect) As String\r\n\r\n    Dim lngIndentLevel As Long\r\n    Dim blnNewline As Boolean\r\n    Dim blnInlineParentheses As Boolean\r\n    Dim blnIncreaseSpecialIndent As Boolean\r\n    Dim blnIncreateBlockIndent As Boolean\r\n    Dim colIndents As Collection\r\n    Dim strIndent As String\r\n    Dim blnAddedNewline As Boolean\r\n    Dim intInlineCount As Integer\r\n    Dim blnInlineIndented As Boolean\r\n    Dim blnClauseLimit As Boolean\r\n    Dim lngToken As Long\r\n    Dim lngToken2 As Long\r\n    Dim strNextValue As String\r\n    Dim intNextType As eTokenTypes\r\n    Dim cReturn As clsConcat\r\n    Dim intTokenType As eTokenTypes\r\n    Dim strTokenValue As String\r\n    Dim lngLength As Long\r\n    Dim varItem As Variant\r\n    Dim strType As String\r\n\r\n    Perf.CategoryStart \"Format SQL\"\r\n    Perf.OperationStart \"Formating\"\r\n\r\n    ' Tokenize the string, if provided\r\n    If strSql <> vbNullString Then Tokenize strSql, intDialect\r\n\r\n    ' Set up collection to hold types of indents\r\n    Set colIndents = New Collection\r\n    Set cReturn = New clsConcat\r\n\r\n    ' Build formatted output from tokens\r\n    For lngToken = 1 To m_colTokens.Count\r\n\r\n        intTokenType = m_colTokens(lngToken)(0)\r\n        strTokenValue = m_colTokens(lngToken)(1)\r\n\r\n        ' Breakpoint for debugging\r\n        If strTokenValue = \";;;\" Then Stop\r\n\r\n        ' Only process non-whitespace tokens\r\n        If intTokenType = ttWhitespace Then GoTo NextToken\r\n\r\n        ' If we are increasing the special indent level\r\n        If blnIncreaseSpecialIndent Then\r\n            IncreaseIndent colIndents, lngIndentLevel, \"special\"\r\n            blnIncreaseSpecialIndent = False\r\n        End If\r\n\r\n        ' If we are increasing the block indent level\r\n        If blnIncreateBlockIndent Then\r\n            IncreaseIndent colIndents, lngIndentLevel, \"block\"\r\n            blnIncreateBlockIndent = False\r\n        End If\r\n\r\n        ' If we need a new line before the token\r\n        If blnNewline Then\r\n            cReturn.RTrim\r\n            cReturn.Add vbCrLf, StrRepeat(vbTab, lngIndentLevel)\r\n            blnNewline = False\r\n            blnAddedNewline = True\r\n        ElseIf cReturn.Length = 0 Then\r\n            ' Avoid adding a newline at beginning\r\n            blnAddedNewline = True\r\n        Else\r\n            blnAddedNewline = False\r\n        End If\r\n\r\n        ' Display comments directly where they appear in the source\r\n        If IsTokenType(intTokenType, ttComment, ttBlockComment) Then\r\n            If intTokenType = ttBlockComment Then\r\n                ' Indent multiline block comment to current indent level\r\n                strIndent = StrRepeat(vbTab, lngIndentLevel)\r\n                cReturn.RTrim \" \" & vbTab\r\n                cReturn.Add vbCrLf, strIndent\r\n                cReturn.Add Replace(strTokenValue, vbCrLf, vbCrLf & strIndent)\r\n            Else\r\n                cReturn.Add strTokenValue\r\n            End If\r\n            blnNewline = True\r\n            GoTo NextToken\r\n        End If\r\n\r\n        ' If inside parentheses\r\n        If blnInlineParentheses Then\r\n            ' Check for end of inline parentheses\r\n            If strTokenValue = \")\" Then\r\n                cReturn.RTrim\r\n\r\n                If blnInlineIndented Then\r\n                    ' Reset indent level\r\n                    Do While colIndents.Count > 0\r\n                        ' Get type of current indent\r\n                        strType = colIndents(1)\r\n                        ' Reduce the indents\r\n                        DecreaseIndent colIndents, lngIndentLevel\r\n                        ' Exit after removing a block indent\r\n                        If strType = \"block\" Then Exit Do\r\n                    Loop\r\n\r\n'                    DecreaseIndent colIndents, lngIndentLevel\r\n                    cReturn.Add vbCrLf, StrRepeat(vbTab, lngIndentLevel)\r\n                End If\r\n\r\n                blnInlineParentheses = False\r\n                cReturn.Add \") \"\r\n                GoTo NextToken\r\n            End If\r\n\r\n            ' Break to new line if we reach 30 characters\r\n            If strTokenValue = \",\" Then\r\n                If intInlineCount >= 30 Then\r\n                    intInlineCount = 0\r\n                    blnNewline = True\r\n                End If\r\n            End If\r\n\r\n            ' Keep count of characters within parentheses\r\n            intInlineCount = intInlineCount + Len(strTokenValue)\r\n        End If\r\n\r\n        ' Opening parentheses increase the block indent level and start a new line\r\n        If strTokenValue = \"(\" Then\r\n            ' First check if this should be an inline parentheses block\r\n            ' Examples are \"NOW()\", \"COUNT(*)\", \"int(10)\", key(`somecolumn`), DECIMAL(7,2)\r\n            ' Allow up to 3 non-whitespace tokens inside inline parentheses\r\n            lngLength = 0\r\n            ' Begin secondary loop to look at the next tokens without changing\r\n            ' primary token iteration.\r\n            lngToken2 = lngToken\r\n            Do\r\n                ' Get next non-whitespace token\r\n                lngToken2 = GetNextTokenID(lngToken2, ttWhitespace)\r\n\r\n                If lngToken2 > m_colTokens.Count _\r\n                    Or lngToken2 > (lngToken + 250) Then\r\n                    ' Reached end of string\r\n                    Exit Do\r\n                End If\r\n\r\n                ' Get type and value of next token\r\n                intNextType = m_colTokens(lngToken2)(0)\r\n                strNextValue = m_colTokens(lngToken2)(1)\r\n\r\n                ' Reached closing parentheses, able to inline it\r\n                If strNextValue = \")\" Then\r\n                    blnInlineParentheses = True\r\n                    intInlineCount = 0\r\n                    blnInlineIndented = False\r\n                    Exit Do\r\n                End If\r\n\r\n                ' Reached an invalid token for inline parentheses\r\n                If strNextValue = \";\" Or strNextValue = \"(\" Then\r\n                    Exit Do\r\n                End If\r\n\r\n                ' Reached an invalid token type for inline parentheses\r\n                Select Case intTokenType\r\n                    Case ttReservedTopLevel, _\r\n                        ttReservedNewline, _\r\n                        ttComment, _\r\n                        ttBlockComment\r\n                        Exit Do\r\n                End Select\r\n\r\n                ' Add to total length\r\n                lngLength = lngLength + (Len(strNextValue))\r\n\r\n            ' Look at next token ahead of current position\r\n            Loop\r\n\r\n            ' Break long inline parentheses into multiple lines\r\n            If blnInlineParentheses And (lngLength > 30) Then\r\n                blnIncreateBlockIndent = True\r\n                blnInlineIndented = True\r\n                blnNewline = True\r\n            End If\r\n\r\n            ' Take out the preceding space unless there was whitespace there in the original query\r\n            If m_colTokens.Count > 0 Then\r\n                If Not m_colTokens(lngToken - 1)(0) = ttWhitespace Then\r\n                    cReturn.RTrim\r\n                End If\r\n            End If\r\n\r\n            ' If this is not an inline parenthetical expression, indent to new line\r\n            If Not blnInlineParentheses Then\r\n                IncreaseIndent colIndents, lngIndentLevel, \"parentheses\"\r\n                ' Add a newline after the opening parentheses\r\n                blnNewline = True\r\n            End If\r\n\r\n        ElseIf strTokenValue = \")\" Then\r\n            ' Closing parentheses decrease the parentheses indent level\r\n            ' Remove whitespace before the closing parentheses\r\n            cReturn.RTrim\r\n\r\n            ' Reset indent level back to the opening parenthese\r\n            Do While colIndents.Count > 0\r\n                ' Get type of current indent\r\n                strType = colIndents(1)\r\n                ' Reduce the indents\r\n                DecreaseIndent colIndents, lngIndentLevel\r\n                ' Exit after removing a parentheses indent\r\n                If strType = \"parentheses\" Then Exit Do\r\n            Loop\r\n\r\n            ' Add a newline before the closing parentheses (if not already added)\r\n            If Not blnAddedNewline Then\r\n                cReturn.RTrim vbTab\r\n                cReturn.Add vbCrLf, StrRepeat(vbTab, lngIndentLevel)\r\n            End If\r\n\r\n        ElseIf intTokenType = ttReservedTopLevel Then\r\n            ' Top level reserved words start a new line and increase the special indent level\r\n            blnIncreaseSpecialIndent = True\r\n\r\n            ' If the last indent type was 'special', decrease the special indent for this round\r\n            If colIndents.Count > 0 Then\r\n                If colIndents(1) = \"special\" Then\r\n                    DecreaseIndent colIndents, lngIndentLevel\r\n                End If\r\n            End If\r\n\r\n            ' Add a newline after the top level reserved word\r\n            blnNewline = True\r\n            ' Add a newline before the top level reserved word (if not already added)\r\n            If Not blnAddedNewline Then\r\n                cReturn.RTrim\r\n                cReturn.Add vbCrLf, StrRepeat(vbTab, lngIndentLevel)\r\n            Else\r\n                ' If we already added a newline, redo the indentation since it may be different now\r\n                cReturn.RTrim vbTab\r\n                cReturn.Add StrRepeat(vbTab, lngIndentLevel)\r\n            End If\r\n\r\n            ' Purge any extra whitespace\r\n            PurgeExtraWhitespace strTokenValue\r\n\r\n            ' If SQL 'LIMIT' clause, start variable to reset newline\r\n            If (UCase(strTokenValue) = \"LIMIT\") And Not blnInlineParentheses Then\r\n                blnClauseLimit = True\r\n            End If\r\n\r\n        ElseIf blnClauseLimit And (strTokenValue <> \",\") _\r\n            And (Not IsTokenType(intTokenType, ttNumber, ttWhitespace)) Then\r\n            ' Checks if we are out of the limit clause\r\n            blnClauseLimit = False\r\n\r\n        ElseIf (strTokenValue = \",\") And Not blnInlineParentheses Then\r\n            ' Commas start a new line (unless within inline parentheses or SQL 'LIMIT' clause)\r\n            ' If the previous TOKEN_VALUE is 'LIMIT', resets new line\r\n            If blnClauseLimit Then\r\n                blnNewline = False\r\n                blnClauseLimit = False\r\n            Else\r\n                ' All other cases of commas\r\n                blnNewline = True\r\n            End If\r\n\r\n        ElseIf intTokenType = ttReservedNewline Then\r\n            ' Newline reserved words start a new line\r\n            ' Add a newline before the reserved word (if not already added)\r\n            If Not blnAddedNewline Then\r\n                cReturn.RTrim\r\n                cReturn.Add vbCrLf, StrRepeat(vbTab, lngIndentLevel)\r\n            End If\r\n            PurgeExtraWhitespace strTokenValue\r\n\r\n        ElseIf intTokenType = ttBoundary Then\r\n            ' Multiple boundary characters in a row should not have\r\n            ' spaces between them (not including parentheses)\r\n            lngToken2 = GetPreviousTokenID(lngToken, ttWhitespace)\r\n            If m_colTokens(lngToken2)(0) = ttBoundary Then\r\n                ' If previous non-whitespace character was a boundary, then trim\r\n                ' any whitespace going back to the boundary.\r\n                cReturn.RTrim\r\n            End If\r\n        End If\r\n\r\n        ' If the token shouldn't have a space before it\r\n        If strTokenValue = \".\" _\r\n            Or strTokenValue = \",\" _\r\n            Or strTokenValue = \";\" _\r\n            Or (strTokenValue = \"!\" And m_intDialect = esdAccess) _\r\n            Or (strTokenValue = \":\" And m_intDialect = esdAccess) Then\r\n            ' Trim any whitespace\r\n            cReturn.RTrim\r\n        End If\r\n\r\n        ' Add current token value, followed by a space\r\n        cReturn.Add strTokenValue, \" \"\r\n\r\n        ' If the token shouldn't have a space after it\r\n        If strTokenValue = \"(\" _\r\n            Or strTokenValue = \".\" _\r\n            Or (strTokenValue = \"!\" And m_intDialect = esdAccess) _\r\n            Or (strTokenValue = \":\" And m_intDialect = esdAccess) Then\r\n            cReturn.RTrim\r\n        End If\r\n\r\n        ' If this is the \"-\" of a negative number, it shouldn't have a space after it\r\n        If strTokenValue <> \"-\" Then\r\n            GoTo NextToken\r\n        End If\r\n\r\n        ' Check next token for number\r\n        lngToken2 = GetNextTokenID(lngToken, ttWhitespace)\r\n        If lngToken2 = 0 Then GoTo NextToken\r\n        If m_colTokens(lngToken2)(0) <> ttNumber Then\r\n            GoTo NextToken\r\n        End If\r\n\r\n        ' Check previous token\r\n        lngToken2 = GetPreviousTokenID(lngToken, ttWhitespace)\r\n        If lngToken2 = 0 Then\r\n            GoTo NextToken\r\n        Else\r\n            intTokenType = m_colTokens(lngToken2)(0)\r\n            If IsTokenType(intTokenType, _\r\n                ttQuote, _\r\n                ttBacktickQuote, _\r\n                ttWord, _\r\n                ttNumber) Then\r\n                GoTo NextToken\r\n            End If\r\n        End If\r\n\r\n        ' Trim whitespace after dash\r\n        cReturn.RTrim\r\n\r\nNextToken:\r\n    Next lngToken\r\n\r\n    ' If there are unmatched parentheses\r\n    For Each varItem In colIndents\r\n        If CStr(varItem) = \"block\" Then\r\n            cReturn.RTrim\r\n            cReturn.Add vbCrLf, \"WARNING: unclosed parentheses or section\"\r\n            Exit For\r\n        End If\r\n    Next varItem\r\n\r\n    ' Trim any trailing spaces from completed string\r\n    cReturn.RTrim\r\n\r\n    ' Final formatting of tab indents when returning formatted string\r\n    FormatSQL = Replace(cReturn.GetStr, vbTab, \"  \")\r\n\r\n    Perf.OperationEnd\r\n    Perf.CategoryEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Tokenize\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Take a SQL string and break it down into tokens.\r\n'           : Each token is an array with type(0) and value(1).\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Tokenize(strSql As String, intDialect As eSqlDialect)\r\n\r\n    Const cstrBreakAfter As String = \"LIMIT\" & \";;;;\"\r\n\r\n    Dim strMatch As String\r\n    Dim lngLastPos As Long\r\n\r\n    ' Reset collection of token items\r\n    Set m_colTokens = New Collection\r\n    m_strSql = strSql\r\n    m_lngPos = 1\r\n    m_varWordCache(1) = Empty\r\n    m_varWordCache(2) = Empty\r\n\r\n    ' Set SQL dialect\r\n    m_intDialect = intDialect\r\n\r\n    Perf.CategoryStart \"Tokenize SQL\"\r\n\r\n    ' Loop through SQL, converting string into tokens\r\n    Do While m_lngPos < Len(strSql)\r\n\r\n        ' Debugging breakpoint\r\n        If NextChar(cstrBreakAfter) Then Stop\r\n\r\n        ' Sequentially move through various possible types of tokens,\r\n        ' adding the token when we find a match.\r\n\r\n        ' Whitespace\r\n        If MatchWhitespace(strMatch) Then\r\n            AddToken ttWhitespace, strMatch\r\n\r\n        ' Date literal (Microsoft Access SQL)\r\n        ElseIf MatchAccessDateLiteral(strMatch) Then\r\n            AddToken ttQuote, strMatch\r\n\r\n        ' Single line comment\r\n        ElseIf NextChar(\"#\") Or NextChar(\"--\") Then\r\n            AddToken ttComment, GetTill(vbCrLf, vbLf)\r\n\r\n        ' Block comment\r\n        ElseIf NextChar(\"/*\") Then\r\n            AddToken ttBlockComment, GetTill(\"*/\")\r\n\r\n        ' Backtick quote\r\n        ElseIf NextChar(\"`\") Then\r\n            AddToken ttBacktickQuote, GetQuotedString\r\n\r\n        ' Quoted string\r\n        ElseIf NextChar(\"\"\"\") Or NextChar(\"'\") Or NextChar(\"[\") Then\r\n            AddToken ttQuote, GetQuotedString\r\n\r\n        ' User defined variable\r\n        ElseIf (NextChar(\"@\") Or NextChar(\":\")) _\r\n            And (RemainingChars > 1) And (m_intDialect <> esdAccess) Then\r\n\r\n            ' Check for quoted variable name\r\n            If PeekChar(1, \"\"\"\") Or PeekChar(1, \"`\") Or PeekChar(1, \"'\") Then\r\n                AddToken ttVariable, GetRange & GetQuotedString(1)\r\n            Else\r\n                ' Non-quoted variable name\r\n                If HasMatches(\"^(\" & GetRange & \"[a-zA-Z0-9\\._\\$]+)\", strMatch) Then\r\n                    AddToken ttVariable, strMatch\r\n                Else\r\n                    ' Failed to parse name\r\n                    'TODO: log error\r\n                End If\r\n            End If\r\n\r\n        ' Number (decimal, bindary, or hex)\r\n        ElseIf MatchNumber(strMatch) Then\r\n            AddToken ttNumber, strMatch\r\n\r\n        ' Boundary character (punctuation and symbols)\r\n        ElseIf NextBoundary = m_lngPos Then\r\n            AddToken ttBoundary, GetRange\r\n\r\n        ' A reserved word cannot be preceded by a \".\"\r\n        ' This makes it so in \"mytable.from\", \"from\" is not considered a reserved word\r\n        ElseIf PeekChar(-1, \".\") _\r\n            Or (PeekChar(-1, \"!\") And m_intDialect = esdAccess) _\r\n            Or (PeekChar(-1, \":\") And m_intDialect = esdAccess) Then\r\n\r\n            ' Likely an object name\r\n            If HasMatches(\"^(.*?)($|\\s|[\"\"\\'`]|\" & RegExBoundaries & \")\", strMatch) Then\r\n                AddToken ttWord, strMatch\r\n            Else\r\n                Stop\r\n            End If\r\n\r\n        ' Check for reserved words or functions\r\n        Else\r\n\r\n            ' Top level reserved word\r\n            If InList(eltReservedToplevel, strMatch) Then\r\n                AddToken ttReservedTopLevel, strMatch\r\n\r\n            ' Newline reserved word\r\n            ElseIf InList(eltReservedNewline, strMatch) Then\r\n                AddToken ttReservedNewline, strMatch\r\n\r\n            ' Other reserved word\r\n            ElseIf InList(eltReserved, strMatch) Then\r\n                AddToken ttReserved, strMatch\r\n\r\n            ' A function must be followed by \"(\"\r\n            ' This makes it so \"count(\" is considered a function, but \"count\" alone is not\r\n            ElseIf HasMatches(\"^((\" & cstrFunctions & \")[(]|\\s|[)])\", strMatch) Then\r\n                ' Add the function, but not the opening parenthesis\r\n                AddToken ttReserved, GetRange(, Len(strMatch) - 1)\r\n\r\n            ' Non-reserved word\r\n            ElseIf HasMatches(\"^(.*?)($|\\s|[\"\"\\'`]|\" & RegExBoundaries & \")\", strMatch) Then\r\n                AddToken ttWord, strMatch\r\n\r\n            Else\r\n                ' TODO: check for errors\r\n                Stop\r\n\r\n            End If\r\n\r\n        End If\r\n\r\n        ' Make sure we don't get stuck on the same position\r\n        If m_lngPos = lngLastPos Then\r\n            Log.Error eelError, \"Unable tp parse SQL after position \" & m_lngPos, ModuleName(Me) & \".Tokenize\"\r\n            AddToken ttUnknown, GetRange(Len(m_strSql) - m_lngPos)\r\n            Exit Do\r\n        Else\r\n            lngLastPos = m_lngPos\r\n        End If\r\n\r\n    ' Move to next token\r\n    Loop\r\n\r\n    ' Reset position\r\n    m_lngPos = 1\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetMatches\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Returns true if we match on the RegEx expression, and sets objMatches to\r\n'           : the resulting match.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function HasMatches(strRegEx As String, ByRef strMatches As String) As Boolean\r\n\r\n    Static oReg As VBScript_RegExp_55.RegExp\r\n\r\n    If oReg Is Nothing Then Set oReg = New VBScript_RegExp_55.RegExp\r\n\r\n    Dim objMatches As VBScript_RegExp_55.MatchCollection\r\n\r\n    Perf.OperationStart \"RegEx\"\r\n    With oReg ' New VBScript_RegExp_55.RegExp\r\n        .Pattern = strRegEx\r\n        .IgnoreCase = True\r\n        .Global = False\r\n        Set objMatches = .Execute(Mid$(m_strSql, m_lngPos))\r\n        If objMatches.Count = 0 Then\r\n            strMatches = vbNullString\r\n        Else\r\n            If objMatches(0).SubMatches.Count > 0 Then\r\n                ' Use first submatch, if found\r\n                If Len(objMatches(0).SubMatches(0)) Then\r\n                    strMatches = objMatches(0).SubMatches(0)\r\n                Else\r\n                    ' Use primary match\r\n                    strMatches = objMatches(0)\r\n                End If\r\n            Else\r\n                ' Fall back to primary match\r\n                strMatches = objMatches(0)\r\n            End If\r\n            HasMatches = True\r\n        End If\r\n    End With\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddToken\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Add a token to the current collection and moves the position marker\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub AddToken(intType As eTokenTypes, strValue As String)\r\n    Perf.OperationStart \"Add Token\"\r\n    m_colTokens.Add Array(intType, strValue)\r\n    m_lngPos = m_lngPos + Len(strValue)\r\n    Perf.OperationEnd\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : NextChar\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Return next one or more characters from current position.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function NextChar(strMatch As String) As Boolean\r\n    If (m_lngPos + 1) > Len(m_strSql) Then Exit Function\r\n    NextChar = (Mid$(m_strSql, m_lngPos, Len(strMatch)) = strMatch)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PeekChar\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Peek ahead at another character other than the next one.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function PeekChar(lngOffset As Long, strMatchChar As String) As Boolean\r\n    If (m_lngPos + lngOffset) < 1 Then Exit Function\r\n    If (m_lngPos + lngOffset) > Len(m_strSql) Then Exit Function\r\n    Perf.OperationStart \"Peek Character\"\r\n    PeekChar = (Mid$(m_strSql, m_lngPos + lngOffset, 1) = strMatchChar)\r\n    Perf.OperationEnd\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTill\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Return the string until we find the stop characters, or the remaining\r\n'           : string if the stop characters were not found.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetTill(strStopAt As String, Optional strAltStopAt As String, Optional blnIncludeStopChars As Boolean = False) As String\r\n\r\n    Dim lngPos As Long\r\n    Dim lngLen As Long\r\n    Dim strStop As String\r\n\r\n    Perf.OperationStart \"GetTill\"\r\n\r\n    ' See if the searched string exists\r\n    lngPos = InStr(m_lngPos + 1, m_strSql, strStopAt)\r\n\r\n    ' Try again using alternate stop\r\n    If lngPos = 0 Then\r\n        lngPos = InStr(m_lngPos, m_strSql, strAltStopAt)\r\n        strStop = strAltStopAt\r\n    Else\r\n        strStop = strStopAt\r\n    End If\r\n\r\n    ' Make sure we found a match\r\n    If lngPos = 0 Then\r\n        ' No match. Return the remaining string\r\n        GetTill = Mid$(m_strSql, m_lngPos)\r\n    Else\r\n        lngLen = lngPos - m_lngPos\r\n        ' Add in the length of the matched stop characters, if requested\r\n        If blnIncludeStopChars Then lngLen = lngLen + Len(strStop)\r\n        ' Return the string\r\n        GetTill = Mid$(m_strSql, m_lngPos, lngLen)\r\n    End If\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetRange\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Get a range of characters (kind of like a relative Mid$() function)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetRange(Optional lngStartOffset As Long, Optional lngLength As Long = 1) As String\r\n    GetRange = Mid$(m_strSql, m_lngPos + lngStartOffset, lngLength)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetQuotedString\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Return a quoted string (dialect-specific)\r\n'           : https://stackoverflow.com/q/10573922/4121863\r\n'           : https://stackoverflow.com/q/9719869/4121863\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetQuotedString(Optional lngStartOffset As Long = 0) As String\r\n\r\n    Dim strExp As String\r\n    Dim objMatches As VBScript_RegExp_55.MatchCollection\r\n\r\n    Perf.OperationStart \"Get Quoted String\"\r\n    With New clsConcat\r\n        ' Build out RegEx expression\r\n        .Add \"^(\"\r\n\r\n            ' Accomodate dialect-specific variants\r\n            Select Case m_intDialect\r\n\r\n                Case esdMySQL\r\n                    ' Backtick quoted string using `` to escape\r\n                    .Add \"((`[^`]*($|`))+)|\"\r\n\r\n                    ' Double quoted string using \"\" or \\\" to escape\r\n                    .Add \"((\"\"[^\"\"\\\\\\\\]*(?:\\\\\\\\.[^\"\"\\\\\\\\]*)*(\"\"|$))+)|\"\r\n\r\n                    ' Single quoted string using '' or \\' to escape\r\n                    .Add \"((\\'[^\\'\\\\\\\\]*(?:\\\\\\\\.[^\\'\\\\\\\\]*)*(\\'|$))+)\"\r\n\r\n                Case Else\r\n                    ' Square bracket quoted string (SQL Server) using ]] to escape\r\n                    .Add \"((\\[[^\\]]*($|\\]))(\\][^\\]]*($|\\]))*)|\"\r\n\r\n                    ' Double quoted string using \"\" to escape\r\n                    .Add \"((\"\"[^\"\"]*(\"\"|$))+)|\"\r\n\r\n                    ' Single quoted string using '' to escape\r\n                    .Add \"((\\'[^\\']*(\\'|$))+)\"\r\n\r\n            End Select\r\n        .Add \")\"\r\n        strExp = .GetStr\r\n    End With\r\n\r\n    ' Apply RegEx\r\n    With New VBScript_RegExp_55.RegExp\r\n        .Global = True\r\n        .Multiline = True\r\n        .Pattern = strExp\r\n        Set objMatches = .Execute(Mid$(m_strSql, m_lngPos + lngStartOffset))\r\n        If objMatches.Count > 0 Then GetQuotedString = objMatches(0)\r\n    End With\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemainingChars\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Return remaining number of characters from current position\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RemainingChars() As Long\r\n    RemainingChars = Len(m_strSql) - m_lngPos\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StrRepeat\r\n' Author    : Adam Waller\r\n' Date      : 8/14/2023\r\n' Purpose   : Repeat a string a specified number of times. (High performance version)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function StrRepeat(strText As String, lngTimes As Long) As String\r\n\r\n    Dim lngCnt As Long\r\n    Dim lngStart As Long\r\n    Dim strReturn As String\r\n\r\n    ' Build full length string to avoid slow string contatenation\r\n    strReturn = Space(Len(strText) * lngTimes)\r\n\r\n    ' Apply repeated text to return string\r\n    For lngCnt = 1 To lngTimes\r\n        ' Calculate start position\r\n        lngStart = 1 + ((lngCnt * Len(strText)) - Len(strText))\r\n        Mid$(strReturn, lngStart, Len(strText)) = strText\r\n    Next lngCnt\r\n\r\n    ' Return repeated string\r\n    StrRepeat = strReturn\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : InList\r\n' Author    : Adam Waller\r\n' Date      : 8/11/2023\r\n' Purpose   : Returns true if the next word or two are found in the list\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function InList(intList As eListType, ByRef strMatch As String) As Boolean\r\n\r\n    Dim intWords As Integer\r\n    Dim intMaxWords As Integer\r\n    Dim strList As String\r\n    Dim strTest As String\r\n    Dim strWords As String\r\n    Dim lngEndPos As Long\r\n    Dim blnFound As Boolean\r\n\r\n    ' Look up list of words\r\n    Select Case intList\r\n        Case eltBoundaries:         strList = cstrBoundaries\r\n        Case eltRegExSpecial:       strList = cstrRegExSpecial\r\n        Case eltFunctions:          strList = cstrFunctions\r\n        Case eltReserved:           strList = cstrReserved\r\n        Case eltReservedNewline:    strList = cstrReservedNewline\r\n        Case eltReservedToplevel:   strList = cstrReservedToplevel\r\n        Case eltReservedAccess:     strList = cstrReservedAccess\r\n    End Select\r\n\r\n    ' Determine max words to look up (i.e. \"LEFT JOIN\")\r\n    If InStr(1, strList, \" \") > 0 Then\r\n        intMaxWords = 2\r\n    Else\r\n        intMaxWords = 1\r\n    End If\r\n\r\n    Perf.OperationStart \"InList\"\r\n    ' Loop to check multiple or single words\r\n    For intWords = intMaxWords To 1 Step -1\r\n\r\n        ' Build test string of word(s)\r\n        strWords = GetNextWords(intWords, lngEndPos)\r\n        strTest = \"|\" & UCase(strWords) & \"|\"\r\n\r\n        ' Make sure we found some words\r\n        If strTest = \"||\" Then Exit For\r\n\r\n        ' See if the words exist in the list\r\n        If InStr(1, strList, strTest) > 0 Then\r\n\r\n            ' If we are formatting a Microsoft Access query, check against Acccess reserved words.\r\n            If m_intDialect = esdAccess And intList <> eltReservedAccess Then\r\n                blnFound = InList(eltReservedAccess, strMatch)\r\n            Else\r\n                ' Use full list for other dialects\r\n                blnFound = True\r\n            End If\r\n\r\n            ' If we found a match, return original string (including any extra whitespace)\r\n            If blnFound Then\r\n                strMatch = Mid$(m_strSql, m_lngPos, lngEndPos - m_lngPos)\r\n                InList = True\r\n                Exit For\r\n            End If\r\n        End If\r\n\r\n    Next intWords\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetNextWords\r\n' Author    : Adam Waller\r\n' Date      : 8/17/2023\r\n' Purpose   : Return the next x words before the next boundary character. (Replacing\r\n'           : any number of whitespace characters with a single space.)\r\n'           : Also returns the ending position in the original string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetNextWords(intCount As Integer, Optional ByRef lngEndPos As Long) As String\r\n\r\n    Dim lngChar As Integer\r\n    Dim lngStart As Long\r\n    Dim intFoundWords As Integer\r\n    Dim lngBoundary As Long\r\n\r\n    ' Cache word lookup so we can avoid repeated calls when checking the same word(s)\r\n    ' against different lists. (For 1 or 2 words)\r\n    If intCount = 1 Or intCount = 2 Then\r\n        If IsArray(m_varWordCache(intCount)) Then\r\n            If m_varWordCache(intCount)(0) = m_lngPos Then\r\n                ' Found matching cache from last lookup\r\n                lngEndPos = m_varWordCache(intCount)(1)\r\n                GetNextWords = m_varWordCache(intCount)(2)\r\n                Exit Function\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' Get next boundary, and exit if we don't have anything before the boundary\r\n    lngBoundary = NextBoundary\r\n    If lngBoundary = m_lngPos Then Exit Function\r\n\r\n    Perf.OperationStart \"Get Next Words\"\r\n    With New clsConcat\r\n        ' Loop through characters up till next boundary\r\n        For lngChar = m_lngPos To lngBoundary - 1\r\n            Select Case AscW(Mid$(m_strSql, lngChar, 1))\r\n                Case 32, 9, 10, 13, 0\r\n                    ' Found whitespace\r\n                    If lngStart > 0 Then\r\n                        ' Complete any previous word\r\n                        If intFoundWords > 0 Then .Add \" \"\r\n                        intFoundWords = intFoundWords + 1\r\n                        .Add Mid$(m_strSql, lngStart, lngChar - lngStart)\r\n                        lngEndPos = lngChar\r\n                        lngStart = 0\r\n                        If intFoundWords >= intCount Then Exit For\r\n                    End If\r\n\r\n                Case Else\r\n                    ' Begin at first non-whitespace character\r\n                    If lngStart = 0 Then lngStart = lngChar\r\n                    ' Exit if we reached the boundary\r\n                    If lngChar = lngBoundary - 1 Then\r\n                        If intFoundWords > 0 Then .Add \" \"\r\n                        .Add Mid$(m_strSql, lngStart, (lngChar - lngStart) + 1)\r\n                        lngEndPos = lngChar + 1\r\n                        Exit For\r\n                    End If\r\n\r\n            End Select\r\n        Next lngChar\r\n\r\n        If lngEndPos = 0 Then Stop\r\n\r\n        ' Prepare results\r\n        GetNextWords = .GetStr\r\n\r\n        ' Save to cache for 1 and 2 word lookups\r\n        If intCount = 1 Or intCount = 2 Then\r\n            m_varWordCache(intCount) = Array(m_lngPos, lngEndPos, GetNextWords)\r\n        End If\r\n    End With\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : NextBoundary\r\n' Author    : Adam Waller\r\n' Date      : 8/17/2023\r\n' Purpose   : Return the position of the next boundary character\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function NextBoundary() As Long\r\n\r\n    Dim lngChar As Long\r\n    Dim lngLowest As Long\r\n\r\n    Perf.OperationStart \"Next Boundary\"\r\n    ' Begin with maximum length, and reduce based on found characters\r\n    lngLowest = Len(m_strSql)\r\n\r\n    ' Loop through next characters\r\n    For lngChar = m_lngPos To Len(m_strSql)\r\n\r\n        ' Use AscW for fastest performance\r\n        Select Case AscW(Mid$(m_strSql, lngChar, 1))\r\n\r\n            ' Boundary characters (Codes extracted from cstrBoundaries)\r\n            Case 44, 59, 58, 41, 40, 46, 61, 60, 62, 43, 45, 42, 47, 33, 94, 37, 124, 38, 35\r\n                NextBoundary = lngChar\r\n                Exit For\r\n\r\n            Case Else\r\n                ' Continue to next character\r\n\r\n        End Select\r\n    Next lngChar\r\n\r\n    ' Return position of first boundary, or length of string if no boundary was found.\r\n    If NextBoundary = 0 Then NextBoundary = Len(m_strSql)\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MatchWhitespace\r\n' Author    : Adam Waller\r\n' Date      : 8/18/2023\r\n' Purpose   : Get any contiguous whitespace from the current position.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function MatchWhitespace(ByRef strMatches As String) As Boolean\r\n\r\n    Dim lngChar As Long\r\n    Dim lngFound As Long\r\n\r\n    Perf.OperationStart \"Get Whitespace\"\r\n\r\n    ' Loop through next characters\r\n    For lngChar = m_lngPos To Len(m_strSql)\r\n\r\n        ' Use AscW for fastest performance\r\n        Select Case AscW(Mid$(m_strSql, lngChar, 1))\r\n\r\n            ' Any whitespace character (space, tab, lf, cr, nullchar)\r\n            Case 32, 9, 10, 13, 0\r\n                lngFound = lngChar\r\n\r\n            ' Non-whitespace character\r\n            Case Else\r\n                Exit For\r\n\r\n        End Select\r\n    Next lngChar\r\n\r\n    ' Set match string if we found whitespace\r\n    If lngFound > 0 Then\r\n        strMatches = Mid$(m_strSql, m_lngPos, (lngFound + 1) - m_lngPos)\r\n        MatchWhitespace = True\r\n    End If\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MatchAccessDateLiteral\r\n' Author    : Adam Waller\r\n' Date      : 9/21/2023\r\n' Purpose   : Returns a matching date literal for Microsoft Access. (I.e. #1/1/2000#)\r\n'           : Checks for a closing hash, and a date value inside.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function MatchAccessDateLiteral(ByRef strMatches As String) As Boolean\r\n\r\n    Dim lngPos As Long\r\n    Dim lngLen As Long\r\n\r\n    ' See if we start with a hash character...\r\n    If AscW(Mid$(m_strSql, m_lngPos, 1)) = 35 Then\r\n\r\n        ' Check for closing hash character\r\n        lngPos = InStr(m_lngPos + 1, m_strSql, \"#\")\r\n        If lngPos > m_lngPos Then\r\n            ' We found another hash somewhere down the line.\r\n            ' See if the distance looks reasonable.\r\n            lngLen = lngPos - m_lngPos\r\n            If lngLen < 50 Then\r\n                ' See if this is a date value\r\n                If IsDate(Mid$(m_strSql, m_lngPos + 1, lngLen - 1)) Then\r\n                    ' Appears to be a valid date. Let's go with it.\r\n                    MatchAccessDateLiteral = True\r\n                    strMatches = Mid$(m_strSql, m_lngPos, lngLen + 1)\r\n                End If\r\n            End If\r\n        End If\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MatchNumber\r\n' Author    : Adam Waller\r\n' Date      : 8/18/2023\r\n' Purpose   : Return a number token from the current position, if found.\r\n'           : Checks for a leading digit before the slower RegEx for the various\r\n'           : types of numbers.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function MatchNumber(ByRef strMatches As String) As Boolean\r\n\r\n    ' Perform an inital very fast test to see if this might be a number.\r\n    Select Case AscW(Mid$(m_strSql, m_lngPos, 1))\r\n        ' Any number 0 to 9\r\n        Case 48 To 57\r\n            ' Continue with RegEx\r\n        Case Else\r\n            ' Not a number\r\n            Exit Function\r\n    End Select\r\n\r\n    ' Now move on with the RegEx to get the entire number\r\n    MatchNumber = HasMatches(\"^([0-9]+(\\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)($|\\s|\"\"\\'`|\" & RegExBoundaries & \")\", strMatches)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsTokenType\r\n' Author    : Adam Waller\r\n' Date      : 8/15/2023\r\n' Purpose   : Returns true if the token type matches any of the specified types.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IsTokenType(intToken As eTokenTypes, ParamArray intMatchTypes() As Variant) As Boolean\r\n    Dim intMatch As Integer\r\n    For intMatch = 0 To UBound(intMatchTypes)\r\n        If intToken = intMatchTypes(intMatch) Then\r\n            IsTokenType = True\r\n            Exit For\r\n        End If\r\n    Next intMatch\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IncreaseIndent\r\n' Author    : Adam Waller\r\n' Date      : 8/15/2023\r\n' Purpose   : Increase the indent level\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub IncreaseIndent(ByRef colIndents As Collection, ByRef lngIndentLevel As Long, strIndentType As String)\r\n    If colIndents Is Nothing Then Exit Sub\r\n    If colIndents.Count = 0 Then\r\n        colIndents.Add strIndentType\r\n    Else\r\n        colIndents.Add strIndentType, , 1\r\n    End If\r\n    lngIndentLevel = lngIndentLevel + 1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DecreaseIndent\r\n' Author    : Adam Waller\r\n' Date      : 8/15/2023\r\n' Purpose   : Remove the first element of the indents collection, shifting the remaining\r\n'           : elements down. (Similar to the PHP array_shift function)\r\n'           : Also reduce the indent level by one.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub DecreaseIndent(ByRef colIndents As Collection, ByRef lngIndentLevel As Long)\r\n    If colIndents Is Nothing Then Exit Sub\r\n    If colIndents.Count = 0 Then Exit Sub\r\n    colIndents.Remove 1\r\n    lngIndentLevel = lngIndentLevel - 1\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PurgeExtraWhitespace\r\n' Author    : Adam Waller\r\n' Date      : 8/15/2023\r\n' Purpose   : Replace any extra whitespace within the string with a single space.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub PurgeExtraWhitespace(ByRef strValue As String)\r\n\r\n    Dim strResult As String\r\n\r\n    ' Check for any space, tab, or vbcrlf\r\n    If (InStr(1, strValue, \" \") > 0) _\r\n        Or (InStr(1, strValue, vbTab) > 0) _\r\n        Or (InStr(1, strValue, vbCrLf) > 0) Then\r\n\r\n        With New VBScript_RegExp_55.RegExp\r\n            .Multiline = True\r\n            '.Global = True\r\n            .Pattern = \"\\s+\"\r\n            strResult = .Replace(strValue, \" \")\r\n            If Len(strResult) Then\r\n                If strResult <> strValue Then strValue = strResult\r\n            End If\r\n        End With\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetPreviousToken\r\n' Author    : Adam Waller\r\n' Date      : 8/15/2023\r\n' Purpose   : Return the previous token ID, optionally skipping any excluded type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetPreviousTokenID(lngCurrentToken As Long, Optional intExceptType As eTokenTypes) As Long\r\n    Dim intToken As Integer\r\n    For intToken = lngCurrentToken - 1 To 1 Step -1\r\n        If intToken = 0 Then\r\n            ' Could not find a matching token, or no previous token\r\n            GetPreviousTokenID = 0\r\n        End If\r\n        If m_colTokens(intToken)(0) <> intExceptType Then\r\n            GetPreviousTokenID = intToken\r\n            Exit For\r\n        End If\r\n    Next intToken\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetNextTokenID\r\n' Author    : Adam Waller\r\n' Date      : 8/15/2023\r\n' Purpose   : Return the next token ID, optionally skipping any excluded type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetNextTokenID(lngCurrentToken As Long, Optional intExceptType As eTokenTypes) As Long\r\n    Dim intToken As Integer\r\n\r\n    If lngCurrentToken + 1 > m_colTokens.Count Then\r\n        GetNextTokenID = lngCurrentToken + 1\r\n        Exit Function\r\n    End If\r\n\r\n    For intToken = lngCurrentToken + 1 To m_colTokens.Count\r\n        If m_colTokens(intToken)(0) <> intExceptType Then\r\n            GetNextTokenID = intToken\r\n            Exit For\r\n        End If\r\n    Next intToken\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RegExBoundaries\r\n' Author    : Adam Waller\r\n' Date      : 8/12/2023\r\n' Purpose   : Return a string of properly escaped regex boundary characters.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RegExBoundaries() As String\r\n\r\n    Dim lngPos As Long\r\n    Dim strChar As String\r\n\r\n    Static strBoundaries As String\r\n\r\n    ' Check for cached list\r\n    If strBoundaries = vbNullString Then\r\n\r\n        ' Build out\r\n        With New clsConcat\r\n            .Add \"(\"\r\n            .AppendOnAdd = \"|\"\r\n            For lngPos = 1 To Len(cstrBoundaries)\r\n                strChar = Mid$(cstrBoundaries, lngPos, 1)\r\n                If InStr(1, cstrRegExSpecial, strChar) > 0 Then\r\n                    ' Escape this character\r\n                    .Add \"\\\", strChar\r\n                Else\r\n                    .Add strChar\r\n                End If\r\n            Next lngPos\r\n            ' Trim final delimiter and cache result\r\n            .Remove 1\r\n            strBoundaries = .GetStr & \")\"\r\n        End With\r\n    End If\r\n\r\n    ' Return list of boundaries\r\n    RegExBoundaries = strBoundaries\r\n\r\nEnd Function\r\n\r\n\r\n'#######################################################################################\r\n'#######################################################################################\r\n'\r\n'                                   TESTING CODE\r\n'\r\n'   The following section contains code and functions used to verify the behavior\r\n'   of this class with various types of queries to ensure consistent output.\r\n'\r\n'#######################################################################################\r\n'#######################################################################################\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SelfTest\r\n' Author    : Adam Waller\r\n' Date      : 8/16/2023\r\n' Purpose   : Perform testing with various sample queries to ensure that the formatting\r\n'           : rules are being applied as intended.\r\n'           : More complex tests can be performed using larger queries and result\r\n'           : files, but this should allow us to cover the primary functionality.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SelfTest()\r\n\r\n    Dim strActual As String\r\n\r\n    ' Test GetNextWords\r\n    Tokenize \"  LEFT \" & vbTab & vbCrLf & \" JOIN test on 1=2\", esdAccess\r\n    Debug.Assert GetNextWords(2) = \"LEFT JOIN\"\r\n    Debug.Assert GetNextWords(1) = \"LEFT\"\r\n\r\n    ' Test simple query with a few features\r\n    Tokenize \"SELECT  5 AS `TEST`\", esdMySQL\r\n\r\n    ' Verify tokens\r\n    Debug.Assert m_colTokens.Count = 7\r\n    Debug.Assert VerifyToken(1, ttReservedTopLevel, \"SELECT\")\r\n    Debug.Assert VerifyToken(2, ttWhitespace, \"  \")\r\n    Debug.Assert VerifyToken(3, ttNumber, \"5\")\r\n    Debug.Assert VerifyToken(4, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(5, ttReserved, \"AS\")\r\n    Debug.Assert VerifyToken(6, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(7, ttBacktickQuote, \"`TEST`\")\r\n\r\n    ' Verify result\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        .Add \"SELECT\"\r\n        .Add \"  5 AS `TEST`\"\r\n        .Remove 2\r\n        strActual = .GetStr\r\n    End With\r\n    Debug.Assert (strActual = FormatSQL)\r\n    If (strActual <> FormatSQL) Then Diff.Strings strActual, FormatSQL\r\n\r\n\r\n    ' Test Access date literal with MySQL inline comment\r\n    Tokenize \"SELECT (#1/1/2000#) AS SampleDate # MySQL inline ## comment\", esdAccess\r\n\r\n    ' Verify tokens\r\n    Debug.Assert m_colTokens.Count = 11\r\n    Debug.Assert VerifyToken(1, ttReservedTopLevel, \"SELECT\")\r\n    Debug.Assert VerifyToken(2, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(3, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(4, ttQuote, \"#1/1/2000#\")\r\n    Debug.Assert VerifyToken(5, ttBoundary, \")\")\r\n    Debug.Assert VerifyToken(6, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(7, ttReserved, \"AS\")\r\n    Debug.Assert VerifyToken(8, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(9, ttWord, \"SampleDate\")\r\n    Debug.Assert VerifyToken(10, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(11, ttComment, \"# MySQL inline ## comment\")\r\n\r\n    ' Verify result\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        .Add \"SELECT\"\r\n        .Add \"  (#1/1/2000#) AS SampleDate # MySQL inline ## comment\"\r\n        .Remove 2\r\n        strActual = .GetStr\r\n    End With\r\n    Debug.Assert (strActual = FormatSQL)\r\n    If (strActual <> FormatSQL) Then Diff.Strings strActual, FormatSQL\r\n\r\n\r\n    ' Example query from https://github.com/doctrine/sql-formatter\r\n    Tokenize \"SELECT count(*),`Column1`,`Testing`, `Testing Three` FROM `Table1`\" & _\r\n            \" WHERE Column1 = 'testing' AND ( (`Column2` = `Column3` OR Column4 >= NOW()) )\" & _\r\n            \" GROUP BY Column1 ORDER BY Column3 DESC LIMIT 5,10\", esdMySQL\r\n\r\n    ' Verify tokens\r\n    Debug.Assert m_colTokens.Count = 66\r\n    Debug.Assert VerifyToken(1, ttReservedTopLevel, \"SELECT\")\r\n    Debug.Assert VerifyToken(2, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(3, ttReserved, \"count\")\r\n    Debug.Assert VerifyToken(4, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(5, ttBoundary, \"*\")\r\n    Debug.Assert VerifyToken(6, ttBoundary, \")\")\r\n    Debug.Assert VerifyToken(7, ttBoundary, \",\")\r\n    Debug.Assert VerifyToken(8, ttBacktickQuote, \"`Column1`\")\r\n    Debug.Assert VerifyToken(9, ttBoundary, \",\")\r\n    Debug.Assert VerifyToken(10, ttBacktickQuote, \"`Testing`\")\r\n    Debug.Assert VerifyToken(11, ttBoundary, \",\")\r\n    Debug.Assert VerifyToken(12, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(13, ttBacktickQuote, \"`Testing Three`\")\r\n    Debug.Assert VerifyToken(14, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(15, ttReservedTopLevel, \"FROM\")\r\n    Debug.Assert VerifyToken(16, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(17, ttBacktickQuote, \"`Table1`\")\r\n    Debug.Assert VerifyToken(18, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(19, ttReservedTopLevel, \"WHERE\")\r\n    Debug.Assert VerifyToken(20, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(21, ttWord, \"Column1\")\r\n    Debug.Assert VerifyToken(22, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(23, ttBoundary, \"=\")\r\n    Debug.Assert VerifyToken(24, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(25, ttQuote, \"'testing'\")\r\n    Debug.Assert VerifyToken(26, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(27, ttReservedNewline, \"AND\")\r\n    Debug.Assert VerifyToken(28, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(29, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(30, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(31, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(32, ttBacktickQuote, \"`Column2`\")\r\n    Debug.Assert VerifyToken(33, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(34, ttBoundary, \"=\")\r\n    Debug.Assert VerifyToken(35, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(36, ttBacktickQuote, \"`Column3`\")\r\n    Debug.Assert VerifyToken(37, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(38, ttReservedNewline, \"OR\")\r\n    Debug.Assert VerifyToken(39, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(40, ttWord, \"Column4\")\r\n    Debug.Assert VerifyToken(41, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(42, ttBoundary, \">\")\r\n    Debug.Assert VerifyToken(43, ttBoundary, \"=\")\r\n    Debug.Assert VerifyToken(44, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(45, ttWord, \"NOW\")\r\n    Debug.Assert VerifyToken(46, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(47, ttBoundary, \")\")\r\n    Debug.Assert VerifyToken(48, ttBoundary, \")\")\r\n    Debug.Assert VerifyToken(49, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(50, ttBoundary, \")\")\r\n    Debug.Assert VerifyToken(51, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(52, ttReservedTopLevel, \"GROUP BY\")\r\n    Debug.Assert VerifyToken(53, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(54, ttWord, \"Column1\")\r\n    Debug.Assert VerifyToken(55, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(56, ttReservedTopLevel, \"ORDER BY\")\r\n    Debug.Assert VerifyToken(57, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(58, ttWord, \"Column3\")\r\n    Debug.Assert VerifyToken(59, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(60, ttReserved, \"DESC\")\r\n    Debug.Assert VerifyToken(61, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(62, ttReservedTopLevel, \"LIMIT\")\r\n    Debug.Assert VerifyToken(63, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(64, ttNumber, \"5\")\r\n    Debug.Assert VerifyToken(65, ttBoundary, \",\")\r\n    Debug.Assert VerifyToken(66, ttNumber, \"10\")\r\n\r\n    ' Verify result\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        .Add \"SELECT\"\r\n        .Add \"  count(*),\"\r\n        .Add \"  `Column1`,\"\r\n        .Add \"  `Testing`,\"\r\n        .Add \"  `Testing Three`\"\r\n        .Add \"FROM\"\r\n        .Add \"  `Table1`\"\r\n        .Add \"WHERE\"\r\n        .Add \"  Column1 = 'testing'\"\r\n        .Add \"  AND (\"\r\n        .Add \"    (\"\r\n        .Add \"      `Column2` = `Column3`\"\r\n        .Add \"      OR Column4 >= NOW()\"\r\n        .Add \"    )\"\r\n        .Add \"  )\"\r\n        .Add \"GROUP BY\"\r\n        .Add \"  Column1\"\r\n        .Add \"ORDER BY\"\r\n        .Add \"  Column3 DESC\"\r\n        .Add \"LIMIT\"\r\n        .Add \"  5, 10\"\r\n        .Remove 2\r\n        strActual = .GetStr\r\n    End With\r\n    Debug.Assert (strActual = FormatSQL)\r\n    If (strActual <> FormatSQL) Then Diff.Strings strActual, FormatSQL\r\n\r\n\r\n    ' Test multi-part names\r\n    Tokenize \"SELECT [dbo].[field] FROM [server].[schema].[table];\", esdMSSQL\r\n\r\n    ' Verify tokens\r\n    Debug.Assert m_colTokens.Count = 13\r\n    Debug.Assert VerifyToken(1, ttReservedTopLevel, \"SELECT\")\r\n    Debug.Assert VerifyToken(2, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(3, ttQuote, \"[dbo]\")\r\n    Debug.Assert VerifyToken(4, ttBoundary, \".\")\r\n    Debug.Assert VerifyToken(5, ttQuote, \"[field]\")\r\n    Debug.Assert VerifyToken(6, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(7, ttReservedTopLevel, \"FROM\")\r\n    Debug.Assert VerifyToken(8, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(9, ttQuote, \"[server]\")\r\n    Debug.Assert VerifyToken(10, ttBoundary, \".\")\r\n    Debug.Assert VerifyToken(11, ttQuote, \"[schema]\")\r\n    Debug.Assert VerifyToken(12, ttBoundary, \".\")\r\n    Debug.Assert VerifyToken(13, ttQuote, \"[table]\")\r\n\r\n    ' Verify result\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        .Add \"SELECT\"\r\n        .Add \"  [dbo].[field]\"\r\n        .Add \"FROM\"\r\n        .Add \"  [server].[schema].[table]\"\r\n        .Remove 2\r\n        strActual = .GetStr\r\n    End With\r\n    Debug.Assert (strActual = FormatSQL)\r\n    If (strActual <> FormatSQL) Then Diff.Strings strActual, FormatSQL\r\n\r\n\r\n    ' Test parameter expression\r\n    Tokenize \"SELECT [Forms]![frmColors]![Text18];\", esdAccess\r\n\r\n    ' Verify tokens\r\n    Debug.Assert m_colTokens.Count = 7\r\n    Debug.Assert VerifyToken(1, ttReservedTopLevel, \"SELECT\")\r\n    Debug.Assert VerifyToken(2, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(3, ttQuote, \"[Forms]\")\r\n    Debug.Assert VerifyToken(4, ttBoundary, \"!\")\r\n    Debug.Assert VerifyToken(5, ttQuote, \"[frmColors]\")\r\n    Debug.Assert VerifyToken(6, ttBoundary, \"!\")\r\n    Debug.Assert VerifyToken(7, ttQuote, \"[Text18]\")\r\n\r\n    ' Verify result\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        .Add \"SELECT\"\r\n        .Add \"  [Forms]![frmColors]![Text18]\"\r\n        .Remove 2\r\n        strActual = .GetStr\r\n    End With\r\n    Debug.Assert (strActual = FormatSQL)\r\n    If (strActual <> FormatSQL) Then Diff.Strings strActual, FormatSQL\r\n\r\n\r\n    ' Test unquoted path (See issue #447)\r\n    Tokenize \"SELECT foo.* INTO (C:\\path\\to\\bar.accdb) fizz FROM bazz;\", esdAccess\r\n\r\n    ' Verify tokens\r\n    Debug.Assert m_colTokens.Count = 21\r\n    Debug.Assert VerifyToken(1, ttReservedTopLevel, \"SELECT\")\r\n    Debug.Assert VerifyToken(2, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(3, ttWord, \"foo\")\r\n    Debug.Assert VerifyToken(4, ttBoundary, \".\")\r\n    Debug.Assert VerifyToken(5, ttBoundary, \"*\")\r\n    Debug.Assert VerifyToken(6, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(7, ttReserved, \"INTO\")\r\n    Debug.Assert VerifyToken(8, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(9, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(10, ttWord, \"C\")\r\n    Debug.Assert VerifyToken(11, ttBoundary, \":\")\r\n    Debug.Assert VerifyToken(12, ttWord, \"\\path\\to\\bar\")\r\n    Debug.Assert VerifyToken(13, ttBoundary, \".\")\r\n    Debug.Assert VerifyToken(14, ttWord, \"accdb\")\r\n    Debug.Assert VerifyToken(15, ttBoundary, \")\")\r\n    Debug.Assert VerifyToken(16, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(17, ttWord, \"fizz\")\r\n    Debug.Assert VerifyToken(18, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(19, ttReservedTopLevel, \"FROM\")\r\n    Debug.Assert VerifyToken(20, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(21, ttWord, \"bazz\")\r\n\r\n    ' Verify result\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        .Add \"SELECT\"\r\n        .Add \"  foo.* INTO (C:\\path\\to\\bar.accdb) fizz\"\r\n        .Add \"FROM\"\r\n        .Add \"  bazz\"\r\n        .Remove 2\r\n        strActual = .GetStr\r\n    End With\r\n    Debug.Assert (strActual = FormatSQL)\r\n    If (strActual <> FormatSQL) Then Diff.Strings strActual, FormatSQL\r\n\r\n\r\n    ' Test nested where clause (Issue #486)\r\n    Tokenize \"SELECT MSysQueries.Attribute, MSysQueries.Flag FROM MSysQueries\" & _\r\n        \" WHERE (((MSysQueries.Flag) In (SELECT Flag from [MSysQueries] where flag=0)));\", esdAccess\r\n\r\n    ' Verify tokens\r\n    Debug.Assert m_colTokens.Count = 44\r\n    Debug.Assert VerifyToken(1, ttReservedTopLevel, \"SELECT\")\r\n    Debug.Assert VerifyToken(2, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(3, ttWord, \"MSysQueries\")\r\n    Debug.Assert VerifyToken(4, ttBoundary, \".\")\r\n    Debug.Assert VerifyToken(5, ttWord, \"Attribute\")\r\n    Debug.Assert VerifyToken(6, ttBoundary, \",\")\r\n    Debug.Assert VerifyToken(7, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(8, ttWord, \"MSysQueries\")\r\n    Debug.Assert VerifyToken(9, ttBoundary, \".\")\r\n    Debug.Assert VerifyToken(10, ttWord, \"Flag\")\r\n    Debug.Assert VerifyToken(11, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(12, ttReservedTopLevel, \"FROM\")\r\n    Debug.Assert VerifyToken(13, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(14, ttWord, \"MSysQueries\")\r\n    Debug.Assert VerifyToken(15, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(16, ttReservedTopLevel, \"WHERE\")\r\n    Debug.Assert VerifyToken(17, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(18, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(19, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(20, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(21, ttWord, \"MSysQueries\")\r\n    Debug.Assert VerifyToken(22, ttBoundary, \".\")\r\n    Debug.Assert VerifyToken(23, ttWord, \"Flag\")\r\n    Debug.Assert VerifyToken(24, ttBoundary, \")\")\r\n    Debug.Assert VerifyToken(25, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(26, ttReserved, \"In\")\r\n    Debug.Assert VerifyToken(27, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(28, ttBoundary, \"(\")\r\n    Debug.Assert VerifyToken(29, ttReservedTopLevel, \"SELECT\")\r\n    Debug.Assert VerifyToken(30, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(31, ttWord, \"Flag\")\r\n    Debug.Assert VerifyToken(32, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(33, ttReservedTopLevel, \"from\")\r\n    Debug.Assert VerifyToken(34, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(35, ttQuote, \"[MSysQueries]\")\r\n    Debug.Assert VerifyToken(36, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(37, ttReservedTopLevel, \"where\")\r\n    Debug.Assert VerifyToken(38, ttWhitespace, \" \")\r\n    Debug.Assert VerifyToken(39, ttWord, \"flag\")\r\n    Debug.Assert VerifyToken(40, ttBoundary, \"=\")\r\n    Debug.Assert VerifyToken(41, ttNumber, \"0\")\r\n    Debug.Assert VerifyToken(42, ttBoundary, \")\")\r\n    Debug.Assert VerifyToken(43, ttBoundary, \")\")\r\n    Debug.Assert VerifyToken(44, ttBoundary, \")\")\r\n\r\n    ' Verify result\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        .Add \"SELECT\"\r\n        .Add \"  MSysQueries.Attribute,\"\r\n        .Add \"  MSysQueries.Flag\"\r\n        .Add \"FROM\"\r\n        .Add \"  MSysQueries\"\r\n        .Add \"WHERE\"\r\n        .Add \"  (\"\r\n        .Add \"    (\"\r\n        .Add \"      (MSysQueries.Flag) In (\"\r\n        .Add \"        SELECT\"\r\n        .Add \"          Flag\"\r\n        .Add \"        from\"\r\n        .Add \"          [MSysQueries]\"\r\n        .Add \"        where\"\r\n        .Add \"          flag = 0\"\r\n        .Add \"      )\"\r\n        .Add \"    )\"\r\n        .Add \"  )\"\r\n        .Remove 2\r\n        strActual = .GetStr\r\n    End With\r\n    Debug.Assert (strActual = FormatSQL)\r\n    If (strActual <> FormatSQL) Then Diff.Strings strActual, FormatSQL\r\n\r\n\r\n'    PrintTokens\r\n'    BuildTestFromTokens\r\n'    Diff.Strings strActual, FormatSQL\r\n\r\n    ' Test performance\r\n    TestPerformance\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Perf\r\n' Author    : Adam Waller\r\n' Date      : 8/17/2023\r\n' Purpose   : Subclass global performance function to turn on or off for this class\r\n'           : independent of the global performance monitoring.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function Perf() As clsPerformance\r\n    Static cInternal As clsPerformance\r\n    If cInternal Is Nothing Then\r\n        Set cInternal = New clsPerformance\r\n        ' Disable detailed internal performance monitoring by default\r\n        cInternal.Enabled = False\r\n    End If\r\n    Set Perf = cInternal\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyToken\r\n' Author    : Adam Waller\r\n' Date      : 8/16/2023\r\n' Purpose   : Verify a token type and value based on index position.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function VerifyToken(intIndex As Integer, intType As eTokenTypes, strValue As String) As Boolean\r\n    If m_colTokens Is Nothing Then Exit Function\r\n    If intIndex > m_colTokens.Count Then Exit Function\r\n    If m_colTokens(intIndex)(0) = intType Then\r\n        VerifyToken = (m_colTokens(intIndex)(1) = strValue)\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildTestFromTokens\r\n' Author    : Adam Waller\r\n' Date      : 8/16/2023\r\n' Purpose   : Builds a verification test from the current tokens.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function BuildTestFromTokens()\r\n\r\n    Dim intToken As Integer\r\n    Dim strType As String\r\n    Dim strValue As String\r\n    Dim varLines As Variant\r\n    Dim intLine As Integer\r\n\r\n    Debug.Print vbCrLf & vbCrLf & \"    ' Verify tokens\"\r\n    Debug.Print \"    Debug.Assert m_colTokens.Count = \" & m_colTokens.Count\r\n\r\n    ' Loop through tokens\r\n    For intToken = 1 To m_colTokens.Count\r\n\r\n        ' Get type and value\r\n        strType = TypeEnumToString(m_colTokens(intToken)(0))\r\n        strValue = Replace(m_colTokens(intToken)(1), \"\"\"\", \"\"\"\"\"\")\r\n\r\n        ' Print test to debug window\r\n        Debug.Print \"    Debug.Assert VerifyToken(\" & intToken & \", \" & strType & \", \"\"\" & strValue & \"\"\")\"\r\n\r\n    Next intToken\r\n\r\n    ' Output formatted SQL\r\n    varLines = Split(FormatSQL, vbCrLf)\r\n    Debug.Print vbCrLf & \"    ' Verify result\"\r\n    Debug.Print \"    With New clsConcat\"\r\n    Debug.Print \"        .AppendOnAdd = vbCrLf\"\r\n\r\n    ' Loop through lines, outputting SQL with line breaks\r\n    For intLine = 0 To UBound(varLines)\r\n        Debug.Print \"        .Add \"\"\" & Replace(varLines(intLine), \"\"\"\", \"\"\"\"\"\") & \"\"\"\"\r\n    Next intLine\r\n    Debug.Print \"        .Remove 2\"\r\n    Debug.Print \"        strActual = .GetStr\"\r\n    Debug.Print \"    End With\"\r\n    Debug.Print \"    Debug.Assert (strActual = FormatSQL)\"\r\n    Debug.Print \"    If (strActual <> FormatSQL) Then Diff.Strings strActual, FormatSQL\"\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PrintTokens\r\n' Author    : Adam Waller\r\n' Date      : 8/14/2023\r\n' Purpose   : Print the current sequence of tokens (type and value) for debugging\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub PrintTokens()\r\n\r\n    Dim varToken As Variant\r\n    Dim strType As String\r\n\r\n    If m_colTokens Is Nothing Then\r\n        Debug.Print \"No tokens found\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Print header\r\n    Debug.Print \"---------------------------------------------------\"\r\n    Debug.Print \"TOKEN NAME          TOKEN VALUE\"\r\n    Debug.Print \"---------------------------------------------------\"\r\n\r\n    ' Loop through Tokens\r\n    For Each varToken In m_colTokens\r\n\r\n        ' Print type name with padding to start values at same position\r\n        strType = TypeEnumToString(varToken(0))\r\n        Debug.Print strType;\r\n\r\n        ' Add padding to start values at same position\r\n        Debug.Print Space(20 - Len(strType));\r\n\r\n        ' Print value\r\n        Debug.Print varToken(1)\r\n\r\n    Next varToken\r\n    Debug.Print \"---------------------------------------------------\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TypeEnumToString\r\n' Author    : Adam Waller\r\n' Date      : 8/16/2023\r\n' Purpose   : Convert token type enum to string. (Used for debug output and automated\r\n'           : test building.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function TypeEnumToString(intType As Variant) As String\r\n    Select Case intType\r\n        Case ttUnknown:             TypeEnumToString = \"ttUnknown\"\r\n        Case ttWhitespace:          TypeEnumToString = \"ttWhitespace\"\r\n        Case ttWord:                TypeEnumToString = \"ttWord\"\r\n        Case ttQuote:               TypeEnumToString = \"ttQuote\"\r\n        Case ttBacktickQuote:       TypeEnumToString = \"ttBacktickQuote\"\r\n        Case ttReserved:            TypeEnumToString = \"ttReserved\"\r\n        Case ttReservedTopLevel:    TypeEnumToString = \"ttReservedTopLevel\"\r\n        Case ttReservedNewline:     TypeEnumToString = \"ttReservedNewline\"\r\n        Case ttBoundary:            TypeEnumToString = \"ttBoundary\"\r\n        Case ttComment:             TypeEnumToString = \"ttComment\"\r\n        Case ttBlockComment:        TypeEnumToString = \"ttBlockComment\"\r\n        Case ttNumber:              TypeEnumToString = \"ttNumber\"\r\n        Case ttError:               TypeEnumToString = \"ttError\"\r\n        Case ttVariable:            TypeEnumToString = \"ttVariable\"\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAscWFromString\r\n' Author    : Adam Waller\r\n' Date      : 8/18/2023\r\n' Purpose   : Print out AscW indexes of each character in the string.\r\n'           : (Use this when building case statements)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetAscWFromString(strText As String) As String\r\n    Dim lngCnt As Long\r\n    Debug.Print\r\n    For lngCnt = 1 To Len(strText)\r\n        Debug.Print AscW(Mid$(strText, lngCnt, 1)) & \", \";\r\n    Next lngCnt\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PerformanceTesting\r\n' Author    : Adam Waller\r\n' Date      : 8/18/2023\r\n' Purpose   : Test performance of tokenizing and building formatted string\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function TestPerformance()\r\n\r\n    Dim lngCnt As Long\r\n    Dim lngMax As Long\r\n\r\n    ' Number of iterations to perform\r\n    lngMax = 100\r\n\r\n    ' Test performance of tokenizing a query\r\n    Perf.Enabled = True\r\n    Perf.StartTiming\r\n    For lngCnt = 1 To lngMax\r\n        Tokenize \"SELECT count(*),`Column1`,`Testing`, `Testing Three` FROM `Table1`\" & _\r\n                \" WHERE Column1 = 'testing' AND ( (`Column2` = `Column3` OR Column4 >= NOW()) )\" & _\r\n                \" GROUP BY Column1 ORDER BY Column3 DESC LIMIT 5,10\", esdMySQL\r\n    Next lngCnt\r\n\r\n    ' Test performance of formatting SQL\r\n    For lngCnt = 1 To lngMax\r\n        FormatSQL\r\n    Next lngCnt\r\n\r\n    Perf.EndTiming\r\n    Debug.Print Perf.GetReports\r\n    Perf.Enabled = False\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsTranslation.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsTranslation\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsTranslation\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2021\r\n' Purpose   : Used for the translation of strings to different languages, similar to\r\n'           : the gettext toolset.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' Enum for sections\r\nPrivate Enum eFileSection\r\n    efsUnknown\r\n    efsHeader\r\n    efsContext\r\n    efsReference\r\n    efsMessageId\r\n    efsMessageString\r\nEnd Enum\r\n\r\n' Cache strings to dictionary objects so we don't have to do database lookups\r\n' each time we need to return translated strings\r\nPrivate Type udtThis\r\n    dStrings As Dictionary      ' Untranslated strings (value of string ID)\r\n    dTranslation As Dictionary  ' Translated strings in local language (key of string ID)\r\n    dTranslated As Dictionary   ' Translated strings (key of translated string)\r\n    CurrentLanguage As String   ' Identifier of current language (i.e. \"en_US\")\r\nEnd Type\r\nPrivate this As udtThis\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetLanguageName\r\n' Author    : Adam Waller\r\n' Date      : 5/26/2021\r\n' Purpose   : Return the display name for the language. (Add new languages here)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetLanguageName(strIdentifier As String) As String\r\n    Select Case strIdentifier\r\n        Case \"en_US\":   GetLanguageName = \"English\"\r\n        Case \"pt_BR\":   GetLanguageName = \"Brazilian Portuguese\"\r\n        Case \"en_TEST\": GetLanguageName = \"English (Localization Testing)\"\r\n        ' For undefined languages, use the identifier for now.\r\n        Case Else:      GetLanguageName = strIdentifier\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : T\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2021\r\n' Purpose   : Return the translated version of the string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function T(strText As String, Optional strReference As String, _\r\n    Optional strContext As String, Optional strComments As String, _\r\n    Optional var0, Optional var1, Optional var2, Optional var3, Optional var4, _\r\n    Optional var5, Optional var6, Optional var7, Optional var8, Optional var9) As String\r\n\r\n    Dim strNew As String\r\n    Dim strKey As String\r\n\r\n    ' Skip processing if no value is passed\r\n    If strText = vbNullString Then Exit Function\r\n\r\n    ' Make sure the language has been initialized\r\n    CheckInit\r\n\r\n    ' Fall back to English (untranslated)\r\n    strNew = strText\r\n\r\n    ' Check for the master string\r\n    strKey = BuildKey(strContext, strText)\r\n    If this.dStrings.Exists(strKey) Then\r\n        If this.dTranslation.Exists(this.dStrings(strKey)) Then\r\n            ' Use translated string\r\n            strNew = this.dTranslation(this.dStrings(strKey))\r\n        End If\r\n    Else\r\n        If this.dTranslated.Exists(strText) Then\r\n            ' We have already translated this string. Don't add the translation\r\n            ' as a new string in our translation database.\r\n        Else\r\n            ' Add to master list of strings (no translation exists)\r\n            this.dStrings.Add strKey, SaveString(strText, strReference, strContext, strComments)\r\n            this.dTranslated.Add strText, vbNullString\r\n        End If\r\n    End If\r\n\r\n    ' Fill in any replacements\r\n    If InStr(1, strNew, \"{\") > 0 Then\r\n        If Not IsMissing(var0) Then strNew = Replace(strNew, \"{0}\", CStr(var0))\r\n        If Not IsMissing(var1) Then strNew = Replace(strNew, \"{1}\", CStr(var1))\r\n        If Not IsMissing(var2) Then strNew = Replace(strNew, \"{2}\", CStr(var2))\r\n        If Not IsMissing(var3) Then strNew = Replace(strNew, \"{3}\", CStr(var3))\r\n        If Not IsMissing(var4) Then strNew = Replace(strNew, \"{4}\", CStr(var4))\r\n        If Not IsMissing(var5) Then strNew = Replace(strNew, \"{5}\", CStr(var5))\r\n        If Not IsMissing(var6) Then strNew = Replace(strNew, \"{6}\", CStr(var6))\r\n        If Not IsMissing(var7) Then strNew = Replace(strNew, \"{7}\", CStr(var7))\r\n        If Not IsMissing(var8) Then strNew = Replace(strNew, \"{8}\", CStr(var8))\r\n        If Not IsMissing(var9) Then strNew = Replace(strNew, \"{9}\", CStr(var9))\r\n    End If\r\n\r\n    ' Maintain index of fully translated strings, so we don't attempt to translate\r\n    ' a string twice.\r\n    If Not this.dTranslated.Exists(strNew) Then this.dTranslated.Add strNew, vbNullString\r\n\r\n    ' Return translated string\r\n    T = strNew\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ApplyTo\r\n' Author    : Adam Waller\r\n' Date      : 5/17/2021\r\n' Purpose   : Apply language translation to a form object (From English values)\r\n'           : This also includes placeholder replacements. (Use tag property for {0})\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ApplyTo(frmObject As Form)\r\n\r\n    Dim ctl As Control\r\n    Dim ctl2 As Control\r\n    Dim strReference As String\r\n    Dim strName As String\r\n    Dim dReferences As Dictionary\r\n\r\n    Set dReferences = New Dictionary\r\n\r\n    ' Loop through all controls\r\n    For Each ctl In frmObject.Controls\r\n\r\n        ' Reset reference string\r\n        strReference = frmObject.Name & \".\" & ctl.Name\r\n\r\n        ' Only check certain types of controls\r\n        Select Case TypeName(ctl)\r\n            Case \"Label\"\r\n\r\n                ' Check for associated control\r\n                ' (It is easier to go from the object to the label, but not\r\n                '  all labels may have objects, so we loop through other controls\r\n                On Error Resume Next\r\n                For Each ctl2 In frmObject.Controls\r\n                    strName = vbNullString\r\n                    strName = ctl2.Controls(0).Name\r\n                    If strName = ctl.Name Then\r\n                        ' Found associated label\r\n                        ' Add extended context\r\n                        strReference = strReference & \"(\" & ctl2.Name & \")\"\r\n                        Exit For\r\n                    End If\r\n                Next ctl2\r\n                If DebugMode(False) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n                ' Translation caption\r\n                ctl.Caption = T(ctl.Caption, strReference)\r\n\r\n            Case \"TextBox\"\r\n                ' Nothing to translate\r\n\r\n            Case \"CommandButton\"\r\n                ctl.Caption = T(ctl.Caption, strReference)\r\n\r\n            Case \"Page\"\r\n                ' Tab control page caption\r\n                strReference = frmObject.Name & \".\" & ctl.Parent.Name & \".\" & ctl.Name\r\n                ctl.Caption = T(ctl.Caption, strReference)\r\n\r\n            Case Else\r\n                'Debug.Print TypeName(ctl)\r\n\r\n        End Select\r\n\r\n        ' Maintain list of references so we can purge unused entries\r\n        If Len(strReference) Then If Not dReferences.Exists(strReference) Then dReferences.Add strReference, vbNullString\r\n\r\n    Next ctl\r\n\r\n    ' Other properties\r\n    frmObject.Caption = T(frmObject.Caption, frmObject.Name & \".Caption\")\r\n    dReferences.Add frmObject.Name & \".Caption\", vbNullString\r\n\r\n    ' Purge any unused references\r\n    If Me.Contribute Then PurgeStrings frmObject.Name & \".\", dReferences\r\n    PurgeStrings frmObject.Name & \".\", dReferences\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TranslateStrings\r\n' Author    : Adam Waller\r\n' Date      : 10/4/2024\r\n' Purpose   : Translate a dictionary of key-value pairs where the key is the string to\r\n'           : translate, and the value is the translation. This can be used to translate\r\n'           : strings from an external component, like a menu handler.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function TranslateStrings(dStrings As Dictionary) As Dictionary\r\n    Dim varKey As Variant\r\n    Set TranslateStrings = New Dictionary\r\n    With TranslateStrings\r\n        For Each varKey In dStrings.Keys\r\n            .Add varKey, T(CStr(varKey))\r\n        Next varKey\r\n    End With\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportTemplate\r\n' Author    : Adam Waller\r\n' Date      : 11/7/2024\r\n' Purpose   : Exports the template file for the English strings used for translation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExportTemplate()\r\n    WriteFileNoBom BuildFileContent(\"en_US\"), FSO.BuildPath(TranslationsPath, PROJECT_NAME & \".pot\")\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportTranslations\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2021\r\n' Purpose   : Export translations to files\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExportTranslations()\r\n\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n    Dim strLanguage As String\r\n    Dim strFolder As String\r\n    Dim strFile As String\r\n\r\n    strFolder = TranslationsPath\r\n    If strFolder = vbNullString Then Exit Sub\r\n\r\n    Set dbs = CodeDb\r\n    Set rst = dbs.OpenRecordset(\"tblLanguages\", dbOpenSnapshot)\r\n    With rst\r\n        Do While Not .EOF\r\n            strLanguage = Nz(!ID)\r\n            If Len(strLanguage) And strLanguage <> \"en_US\" Then\r\n                ' Build translation work file\r\n                strFile = FSO.BuildPath(strFolder, strLanguage & \".po\")\r\n                WriteFileNoBom BuildFileContent(strLanguage), strFile\r\n            End If\r\n            .MoveNext\r\n        Loop\r\n        .Close\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadTranslations\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2021\r\n' Purpose   : Load translation data from translation files\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadTranslations()\r\n\r\n    Dim strBase As String\r\n    Dim strFile As String\r\n    Dim dFiles As Dictionary\r\n    Dim varFile As Variant\r\n\r\n    strBase = AddSlash(TranslationsPath)\r\n\r\n    ' Load English translation\r\n    strFile = strBase & PROJECT_NAME & \".pot\"\r\n    If FSO.FileExists(strFile) Then ImportTranslation strFile\r\n\r\n    ' Load other translations\r\n    Set dFiles = GetFileList(strBase, \"*.po\")\r\n    For Each varFile In dFiles.Keys\r\n        strFile = strBase & varFile\r\n        ImportTranslation strFile\r\n    Next varFile\r\n\r\n    ' Reload current translation\r\n    LoadLanguage Me.Language\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TranslationsPath\r\n' Author    : Adam Waller\r\n' Date      : 3/22/2024\r\n' Purpose   : Get translations path from the following locations:\r\n'           :   1. Saved translation path (If contribute is on)\r\n'           :   2. Local translations path, relative to CodeDB\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get TranslationsPath() As String\r\n\r\n    Dim strPath As String\r\n\r\n    '   1. Saved translations path\r\n    If Me.Contribute Then\r\n        strPath = GetSetting(PROJECT_NAME, \"Language\", \"Translation Path\", vbNullString)\r\n        If HasTranslationFiles(strPath) Then\r\n            TranslationsPath = strPath\r\n            Exit Property\r\n        End If\r\n    End If\r\n\r\n    '   2. Local translations folder\r\n    strPath = FSO.BuildPath(CodeProject.Path, \"Translation\")\r\n    If HasTranslationFiles(strPath) Then\r\n        TranslationsPath = strPath\r\n    End If\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StringCount\r\n' Author    : Adam Waller\r\n' Date      : 11/7/2024\r\n' Purpose   : Return count of strings from table. (Used to verify that we have\r\n'           : successfully loaded strings.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get StringCount() As Long\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n    Set dbs = CodeDb\r\n    Set rst = dbs.OpenRecordset(\"select count(*) from tblStrings\", dbOpenSnapshot, dbReadOnly)\r\n    StringCount = Nz(rst.Fields(0))\r\n    rst.Close\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ShowStrings\r\n' Author    : Adam Waller\r\n' Date      : 11/8/2024\r\n' Purpose   : Open the strings table directly for review.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ShowStrings()\r\n    DoCmd.OpenTable \"tblStrings\"\r\n    DoCmd.OpenTable \"tblTranslation\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TranslationsPath\r\n' Author    : Adam Waller\r\n' Date      : 3/23/2024\r\n' Purpose   : Set the translations path\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Let TranslationsPath(strFolderPath As String)\r\n    SaveSetting PROJECT_NAME, \"Language\", \"Translation Path\", strFolderPath\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasTranslationFiles\r\n' Author    : Adam Waller\r\n' Date      : 3/22/2024\r\n' Purpose   : Returns true if the folder contains the master template file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function HasTranslationFiles(strFolder As String) As Boolean\r\n    If FSO.FolderExists(strFolder) Then\r\n        ' Check for the template file \"MSAccessVCS.pot\"\r\n        HasTranslationFiles = FSO.FileExists(FSO.BuildPath(strFolder, PROJECT_NAME & \".pot\"))\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Contribute\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2021\r\n' Purpose   : Whether the user desires to contribute to translations\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Contribute() As Boolean\r\n    Contribute = GetSetting(PROJECT_NAME, \"Language\", \"Contribute To Translations\", False)\r\nEnd Property\r\nPublic Property Let Contribute(blnContributeToTranslations As Boolean)\r\n    SaveSetting PROJECT_NAME, \"Language\", \"Contribute To Translations\", blnContributeToTranslations\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Language\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2021\r\n' Purpose   : Selected language\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Language() As String\r\n    Language = GetSetting(PROJECT_NAME, \"Language\", \"Language\", \"en_US\")\r\nEnd Property\r\nPublic Property Let Language(strLanguage As String)\r\n    SaveSetting PROJECT_NAME, \"Language\", \"Language\", strLanguage\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PurgeStrings\r\n' Author    : Adam Waller\r\n' Date      : 8/23/2024\r\n' Purpose   : Purges translation strings from form controls that no longer exist\r\n'           : (Note that renamed controls are also purged and added as untranslated)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub PurgeStrings(strObjectPrefix As String, dReferences As Dictionary)\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim strSql As String\r\n\r\n    Set dbs = CodeDb\r\n    strSql = \"select * from tblStrings where Reference like \"\"\" & strObjectPrefix & \"*\"\"\"\r\n    Set rst = dbs.OpenRecordset(strSql, dbOpenDynaset)\r\n    With rst\r\n        Do While Not .EOF\r\n            If Len(Nz(!Reference)) > 0 Then\r\n                If Not dReferences.Exists(Nz(!Reference)) Then\r\n                    ' Delete orphaned string entry\r\n                    ' (Translations will be removed automatically when\r\n                    '  the translation files are rewritten.)\r\n                    Debug.Print \"Removing translation string: \" & Nz(!Reference) & \": \" & Nz(!msgid)\r\n                    .Delete\r\n                End If\r\n            End If\r\n            .MoveNext\r\n        Loop\r\n        .Close\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildKey\r\n' Author    : Adam Waller\r\n' Date      : 8/21/2024\r\n' Purpose   : Build key from context and string\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function BuildKey(strContext As String, strText As String) As String\r\n    If Len(strContext) > 0 Then\r\n        BuildKey = strContext & \"|\" & strText\r\n    Else\r\n        BuildKey = strText\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveString\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2021\r\n' Purpose   : Save the string to the database table and return ID\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SaveString(strText As String, strReference As String, _\r\n    strContext As String, strComments As String) As Long\r\n\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n\r\n    Set dbs = CodeDb\r\n    Set rst = dbs.OpenRecordset(\"tblStrings\")\r\n\r\n    With rst\r\n        .AddNew\r\n            !msgid = strText\r\n            !Reference = ZN(strReference)\r\n            !Context = ZN(strContext)\r\n            !Comments = ZN(strComments)\r\n        .Update\r\n        .Bookmark = .LastModified\r\n        SaveString = !ID\r\n        .Close\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetLanguage\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2021\r\n' Purpose   : Set the current language\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetLanguage(strLanguage As String)\r\n    LoadLanguage strLanguage\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckInit\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2021\r\n' Purpose   : Ensure that the language strings have been loaded\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub CheckInit()\r\n    If this.CurrentLanguage = vbNullString Then\r\n        Set this.dStrings = New Dictionary\r\n        Set this.dTranslation = New Dictionary\r\n        Set this.dTranslated = New Dictionary\r\n        LoadLanguage GetCurrentLanguage\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadLanguage\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2021\r\n' Purpose   : Loads the language entries into the dictionary objects.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub LoadLanguage(strLanguage As String)\r\n\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n    Dim strKey As String\r\n    Dim strSql As String\r\n    Dim strPath As String\r\n\r\n    this.CurrentLanguage = strLanguage\r\n    Set this.dStrings = New Dictionary\r\n    Set this.dTranslation = New Dictionary\r\n    Set this.dTranslated = New Dictionary\r\n\r\n    ' Load strings and translations\r\n    Set dbs = CodeDb\r\n    strSql = \"select * from qryTranslatedStrings where (Lang = '\" & strLanguage & \"')\"\r\n    Set rst = dbs.OpenRecordset(strSql, dbOpenDynaset)\r\n    With rst\r\n        Do While Not .EOF\r\n            strKey = BuildKey(Nz(!Context), Nz(!msgid))\r\n            If Not this.dStrings.Exists(strKey) Then this.dStrings.Add strKey, CStr(!ID)\r\n            If Nz(!Translation) <> vbNullString Then\r\n                If Not this.dTranslation.Exists(CStr(!ID)) Then\r\n                    this.dTranslation.Add CStr(!ID), Nz(!Translation)\r\n                End If\r\n            End If\r\n            .MoveNext\r\n        Loop\r\n        .Close\r\n    End With\r\n\r\n    ' Reload the ribbon to reflect any translation changes there.\r\n    If RibbonLoaded Then\r\n        ' Export strings to json file for use in the ribbon.\r\n        strPath = GetInstallSettings.strInstallFolder & PathSep & \"Ribbon.json\"\r\n        WriteFile ConvertToJson(GetRibbonStrings, 2), strPath\r\n        modCOMAddIn.ReloadRibbon\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CurrentLanguage\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2021\r\n' Purpose   : Return the currently selected language, falling back to operating system\r\n'           : UI language, then to US English.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetCurrentLanguage() As String\r\n    GetCurrentLanguage = Coalesce(this.CurrentLanguage, Me.Language, GetOsLanguage, \"en_US\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetOsLanguage\r\n' Author    : Adam Waller\r\n' Date      : 8/22/2024\r\n' Purpose   :\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetOsLanguage() As String\r\n    GetOsLanguage = LanguageIdToString(LanguageSettings.LanguageID(msoLanguageIDUI))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ImportTranslation\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2021\r\n' Purpose   : Import a translation file. (*.po/*.pot)\r\n'           : See https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html\r\n'           : for details on .po file format.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ImportTranslation(strFile As String)\r\n\r\n    Dim strName As String\r\n    Dim strLanguage As String\r\n    Dim strContent As String\r\n    Dim varLines As Variant\r\n    Dim lngStart As Long\r\n    Dim lngLine As Long\r\n    Dim intSection As eFileSection\r\n    Dim intLastSection As eFileSection\r\n    Dim strLine As String\r\n    Dim dItem As Dictionary\r\n    Dim cText As clsConcat\r\n    Dim cHeader As clsConcat\r\n    Dim colEntries As Collection\r\n\r\n    ' Read file contents\r\n    strContent = ReadFile(strFile)\r\n    If strContent = vbNullString Then Exit Sub\r\n\r\n    ' Add a blank line at the end to make sure we capture the last entry\r\n    strContent = strContent & vbCrLf\r\n\r\n    ' Get language from file name\r\n    strName = FSO.GetFileName(strFile)\r\n    If strName = PROJECT_NAME & \".pot\" Then\r\n        ' Template file (English strings)\r\n        strLanguage = \"en_US\"\r\n    Else\r\n        ' Other language\r\n        strLanguage = FSO.GetBaseName(strName)\r\n    End If\r\n\r\n    ' Split into lines\r\n    varLines = Split(strContent, vbCrLf)\r\n\r\n    ' Initialize structures\r\n    intSection = efsHeader\r\n    intLastSection = efsHeader\r\n    Set colEntries = New Collection\r\n    Set dItem = New Dictionary\r\n    Set cText = New clsConcat\r\n    Set cHeader = New clsConcat\r\n    cText.AppendOnAdd = vbCrLf\r\n    cHeader.AppendOnAdd = vbCrLf\r\n\r\n    ' Loop through lines, build collection of entries\r\n    For lngLine = 0 To UBound(varLines)\r\n        strLine = Trim(varLines(lngLine))\r\n\r\n        ' Check for new section\r\n        If strLine = vbNullString Then\r\n            intSection = efsUnknown\r\n            lngStart = 1\r\n\r\n        ' Check for context entry\r\n        ElseIf StartsWith(strLine, \"msgctxt \") Then\r\n            intSection = efsContext\r\n            dItem(\"Context\") = Mid$(strLine, 9)\r\n            lngStart = 9\r\n\r\n        ' Check for reference entry\r\n        ElseIf StartsWith(strLine, \"#: \") Then\r\n            intSection = efsReference\r\n            dItem(\"Reference\") = Mid$(strLine, 4)\r\n            lngStart = 4\r\n\r\n        ' Header\r\n        ElseIf StartsWith(strLine, \"# \") Then\r\n            intSection = efsHeader\r\n            lngStart = 3\r\n\r\n        ' Blank header line\r\n        ElseIf strLine = \"#\" Then\r\n            intSection = efsHeader\r\n            lngStart = 2\r\n\r\n        ' Message\r\n        ElseIf StartsWith(strLine, \"msgid \") Then\r\n            intSection = efsMessageId\r\n            lngStart = 7\r\n\r\n        ' Translation\r\n        ElseIf StartsWith(strLine, \"msgstr \") Then\r\n            intSection = efsMessageString\r\n            lngStart = 8\r\n\r\n        ' Pick back up at first character for multi-line\r\n        Else\r\n            lngStart = 1\r\n\r\n        End If\r\n\r\n        ' Check for section change\r\n        If intSection <> intLastSection Then\r\n            ' Finish out the last section\r\n            If cText.Length > 0 Then\r\n                cText.Remove Len(vbCrLf)\r\n                Select Case intLastSection\r\n                    Case efsHeader:         dItem(\"Comments\") = UnQ(cText.GetStr)\r\n                    Case efsMessageId:      dItem(\"msgid\") = UnQ(cText.GetStr)\r\n                    Case efsMessageString:  dItem(\"Translation\") = UnQ(cText.GetStr)\r\n                End Select\r\n                cText.Clear\r\n            End If\r\n            ' Add to new section\r\n            cText.Add Mid$(strLine, lngStart)\r\n            intLastSection = intSection\r\n            ' See if we are ready to finish out the previous entry\r\n            If intSection = efsUnknown Then\r\n                If dItem.Count > 1 Then\r\n                    colEntries.Add dItem\r\n                    Set dItem = New Dictionary\r\n                End If\r\n            End If\r\n        Else\r\n            ' Continue current section\r\n            cText.Add Mid$(strLine, lngStart)\r\n        End If\r\n\r\n    ' Move to next line in file\r\n    Next lngLine\r\n\r\n    ' Export file for debugging parsing process\r\n    'Set dItem = New Dictionary: dItem.Add \"Entries\", colEntries: WriteFile _\r\n        BuildJsonFile(TypeName(Me), dItem, \"Language File Parsed Output\"), _\r\n        TranslationsPath & PathSep & strLanguage & \".json\"\r\n\r\n    ' Save to add-in database\r\n    AddLanguage strLanguage\r\n    If strLanguage = \"en_US\" Then\r\n        SaveStringEntriesToDatabase colEntries\r\n    Else\r\n        SaveTranslationEntriesToDatabase colEntries, strLanguage\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveStringEntriesToDatabase\r\n' Author    : Adam Waller\r\n' Date      : 11/7/2024\r\n' Purpose   : Save the master list of translation strings (en_US)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SaveStringEntriesToDatabase(colEntries As Collection)\r\n\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n    Dim lngCnt As Long\r\n    Dim dItem As Dictionary\r\n    Dim varKey As Variant\r\n\r\n    ' This might be the add-in file during development, such as when loading\r\n    ' language strings after a fresh build, or it might be at runtime when\r\n    ' syncronizing translation strings. Either way, we want to run in CodeDB.\r\n    Set dbs = CodeDb\r\n\r\n    ' Remove all translations, since we are resetting IDs\r\n    dbs.Execute \"delete from tblTranslation\", dbFailOnError\r\n    Set this.dStrings = New Dictionary\r\n\r\n    ' Reload master list of strings\r\n    dbs.Execute \"delete from tblStrings\", dbFailOnError\r\n    Set rst = dbs.OpenRecordset(\"tblStrings\", dbOpenDynaset)\r\n    With rst\r\n        For lngCnt = 1 To colEntries.Count\r\n            Set dItem = colEntries(lngCnt)\r\n            ' Build key for entry\r\n            varKey = BuildKey(dItem(\"Context\"), dItem(\"msgid\"))\r\n            ' See if the key already exists for this string:\r\n            If this.dStrings.Exists(varKey) Then\r\n                ' Ignore duplicate entries\r\n            Else\r\n                ' Add string to table\r\n                .AddNew\r\n                    !msgid = dItem(\"msgid\")\r\n                    !Context = ZN(dItem(\"Context\"))\r\n                    !Reference = ZN(dItem(\"Reference\"))\r\n                    !Comments = ZN(dItem(\"Comments\"))\r\n                .Update\r\n                ' Save ID to dictionary using key\r\n                .Bookmark = .LastModified\r\n                this.dStrings.Add varKey, CLng(!ID)\r\n            End If\r\n        Next lngCnt\r\n        .Close\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveTranslationEntriesToDatabase\r\n' Author    : Adam Waller\r\n' Date      : 11/7/2024\r\n' Purpose   : Save translated strings to `tblTranslation`.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SaveTranslationEntriesToDatabase(colEntries As Collection, strLanguage As String)\r\n\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n    Dim lngCnt As Long\r\n    Dim dItem As Dictionary\r\n    Dim varKey As Variant\r\n    Dim strID As String\r\n    Dim dAdded As Dictionary\r\n\r\n    ' Make sure we don't attempt to add translations before the main strings.\r\n    If this.dStrings Is Nothing Then\r\n        MsgBox2 \"Load English Language First\", _\r\n            \"Please ensure that the default 'en_US' language is loaded before loading other translation files.\", _\r\n            \"Review language loading logic to correct this issue.\", vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Reload language\r\n    Set dbs = CodeDb\r\n    dbs.Execute \"delete from tblTranslation where Language='\" & strLanguage & \"'\", dbFailOnError\r\n    Set rst = dbs.OpenRecordset(\"tblTranslation\")\r\n    Set this.dTranslation = New Dictionary\r\n    Set dAdded = New Dictionary\r\n\r\n    ' Loop through entries, saving unique entries to database\r\n    With rst\r\n        For lngCnt = 1 To colEntries.Count\r\n            Set dItem = colEntries(lngCnt)\r\n            ' Build key for entry\r\n            varKey = BuildKey(dItem(\"Context\"), dItem(\"msgid\"))\r\n            ' See if the key exists for this string:\r\n            If Not this.dStrings.Exists(varKey) Then\r\n                ' Key not found in master list of strings.\r\n                ' Don't load this (likely orphaned) translation string.\r\n            Else\r\n                ' Only add entries that have values\r\n                If Len(dItem(\"Translation\")) Then\r\n                    ' Using a string for the ID so we can use it as a dictionary key\r\n                    strID = this.dStrings(varKey)\r\n                    ' Make sure we have a string ID that has not already been added\r\n                    If Len(strID) And Not dAdded.Exists(strID) Then\r\n                        .AddNew\r\n                            !Language = strLanguage\r\n                            !StringID = CLng(strID)\r\n                            !Translation = dItem(\"Translation\")\r\n                        .Update\r\n                        ' Keep track of which IDs we have added.\r\n                        dAdded.Add strID, vbNullString\r\n                    End If\r\n                End If\r\n            End If\r\n        Next lngCnt\r\n        .Close\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddLanguage\r\n' Author    : Adam Waller\r\n' Date      : 5/26/2021\r\n' Purpose   : Add a language entry\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub AddLanguage(strLanguage As String)\r\n\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n\r\n    Set dbs = CodeDb\r\n\r\n    ' Clear any existing record\r\n    dbs.Execute \"delete from tblLanguages where ID='\" & strLanguage & \"'\", dbFailOnError\r\n\r\n    ' Add new/replacement record\r\n    Set rst = dbs.OpenRecordset(\"tblLanguages\")\r\n    With rst\r\n        .AddNew\r\n            !ID = strLanguage\r\n            !DisplayName = GetLanguageName(strLanguage)\r\n        .Update\r\n        .Close\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildFileContent\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2021\r\n' Purpose   : Creates the .po/.pot file. (en_US will be treated as a template)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function BuildFileContent(strLanguage As String) As String\r\n\r\n    Const cstrBreak As String = \"zzMSAccessVCS\"\r\n\r\n    Dim dbs As Database\r\n    Dim rst As Recordset\r\n\r\n    ' Make sure we are referencing the add-in (code) database, not the open database\r\n    Set dbs = CodeDb\r\n\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n\r\n        ' Load strings from database\r\n        ' (Note that sorting does not always seem to be consistent. Tried `Left(key, 255)`\r\n        ' but this still had the same issue with one of the entries going out of order\r\n        ' every other time I run the query.)\r\n        Set rst = dbs.OpenRecordset( _\r\n            \"select * from qryTranslatedStrings where Lang='\" & strLanguage & \"'\" & _\r\n            \" order by SortRank, key\", dbOpenSnapshot)\r\n\r\n        ' Loop through strings\r\n        Do While Not rst.EOF\r\n            If rst!msgid = cstrBreak Then Stop\r\n            If Nz(rst!Comments) <> vbNullString Then .Add FormatComment(Nz(rst!Comments))\r\n            If Nz(rst!Reference) <> vbNullString Then .Add \"#: \", Nz(rst!Reference)\r\n            If Nz(rst!Context) <> vbNullString Then .Add \"msgctxt \", Q(Nz(rst!Context))\r\n            .Add \"msgid \", Q(Nz(rst!msgid))\r\n            If strLanguage = \"en_TEST\" Then\r\n                ' Create test strings by captializing English\r\n                ' (This provides an easy way to verify the translation implementation\r\n                '  in the user interface and log files.)\r\n                .Add \"msgstr \", Q(UCase(Nz(rst!msgid)))\r\n            Else\r\n                ' Add the translation string\r\n                .Add \"msgstr \", Q(Nz(rst!Translation))\r\n            End If\r\n            .Add vbNullString ' (blank line before new entry)\r\n            rst.MoveNext\r\n        Loop\r\n        rst.Close\r\n\r\n        ' Remove final blank line\r\n        If .Length > 2 Then .Remove 2\r\n\r\n        ' Return assembled content\r\n        BuildFileContent = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Q\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2021\r\n' Purpose   : Quotes the string, and escapes any embedded quotes. Also breaks long\r\n'           : strings into multiple lines and replaces vbCrLf with \\n. Additionally,\r\n'           : \\n adds an additional line break.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function Q(strText As String) As String\r\n\r\n    ' Maximum line length (Matches Poedit output)\r\n    Const MAX_LEN As Integer = 74\r\n\r\n    Dim strNew As String\r\n    Dim intPos As Integer\r\n    Dim intStart As Integer\r\n    Dim varLines As Variant\r\n    Dim lngLine As Long\r\n    Dim strLine As String\r\n\r\n    ' Flag for debugging\r\n    'If StartsWith(strText, \"Use this tool to duplicate\") Then Stop\r\n\r\n    ' Replace newlines and quotes with placeholder\r\n    strNew = Replace(strText, vbCrLf, \"\\n\")\r\n    strNew = Replace(strNew, vbLf, \"\\n\")\r\n    strNew = Replace(strNew, vbCr, \"\\n\")\r\n    strNew = Replace(strNew, \"\"\"\", \"\\\"\"\")\r\n\r\n    ' Short curcuit on simple strings\r\n    If InStr(1, strNew, \"\\n\") = 0 And Len(strNew) + 5 < MAX_LEN Then\r\n        ' Return single line\r\n        Q = \"\"\"\" & strNew & \"\"\"\"\r\n        Exit Function\r\n    End If\r\n\r\n    ' Split into lines using \\n\r\n    varLines = Split(strNew, \"\\n\")\r\n\r\n    With New clsConcat\r\n\r\n        ' Start with blank string\r\n        .Add \"\"\"\"\"\"\r\n\r\n        ' Loop through lines, building return string\r\n        For lngLine = 0 To UBound(varLines)\r\n            strLine = varLines(lngLine)\r\n\r\n            ' Add line breaks for over 70 characters.\r\n            ' (80 characters is standard for PO files)\r\n            If Len(strLine) > MAX_LEN Then\r\n\r\n                ' Begin at first character\r\n                intStart = 1\r\n\r\n                ' Continue while start is less than full length of line\r\n                Do While intStart < Len(strLine)\r\n                    intPos = MAX_LEN\r\n                    ' Look for space closer to max length first\r\n                    intPos = InStrRev(Mid$(strLine, intStart, MAX_LEN), \" \", intPos)\r\n                    ' If no space found, use full max length\r\n                    If intPos = 0 Then intPos = MAX_LEN\r\n                    ' Break string here, and move start\r\n                    .Add vbCrLf, \"\"\"\", Mid$(strLine, intStart, intPos), \"\"\"\"\r\n                    intStart = intStart + intPos\r\n                    ' Add final partial string\r\n                    If Len(strLine) - intStart < MAX_LEN Then\r\n                        .Add vbCrLf, \"\"\"\", Mid$(strLine, intStart)\r\n                        Exit Do\r\n                    End If\r\n                Loop\r\n                ' We have now reached the end of that line.\r\n                ' Add split delimiter for all but last line\r\n                If lngLine < UBound(varLines) Then .Add \"\\n\"\r\n                .Add \"\"\"\"\r\n            Else\r\n                ' Skip any final blank line\r\n                If strLine = vbNullString And lngLine = UBound(varLines) Then Exit For\r\n                ' Less than max length. Add full string\r\n                .Add vbCrLf, \"\"\"\", strLine\r\n                ' Add split delimiter for all but last line\r\n                If lngLine < UBound(varLines) Then .Add \"\\n\"\r\n                .Add \"\"\"\"\r\n            End If\r\n        Next lngLine\r\n\r\n        ' Return multi-line result\r\n        Q = .GetStr\r\n\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UnQ\r\n' Author    : Adam Waller\r\n' Date      : 6/22/2024\r\n' Purpose   : Unquote a string\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function UnQ(strText As String)\r\n\r\n    Dim strNew As String\r\n\r\n    ' Trim any leading and trailing spaces\r\n    strNew = Trim(strText)\r\n\r\n    ' Combine lines that were previously split\r\n    strNew = Replace(strNew, \"\"\"\" & vbCrLf & \"\"\"\", vbNullString)\r\n\r\n    ' Restore newlines and quotes\r\n    strNew = Replace(strNew, \"\\n\", vbLf)    ' LF (not CRLF) is standard for gettext\r\n    strNew = Replace(strNew, \"\\\"\"\", \"\"\"\")\r\n\r\n    ' Remove beginning and trailing quotes\r\n    If Left$(strNew, 1) = \"\"\"\" Then strNew = Mid$(strNew, 2)\r\n    If Right$(strNew, 1) = \"\"\"\" Then strNew = Left$(strNew, Len(strNew) - 1)\r\n\r\n    ' Return result\r\n    UnQ = strNew\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FormatComment\r\n' Author    : Adam Waller\r\n' Date      : 11/8/2024\r\n' Purpose   : Format a single or multi-line comment.\r\n'           : Note: This does not automatically wrap lines, but preserves existing\r\n'           : line breaks.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function FormatComment(strComment As String) As String\r\n\r\n    Dim strNew As String\r\n    Dim varLines As Variant\r\n    Dim strLine As String\r\n    Dim lngLine As Long\r\n\r\n    ' Standardize on vbCrLf\r\n    strNew = Replace(strComment, vbCrLf, \"\\n\")\r\n    strNew = Replace(strNew, vbCr, \"\\n\")\r\n    strNew = Replace(strNew, vbLf, \"\\n\")\r\n    strNew = Replace(strNew, \"\\n\", vbCrLf)\r\n\r\n    With New clsConcat\r\n        ' Split by lines\r\n        varLines = Split(strNew, vbCrLf)\r\n        For lngLine = 0 To UBound(varLines)\r\n            strLine = varLines(lngLine)\r\n            .Add \"# \", strLine, vbCrLf\r\n        Next lngLine\r\n        If .Length > 2 Then .Remove Len(vbCrLf)\r\n        FormatComment = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LanguageIdToString\r\n' Author    : Adam Waller\r\n' Date      : 8/21/2024\r\n' Purpose   : Used to translate the UI language constant to the language code.\r\n'           : Application.LanguageSettings.LanguageID(msoLanguageIDUI)\r\n'           : Compiled using vlookup in Excel from the following two sources:\r\n'           : https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/\r\n'           : https://learn.microsoft.com/en-us/office/vba/api/Office.MsoLanguageID\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function LanguageIdToString(lngID) As String\r\n\r\n    Select Case lngID\r\n        Case 1078:  LanguageIdToString = \"af_ZA\" 'South Africa (msoLanguageIDAfrikaans)\r\n        Case 1052:  LanguageIdToString = \"sq_AL\" 'Albania (msoLanguageIDAlbanian)\r\n        Case 1118:  LanguageIdToString = \"am_ET\" 'Ethiopia (msoLanguageIDAmharic)\r\n        Case 1025:  LanguageIdToString = \"ar_SA\" 'Saudi Arabia (msoLanguageIDArabic)\r\n        Case 5121:  LanguageIdToString = \"ar_DZ\" 'Algeria (msoLanguageIDArabicAlgeria)\r\n        Case 15361: LanguageIdToString = \"ar_BH\" 'Bahrain (msoLanguageIDArabicBahrain)\r\n        Case 3073:  LanguageIdToString = \"ar_EG\" 'Egypt (msoLanguageIDArabicEgypt)\r\n        Case 2049:  LanguageIdToString = \"ar_IQ\" 'Iraq (msoLanguageIDArabicIraq)\r\n        Case 11265: LanguageIdToString = \"ar_JO\" 'Jordan (msoLanguageIDArabicJordan)\r\n        Case 13313: LanguageIdToString = \"ar_KW\" 'Kuwait (msoLanguageIDArabicKuwait)\r\n        Case 12289: LanguageIdToString = \"ar_LB\" 'Lebanon (msoLanguageIDArabicLebanon)\r\n        Case 4097:  LanguageIdToString = \"ar_LY\" 'Libya (msoLanguageIDArabicLibya)\r\n        Case 6145:  LanguageIdToString = \"ar_MA\" 'Morocco (msoLanguageIDArabicMorocco)\r\n        Case 8193:  LanguageIdToString = \"ar_OM\" 'Oman (msoLanguageIDArabicOman)\r\n        Case 16385: LanguageIdToString = \"ar_QA\" 'Qatar (msoLanguageIDArabicQatar)\r\n        Case 10241: LanguageIdToString = \"ar_SY\" 'Syria (msoLanguageIDArabicSyria)\r\n        Case 7169:  LanguageIdToString = \"ar_TN\" 'Tunisia (msoLanguageIDArabicTunisia)\r\n        Case 14337: LanguageIdToString = \"ar_AE\" 'U.A.E. (msoLanguageIDArabicUAE)\r\n        Case 9217:  LanguageIdToString = \"ar_YE\" 'Yemen (msoLanguageIDArabicYemen)\r\n        Case 1067:  LanguageIdToString = \"hy_AM\" 'Armenia (msoLanguageIDArmenian)\r\n        Case 1101:  LanguageIdToString = \"as_IN\" 'India (msoLanguageIDAssamese)\r\n        Case 2092:  LanguageIdToString = \"az_Cyrl_AZ\" 'Azerbaijan (msoLanguageIDAzeriCyrillic)\r\n        Case 1068:  LanguageIdToString = \"az_Latn_AZ\" 'Azerbaijan (msoLanguageIDAzeriLatin)\r\n        Case 1069:  LanguageIdToString = \"eu_ES\" 'Spain (msoLanguageIDBasque)\r\n        Case 2067:  LanguageIdToString = \"nl_BE\" 'Belgium (msoLanguageIDBelgianDutch)\r\n        Case 2060:  LanguageIdToString = \"fr_BE\" 'Belgium (msoLanguageIDBelgianFrench)\r\n        Case 1093:  LanguageIdToString = \"bn_IN\" 'India (msoLanguageIDBengali)\r\n        Case 4122:  LanguageIdToString = \"hr_BA\" 'Bosnia and Herzegovina (msoLanguageIDBosnian)\r\n        Case 8218:  LanguageIdToString = \"bs_Cyrl_BA\" 'Bosnia and Herzegovina (msoLanguageIDBosnianBosniaHerzegovinaCyrillic)\r\n        Case 5146:  LanguageIdToString = \"bs_Latn_BA\" 'Bosnia and Herzegovina (msoLanguageIDBosnianBosniaHerzegovinaLatin)\r\n        Case 1046:  LanguageIdToString = \"pt_BR\" 'Brazil (msoLanguageIDBrazilianPortuguese)\r\n        Case 1026:  LanguageIdToString = \"bg_BG\" 'Bulgaria (msoLanguageIDBulgarian)\r\n        Case 1109:  LanguageIdToString = \"my_MM\" 'Myanmar (msoLanguageIDBurmese)\r\n        Case 1059:  LanguageIdToString = \"be_BY\" 'Belarus (msoLanguageIDByelorussian)\r\n        Case 1027:  LanguageIdToString = \"ca_ES\" 'Spain (msoLanguageIDCatalan)\r\n        Case 1116:  LanguageIdToString = \"chr_Cher_US\" 'United States (msoLanguageIDCherokee)\r\n        Case 3076:  LanguageIdToString = \"zh_HK\" 'Hong Kong S.A.R. (msoLanguageIDChineseHongKongSAR)\r\n        Case 5124:  LanguageIdToString = \"zh_MO\" 'Macao S.A.R. (msoLanguageIDChineseMacaoSAR)\r\n        Case 4100:  LanguageIdToString = \"zh_SG\" 'Singapore (msoLanguageIDChineseSingapore)\r\n        Case 1050:  LanguageIdToString = \"hr_HR\" 'Croatia (msoLanguageIDCroatian)\r\n        Case 1029:  LanguageIdToString = \"cs_CZ\" 'Czech Republic (msoLanguageIDCzech)\r\n        Case 1030:  LanguageIdToString = \"da_DK\" 'Denmark (msoLanguageIDDanish)\r\n        Case 1125:  LanguageIdToString = \"dv_MV\" 'Maldives (msoLanguageIDDivehi)\r\n        Case 1043:  LanguageIdToString = \"nl_NL\" 'Netherlands (msoLanguageIDDutch)\r\n        Case 3081:  LanguageIdToString = \"en_AU\" 'Australia (msoLanguageIDEnglishAUS)\r\n        Case 10249: LanguageIdToString = \"en_BZ\" 'Belize (msoLanguageIDEnglishBelize)\r\n        Case 4105:  LanguageIdToString = \"en_CA\" 'Canada (msoLanguageIDEnglishCanadian)\r\n        Case 9225:  LanguageIdToString = \"en_029\" 'Caribbean (msoLanguageIDEnglishCaribbean)\r\n        Case 6153:  LanguageIdToString = \"en_IE\" 'Ireland (msoLanguageIDEnglishIreland)\r\n        Case 8201:  LanguageIdToString = \"en_JM\" 'Jamaica (msoLanguageIDEnglishJamaica)\r\n        Case 5129:  LanguageIdToString = \"en_NZ\" 'New Zealand (msoLanguageIDEnglishNewZealand)\r\n        Case 13321: LanguageIdToString = \"en_PH\" 'Republic of the Philippines (msoLanguageIDEnglishPhilippines)\r\n        Case 7177:  LanguageIdToString = \"en_ZA\" 'South Africa (msoLanguageIDEnglishSouthAfrica)\r\n        Case 11273: LanguageIdToString = \"en_TT\" 'Trinidad and Tobago (msoLanguageIDEnglishTrinidadTobago)\r\n        Case 2057:  LanguageIdToString = \"en_GB\" 'United Kingdom (msoLanguageIDEnglishUK)\r\n        Case 1033:  LanguageIdToString = \"en_US\" 'United States (msoLanguageIDEnglishUS)\r\n        Case 12297: LanguageIdToString = \"en_ZW\" 'Zimbabwe (msoLanguageIDEnglishZimbabwe)\r\n        Case 1061:  LanguageIdToString = \"et_EE\" 'Estonia (msoLanguageIDEstonian)\r\n        Case 1080:  LanguageIdToString = \"fo_FO\" 'Faroe Islands (msoLanguageIDFaeroese)\r\n        Case 1065:  LanguageIdToString = \"fa_IR\" 'Iran (msoLanguageIDFarsi)\r\n        Case 1124:  LanguageIdToString = \"fil_PH\" 'Philippines (msoLanguageIDFilipino)\r\n        Case 1035:  LanguageIdToString = \"fi_FI\" 'Finland (msoLanguageIDFinnish)\r\n        Case 1036:  LanguageIdToString = \"fr_FR\" 'France (msoLanguageIDFrench)\r\n        Case 11276: LanguageIdToString = \"fr_CM\" 'Cameroon (msoLanguageIDFrenchCameroon)\r\n        Case 3084:  LanguageIdToString = \"fr_CA\" 'Canada (msoLanguageIDFrenchCanadian)\r\n        Case 9228:  LanguageIdToString = \"fr_CD\" 'Congo, DRC (msoLanguageIDFrenchCongoDRC)\r\n        Case 12300: LanguageIdToString = \"fr_CI\" 'Côte d'Ivoire (msoLanguageIDFrenchCotedIvoire)\r\n        Case 15372: LanguageIdToString = \"fr_HT\" 'Haiti (msoLanguageIDFrenchHaiti)\r\n        Case 5132:  LanguageIdToString = \"fr_LU\" 'Luxembourg (msoLanguageIDFrenchLuxembourg)\r\n        Case 13324: LanguageIdToString = \"fr_ML\" 'Mali (msoLanguageIDFrenchMali)\r\n        Case 6156:  LanguageIdToString = \"fr_MC\" 'Principality of Monaco (msoLanguageIDFrenchMonaco)\r\n        Case 14348: LanguageIdToString = \"fr_MA\" 'Morocco (msoLanguageIDFrenchMorocco)\r\n        Case 8204:  LanguageIdToString = \"fr_RE\" 'Reunion (msoLanguageIDFrenchReunion)\r\n        Case 10252: LanguageIdToString = \"fr_SN\" 'Senegal (msoLanguageIDFrenchSenegal)\r\n        Case 7180:  LanguageIdToString = \"fr_029\" 'Caribbean (msoLanguageIDFrenchWestIndies)\r\n        Case 1122:  LanguageIdToString = \"fy_NL\" 'Netherlands (msoLanguageIDFrisianNetherlands)\r\n        Case 1127:  LanguageIdToString = \"ff_NG\" 'Nigeria (msoLanguageIDFulfulde)\r\n        Case 1127:  LanguageIdToString = \"ff_Latn_NG\" 'Nigeria (msoLanguageIDFulfulde)\r\n        Case 2108:  LanguageIdToString = \"ga_IE\" 'Ireland (msoLanguageIDGaelicIreland)\r\n        Case 1110:  LanguageIdToString = \"gl_ES\" 'Spain (msoLanguageIDGalician)\r\n        Case 1079:  LanguageIdToString = \"ka_GE\" 'Georgia (msoLanguageIDGeorgian)\r\n        Case 1031:  LanguageIdToString = \"de_DE\" 'Germany (msoLanguageIDGerman)\r\n        Case 3079:  LanguageIdToString = \"de_AT\" 'Austria (msoLanguageIDGermanAustria)\r\n        Case 5127:  LanguageIdToString = \"de_LI\" 'Liechtenstein (msoLanguageIDGermanLiechtenstein)\r\n        Case 4103:  LanguageIdToString = \"de_LU\" 'Luxembourg (msoLanguageIDGermanLuxembourg)\r\n        Case 1032:  LanguageIdToString = \"el_GR\" 'Greece (msoLanguageIDGreek)\r\n        Case 1140:  LanguageIdToString = \"gn_PY\" 'Paraguay (msoLanguageIDGuarani)\r\n        Case 1095:  LanguageIdToString = \"gu_IN\" 'India (msoLanguageIDGujarati)\r\n        Case 1128:  LanguageIdToString = \"ha_Latn_NG\" 'Nigeria (msoLanguageIDHausa)\r\n        Case 1141:  LanguageIdToString = \"haw_US\" 'United States (msoLanguageIDHawaiian)\r\n        Case 1037:  LanguageIdToString = \"he_IL\" 'Israel (msoLanguageIDHebrew)\r\n        Case 1081:  LanguageIdToString = \"hi_IN\" 'India (msoLanguageIDHindi)\r\n        Case 1038:  LanguageIdToString = \"hu_HU\" 'Hungary (msoLanguageIDHungarian)\r\n        Case 1039:  LanguageIdToString = \"is_IS\" 'Iceland (msoLanguageIDIcelandic)\r\n        Case 1136:  LanguageIdToString = \"ig_NG\" 'Nigeria (msoLanguageIDIgbo)\r\n        Case 1057:  LanguageIdToString = \"id_ID\" 'Indonesia (msoLanguageIDIndonesian)\r\n        Case 1117:  LanguageIdToString = \"iu_Cans_CA\" 'Canada (msoLanguageIDInuktitut)\r\n        Case 1040:  LanguageIdToString = \"it_IT\" 'Italy (msoLanguageIDItalian)\r\n        Case 1041:  LanguageIdToString = \"ja_JP\" 'Japan (msoLanguageIDJapanese)\r\n        Case 1099:  LanguageIdToString = \"kn_IN\" 'India (msoLanguageIDKannada)\r\n        Case 1137:  LanguageIdToString = \"kr_Latn_NG\" 'Nigeria (msoLanguageIDKanuri)\r\n        Case 1120:  LanguageIdToString = \"ks_Arab\" 'Perso-Arabic (msoLanguageIDKashmiri)\r\n        Case 2144:  LanguageIdToString = \"ks_Deva_IN\" 'India (msoLanguageIDKashmiriDevanagari)\r\n        Case 1087:  LanguageIdToString = \"kk_KZ\" 'Kazakhstan (msoLanguageIDKazakh)\r\n        Case 1107:  LanguageIdToString = \"km_KH\" 'Cambodia (msoLanguageIDKhmer)\r\n        Case 1088:  LanguageIdToString = \"ky_KG\" 'Kyrgyzstan (msoLanguageIDKirghiz)\r\n        Case 1111:  LanguageIdToString = \"kok_IN\" 'India (msoLanguageIDKonkani)\r\n        Case 1042:  LanguageIdToString = \"ko_KR\" 'Korea (msoLanguageIDKorean)\r\n        Case 1108:  LanguageIdToString = \"lo_LA\" 'Lao P.D.R. (msoLanguageIDLao)\r\n        Case 1142:  LanguageIdToString = \"la_VA\" 'Vatican City (msoLanguageIDLatin)\r\n        Case 1062:  LanguageIdToString = \"lv_LV\" 'Latvia (msoLanguageIDLatvian)\r\n        Case 1063:  LanguageIdToString = \"lt_LT\" 'Lithuania (msoLanguageIDLithuanian)\r\n        Case 1071:  LanguageIdToString = \"mk_MK\" 'North Macedonia (msoLanguageIDMacedonianFYROM)\r\n        Case 1100:  LanguageIdToString = \"ml_IN\" 'India (msoLanguageIDMalayalam)\r\n        Case 2110:  LanguageIdToString = \"ms_BN\" 'Brunei Darussalam (msoLanguageIDMalayBruneiDarussalam)\r\n        Case 1086:  LanguageIdToString = \"ms_MY\" 'Malaysia (msoLanguageIDMalaysian)\r\n        Case 1082:  LanguageIdToString = \"mt_MT\" 'Malta (msoLanguageIDMaltese)\r\n        Case 1153:  LanguageIdToString = \"mi_NZ\" 'New Zealand (msoLanguageIDMaori)\r\n        Case 1102:  LanguageIdToString = \"mr_IN\" 'India (msoLanguageIDMarathi)\r\n        Case 2058:  LanguageIdToString = \"es_MX\" 'Mexico (msoLanguageIDMexicanSpanish)\r\n        Case 1104:  LanguageIdToString = \"mn_MN\" 'Mongolia (msoLanguageIDMongolian)\r\n        Case 1121:  LanguageIdToString = \"ne_NP\" 'Nepal (msoLanguageIDNepali)\r\n        Case 1044:  LanguageIdToString = \"nb_NO\" 'Norway (msoLanguageIDNorwegianBokmol)\r\n        Case 2068:  LanguageIdToString = \"nn_NO\" 'Norway (msoLanguageIDNorwegianNynorsk)\r\n        Case 1096:  LanguageIdToString = \"or_IN\" 'India (msoLanguageIDOriya)\r\n        Case 1138:  LanguageIdToString = \"om_ET\" 'Ethiopia (msoLanguageIDOromo)\r\n        Case 1123:  LanguageIdToString = \"ps_AF\" 'Afghanistan (msoLanguageIDPashto)\r\n        Case 1045:  LanguageIdToString = \"pl_PL\" 'Poland (msoLanguageIDPolish)\r\n        Case 2070:  LanguageIdToString = \"pt_PT\" 'Portugal (msoLanguageIDPortuguese)\r\n        Case 1094:  LanguageIdToString = \"pa_IN\" 'India (msoLanguageIDPunjabi)\r\n        Case 1131:  LanguageIdToString = \"quz_BO\" 'Bolivia (msoLanguageIDQuechuaBolivia)\r\n        Case 2155:  LanguageIdToString = \"quz_EC\" 'Ecuador (msoLanguageIDQuechuaEcuador)\r\n        Case 3179:  LanguageIdToString = \"quz_PE\" 'Peru (msoLanguageIDQuechuaPeru)\r\n        Case 1047:  LanguageIdToString = \"rm_CH\" 'Switzerland (msoLanguageIDRhaetoRomanic)\r\n        Case 1048:  LanguageIdToString = \"ro_RO\" 'Romania (msoLanguageIDRomanian)\r\n        Case 2072:  LanguageIdToString = \"ro_MD\" 'Moldova (msoLanguageIDRomanianMoldova)\r\n        Case 1049:  LanguageIdToString = \"ru_RU\" 'Russia (msoLanguageIDRussian)\r\n        Case 2073:  LanguageIdToString = \"ru_MD\" 'Moldova (msoLanguageIDRussianMoldova)\r\n        Case 1083:  LanguageIdToString = \"se_NO\" 'Norway (msoLanguageIDSamiLappish)\r\n        Case 1103:  LanguageIdToString = \"sa_IN\" 'India (msoLanguageIDSanskrit)\r\n        Case 1132:  LanguageIdToString = \"nso_ZA\" 'South Africa (msoLanguageIDSepedi)\r\n        Case 7194:  LanguageIdToString = \"sr_Cyrl_BA\" 'Bosnia and Herzegovina (msoLanguageIDSerbianBosniaHerzegovinaCyrillic)\r\n        Case 6170:  LanguageIdToString = \"sr_Latn_BA\" 'Bosnia and Herzegovina (msoLanguageIDSerbianBosniaHerzegovinaLatin)\r\n        Case 3098:  LanguageIdToString = \"sr_Cyrl_CS\" 'Serbia and Montenegro (Former) (msoLanguageIDSerbianCyrillic)\r\n        Case 2074:  LanguageIdToString = \"sr_Latn_CS\" 'Serbia and Montenegro (Former) (msoLanguageIDSerbianLatin)\r\n        Case 1072:  LanguageIdToString = \"st_ZA\" 'South Africa (msoLanguageIDSesotho)\r\n        Case 2052:  LanguageIdToString = \"zh_CN\" 'People's Republic of China (msoLanguageIDSimplifiedChinese)\r\n        Case 2137:  LanguageIdToString = \"sd_Arab_PK\" 'Islamic Republic of Pakistan (msoLanguageIDSindhiPakistan)\r\n        Case 1115:  LanguageIdToString = \"si_LK\" 'Sri Lanka (msoLanguageIDSinhalese)\r\n        Case 1051:  LanguageIdToString = \"sk_SK\" 'Slovakia (msoLanguageIDSlovak)\r\n        Case 1060:  LanguageIdToString = \"sl_SI\" 'Slovenia (msoLanguageIDSlovenian)\r\n        Case 1143:  LanguageIdToString = \"so_SO\" 'Somalia (msoLanguageIDSomali)\r\n        Case 1070:  LanguageIdToString = \"hsb_DE\" 'Germany (msoLanguageIDSorbian)\r\n        Case 1034:  LanguageIdToString = \"es_ES_tradnl\" 'Spain (msoLanguageIDSpanish)\r\n        Case 11274: LanguageIdToString = \"es_AR\" 'Argentina (msoLanguageIDSpanishArgentina)\r\n        Case 16394: LanguageIdToString = \"es_BO\" 'Bolivia (msoLanguageIDSpanishBolivia)\r\n        Case 13322: LanguageIdToString = \"es_CL\" 'Chile (msoLanguageIDSpanishChile)\r\n        Case 9226:  LanguageIdToString = \"es_CO\" 'Colombia (msoLanguageIDSpanishColombia)\r\n        Case 5130:  LanguageIdToString = \"es_CR\" 'Costa Rica (msoLanguageIDSpanishCostaRica)\r\n        Case 7178:  LanguageIdToString = \"es_DO\" 'Dominican Republic (msoLanguageIDSpanishDominicanRepublic)\r\n        Case 12298: LanguageIdToString = \"es_EC\" 'Ecuador (msoLanguageIDSpanishEcuador)\r\n        Case 17418: LanguageIdToString = \"es_SV\" 'El Salvador (msoLanguageIDSpanishElSalvador)\r\n        Case 4106:  LanguageIdToString = \"es_GT\" 'Guatemala (msoLanguageIDSpanishGuatemala)\r\n        Case 18442: LanguageIdToString = \"es_HN\" 'Honduras (msoLanguageIDSpanishHonduras)\r\n        Case 3082:  LanguageIdToString = \"es_ES\" 'Spain (msoLanguageIDSpanishModernSort)\r\n        Case 19466: LanguageIdToString = \"es_NI\" 'Nicaragua (msoLanguageIDSpanishNicaragua)\r\n        Case 6154:  LanguageIdToString = \"es_PA\" 'Panama (msoLanguageIDSpanishPanama)\r\n        Case 15370: LanguageIdToString = \"es_PY\" 'Paraguay (msoLanguageIDSpanishParaguay)\r\n        Case 10250: LanguageIdToString = \"es_PE\" 'Peru (msoLanguageIDSpanishPeru)\r\n        Case 20490: LanguageIdToString = \"es_PR\" 'Puerto Rico (msoLanguageIDSpanishPuertoRico)\r\n        Case 14346: LanguageIdToString = \"es_UY\" 'Uruguay (msoLanguageIDSpanishUruguay)\r\n        Case 8202:  LanguageIdToString = \"es_VE\" 'Bolivarian Republic of Venezuela (msoLanguageIDSpanishVenezuela)\r\n        Case 1089:  LanguageIdToString = \"sw_KE\" 'Kenya (msoLanguageIDSwahili)\r\n        Case 1053:  LanguageIdToString = \"sv_SE\" 'Sweden (msoLanguageIDSwedish)\r\n        Case 2077:  LanguageIdToString = \"sv_FI\" 'Finland (msoLanguageIDSwedishFinland)\r\n        Case 4108:  LanguageIdToString = \"fr_CH\" 'Switzerland (msoLanguageIDSwissFrench)\r\n        Case 2055:  LanguageIdToString = \"de_CH\" 'Switzerland (msoLanguageIDSwissGerman)\r\n        Case 2064:  LanguageIdToString = \"it_CH\" 'Switzerland (msoLanguageIDSwissItalian)\r\n        Case 1114:  LanguageIdToString = \"syr_SY\" 'Syria (msoLanguageIDSyriac)\r\n        Case 1064:  LanguageIdToString = \"tg_Cyrl_TJ\" 'Tajikistan (msoLanguageIDTajik)\r\n        Case 1119:  LanguageIdToString = \"tzm_Arab_MA\" 'Morocco (msoLanguageIDTamazight)\r\n        Case 2143:  LanguageIdToString = \"tzm_Latn_DZ\" 'Algeria (msoLanguageIDTamazightLatin)\r\n        Case 1097:  LanguageIdToString = \"ta_IN\" 'India (msoLanguageIDTamil)\r\n        Case 1092:  LanguageIdToString = \"tt_RU\" 'Russia (msoLanguageIDTatar)\r\n        Case 1098:  LanguageIdToString = \"te_IN\" 'India (msoLanguageIDTelugu)\r\n        Case 1054:  LanguageIdToString = \"th_TH\" 'Thailand (msoLanguageIDThai)\r\n        Case 1105:  LanguageIdToString = \"bo_CN\" 'People's Republic of China (msoLanguageIDTibetan)\r\n        Case 2163:  LanguageIdToString = \"ti_ER\" 'Eritrea (msoLanguageIDTigrignaEritrea)\r\n        Case 1139:  LanguageIdToString = \"ti_ET\" 'Ethiopia (msoLanguageIDTigrignaEthiopic)\r\n        Case 1028:  LanguageIdToString = \"zh_TW\" 'Taiwan (msoLanguageIDTraditionalChinese)\r\n        Case 1073:  LanguageIdToString = \"ts_ZA\" 'South Africa (msoLanguageIDTsonga)\r\n        Case 1074:  LanguageIdToString = \"tn_ZA\" 'South Africa (msoLanguageIDTswana)\r\n        Case 1055:  LanguageIdToString = \"tr_TR\" 'Turkey (msoLanguageIDTurkish)\r\n        Case 1090:  LanguageIdToString = \"tk_TM\" 'Turkmenistan (msoLanguageIDTurkmen)\r\n        Case 1058:  LanguageIdToString = \"uk_UA\" 'Ukraine (msoLanguageIDUkrainian)\r\n        Case 1056:  LanguageIdToString = \"ur_PK\" 'Islamic Republic of Pakistan (msoLanguageIDUrdu)\r\n        Case 2115:  LanguageIdToString = \"uz_Cyrl_UZ\" 'Uzbekistan (msoLanguageIDUzbekCyrillic)\r\n        Case 1091:  LanguageIdToString = \"uz_Latn_UZ\" 'Uzbekistan (msoLanguageIDUzbekLatin)\r\n        Case 1075:  LanguageIdToString = \"ve_ZA\" 'South Africa (msoLanguageIDVenda)\r\n        Case 1066:  LanguageIdToString = \"vi_VN\" 'Vietnam (msoLanguageIDVietnamese)\r\n        Case 1106:  LanguageIdToString = \"cy_GB\" 'United Kingdom (msoLanguageIDWelsh)\r\n        Case 1076:  LanguageIdToString = \"xh_ZA\" 'South Africa (msoLanguageIDXhosa)\r\n        Case 1144:  LanguageIdToString = \"ii_CN\" 'People's Republic of China (msoLanguageIDYi)\r\n        Case 1085:  LanguageIdToString = \"yi_001\" 'World (msoLanguageIDYiddish)\r\n        Case 1130:  LanguageIdToString = \"yo_NG\" 'Nigeria (msoLanguageIDYoruba)\r\n        Case 1077:  LanguageIdToString = \"zu_ZA\" 'South Africa (msoLanguageIDZulu)\r\n    End Select\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Terminate\r\n' Author    : Adam Waller\r\n' Date      : 8/23/2024\r\n' Purpose   : Clear object references\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Terminate()\r\n    Set this.dStrings = Nothing\r\n    Set this.dTranslation = Nothing\r\n    Set this.dTranslated = Nothing\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsVCSIndex.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsVCSIndex\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsVCSIndex\r\n' Author    : Adam Waller\r\n' Date      : 11/25/2020\r\n' Purpose   : Maintain index of source files and database objects so that changes\r\n'           : can be detected.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' File name for index\r\nPrivate Const cstrFileName As String = \"vcs-index.json\"\r\n\r\n' General properties\r\nPublic MergeBuildDate As Date\r\nPublic FullBuildDate As Date\r\nPublic ExportDate As Date\r\nPublic FullExportDate As Date\r\nPublic OptionsHash As String\r\nPublic Disabled As Boolean\r\n\r\n' Git integration\r\nPublic LastMergedCommit As String\r\n\r\n' Action types for update function\r\nPublic Enum eIndexOperationType\r\n    eatExport\r\n    eatImport\r\n    eatAltExport    ' Alternate export folder, such as a scan for changes\r\nEnd Enum\r\n\r\n' Index of component/file information, based on source files\r\nPrivate m_dIndex As Dictionary\r\nPrivate m_dGitIndex As Dictionary\r\nPrivate m_strFile As String\r\nPrivate m_Git As clsGitIntegration\r\nPrivate m_Conflicts As clsConflicts\r\nPrivate m_strTempExportFolderPath As String\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadFromFile\r\n' Author    : Adam Waller\r\n' Date      : 11/24/2020\r\n' Purpose   : Load the state for the project.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadFromFile(Optional strFile As String)\r\n\r\n    Dim dFile As Dictionary\r\n    Dim dItem As Dictionary\r\n    Dim varKey As Variant\r\n    Dim blnSavedValue As Boolean\r\n\r\n    ' Exit if we have disabled the index functionality\r\n    If Me.Disabled Then Exit Sub\r\n\r\n    ' Reset class to blank values\r\n    Call Class_Initialize\r\n\r\n    ' Load properties\r\n    m_strFile = strFile\r\n    If m_strFile = vbNullString Then m_strFile = DefaultFilePath\r\n    If FSO.FileExists(m_strFile) Then\r\n        ' Convert dates back to local time for processing\r\n        blnSavedValue = JsonOptions.ConvertDateToIso\r\n        JsonOptions.ConvertDateToIso = True\r\n        Set dFile = ReadJsonFile(m_strFile)\r\n        JsonOptions.ConvertDateToIso = blnSavedValue\r\n        If Not dFile Is Nothing Then\r\n            If dFile.Exists(\"Items\") Then\r\n                ' Load properties from class\r\n                For Each varKey In dFile(\"Items\").Keys\r\n                    If m_dIndex.Exists(varKey) Then\r\n                        Select Case varKey\r\n                            Case \"Components\", \"AlternateExport\"\r\n                                ' Load as dictionary\r\n                                Set dItem = dFile(\"Items\")(varKey)\r\n                                Set m_dIndex(varKey) = dItem\r\n                            Case Else\r\n                                ' Set property by name\r\n                                CallByName Me, CStr(varKey), VbLet, Nz(dFile(\"Items\")(varKey), 0)\r\n                        End Select\r\n                    End If\r\n                Next varKey\r\n            End If\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Save\r\n' Author    : Adam Waller\r\n' Date      : 11/24/2020\r\n' Purpose   : Save to a file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Save(Optional strInFolder As String)\r\n\r\n    Dim varKey As Variant\r\n    Dim varValue As Variant\r\n    Dim strFile As String\r\n    Dim blnSavedValue As Boolean\r\n\r\n    ' Exit if we have disabled the index functionality\r\n    If Me.Disabled Then Exit Sub\r\n\r\n    ' Load dictionary from properties\r\n    For Each varKey In m_dIndex.Keys\r\n        Select Case varKey\r\n            Case \"Components\", \"AlternateExport\"\r\n            Case Else\r\n                varValue = CallByName(Me, CStr(varKey), VbGet)\r\n                ' Save blank dates as null\r\n                If Right(varKey, 4) = \"Date\" Then\r\n                    m_dIndex(varKey) = ZNDate(varValue)\r\n                Else\r\n                    m_dIndex(varKey) = CStr(varValue)\r\n                End If\r\n        End Select\r\n    Next varKey\r\n\r\n    ' Sort files and components\r\n    SortComponentSection m_dIndex, \"Components\"\r\n\r\n    ' Remove the AlternateExport section, since this is not\r\n    ' needed after the completion of the export process.\r\n    If m_dIndex.Exists(\"AlternateExport\") Then m_dIndex.Remove \"AlternateExport\"\r\n\r\n    ' Build file path\r\n    If strInFolder = vbNullString Then\r\n        strFile = m_strFile\r\n    Else\r\n        strFile = StripSlash(strInFolder) & PathSep & cstrFileName\r\n    End If\r\n\r\n    ' Turn on ISO date conversion to save index dates in UTC\r\n    blnSavedValue = JsonOptions.ConvertDateToIso\r\n    JsonOptions.ConvertDateToIso = True\r\n\r\n    ' Save index to file\r\n    If m_strFile <> vbNullString Then\r\n        WriteFile BuildJsonFile(TypeName(Me), m_dIndex, \"Version Control System Index\"), strFile\r\n    End If\r\n\r\n    ' Restore previous setting\r\n    JsonOptions.ConvertDateToIso = blnSavedValue\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SortComponentSection\r\n' Author    : Adam Waller\r\n' Date      : 9/1/2022\r\n' Purpose   : Save a named section of components as\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SortComponentSection(ByRef dIndex As Dictionary, strSection As String)\r\n\r\n    Dim dComponents As Dictionary\r\n    Dim varCat As Variant\r\n\r\n    If dIndex.Exists(strSection) Then\r\n        Set dComponents = dIndex(strSection)\r\n        For Each varCat In dComponents.Keys\r\n            ' Sort list of files in each category\r\n            Set dComponents(varCat) = SortDictionaryByKeys(dComponents(varCat))\r\n        Next varCat\r\n\r\n        ' Sort list of categories\r\n        Set dIndex(strSection) = SortDictionaryByKeys(dComponents)\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Update\r\n' Author    : Adam Waller\r\n' Date      : 11/30/2020\r\n' Purpose   : Updates an item in the index.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Update(cItem As IDbComponent, intAction As eIndexOperationType, _\r\n    strFileHash As String, Optional strOtherHash As String, Optional dteDateTime As Date) As Dictionary\r\n\r\n    Dim dItem As Dictionary\r\n    Dim strSection As String\r\n\r\n    ' Exit if we have disabled the index functionality\r\n    If Me.Disabled Then Exit Function\r\n\r\n    ' Determine section\r\n    If intAction = eatAltExport Then\r\n        strSection = \"AlternateExport\"\r\n    Else\r\n        strSection = \"Components\"\r\n    End If\r\n\r\n    ' Look up dictionary item, creating if needed.\r\n    Set dItem = Me.Item(cItem, , strSection).dParent\r\n\r\n    ' Update dictionary values\r\n    With dItem\r\n\r\n        ' Update hash\r\n        If strFileHash = vbNullString Then\r\n            ' Remove hash if not used.\r\n            If .Exists(\"FileHash\") Then .Remove \"FileHash\"\r\n        Else\r\n            .Item(\"FileHash\") = strFileHash\r\n        End If\r\n\r\n        ' Code module hash (Detects changes to VBA code that are not reflected in modified date)\r\n        If strOtherHash = vbNullString Then\r\n            ' Remove hash if not used.\r\n            If .Exists(\"OtherHash\") Then .Remove \"OtherHash\"\r\n        Else\r\n            .Item(\"OtherHash\") = strOtherHash\r\n        End If\r\n\r\n        ' Add timestamp, defaulting to now\r\n        If dteDateTime = 0 Then dteDateTime = Now\r\n        Select Case intAction\r\n            Case eatExport, eatAltExport\r\n                .Item(\"ExportDate\") = dteDateTime\r\n            Case eatImport\r\n                .Item(\"ImportDate\") = dteDateTime\r\n        End Select\r\n\r\n        ' Save timestamp of exported source file.\r\n        dteDateTime = GetSourceModifiedDate(cItem, cItem.SourceFile)\r\n        .Item(\"SourceModified\") = ZNDate(dteDateTime)\r\n\r\n        ' Save hash of file properties\r\n        .Item(\"FilePropertiesHash\") = GetSourceFilesPropertyHash(cItem)\r\n\r\n    End With\r\n\r\n    ' Return dictionary object in case the caller wants to\r\n    ' manipulate additional values.\r\n    Set Update = dItem\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UpdateFromAltExport\r\n' Author    : Adam Waller\r\n' Date      : 9/1/2022\r\n' Purpose   : Update the index entry from the alternate export section. This is done\r\n'           : after resolving a conflict where the exported file in the temp folder is\r\n'           : moved to the primary export folder. (This way we don't have to compute\r\n'           : the hashes and index values again.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub UpdateFromAltExport(cItem As IDbComponent)\r\n\r\n    Dim strFile As String\r\n    Dim dAltItem As Dictionary\r\n    Dim varKey As Variant\r\n\r\n    ' Exit if we have disabled the index functionality\r\n    If Me.Disabled Then Exit Sub\r\n\r\n    ' Get file name from component\r\n    strFile = FSO.GetFileName(cItem.SourceFile)\r\n\r\n    ' Look for entry in AlternateExport section\r\n    With m_dIndex(\"AlternateExport\")\r\n        If .Exists(cItem.Category) Then\r\n            If .Item(cItem.Category).Exists(strFile) Then\r\n                ' Get reference to alternate export entry\r\n                Set dAltItem = .Item(cItem.Category).Item(strFile)\r\n            End If\r\n        End If\r\n    End With\r\n\r\n    ' Update entry in main component section with values\r\n    ' from the alternate export section.\r\n    If Not dAltItem Is Nothing Then\r\n        With LoadItem(cItem, strFile, \"Components\").dParent\r\n            For Each varKey In dAltItem.Keys\r\n                .Item(varKey) = dAltItem(varKey)\r\n            Next varKey\r\n        End With\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Remove\r\n' Author    : Adam Waller\r\n' Date      : 12/2/2020\r\n' Purpose   : Remove an item from the index when the object and file no longer exist.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Remove(cItem As IDbComponent, Optional strSourceFile As String)\r\n\r\n    Dim strFile As String\r\n\r\n    ' Exit if we have disabled the index functionality\r\n    If Me.Disabled Then Exit Sub\r\n\r\n    ' Get just the file name from the path.\r\n    strFile = FSO.GetFileName(Nz2(strSourceFile, cItem.SourceFile))\r\n\r\n    ' Remove dictionary objects.\r\n    With m_dIndex(\"Components\")\r\n        If .Exists(cItem.Category) Then\r\n            If .Item(cItem.Category).Exists(strFile) Then\r\n                .Item(cItem.Category).Remove strFile\r\n                ' Remove category if no more items\r\n                If .Item(cItem.Category).Count = 0 Then\r\n                    .Remove cItem.Category\r\n                End If\r\n            End If\r\n        End If\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetItem\r\n' Author    : Adam Waller\r\n' Date      : 11/30/2020\r\n' Purpose   : Returns a dictionary object with the saved values, creating if needed.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Item(cItem As IDbComponent, Optional strSourceFile As String, _\r\n    Optional strSection As String = \"Components\") As clsVCSIndexItem\r\n\r\n    Dim strFile As String\r\n\r\n    ' Exit if we have disabled the index functionality\r\n    If Me.Disabled Then Exit Function\r\n\r\n    ' Get just the file name from the path.\r\n    strFile = FSO.GetFileName(Nz2(strSourceFile, cItem.SourceFile))\r\n    Set Item = LoadItem(cItem, strFile, strSection)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadItem\r\n' Author    : Adam Waller\r\n' Date      : 9/23/2021\r\n' Purpose   : Loads the item from the index, creating if it does not exist.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function LoadItem(cItem As IDbComponent, strSourceFile As String, strSection As String) As clsVCSIndexItem\r\n\r\n    Dim dItem As Dictionary\r\n\r\n    ' Get or create dictionary objects.\r\n    With m_dIndex(strSection)\r\n        If Not .Exists(cItem.Category) Then Set .Item(cItem.Category) = New Dictionary\r\n        If Not .Item(cItem.Category).Exists(strSourceFile) Then Set .Item(cItem.Category)(strSourceFile) = New Dictionary\r\n        Set dItem = .Item(cItem.Category)(strSourceFile)\r\n        Set LoadItem = New clsVCSIndexItem\r\n        With LoadItem\r\n            ' Load properties to class\r\n            Set .dParent = dItem\r\n            .ExportDate = Nz2(dNZ(dItem, \"ExportDate\"), 0)\r\n            .ImportDate = Nz2(dNZ(dItem, \"ImportDate\"), 0)\r\n            .SourceModified = Nz2(dNZ(dItem, \"SourceModified\"), 0)\r\n            .FilePropertiesHash = dNZ(dItem, \"FilePropertiesHash\")\r\n            .FileHash = dNZ(dItem, \"FileHash\")\r\n            .OtherHash = dNZ(dItem, \"OtherHash\")\r\n        End With\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Exists\r\n' Author    : Adam Waller\r\n' Date      : 12/2/2020\r\n' Purpose   : Returns true if the file exists in the index.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Exists(cCategory As IDbComponent, strSourceFilePath As String) As Boolean\r\n\r\n    Dim strFile As String\r\n    Dim blnExists\r\n\r\n    ' Get just the file name from the path.\r\n    strFile = FSO.GetFileName(strSourceFilePath)\r\n\r\n    ' See if the entry exists in the index\r\n    With m_dIndex(\"Components\")\r\n        If .Exists(cCategory.Category) Then\r\n            blnExists = .Item(cCategory.Category).Exists(strFile)\r\n        End If\r\n    End With\r\n\r\n    ' Return result\r\n    Exists = blnExists\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DefaultDevModeHash\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2021\r\n' Purpose   : Return a hash of the printer settings from the default printer. (Used to\r\n'           : determine whether a form or report is using any specific or custom\r\n'           : print settings, thereby requiring us to save the print settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get DefaultDevModeHash() As String\r\n\r\n    ' Cache the result for future calls\r\n    Static strHash As String\r\n\r\n    If strHash = vbNullString Then\r\n        With New clsDevMode\r\n            .LoadFromDefaultPrinter\r\n            strHash = .GetHash\r\n        End With\r\n    End If\r\n\r\n    ' Return hash\r\n    DefaultDevModeHash = strHash\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetModifiedSourceFiles\r\n' Author    : Adam Waller\r\n' Date      : 12/2/2020\r\n' Purpose   : Return a collection of source files that appear to be different from\r\n'           : the index hash of the previous export. File modified dates will be used\r\n'           ' to determine which files have changed.\r\n'           : NOTE: This will also include source file paths for database objects that\r\n'           : no longer exist. (Orphaned database objects not represented in source.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetModifiedSourceFiles(cCategory As IDbComponent) As Dictionary\r\n\r\n    Dim dAllFiles As Dictionary\r\n    Dim dItems As Dictionary\r\n    Dim varItem As Variant\r\n    Dim cItem As IDbComponent\r\n    Dim strHash As String\r\n    Dim strFile As String\r\n    Dim strPath As String\r\n    Dim blnModified As Boolean\r\n    Dim blnMissing As Boolean\r\n\r\n    Set GetModifiedSourceFiles = New Dictionary\r\n    With GetModifiedSourceFiles\r\n\r\n        ' Get a full list of source files and database objects\r\n        Set dAllFiles = cCategory.GetFileList\r\n        Set dItems = cCategory.GetAllFromDB\r\n\r\n        ' Loop through files and check modified dates.\r\n        ' Get a list of all the files for this component.\r\n        For Each varItem In dAllFiles\r\n            strFile = varItem\r\n\r\n            ' Reset flags\r\n            blnModified = True\r\n            blnMissing = False\r\n\r\n            ' See if the database object currently exists\r\n            If Not cCategory.SingleFile Then\r\n                blnMissing = Not dItems.Exists(strFile)\r\n            End If\r\n\r\n            ' Check the index for any existing entry, so we can compare the saved modified date.\r\n            If (Not blnMissing) And Me.Exists(cCategory, strFile) Then\r\n                ' Get hash of current source files.\r\n                strHash = GetSourceFilesPropertyHash(cCategory, strFile)\r\n                ' Build the path to find the item in the index.\r\n                strPath = Join(Array(\"Components\", cCategory.Category, FSO.GetFileName(strFile), \"FilePropertiesHash\"), \"\\\")\r\n                ' File is considered not modified if the index date matches the file modification date.\r\n                blnModified = Not (dNZ(m_dIndex, strPath) = strHash)\r\n            End If\r\n\r\n            ' Add modified files to collection\r\n            If blnModified Then .Add strFile, vbNullString\r\n        Next varItem\r\n\r\n        ' Now loop through database objects, and compare to source file list\r\n        ' to identify orphaned database objects no longer represented in source.\r\n        For Each varItem In dItems.Items\r\n            Set cItem = varItem\r\n            ' We are only concerned about non single-file items.\r\n            If Not cItem.SingleFile Then\r\n                ' See if the path exists in the list of source files\r\n                If Not dAllFiles.Exists(cItem.SourceFile) Then\r\n                    ' This object does not appear to exist in the source files.\r\n                    ' Add to list of files to process. (This will remove the\r\n                    ' existing database object during the merge.)\r\n                    .Add cItem.SourceFile, vbNullString\r\n                End If\r\n            End If\r\n        Next varItem\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Conflicts\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2021\r\n' Purpose   : Expose conflicts as an extension of the index\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Conflicts() As clsConflicts\r\n    If m_Conflicts Is Nothing Then Set m_Conflicts = New clsConflicts\r\n    Set Conflicts = m_Conflicts\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckExportConflicts\r\n' Author    : Adam Waller\r\n' Date      : 9/15/2021\r\n' Purpose   : Check for potential conflicts when exporting these items\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CheckExportConflicts(dDbComponents As Dictionary)\r\n\r\n    Dim varKey As Variant\r\n    Dim cItem As IDbComponent\r\n    Dim sngStart As Single\r\n\r\n    ' Capture start time so we can display some progress if this\r\n    ' scan takes a while.\r\n    sngStart = Timer\r\n\r\n    ' Loop through all components\r\n    For Each varKey In dDbComponents.Keys\r\n        Set cItem = dDbComponents(varKey)\r\n        If IsExportConflict(cItem) Then\r\n            ' Add to list of conflicts\r\n            Me.Conflicts.Add cItem, _\r\n                CStr(varKey), _\r\n                Me.Item(cItem).ExportDate, _\r\n                GetSourceModifiedDate(cItem, cItem.SourceFile), _\r\n                ercOverwrite\r\n        End If\r\n        ' Increment the progress bar\r\n        Log.Increment\r\n        ' Exit loop if single source file\r\n        If cItem.SingleFile Then Exit For\r\n        ' Display more detail if export runs more than 1 second.\r\n        If sngStart > 0 And (Timer - sngStart > 1) Then\r\n            Log.Add \" - Checking \" & dDbComponents.Count & \" \" & LCase(cItem.Category) & \"...\", , , , , True\r\n            sngStart = 0\r\n        End If\r\n    Next varKey\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsExportConflict\r\n' Author    : Adam Waller\r\n' Date      : 9/15/2021\r\n' Purpose   : Returns true if exporting the item would conflict with an existing file\r\n'           : that is different AND newer than the last export of this object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IsExportConflict(cItem As IDbComponent) As Boolean\r\n\r\n    Dim blnConflict As Boolean\r\n    Dim strHash As String\r\n    Dim strFile As String\r\n    Dim strTempFile As String\r\n    Dim blnCompareTemp As Boolean\r\n\r\n    ' Check for special cases based on component type\r\n    If cItem.ComponentType = edbTableData Then\r\n        ' Table data is not considered an export conflict, and should\r\n        ' always be overwritten.\r\n        IsExportConflict = False\r\n        Exit Function\r\n    End If\r\n\r\n    ' See if the export file even exists before we worry about conflicts.\r\n    strFile = cItem.SourceFile\r\n    If FSO.FileExists(strFile) Then\r\n\r\n        ' Get a hash of the file properties\r\n        strHash = GetSourceFilesPropertyHash(cItem)\r\n\r\n        ' Check to see if this object is in the index\r\n        ' (Has been exported/imported before.)\r\n        If Me.Exists(cItem, strFile) Then\r\n\r\n            ' Item found in index.\r\n            ' Check to see if it matches the property hash in the index.\r\n            If Me.Item(cItem).FilePropertiesHash = strHash Then\r\n                ' Source file has not changed since last export/import.\r\n                ' We should be good to overwrite it.\r\n            Else\r\n                ' Doesn't match. This could be a conflict, depending on the dates.\r\n                ' If the modified date of the source file is NEWER than the\r\n                ' last export date...\r\n                If Me.Item(cItem).ExportDate < GetSourceModifiedDate(cItem) Then\r\n                    ' ...and the content is different, then we have a conflict.\r\n                    blnCompareTemp = True\r\n                End If\r\n            End If\r\n        Else\r\n            ' Without an existing index entry, we will need to compare a fresh\r\n            ' export of the item to the source file and check for differing content.\r\n            blnCompareTemp = True\r\n        End If\r\n    End If\r\n\r\n    ' Compare the content hash of a fresh export to see if it matches the existing file.\r\n    If blnCompareTemp Then\r\n        strTempFile = Replace(strFile, Options.GetExportFolder, GetTempExportFolder)\r\n        Log.Add \"  Exporting \" & cItem.Name, False\r\n        cItem.Export strTempFile\r\n        blnConflict = Not SourceMatches(cItem, strFile, strTempFile)\r\n    End If\r\n\r\n    ' Return result\r\n    IsExportConflict = blnConflict\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckImportConflicts\r\n' Author    : Adam Waller\r\n' Date      : 2/24/2023\r\n' Purpose   : Check for potential conflicts when merging into this database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CheckMergeConflicts(cComponent As IDbComponent, dFiles As Dictionary)\r\n\r\n    Dim varKey As Variant\r\n    Dim cItem As IDbComponent\r\n    Dim sngStart As Single\r\n    Dim strFile As String\r\n    Dim intConflict As eResolveConflict\r\n\r\n    ' Capture start time so we can display some progress if this\r\n    ' scan takes a while.\r\n    sngStart = Timer\r\n    Perf.OperationStart \"Check Merge Conflicts\"\r\n\r\n    ' Loop through the list of source files\r\n    ' (Each key is a full path to a source file)\r\n    For Each varKey In dFiles.Keys\r\n        strFile = varKey\r\n        intConflict = IsMergeConflict(cComponent, strFile)\r\n        If intConflict Then\r\n            ' Get database item\r\n            If cComponent.GetAllFromDB.Exists(strFile) Then\r\n                ' Get individual item (form, report)\r\n                Set cItem = cComponent.GetAllFromDB.Item(strFile)\r\n            Else\r\n                ' Get parent class for single file items\r\n                Set cItem = cComponent\r\n            End If\r\n            ' Add to list of conflicts\r\n            Me.Conflicts.Add cItem, _\r\n                strFile, _\r\n                cItem.DateModified, _\r\n                GetSourceModifiedDate(cComponent, strFile), _\r\n                intConflict, _\r\n                strFile, _\r\n                ercSkip\r\n        End If\r\n        ' Increment the progress bar\r\n        Log.Increment\r\n        ' Exit loop if single source file\r\n        If cComponent.SingleFile Then Exit For\r\n        ' Display more detail if export runs more than 1 second.\r\n        If sngStart > 0 And (Timer - sngStart > 1) Then\r\n            Log.Add \" - Checking \" & dFiles.Count & \" \" & LCase(cComponent.Category) & \"...\", , , , , True\r\n            sngStart = 0\r\n        End If\r\n    Next varKey\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsMergeConflict\r\n' Author    : Adam Waller\r\n' Date      : 12/5/2023\r\n' Purpose   : Returns true if we are about to overwrite changes in the database that\r\n'           : don't match the current source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IsMergeConflict(cItemClass As IDbComponent, strSourceFile As String) As eResolveConflict\r\n\r\n    Dim cItem As IDbComponent\r\n    Dim strTempFile As String\r\n\r\n    ' Check for special cases based on component type\r\n    If cItemClass.ComponentType = edbTableData Then\r\n        ' Table data is not considered an import conflict, and should\r\n        ' always be overwritten.\r\n        IsMergeConflict = ercNone\r\n        Exit Function\r\n    End If\r\n\r\n    ' Single file objects are treated a little differently\r\n    If cItemClass.SingleFile Then\r\n        If cItemClass.IsModified Then IsMergeConflict = ercOverwrite\r\n    Else\r\n        ' See if the database object even exists before we worry about conflicts.\r\n        If cItemClass.GetAllFromDB.Exists(strSourceFile) Then\r\n\r\n            ' Get class instance for database item\r\n            Set cItem = cItemClass.GetAllFromDB.Item(strSourceFile)\r\n\r\n            ' See if this object appears to be modified since the last import/export\r\n            If cItem.IsModified Then\r\n                If Not FSO.FileExists(strSourceFile) Then\r\n                    ' Modified object, and source file does not exist. Could be a case\r\n                    ' where we are removing an orphaned database object.\r\n                    IsMergeConflict = ercDelete\r\n                Else\r\n                    ' Just because the modification date is different doesn't always\r\n                    ' mean the object definition has actually changed. By comparing\r\n                    ' a fresh export of the object, we can determine for sure if the\r\n                    ' source files match.\r\n                    strTempFile = Replace(strSourceFile, Options.GetExportFolder, GetTempExportFolder)\r\n                    Log.Add \"  Exporting \" & cItem.Name, False\r\n                    cItem.Export strTempFile\r\n                    ' Because we are using a newly exported temp file, the file properties\r\n                    ' hash won't match, since the file dates are going to be different.\r\n                    If Not SourceMatches(cItemClass, strSourceFile, strTempFile) Then\r\n                        IsMergeConflict = ercOverwrite\r\n                    End If\r\n                End If\r\n            Else\r\n                ' Based on the index, it does not appear that this item\r\n                ' has changed since the last export or import operation.\r\n                ' We should be good to overwrite (or skip importing) this\r\n                ' object in a merge operation.\r\n            End If\r\n        Else\r\n            ' Database object does not exist. No conflict to merge this file.\r\n        End If\r\n\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SourceMatches\r\n' Author    : Adam Waller\r\n' Date      : 12/7/2023\r\n' Purpose   : Returns true if the two represented source components match at a source\r\n'           : file level, not counting file modification dates.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function SourceMatches(cType As IDbComponent, strFile1 As String, strFile2 As String) As Boolean\r\n\r\n    Dim dPaths(1 To 2) As Dictionary\r\n    Dim strFiles(1 To 2) As String\r\n    Dim intCnt As Integer\r\n    Dim varExt As Variant\r\n    Dim strPath As String\r\n    Dim varKey As Variant\r\n\r\n    strFiles(1) = strFile1\r\n    strFiles(2) = strFile2\r\n\r\n    ' Start by retrieving the paths of existing files for this component.\r\n    For intCnt = 1 To 2\r\n        Set dPaths(intCnt) = New Dictionary\r\n        For Each varExt In cType.FileExtensions\r\n            strPath = SwapExtension(strFiles(intCnt), CStr(varExt))\r\n            If FSO.FileExists(strPath) Then\r\n                ' Add path to existing source file\r\n                dPaths(intCnt).Add FSO.GetFileName(strPath), strPath\r\n            End If\r\n        Next varExt\r\n    Next intCnt\r\n\r\n    ' Simple comparison of dictionaries\r\n    If dPaths(1).Count <> dPaths(2).Count Then Exit Function\r\n    ' Check for matching keys\r\n    For Each varKey In dPaths(1).Keys\r\n        If Not dPaths(2).Exists(varKey) Then Exit Function\r\n    Next varKey\r\n\r\n    ' If count and keys match, check file sizes\r\n    For Each varKey In dPaths(1).Keys\r\n        If FSO.GetFile(dPaths(1).Item(varKey)).Size _\r\n        <> FSO.GetFile(dPaths(2).Item(varKey)).Size Then Exit Function\r\n    Next varKey\r\n\r\n    ' Compare hash of content\r\n    For Each varKey In dPaths(1).Keys\r\n        If GetFileHash(CStr(dPaths(1).Item(varKey))) _\r\n        <> GetFileHash(CStr(dPaths(2).Item(varKey))) Then Exit Function\r\n    Next varKey\r\n\r\n    ' If we have reached this point without exiting, then the source must match.\r\n    SourceMatches = True\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTempExportFolder\r\n' Author    : Adam Waller\r\n' Date      : 8/30/2022\r\n' Purpose   : Returns the temp folder used for temporary source exports when the\r\n'           : modified status of the the component cannot be determined without running\r\n'           : an export operation. Persisting this folder during the export operation\r\n'           : allows us to perform a single export of the component while still giving\r\n'           : the user the ability to decide whether to overwrite the existing file.\r\n'           : NOTE: If the folder does not exist, a new one will be created.\r\n'           : This should be cleared after the export operation using the corresponding\r\n'           : ClearTempExportFolder sub.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get GetTempExportFolder() As String\r\n    If m_strTempExportFolderPath = vbNullString Then m_strTempExportFolderPath = GetTempFolder(\"VCS\")\r\n    GetTempExportFolder = m_strTempExportFolderPath & PathSep\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ClearTempExportFolder\r\n' Author    : Adam Waller\r\n' Date      : 8/30/2022\r\n' Purpose   : Removes the temporary export folder, including any source files exported\r\n'           : into this folder while scanning for changes.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ClearTempExportFolder()\r\n    If m_strTempExportFolderPath <> vbNullString Then\r\n        If FSO.FolderExists(m_strTempExportFolderPath) Then\r\n            LogUnhandledErrors\r\n            On Error Resume Next\r\n            ' Use FSO to delete the folder and contents\r\n            FSO.DeleteFolder m_strTempExportFolderPath, True\r\n            CatchAny eelWarning, \"Unable to delete temp folder: '\" & m_strTempExportFolderPath & _\r\n                \"' You will need to manually remove this folder.\", ModuleName(Me) & \".ClearTempExportFolder\"\r\n        End If\r\n        m_strTempExportFolderPath = vbNullString\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Git\r\n' Author    : Adam Waller\r\n' Date      : 1/19/2021\r\n' Purpose   : Reference to the Git integration class\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function Git() As clsGitIntegration\r\n    If m_Git Is Nothing Then Set m_Git = New clsGitIntegration\r\n    Set Git = m_Git\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileName\r\n' Author    : Adam Waller\r\n' Date      : 11/24/2020\r\n' Purpose   : Return file name for git state json file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function DefaultFilePath() As String\r\n    If DatabaseFileOpen Then DefaultFilePath = Options.GetExportFolder & cstrFileName\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 11/24/2020\r\n' Purpose   : Set up the dictionary object and keys for reflection.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n\r\n    Set m_dIndex = New Dictionary\r\n    With m_dIndex\r\n        .Add \"MergeBuildDate\", Null\r\n        .Add \"FullBuildDate\", Null\r\n        .Add \"ExportDate\", Null\r\n        .Add \"FullExportDate\", Null\r\n        .Add \"OptionsHash\", vbNullString\r\n        .Add \"LastMergedCommit\", vbNullString\r\n        Set .Item(\"Components\") = New Dictionary\r\n        Set .Item(\"AlternateExport\") = New Dictionary\r\n    End With\r\n\r\n    ' Load Git integration\r\n    Set m_dGitIndex = Nothing\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsVCSIndexItem.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsVCSIndexItem\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsVCSIndexItem\r\n' Author    : Adam Waller\r\n' Date      : 9/23/2021\r\n' Purpose   : An index item, representing a single database object and corresponding\r\n'           : source file.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPublic dParent As Dictionary\r\nPublic ImportDate As Date\r\nPublic ExportDate As Date\r\nPublic SourceModified As Date\r\nPublic FileHash As String\r\nPublic OtherHash As String\r\nPublic FilePropertiesHash As String\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsVersionControl.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsVersionControl\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = True\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsVersionControl\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2021\r\n' Purpose   : This class is publicly exposed through the VCS object in modAPI and can\r\n'           : be used to automate add-in functionality from other systems.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Show\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2021\r\n' Purpose   : Show the Version Control System main form\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Show()\r\n    If HasFormOpen Then Exit Sub\r\n    DoCmd.OpenForm \"frmVCSMain\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ShowOptions\r\n' Author    : Adam Waller\r\n' Date      : 3/4/2022\r\n' Purpose   : Show the options form\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ShowOptions()\r\n    If HasFormOpen Then Exit Sub\r\n    If DatabaseFileOpen Then\r\n        ' Force reload of options from current project before opening the form.\r\n        Set modObjects.Options = Nothing\r\n        DoCmd.OpenForm \"frmVCSOptions\"\r\n    Else\r\n        ' The table data subform may crash Access when the options form is closed with no database open.\r\n        MsgBox2 \"No Database Open\", \"Please open a database file before configuring VCS options.\", , vbInformation\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Export\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2021\r\n' Purpose   : Export the source code for the current database\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Export()\r\n    If HasFormOpen Then Exit Sub\r\n    RunExport ecfAllObjects\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportVBA\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2022\r\n' Purpose   : Export just the VBA related components\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExportVBA()\r\n    If HasFormOpen Then Exit Sub\r\n    RunExport ecfVBAItems\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportSelected\r\n' Author    : Adam Waller\r\n' Date      : 2/21/2023\r\n' Purpose   : Export the selected object\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExportSelected()\r\n\r\n    Dim objSelected As AccessObject\r\n\r\n    If HasFormOpen Then Exit Sub\r\n\r\n    Set objSelected = GetSelectedNavPaneObject\r\n    If objSelected Is Nothing Then\r\n        MsgBox2 \"Please Select an Object First\", _\r\n            \"Select a single object in the Navigation Pane to export (this item must have the keyboard focus).\", _\r\n            \"If you are using a non-English version of Access and it is still not working, please open an issue on GitHub to add support for your language.\", vbInformation\r\n    Else\r\n        ' Export the item\r\n        RunExport , objSelected\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RunExport\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2022\r\n' Purpose   : Handle different kinds of exports based on filter\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RunExport(Optional intFilter As eContainerFilter = ecfAllObjects, Optional objItem As AccessObject)\r\n    DoCmd.OpenForm \"frmVCSMain\", , , , , acHidden\r\n    With Form_frmVCSMain\r\n        If objItem Is Nothing Then\r\n            .intContainerFilter = intFilter\r\n        Else\r\n            Set .objSingleObject = objItem\r\n        End If\r\n        .Visible = True\r\n        .cmdExport_Click\r\n        If Log.ErrorLevel < eelError Then .AutoClose\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Build\r\n' Author    : Adam Waller\r\n' Date      : 3/30/2022\r\n' Purpose   : Initiate a full build from source\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Build(Optional strSourceFolder As String)\r\n    If HasFormOpen Then Exit Sub\r\n    DoCmd.OpenForm \"frmVCSMain\", , , , , acHidden\r\n    With Form_frmVCSMain\r\n        ' Make sure we are doing a full build.\r\n        If Not .chkFullBuild Then .chkFullBuild = True\r\n        .strSourcePath = strSourceFolder\r\n        .cmdBuild_Click\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergeBuild\r\n' Author    : Adam Waller\r\n' Date      : 2/21/2023\r\n' Purpose   : Merge modified source files into the existing database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MergeBuild()\r\n    If HasFormOpen Then Exit Sub\r\n    DoCmd.OpenForm \"frmVCSMain\", , , , , acHidden\r\n    With Form_frmVCSMain\r\n        ' See if merge build is available\r\n        If .chkFullBuild.Enabled Then\r\n            ' Initiate the merge build\r\n            .chkFullBuild = False\r\n            .cmdBuild_Click\r\n        Else\r\n            MsgBox2 \"Merge Build Not Available\", _\r\n                \"A full build must be performed before you can merge from source.\", _\r\n                \"Please perform a full build of this database first.\", vbInformation\r\n        End If\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildAs\r\n' Author    : Adam Waller\r\n' Date      : 7/8/2023\r\n' Purpose   : Build the source files to a new file name or location.\r\n'           : If source files are identified for the currently open database, it will\r\n'           : build from those files. Otherwise it will ask the use to select a folder\r\n'           : with source files.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub BuildAs()\r\n\r\n    Dim strNewDbPath As String\r\n    Dim strSourceFolder As String\r\n\r\n    If HasFormOpen Then Exit Sub\r\n\r\n    ' See if we can find source files for the currently open database.\r\n    If DatabaseFileOpen Then\r\n        If FolderHasVcsOptionsFile(Options.GetExportFolder) Then\r\n            ' Get the source folder location.\r\n            strSourceFolder = Options.GetExportFolder\r\n        End If\r\n    End If\r\n\r\n    ' If we aren't doing the current database, then prompt user to find a folder\r\n    ' with source files to use for the build.\r\n    If strSourceFolder = vbNullString Then\r\n\r\n        ' Show a folder picker to select the file with source code.\r\n        With Application.FileDialog(msoFileDialogFolderPicker)\r\n            .AllowMultiSelect = False\r\n            .ButtonName = \"Select Source Files Folder\"\r\n            .Title = \"Select Source Folder\"\r\n            .Show\r\n            If .SelectedItems.Count > 0 Then\r\n                ' Selected a folder\r\n                If FolderHasVcsOptionsFile(.SelectedItems(1)) Then\r\n                    ' Has source files\r\n                    strSourceFolder = .SelectedItems(1) & PathSep\r\n                Else\r\n                    MsgBox2 \"Source files not found\", \"Required source files were not found in this folder.\", _\r\n                        \"You selected: \" & .SelectedItems(1), vbExclamation\r\n                    Exit Sub\r\n                End If\r\n            Else\r\n                ' Canceled dialog\r\n                Exit Sub\r\n            End If\r\n        End With\r\n    End If\r\n\r\n    ' At this point, we should have identified the source files folder.\r\n    ' Now, we need to get the desired new database file name.\r\n    With Application.FileDialog(msoFileDialogSaveAs)\r\n        .AllowMultiSelect = False\r\n        .ButtonName = \"Build Here\"\r\n        .Title = \"Build New Database File\"\r\n        If DatabaseFileOpen Then\r\n            .InitialFileName = CurrentProject.FullName\r\n        Else\r\n            .InitialFileName = GetOriginalDbFullPathFromSource(strSourceFolder)\r\n        End If\r\n        .Show\r\n        ' Return path to new file\r\n        If .SelectedItems.Count > 0 Then strNewDbPath = .SelectedItems(1)\r\n    End With\r\n\r\n    ' Proceed with build if we have the new path.\r\n    If Len(strNewDbPath) Then\r\n        modImportExport.Build strSourceFolder, True, , strNewDbPath\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergeAllSource\r\n' Author    : Adam Waller\r\n' Date      : 5/16/2023\r\n' Purpose   : Merge ALL source files into the current database, regardless of modified\r\n'           : status. (Used for testing and development purposes.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MergeAllSource()\r\n    If HasFormOpen Then Exit Sub\r\n    modImportExport.MergeAllSource\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadSelected\r\n' Author    : Adam Waller\r\n' Date      : 2/21/2023\r\n' Purpose   : Load the selected item from source files\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadSelected()\r\n\r\n    Dim objSelected As AccessObject\r\n    Dim cComponentClass As IDbComponent\r\n    Dim strObjectName As String\r\n    Dim strSourceFilePath As String\r\n\r\n    If HasFormOpen Then Exit Sub\r\n\r\n    Set objSelected = GetSelectedNavPaneObject\r\n    If objSelected Is Nothing Then\r\n        MsgBox2 \"Please Select an Object First\", _\r\n            \"Select a single object in the Navigation Pane to reload from source (this item must have the keyboard focus).\", _\r\n            \"If you are using a non-English version of Access and it is still not working, please open an issue on GitHub to add support for your language.\", vbInformation\r\n    Else\r\n        ' Remove all object references to selected item\r\n        Set cComponentClass = GetClassFromObject(objSelected)\r\n        With cComponentClass\r\n            strObjectName = .Name\r\n            strSourceFilePath = .SourceFile\r\n            Set .DbObject = Nothing\r\n        End With\r\n        Set objSelected = Nothing\r\n\r\n        ' Import the object from source files\r\n        DoCmd.OpenForm \"frmVCSMain\", , , , , acHidden\r\n        Form_frmVCSMain.StartBuild False\r\n        LoadSingleObject cComponentClass, strObjectName, strSourceFilePath\r\n        Form_frmVCSMain.FinishBuild False\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetOptions\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2021\r\n' Purpose   : Access the options\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Options() As clsOptions\r\n    Set Options = modObjects.Options\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OpenRepository\r\n' Author    : Adam Waller\r\n' Date      : 2/8/2025\r\n' Purpose   : Open the configured tool for interacting with the repository.\r\n'           : (I.e GitHub Desktop or SourceTree)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub OpenRepository()\r\n\r\n    Dim strFolder As String\r\n    Dim strPath As String\r\n\r\n    ' Build quoted path to source folder\r\n    strFolder = \"\"\"\" & StripSlash(Options.GetExportFolder) & \"\"\"\"\r\n\r\n    ' Launch user-specified tool\r\n    Select Case Nz2(GetSetting(PROJECT_NAME, \"Options\", \"Open Repository Tool\"), 0)\r\n        Case eraUndefined\r\n            MsgBox2 \"No Repository Tool Selected\", _\r\n                \"Please choose a program to launch in the add-in Options\", _\r\n                \"GitHub Desktop is a great choice for most users\", vbInformation\r\n\r\n        Case eraGitHubDesktop\r\n            ' Open GitHub Desktop, and switch to the relevant repository.\r\n            ' Utilizes a built-in batch file installed as a part of GitHub Desktop\r\n            ShellEx \"GitHub.bat\", strFolder, , False\r\n\r\n        Case eraVSCode  ' Visual Studio Code\r\n            ShellEx \"code\", \"--reuse-window \" & strFolder, , False\r\n\r\n        Case eraSourceTree\r\n            ' Look up install path for SourceTree\r\n            strPath = RegRead(\"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\SourceTree\\InstallLocation\")\r\n            ' Check install for all users\r\n            If strPath = vbNullString Then strPath = RegRead(\"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\SourceTree\\InstallLocation\")\r\n            If Len(strPath) Then\r\n                Shell FSO.BuildPath(strPath, \"SourceTree.exe\") & \" -f \" & strFolder\r\n                AppActivate \"Sourcetree\", True  ' Try to switch to this window\r\n            Else\r\n                MsgBox2 \"SourceTree Not Found\", \"Path to SourceTree.exe could not be found in the Windows Registry\", , vbExclamation\r\n            End If\r\n\r\n        Case eraTortoiseGit\r\n            ' See discussion in #580 regarding command to use here\r\n            ShellEx \"TortoiseGitProc.exe\", \"/command:repostatus /closeonend:0 /path:\" & strFolder\r\n\r\n    End Select\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OpenSourceFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2022\r\n' Purpose   : Open the source files folder for the current project\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub OpenSourceFolder()\r\n    Shell \"explorer \"\"\" & Options.GetExportFolder & \"\"\"\", vbNormalFocus\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OpenExportLog\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2022\r\n' Purpose   : Open the export log file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub OpenExportLog()\r\n    OpenLog \"Export.log\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OpenBuildLog\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2022\r\n' Purpose   : Open the build log file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub OpenBuildLog()\r\n    OpenLog \"Build.log\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OpenLog\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2022\r\n' Purpose   : Open a log file by name, or show a message if the file is not found.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub OpenLog(strName As String)\r\n\r\n    Dim strPath As String\r\n\r\n    strPath = Options.GetExportFolder & strName\r\n    If FSO.FileExists(strPath) Then\r\n        ' Note that the parentheses around the path are required for this to work.\r\n        CreateObject(\"Shell.Application\").Open (strPath)\r\n    Else\r\n        MsgBox2 \"Log File Not Found\", \"Could not find the following file in the export location:\", strPath, vbExclamation\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReloadRibbon\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2022\r\n' Purpose   : Reload the add-in ribbon to reflect changes in XML source\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ReloadRibbon()\r\n    modCOMAddIn.ReloadRibbon\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RepairColors\r\n' Author    : Adam Waller\r\n' Date      : 6/8/2021\r\n' Purpose   : Reapply the color properties in the current database\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub RepairColors()\r\n    If HasFormOpen Then Exit Sub\r\n    RepairColorDefinitionBlocks\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SplitFiles\r\n' Author    : Adam Waller\r\n' Date      : 11/14/2023\r\n' Purpose   :\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SplitFiles()\r\n    If HasFormOpen Then Exit Sub\r\n    DoCmd.OpenForm \"frmVCSSplitFiles\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LocalizeLibraryReferences\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2022\r\n' Purpose   : Localize Access database libraries to files in same folder as the\r\n'           : current database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LocalizeLibraryReferences()\r\n    If HasFormOpen Then Exit Sub\r\n    modLibReference.LocalizeLibraryReferences True\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Version\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2021\r\n' Purpose   : Return the version of this (CodeDB) instance of VCS.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Version() As String\r\n    Version = GetVCSVersion\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GoToLink...\r\n' Author    : Adam Waller\r\n' Date      : 3/4/2022\r\n' Purpose   : Wrapper functions to visit online links (called from Ribbon menu)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub GoToLinkAdvancedTools()\r\n    ' TODO: Create page describing advanced tools.\r\n    FollowHyperlink \"https://github.com/joyfullservice/msaccess-vcs-addin/wiki\"\r\nEnd Sub\r\nPublic Sub GoToLinkHome()\r\n    FollowHyperlink \"https://github.com/joyfullservice/msaccess-vcs-addin\"\r\nEnd Sub\r\nPublic Sub GoToLinkDocumentation()\r\n    FollowHyperlink \"https://github.com/joyfullservice/msaccess-vcs-addin/wiki\"\r\nEnd Sub\r\nPublic Sub GoToLinkSupport()\r\n    FollowHyperlink \"https://github.com/joyfullservice/msaccess-vcs-addin/issues\"\r\nEnd Sub\r\nPublic Sub GoToLinkDownload()\r\n    FollowHyperlink \"https://github.com/joyfullservice/msaccess-vcs-addin/releases\"\r\nEnd Sub\r\nPublic Sub ActivateHook()\r\n    If HasFormOpen Then Exit Sub\r\n    If modExportOnSaveHook.ActivateHook = False Then\r\n        MsgBox \"Error activating the hook\", vbCritical, \"Unable to activate hook\"\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 3/28/2022\r\n' Purpose   : Save the current state so we can restore it after the current operation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    SaveState\r\n    ' When the class is initialized, make sure the ribbon is active (if installed).\r\n    ' This way if the COM add-in is not active, it will be automatically activated\r\n    ' when the add-in is opened from the [Database Tools\\Add-ins] menu. (See #451)\r\n    If GetInstallSettings.blnUseRibbonAddIn Then modCOMAddIn.VerifyRibbon\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Terminate\r\n' Author    : Adam Waller\r\n' Date      : 3/28/2022\r\n' Purpose   : Release any outstanding objects and restore the session state\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Terminate()\r\n    modObjects.ReleaseObjects\r\n    RestoreState\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveState\r\n' Author    : Adam Waller\r\n' Date      : 3/28/2022\r\n' Purpose   : Save any user settings before running our code.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SaveState()\r\n\r\n    Dim strValue As String\r\n\r\n    ' Just in case the user terminated the code before the settings could be restored,\r\n    ' check for any existing user settings that might need to be restored.\r\n    If ReadSetting(\"Save Time\") <> vbNullString Then RestoreState\r\n\r\n    ' Save a value so we can delete this section later\r\n    PreserveSetting \"Save Time\", Now()\r\n\r\n    ' Error trapping setting. (We need this to \"Break in Class Modules\" for this add-in)\r\n    strValue = Application.GetOption(\"Error Trapping\")\r\n    If strValue <> \"1\" Then\r\n        PreserveSetting \"Error Trapping\", strValue\r\n        Application.SetOption \"Error Trapping\", 1\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RestoreState\r\n' Author    : Adam Waller\r\n' Date      : 3/28/2022\r\n' Purpose   : Restore user settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RestoreState()\r\n\r\n    Dim strValue As String\r\n\r\n    strValue = ReadSetting(\"Error Trapping\")\r\n    If strValue <> vbNullString Then\r\n        If Application.GetOption(\"Error Trapping\") <> strValue Then\r\n            Application.SetOption \"Error Trapping\", CLng(strValue)\r\n        End If\r\n    End If\r\n\r\n    ' Remove temporary registry section\r\n    DeleteSetting PROJECT_NAME, \"State\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PreserveSetting\r\n' Author    : Adam Waller\r\n' Date      : 3/28/2022\r\n' Purpose   : Wrapper functions for saving and reading session state settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub PreserveSetting(strName As String, strSetting As String)\r\n    SaveSetting PROJECT_NAME, \"State\", strName, strSetting\r\nEnd Sub\r\nPrivate Function ReadSetting(strName As String, Optional strDefault As String) As String\r\n    ReadSetting = GetSetting(PROJECT_NAME, \"State\", strName, strDefault)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasFormOpen\r\n' Author    : Adam Waller\r\n' Date      : 3/29/2024\r\n' Purpose   : Make sure no other forms are currently loaded before initiating a new\r\n'           : operation. (Force linear usage of one operation at a time.)\r\n'           : Also warns user that they must close the form before they can proceed.\r\n'\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function HasFormOpen(Optional blnWarnUser As Boolean = True) As Boolean\r\n\r\n    Dim objForm As AccessObject\r\n    Dim frm As Access.Form\r\n    Dim strCaption As String\r\n\r\n    ' Cache collection of form names used by this add-in\r\n    Static dFormNames As Dictionary\r\n\r\n    ' Build cached list of form names\r\n    If dFormNames Is Nothing Then\r\n        Set dFormNames = New Dictionary\r\n        For Each objForm In CodeProject.AllForms\r\n            dFormNames.Add objForm.Name, vbNullString\r\n        Next objForm\r\n    End If\r\n\r\n    ' Check for open forms (from current database, add-in, or library database)\r\n    For Each frm In Application.Forms\r\n        If dFormNames.Exists(frm.Name) Then\r\n            ' Found a matching form name.\r\n            If frm.CurrentView = acCurViewDesign Then\r\n                ' Design view is okay, since the add-in won't have a form in design\r\n                ' view when running as an add-in. (No conflict)\r\n            Else\r\n                ' A form with the same name. This could be a problem.\r\n                If blnWarnUser Then\r\n                    If frm.Caption = vbNullString Then\r\n                        strCaption = frm.Name\r\n                    Else\r\n                        ' Users may recognize caption better than name\r\n                        strCaption = frm.Caption & \" (\" & frm.Name & \")\"\r\n                    End If\r\n                    MsgBox2 \"Add-in Form Already Open\", _\r\n                        \"Please close '\" & strCaption & \"' before running this action.\", , vbInformation\r\n                    HasFormOpen = True\r\n                    Exit Function\r\n                End If\r\n            End If\r\n        End If\r\n    Next\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsViewDiff.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsViewDiff\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsViewDiff\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Tools for comparing files using 3rd party diff applications\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' Set this to true to keep the temp files instead\r\n' of automatically cleaning them up at the completion\r\n' of the current operation.\r\nPublic PreserveTempFiles As Boolean\r\n\r\n\r\n' Collection of temp file names (full paths) to clean up later\r\nPrivate m_colTempFiles As Collection\r\nPrivate m_strToolName As String\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DiffStringWithFile\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Compare string content with an existing file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub StringWithFile(strContent As String, strFile As String)\r\n    Me.Files WriteTemp(strContent), strFile\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileWithString\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Compare with the file on the left and string on the right.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub FileWithString(strFile As String, strContent As String)\r\n    Me.Files strFile, WriteTemp(strContent)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DiffStrings\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Show a diff with two strings\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Strings(strContent1 As String, strContent2 As String)\r\n    Me.Files WriteTemp(strContent1), WriteTemp(strContent2)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Files\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Diff two files\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Files(strFile1 As String, strFile2 As String)\r\n    RunCompare strFile1, strFile2\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : WriteTemp\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Write a string to a temp file, and add to the collection.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function WriteTemp(strContent As String) As String\r\n\r\n    Dim strFile As String\r\n\r\n    ' Just in case...\r\n    If m_colTempFiles Is Nothing Then Set m_colTempFiles = New Collection\r\n\r\n    ' Create temp file\r\n    strFile = GetTempFile\r\n    WriteFile strContent, strFile\r\n    m_colTempFiles.Add strFile\r\n\r\n    ' Return path of temp file\r\n    WriteTemp = strFile\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ToolName\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Get the name of the Diff tool\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get ToolName() As String\r\n    If m_strToolName = vbNullString Then m_strToolName = GetSetting(PROJECT_NAME, \"Options\", \"Diff Tool\")\r\n    ToolName = m_strToolName\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ToolName\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Set the diff tool\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Let ToolName(strName As String)\r\n    If m_strToolName <> strName Then\r\n        SaveSetting PROJECT_NAME, \"Options\", \"Diff Tool\", strName\r\n        m_strToolName = strName\r\n    End If\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ClearTempFiles\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Delete any remaining temp files after the compare.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ClearTempFiles()\r\n\r\n    Dim varFile As Variant\r\n\r\n    If Not m_colTempFiles Is Nothing Then\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        For Each varFile In m_colTempFiles\r\n            If FSO.FileExists(varFile) Then DeleteFile CStr(varFile)\r\n        Next varFile\r\n        CatchAny eelNoError, vbNullString\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RunCompare\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Compare files using the save compare tool.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RunCompare(strFile1 As String, strFile2 As String)\r\n\r\n    Dim strCmd As String\r\n\r\n    Select Case Me.ToolName\r\n\r\n        Case vbNullString\r\n            ' No tool defined\r\n            MsgBox2 \"No Diff Tool Specified\", _\r\n                \"Please specify a diff tool to use for file comparison.\", _\r\n                \"This setting may be changed in the add-in options.\", vbExclamation\r\n            Exit Sub\r\n\r\n        Case \"WinMerge\"\r\n            strCmd = \"WinMerge \"\"{file1}\"\" \"\"{file2}\"\"\"\r\n\r\n        Case \"VSCode\"\r\n            strCmd = VSCodePath\r\n            If strCmd = vbNullString Then strCmd = \"code\"\r\n            strCmd = strCmd & \" --diff \"\"{file1}\"\" \"\"{file2}\"\"\"\r\n\r\n        Case \"TortoiseGitDiff\"\r\n            strCmd = \"TortoiseGitProc.exe /Command:diff -path \"\"{file1}\"\" -path2 \"\"{file2}\"\" -closeonend 2\"\r\n\r\n        Case Else\r\n            ' Assume they have a custom path defined\r\n            strCmd = Me.ToolName & \" \"\"{file1}\"\" \"\"{file2}\"\"\"\r\n\r\n    End Select\r\n\r\n    ' Fill in file name placeholders\r\n    strCmd = MultiReplace(strCmd, _\r\n        \"{file1}\", strFile1, _\r\n        \"{file2}\", strFile2)\r\n\r\n    ' Run command to launch compare\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    If strCmd <> vbNullString Then\r\n        With New WshShell\r\n            .Run strCmd\r\n        End With\r\n    End If\r\n    If Catch(-2147024894) Then Log.Error eelError, \"Unable to run '\" & Me.ToolName & \"' to compare files\", ModuleName(Me) & \".RunCompare\"\r\n    CatchAny eelError, \"Unable to compare files: \" & strCmd, ModuleName(Me) & \".RunCompare\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VSCodePath\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Attempt to find the VS Code path from the default install location\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function VSCodePath() As String\r\n\r\n    Dim strPath As String\r\n\r\n    ' Check User Local App Data Folder\r\n    strPath = Environ(\"LocalAppData\") & \"\\Programs\\Microsoft VS Code\\Code.exe\"\r\n\r\n    If FSO.FileExists(strPath) Then\r\n        VSCodePath = \"\"\"\" & strPath & \"\"\"\"\r\n    Else\r\n        ' Not found, check Program Files\r\n        strPath = Environ(\"ProgramFiles\") & \"\\Microsoft VS Code\\Code.exe\"\r\n        If FSO.FileExists(strPath) Then VSCodePath = \"\"\"\" & strPath & \"\"\"\"\r\n    End If\r\n\r\nExit_Here:\r\n\r\n    Exit Function\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasValidCompareTool\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Returns true if a compare tool has been selected, and appears to be\r\n'           : valid for use. (This can be extended to validate the presence of specific\r\n'           : tools in the current environment.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function HasValidCompareTool() As Boolean\r\n\r\n    ' Check the current option for compare tool.\r\n    Select Case Me.ToolName\r\n\r\n        Case vbNullString\r\n            ' No tool selected in options\r\n            HasValidCompareTool = False\r\n\r\n        Case \"WinMerge\", \"VSCode\", \"TortoiseGitDiff\"\r\n            ' These tools probably exist in the environment path\r\n            HasValidCompareTool = True\r\n\r\n        Case Else\r\n            ' A custom file path was likely used. Assume it is valid.\r\n            HasValidCompareTool = True\r\n\r\n    End Select\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Set up the temp files collection\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    Set m_colTempFiles = New Collection\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Terminate\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   :\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Terminate()\r\n    If Not Me.PreserveTempFiles Then Me.ClearTempFiles\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/clsWorker.cls",
    "content": "﻿VERSION 1.0 CLASS\r\nBEGIN\r\n  MultiUse = -1  'True\r\nEND\r\nAttribute VB_Name = \"clsWorker\"\r\nAttribute VB_GlobalNameSpace = False\r\nAttribute VB_Creatable = False\r\nAttribute VB_PredeclaredId = False\r\nAttribute VB_Exposed = False\r\n'---------------------------------------------------------------------------------------\r\n' Module    : clsWorker\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : This module has two primary purposes.\r\n'           :\r\n'           : 1. To run commands in the `current database` that would otherwise risk\r\n'           : running in the add-in database. (Deleting a database object, for example,\r\n'           : will default to the CodeDB before CurrentDB.)\r\n'           :\r\n'           : 2. Opens up the opportunity for parallel execution when VBScript can run\r\n'           : operations asynchrously. (For example, scanning through a large number of\r\n'           : source files can be slow, but this gives us the ability to scan multiple\r\n'           : folders at the same time which should improve performance on SSD drives.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n' Worker Job queue of jobs in progress\r\nPublic Queue As Dictionary\r\n\r\n' Generic placeholder objects so we can compile the VBScript in the VBA IDE\r\nPrivate WScript As Object\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Run_UninstallAddin\r\n' Author    : Adam Waller\r\n' Date      : 5/22/2023\r\n' Purpose   : Remove the actual add-in file and lock file during uninstall.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Run_UninstallAddin()\r\n    CallWorker PROJECT_NAME, 10, \"Uninstall\", \"\"\"\" & GetAddInFileName & \"\"\"\"\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DeleteDatabaseObject\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Delete a database object from the current database. (The reason we are\r\n'           : not just using the native command is because when it is initiated from\r\n'           : the add-in, it will delete any object with the same name in the add-in\r\n'           : before deleting the one in the current database. This will cause the\r\n'           : add-in file to be currupted, and the operation will fail.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function WIP_DeleteDatabaseObject(intType As AcObjectType, strName As String, Optional blnWaitForQueue As Boolean = True)\r\n\r\n    ' Don't use the following method from the add-in! Instead, we need to call\r\n    ' it from an external process so it deletes from the current database first.\r\n    'DoCmd.DeleteObject acModule, \"basModule\"\r\n    CallWorker strName, 5, \"DeleteDatabaseObject\", intType, strName\r\n    If blnWaitForQueue Then Me.WaitForQueue 5, \"DeleteDatabaseObject\", 0.25\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RenameDatabaseObject\r\n' Author    : Adam Waller\r\n' Date      : 3/3/2023\r\n' Purpose   : Rename the specified database object\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function WIP_RenameDatabaseObject(strNewName As String, intType As AcObjectType, _\r\n    strOldName As String, Optional blnWaitForQueue As Boolean = True)\r\n    CallWorker strNewName, 3, \"RenameDatabaseObject\", strNewName, intType, strOldName\r\n    If blnWaitForQueue Then Me.WaitForQueue 3, \"RenameDatabaseObject\", 0.1\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : WaitForQueue\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Wait for the job queue to finish processing, checking at each specified\r\n'           : interval. (Don't make the poll interval too high, or the workers may not\r\n'           : be able to complete their callbacks.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub WaitForQueue(dblTimeout As Double, Optional strAction As String = \"All\", Optional sngPollSeconds As Single = 0.5)\r\n\r\n    Dim cJob As clsJob\r\n    Dim varKey As Variant\r\n    Dim blnFound As Boolean\r\n    Dim dblStart As Double\r\n    Dim dblPollStart As Double\r\n    Dim colRemove As Collection\r\n    Dim varJob As Variant\r\n\r\n    ' No need to wait if there is nothing in the queue\r\n    If Me.Queue.Count = 0 Then Exit Sub\r\n\r\n    ' Get start time so we can monitor the timeout\r\n    dblStart = Perf.MicroTimer\r\n    Set colRemove = New Collection\r\n\r\n    ' Monitor performance of waiting for the job queue to clear\r\n    Perf.OperationStart \"Wait for Job Queue\"\r\n\r\n    ' Loop while periodically checking the queue\r\n    Do\r\n        ' Pause for the poll interval\r\n        dblPollStart = Perf.MicroTimer\r\n        Do While dblPollStart + sngPollSeconds > Perf.MicroTimer\r\n            ' Normally we don't want to stay on a constant loop\r\n            ' of doevents, but in this case it is necessary to allow\r\n            ' the worker callbacks to process.\r\n            DoEvents\r\n        Loop\r\n        DoEvents\r\n\r\n        ' We can exit early if there is nothing in the queue\r\n        If Me.Queue.Count = 0 Then Exit Do\r\n\r\n        ' Reset flag for this iteration\r\n        blnFound = False\r\n\r\n        ' Loop through queue of jobs\r\n        For Each varKey In Me.Queue.Keys\r\n            Set cJob = Me.Queue(varKey)\r\n\r\n            ' Check for job timeout (all jobs)\r\n            If Perf.MicroTimer > cJob.Timeout Then\r\n                ' Add to list of items to remove\r\n                colRemove.Add CStr(varKey)\r\n            Else\r\n                ' Look for matching actions\r\n                If cJob.Action = strAction Then\r\n                    blnFound = True\r\n                    Exit For\r\n                End If\r\n            End If\r\n        Next varKey\r\n        Set cJob = Nothing\r\n\r\n        ' Log and remove any individual jobs that have timed out\r\n        For Each varJob In colRemove\r\n            Set cJob = Me.Queue(varJob)\r\n            With cJob\r\n                Log.Error eelError, \"Job \" & .KeyHash & \" (\" & .Action & \") timed out after \" & _\r\n                    Round(Perf.MicroTimer - .Start, 0) & \" seconds.\", ModuleName(Me) & \".WaitForQueue\"\r\n            End With\r\n            Set cJob = Nothing\r\n            If Me.Queue.Exists(varJob) Then Me.Queue.Remove varJob\r\n        Next varJob\r\n\r\n        ' If nothing found of a specified action, we can exit the loop\r\n        If (strAction <> \"All\") And (Not blnFound) Then Exit Do\r\n\r\n        ' Check the overall timeout before we start another iteration\r\n        If dblStart + dblTimeout < Perf.MicroTimer Then\r\n            Log.Error eelError, \"Timed out waiting for job queue\", ModuleName(Me) & \".WaitForQueue\"\r\n            Log.Add \" (\" & Me.Queue.Count & \" jobs still in the queue.)\", False\r\n            Exit Do\r\n        End If\r\n    Loop\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyWorker\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Verify that the work script is installed and unchanged.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function VerifyWorker() As Boolean\r\n\r\n    Static blnVerified As Boolean\r\n\r\n    Dim strContent As String\r\n    Dim strFilePath As String\r\n\r\n    ' Only need to verify this once per session.\r\n    If blnVerified Then\r\n        VerifyWorker = True\r\n        Exit Function\r\n    End If\r\n\r\n    ' Compare hash of file with hash of content\r\n    strContent = GetWorkerScriptContent\r\n    strFilePath = WorkerFilePath\r\n    If FSO.FileExists(strFilePath) Then\r\n        If GetFileHash(WorkerFilePath) = GetStringHash(strContent, True) Then\r\n            ' Existing file matches template.\r\n            blnVerified = True\r\n        End If\r\n    End If\r\n\r\n    ' Update the file if needed.\r\n    If Not blnVerified Then\r\n        WriteFile strContent, strFilePath, \"Windows-1252\"\r\n        blnVerified = True\r\n    End If\r\n    VerifyWorker = True\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetWorkerScriptContent\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Return the content of the script, ready to save to a `Worker.vbs` file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetWorkerScriptContent() As String\r\n\r\n    Dim strContent As String\r\n    Dim varLines As Variant\r\n    Dim strLine As String\r\n    Dim blnSave As Boolean\r\n    Dim lngLine As Long\r\n\r\n    ' Get VBA code from this class\r\n    strContent = GetCodeVBProject.VBComponents(TypeName(Me)).CodeModule.Lines(1, 99999)\r\n\r\n    ' Split into lines\r\n    varLines = Split(strContent, vbCrLf)\r\n\r\n    ' Look for the marker where the placeholder starts, and keep everything after that.\r\n    With New clsConcat\r\n        For lngLine = 0 To UBound(varLines) - 1\r\n            strLine = varLines(lngLine)\r\n            If blnSave Then\r\n                .Add strLine, vbCrLf\r\n            ElseIf strLine = \"' *** BEGIN WORKER SCRIPT ***\" Then\r\n                blnSave = True\r\n                ' Add line to top of VBScript to launch the main sub\r\n                .Add \"Main ' Launch main subroutine\", vbCrLf, vbCrLf\r\n            End If\r\n        Next lngLine\r\n        ' Return script content\r\n        GetWorkerScriptContent = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : WorkerFilePath\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : File path to the installed worker script.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function WorkerFilePath() As String\r\n    WorkerFilePath = FSO.GetParentFolderName(modInstall.GetAddInFileName) _\r\n        & PathSep & \"Worker.vbs\"\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CallWorker\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Call the worker script to run a specified operation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function CallWorker(strKey As String, dblTimeoutSeconds As Double, _\r\n    strAction As String, ParamArray varParams()) As Long\r\n\r\n    Dim intParam As Integer\r\n    Dim strHash As String\r\n    Dim lngHandle As Long\r\n    Dim cJob As clsJob\r\n\r\n    ' Make sure this key/action is not already running\r\n    strHash = GetStringHash(strKey & strAction)\r\n    If Me.Queue.Exists(strHash) Then\r\n        Log.Error eelError, \"Duplicate job in queue: \" & strAction & \".\" & strKey, _\r\n            ModuleName(Me) & \".CallWorker\"\r\n        Exit Function\r\n    End If\r\n\r\n    ' Verify that the work script is installed and current\r\n    VerifyWorker\r\n\r\n    ' Build command with parameters\r\n    With New clsConcat\r\n\r\n        ' Build base command to launch worker\r\n        .Add \"wscript \"\"\", WorkerFilePath, \"\"\"\"\r\n\r\n        ' Add database file as first parameter\r\n        .Add \" \"\"\", CurrentProject.FullName, \"\"\"\"\r\n\r\n        ' Add unique key as second parameter\r\n        .Add \" \", strHash\r\n\r\n        ' Add specified action\r\n        .Add \" \", strAction\r\n\r\n        ' Add any parameters\r\n        For intParam = 0 To UBound(varParams)\r\n            .Add \" \", CStr(varParams(intParam))\r\n        Next intParam\r\n\r\n        ' Add to job queue (before launching, just in case it runs very fast)\r\n        Set cJob = New clsJob\r\n        With cJob\r\n            .KeyHash = strHash\r\n            .Action = strAction\r\n            .Start = Perf.MicroTimer\r\n            .Timeout = .Start + dblTimeoutSeconds\r\n        End With\r\n        Me.Queue.Add strHash, cJob\r\n\r\n        ' Use the Shell command to launch the worker as an external asynchrous\r\n        ' process, and return the process ID to the caller.\r\n        lngHandle = Shell(.GetStr, vbHide)\r\n\r\n    End With\r\n\r\n    ' Return process handle\r\n    CallWorker = lngHandle\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Class_Initialize\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Initialize the worker queue.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub Class_Initialize()\r\n    Set Me.Queue = New Dictionary\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReturnWorker\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Complete any post-call processes and remove from job queue.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ReturnWorker(strKey As String, varParams As Variant)\r\n\r\n    Dim cJob As clsJob\r\n\r\n    ' Guard clause in case the code is stopped while workers are running.\r\n    If Me.Queue Is Nothing Then Exit Function\r\n\r\n    ' Make sure this job exists in the queue\r\n    If Not Me.Queue.Exists(strKey) Then\r\n        Log.Error eelError, \"Returned worker not found in job queue: \" & strKey, _\r\n            ModuleName(Me) & \".ReturnWorker\"\r\n        Exit Function\r\n    Else\r\n        Set cJob = Me.Queue(strKey)\r\n    End If\r\n\r\n    ' Special processing for some actions\r\n    Select Case cJob.Action\r\n        Case Else\r\n            ' No special processing\r\n    End Select\r\n\r\n    ' Log and remove from job queue\r\n    With cJob\r\n        Log.Add \"Worker job \" & .Action & \" (\" & .KeyHash & \") completed in \" & _\r\n            Round(Perf.MicroTimer - .Start, 2) & \" seconds.\"\r\n    End With\r\n    Set cJob = Nothing\r\n    Me.Queue.Remove strKey\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' *** BEGIN WORKER SCRIPT ***\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Main\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Main entry point for worker script\r\n'---------------------------------------------------------------------------------------\r\n'\r\nSub Main()\r\n\r\n    Dim objFSO\r\n    Dim objApp\r\n    Dim strLibPath\r\n    Dim strKey\r\n    Dim strValue\r\n    Dim varReturn\r\n\r\n\r\n    Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\r\n\r\n    ' We are expecting some very specific command line arguments in this worker script.\r\n    If WScript.Arguments.Count < 2 Then\r\n        MsgBox \"Invalid arguments provided to worker script\", vbExclamation\r\n        WScript.Quit\r\n    End If\r\n\r\n    ' The first argument should be the path of the current database\r\n    strValue = WScript.Arguments(0)\r\n    If Not objFSO.FileExists(strValue) Then\r\n        MsgBox \"Database file must exist\", vbExclamation\r\n        WScript.Quit\r\n    Else\r\n        ' Get a reference to the database application\r\n        Set objApp = GetObject(strValue)\r\n        Set objApp.VBE.ActiveVBProject = CurrentVBProject(objApp)\r\n    End If\r\n\r\n    ' The second argument should be the key\r\n    strKey = WScript.Arguments(1)\r\n\r\n    ' Determine specified action (More complex actions can be split out into subs)\r\n    Select Case WScript.Arguments(2)\r\n\r\n        Case \"DeleteDatabaseObject\"\r\n            ' Type, Name\r\n            objApp.DoCmd.DeleteObject WScript.Arguments(3), WScript.Arguments(4)\r\n\r\n        Case \"RenameDatabaseObject\"\r\n            ' New name, Type, Old Name\r\n            objApp.DoCmd.Rename WScript.Arguments(3), WScript.Arguments(4), WScript.Arguments(5)\r\n\r\n        Case \"Uninstall\"\r\n            ' Remove the add-in database file and this worker script.\r\n            ' (This must be done after closing Access)\r\n            ' Args: Access application instance, add-in path\r\n            UninstallAddIn objApp, WScript.Arguments(3)\r\n\r\n        Case Else\r\n            MsgBox \"Unknown Action: \" & WScript.Arguments(1), vbExclamation\r\n\r\n    End Select\r\n\r\n    ' Make sure we still have a valid reference to the application before\r\n    ' attempting any callback operations.\r\n    If Not objApp Is Nothing Then\r\n\r\n        ' Build path to library so we can ensure that we are calling\r\n        ' the add-in function, not a function in the current database.\r\n        strLibPath = GetAddInVBProject(objApp).FileName\r\n        ' Remove file extension from library path.\r\n        strLibPath = Left(strLibPath, Len(strLibPath) - Len(\".accda\"))\r\n\r\n        ' Return key to caller to signal job completion\r\n        objApp.Run strLibPath & \".WorkerCallback\", strKey, varReturn\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CurrentVBProject\r\n' Author    : Adam Waller\r\n' Date      : 3/3/2023\r\n' Purpose   : Get a reference to the VB Project associated with the current database.\r\n'           : (Look for a matching file name between the VB Projects and the database.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nFunction CurrentVBProject(objApp)\r\n\r\n    Dim objProj\r\n    Dim strUncPath\r\n\r\n    ' Use currently active project by default\r\n    Set CurrentVBProject = objApp.VBE.ActiveVBProject\r\n\r\n    ' VBProject filenames are UNC paths\r\n    strUncPath = GetUncPath(objApp.CurrentProject.FullName)\r\n\r\n    If StrComp(objApp.VBE.ActiveVBProject.FileName, strUncPath, vbTextCompare) <> 0 Then\r\n        ' Search for project with matching filename.\r\n        For Each objProj In objApp.VBE.VBProjects\r\n            If StrComp(objProj.FileName, strUncPath, vbTextCompare) = 0 Then\r\n                Set CurrentVBProject = objProj\r\n                Exit For\r\n            End If\r\n        Next\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAddInVBProject\r\n' Author    : Adam Waller\r\n' Date      : 3/3/2023\r\n' Purpose   : Get a reference to the add-in project, even if it has the same name as\r\n'           : the current project.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nFunction GetAddInVBProject(objApp)\r\n\r\n    Dim objProj\r\n    Dim strUncPath\r\n    Dim objFSO\r\n\r\n    Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\r\n\r\n    ' VBProject filenames are UNC paths\r\n    strUncPath = GetUncPath(objApp.CurrentProject.FullName)\r\n\r\n    ' Search for project with matching filename.\r\n    For Each objProj In objApp.VBE.VBProjects\r\n        If StrComp(objFSO.GetFileName(objProj.FileName), \"Version Control.accda\", vbTextCompare) = 0 Then\r\n            ' Check for different file path\r\n            If StrComp(objProj.FileName, strUncPath, vbTextCompare) <> 0 Then\r\n                Set GetAddInVBProject = objProj\r\n                Exit For\r\n            End If\r\n        End If\r\n    Next\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetUncPath\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2020\r\n' Purpose   : Returns the UNC path for a network location (if applicable)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nFunction GetUncPath(strPath)\r\n\r\n    Dim strDrive\r\n    Dim strUNC\r\n    Dim objFSO\r\n\r\n    Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\r\n\r\n    strUNC = strPath\r\n    strDrive = objFSO.GetDriveName(strPath)\r\n    If strDrive <> vbNullString Then\r\n        With objFSO.GetDrive(strDrive)\r\n            If .DriveType = 3 Then  ' Remote\r\n                strUNC = Replace(strPath, strDrive, .ShareName, , 1, vbTextCompare)\r\n            End If\r\n        End With\r\n    End If\r\n    GetUncPath = strUNC\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UninstallAddIn\r\n' Author    : Adam Waller\r\n' Date      : 5/22/2023\r\n' Purpose   : Remove add-in files that would normally be in use during the uninstall\r\n'           : process. Then remove this worker script file itself.\r\n'           : NOTE: No message is given if the uninstall succeeds.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nFunction UninstallAddIn(objApp, strAddInFile)\r\n\r\n    Dim objFSO\r\n    Dim strLockFile\r\n    Dim intCnt\r\n\r\n    Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\r\n\r\n    ' Close the Microsoft Access application\r\n    objApp.Quit\r\n    Set objApp = Nothing\r\n\r\n    ' Build lock file name from add-in name\r\n    strLockFile = Replace(strAddInFile, \".accda\", \".laccda\")\r\n\r\n    ' Resume next on any errors while attempting to delete the file.\r\n    On Error Resume Next\r\n\r\n    ' Wait up to 10 seconds to delete the main add-in file and database lock file.\r\n    For intCnt = 1 To 10\r\n        If objFSO.FileExists(strLockFile) Then objFSO.DeleteFile strLockFile\r\n        If objFSO.FileExists(strAddInFile) Then objFSO.DeleteFile strAddInFile\r\n        If Not objFSO.FileExists(strAddInFile) Then\r\n            ' Clear any errors from failed delete attempts\r\n            If Err Then Err.Clear\r\n            Exit For\r\n        End If\r\n        WScript.Sleep 1000  ' Pause one second\r\n    Next\r\n\r\n    ' Ready or not, here we go with one final attempt. (This time we will show the error message.)\r\n    If objFSO.FileExists(strAddInFile) Then objFSO.DeleteFile strAddInFile\r\n    If Err Then\r\n        MsgBox \"Unable to delete add-in file: \" & strAddInFile & vbCrLf & vbCrLf & _\r\n            \" The file might be in use by another instance of Microsoft Access.\" & vbCrLf & vbCrLf & _\r\n            \"Error \" & Err.Number & \": \" & Err.Description, vbExclamation\r\n    Else\r\n        ' Delete this worker script file.\r\n        objFSO.DeleteFile WScript.ScriptFullName\r\n        WScript.Quit\r\n    End If\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modAPI.bas",
    "content": "﻿Attribute VB_Name = \"modAPI\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modAPI\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2021\r\n' Purpose   : This module exposes a set of VCS tools to other projects.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n' Note, some enums are listed here when they are directly exposed\r\n' through the Options and VCS classes. (Allowing them to be used externally)\r\n' Use hard-coded enum index values to preserve user settings in existing projects\r\n' if new values are added in the future.\r\n\r\n' Control the interaction mode\r\nPublic Enum eInteractionMode\r\n    eimNormal = 0\r\n    eimSilent = 1\r\nEnd Enum\r\n\r\n' Formats used when exporting table data.\r\nPublic Enum eTableDataExportFormat\r\n    etdNoData = 0\r\n    etdTabDelimited = 1\r\n    etdXML = 2\r\n    [_Last]\r\nEnd Enum\r\n\r\n' Sanitize levels used for sanitizing general and color elements in source files.\r\nPublic Enum eSanitizeLevel\r\n    eslNone = 0     ' Don't sanitize anything.\r\n    eslMinimal = 1  ' Sanitize minimal things like GUIDs.\r\n    eslStandard = 2 ' Remove non-critical elements that cause VCS noise between builds.\r\n    eslExtended = 3 ' Remove as much as possible. May have possible negative effects.\r\n    [_Last]         ' Placeholder for the end of the list.\r\nEnd Enum\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HandleRibbonCommand\r\n' Author    : Adam Waller\r\n' Date      : 2/28/2022\r\n' Purpose   : Handle an incoming command from the TwinBasic ribbon COM add-in.\r\n'           : (Allows us to keep all the logic between the XML ribbon file and the\r\n'           :  Access add-in.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function HandleRibbonCommand(strCommand As String, Optional strArgument As String) As Boolean\r\n    ' The function is called by Application.Run which can be re-entrant but we really\r\n    ' don't want it to be since that'd cause errors. To avoid this, we will ignore any\r\n    ' commands while the current command is running.\r\n    Static IsRunning As Boolean\r\n\r\n    On Error GoTo ErrHandler\r\n\r\n    If IsRunning Then\r\n        ' Ignore the re-entry; do NOT go to clean-up.\r\n        Exit Function\r\n    End If\r\n\r\n    IsRunning = True\r\n\r\n    ' Make sure we are not attempting to run this from the current database when making\r\n    ' changes to the add-in itself. (It will re-run the command through the add-in.)\r\n    If RunningOnLocal() Then\r\n        RunInAddIn \"HandleRibbonCommand\", True, strCommand, strArgument\r\n        GoTo CleanUp\r\n    End If\r\n\r\n    ' If a function is not found, this will throw an error. It is up to the ribbon\r\n    ' designer to ensure that the control IDs match public procedures in the VCS\r\n    ' (clsVersionControl) class module.\r\n    ' For example, to run VCS.Export, the ribbon button ID should be named \"btnExport\"\r\n\r\n    ' Trim off control ID prefix when calling command\r\n    If Len(strArgument) Then\r\n        CallByName VCS, Mid(strCommand, 4), VbMethod, strArgument\r\n    Else\r\n        CallByName VCS, Mid(strCommand, 4), VbMethod\r\n    End If\r\n\r\nCleanUp:\r\n    IsRunning = False\r\n    Exit Function\r\n\r\nErrHandler:\r\n    ' An error occurred so we need to make it available for further attempts\r\n    ' but do not handle the error.\r\n    IsRunning = False\r\n\r\n    ' Re-throw\r\n    Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VCS\r\n' Author    : Adam Waller\r\n' Date      : 3/28/2022\r\n' Purpose   : Wrapper for the VCS class, providing easy API access to VCS functions.\r\n'           : *NOTE* that this class is not persisted. This allows us to wrap up and\r\n'           : remove any object references after the call completes.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function VCS() As clsVersionControl\r\n    Set VCS = New clsVersionControl\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Preload\r\n' Author    : Adam Waller\r\n' Date      : 2/22/2025\r\n' Purpose   : A simple generic function that does nothing in particular, but allows\r\n'           : us to call it from external applications (i.e. Ribbon) to ensure that\r\n'           : the add-in has been loaded into Microsoft Access.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Preload()\r\n    ' Hello, Add-In!\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : WorkerCallback\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : A public callback endpoint for worker script operations to check back in\r\n'           : after completion.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function WorkerCallback(strKey As String, Optional varParams As Variant)\r\n    If RunningOnLocal Then\r\n        RunInAddIn \"WorkerCallback\", False, strKey, varParams\r\n    Else\r\n        Worker.ReturnWorker strKey, varParams\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetInteractionMode\r\n' Author    : Adam Waller\r\n' Date      : 6/24/2023\r\n' Purpose   : Control the types of UI interaction. (For example, you might set the\r\n'           : interaction mode to silent during an automated build.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetInteractionMode(intMode As eInteractionMode)\r\n    modVCSUtility.InteractionMode = intMode\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RunningOnLocal\r\n' Author    : Adam Waller\r\n' Date      : 5/18/2022\r\n' Purpose   : Returns true if the code is running in the current database instead of\r\n'           : the add-in database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RunningOnLocal() As Boolean\r\n    RunningOnLocal = (StrComp(CurrentProject.FullName, CodeProject.FullName, vbTextCompare) = 0)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RunInAddIn\r\n' Author    : Adam Waller\r\n' Date      : 3/3/2023\r\n' Purpose   : Run a proceedure with optional parameters in the VCS add-in database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function RunInAddIn(strProcedure As String, blnUseTimer As Boolean, Optional varArg1 As Variant, Optional varArg2 As Variant)\r\n\r\n    Dim projAddIn As VBProject\r\n    Dim strLibName As String\r\n    Dim strRunCmd As String\r\n\r\n    ' Make sure the add-in is loaded.\r\n    If Not AddinLoaded Then LoadVCSAddIn\r\n\r\n    ' When running code from the add-in project itself, it gets a little\r\n    ' tricky because both the add-in and the currentdb have the same VBProject name.\r\n    ' This means we can't just call `Run \"MSAccessVCS.*\" because it will run in\r\n    ' the local project instead of the add-in. We can resolve this by using the\r\n    ' full path to the add-in library instead. (#593)\r\n    Set projAddIn = GetAddInProject\r\n    If RunningOnLocal Then\r\n        strLibName = GetRunCmdAddInFullLibName\r\n    Else\r\n        strLibName = PROJECT_NAME\r\n    End If\r\n\r\n    ' See if we should run the command directly, or with an API timer callback.\r\n    ' (The API timer is helpful when you need to clear the call stack on the\r\n    '  current database before running the add-in code.)\r\n    If blnUseTimer And Not RunningOnLocal Then\r\n        SetTimer strProcedure, CStr(varArg1), CStr(varArg2)\r\n    Else\r\n        ' Build the command to execute using Application.Run\r\n        strRunCmd = strLibName & \".\" & strProcedure\r\n        ' Call based on arguments\r\n        If Not IsMissing(varArg2) Then\r\n            Application.Run strRunCmd, varArg1, varArg2\r\n        ElseIf Not IsMissing(varArg1) Then\r\n            Application.Run strRunCmd, varArg1\r\n        Else\r\n            Application.Run strRunCmd\r\n        End If\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetRunCmdAddInFullLibName\r\n' Author    : Josef Poetzl\r\n' Date      : 2/20/2025\r\n' Purpose   : Return the full path to the add-in library without the file extension.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetRunCmdAddInFullLibName() As String\r\n\r\n   Const cstrAddInFileExtension As String = \".accda\"\r\n   Dim strAddInFileName As String\r\n\r\n   strAddInFileName = GetAddInFileName\r\n   GetRunCmdAddInFullLibName = Left(strAddInFileName, Len(strAddInFileName) - Len(cstrAddInFileExtension))\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExampleLoadAddInAndRunExport\r\n' Author    : Adam Waller\r\n' Date      : 2/21/2025\r\n' Purpose   : This function can be copied to a local database and triggered with a\r\n'           : command line argument or other automation technique to load the VCS\r\n'           : add-in file and initiate an export.\r\n'           : NOTE: This expects the add-in to be installed in the default location\r\n'           : and using the default file name.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ExampleLoadAddInAndRunExport()\r\n    Application.Run Environ$(\"AppData\") & \"\\MSAccessVCS\\Version Control\" & _\r\n        \".HandleRibbonCommand\", \"btnExport\"\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExampleBuildFromSource\r\n' Author    : Adam Waller\r\n' Date      : 2/21/2025\r\n' Purpose   : This function can be copied to a local database and triggered with a\r\n'           : command line argument or other automation technique to load the VCS\r\n'           : add-in file and build this project from source.\r\n'           : NOTE: This expects the add-in to be installed in the default location\r\n'           : and using the default file name.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ExampleBuildFromSource(Optional strSourcePath As String)\r\n    Application.Run Environ$(\"AppData\") & \"\\MSAccessVCS\\Version Control\" & _\r\n        \".HandleRibbonCommand\", \"btnBuild\", strSourcePath\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modAddInMenu.bas",
    "content": "﻿Attribute VB_Name = \"modAddInMenu\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modAddIn\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Functions called by the Database Tools -> Add-Ins menu.\r\n'           : (This was the original way to invoke the add-in before the COM ribbon\r\n'           :  project provided a full ribbon menu.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddInMenuItemLaunch\r\n' Author    : Adam Waller\r\n' Date      : 1/14/2020\r\n' Purpose   : Launch the main add-in form.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddInMenuItemLaunch()\r\n    VCS.Show\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddInOptionsLaunch\r\n' Author    : Hecon5\r\n' Date      : 2/05/2020\r\n' Purpose   : Launch the main add-in form.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddInOptionsLaunch()\r\n    VCS.ShowOptions\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddInMenuItemExport\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : Open main form and start export immediately. (Save users a click)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddInMenuItemExport()\r\n    VCS.Export\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modComAddIn.bas",
    "content": "﻿Attribute VB_Name = \"modCOMAddIn\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modCOMAddIn\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Functions to handling the installing, removing, and verifying the\r\n'           : TwinBasic COM add-in used to provide the ribbon interface.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\nPrivate Const ModuleName As String = \"modComAddIn\"\r\n\r\n' Constants for registry entries\r\nPrivate Const cstrAddinFriendlyName As String = \"Ribbon integration for MSAccessVCS add-in\"\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyComAddin\r\n' Author    : Adam Waller\r\n' Date      : 3/1/2022\r\n' Purpose   : Verify that the ribbon add-in is installed and the latest version.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub VerifyComAddIn()\r\n\r\n    Dim strPath As String\r\n    Dim strFile As String\r\n    Dim strKey As String\r\n    Dim strHash As String\r\n    Dim blnUpdateRibbon As Boolean\r\n    Dim blnInstall As Boolean\r\n\r\n    ' Build path to ribbon folder\r\n    strPath = GetAddInPath\r\n\r\n    ' Ribbon XML file\r\n    strFile = strPath & \"Ribbon.xml\"\r\n    strKey = \"Ribbon XML\"\r\n    If Not FSO.FileExists(strFile) Then\r\n        modResource.ExtractResource strKey, strPath\r\n    Else\r\n        ' In the future we may allow the user to choose whether\r\n        ' to keep their existing ribbon.\r\n        strHash = modResource.GetResourceHash(strKey)\r\n        If strHash <> vbNullString Then\r\n            If strHash <> GetFileHash(strFile) Then\r\n                modResource.ExtractResource strKey, strPath\r\n                blnUpdateRibbon = True\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' Ribbon json strings\r\n    strFile = strPath & \"Ribbon.json\"\r\n    WriteFile ConvertToJson(GetRibbonStrings, 2), strFile\r\n\r\n    ' COM Add-in\r\n    strFile = strPath & GetComAddInFileName\r\n    strKey = \"COM Addin x\" & GetOfficeBitness\r\n\r\n    ' Verify add-in file\r\n    If Not FSO.FileExists(strFile) Then\r\n        blnInstall = True\r\n    Else\r\n        ' Compare to embedded resource file\r\n        strHash = modResource.GetResourceHash(strKey)\r\n        If strHash <> vbNullString Then\r\n            ' Reinstall if the file is different\r\n            If strHash <> GetFileHash(strFile) Then blnInstall = True\r\n        End If\r\n    End If\r\n\r\n    ' Verify COM registration\r\n    If Not blnInstall Then blnInstall = Not DllIsRegistered\r\n\r\n    ' Install/reinstall if needed\r\n    If blnInstall Then\r\n        ' Unload the add-in, so we don't try to overwrite a file that is in use\r\n        UnloadAddIn\r\n        RemoveComDll\r\n        ' Extract the new file from the resources table\r\n        modResource.ExtractResource strKey, strPath\r\n        ' Register the add-in file\r\n        RegisterCOMAddIn\r\n        ' Now we should be able to load the add-in\r\n        ' Note, if the ribbon XML file is not compatible with\r\n        ' the previous version of the COM add-in, we should not\r\n        ' load it immediately, since the old add-in may attemp\r\n        ' to load the incompatible file.\r\n        'LoadAddIn\r\n    Else\r\n        If blnUpdateRibbon Then\r\n            ' Reload the add-in to refresh the ribbon\r\n            UnloadAddIn\r\n            LoadAddIn\r\n        Else\r\n            ' Verify that the ribbon is active\r\n            VerifyRibbon\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReloadRibbon\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2022\r\n' Purpose   : Reloads the Fluent UI ribbon interface to reflect changes to the XML\r\n'           : file, such as switching to a different language.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ReloadRibbon()\r\n    UnloadAddIn\r\n    LoadAddIn\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RibbonLoaded\r\n' Author    : Adam Waller\r\n' Date      : 9/2/2024\r\n' Purpose   : Returns true if the ribbon is currently loaded\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function RibbonLoaded() As Boolean\r\n    Dim objAddIn As COMAddIn\r\n    Set objAddIn = GetCOMAddIn\r\n    If Not GetCOMAddIn Is Nothing Then\r\n        RibbonLoaded = objAddIn.Connect\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyRibbon\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2023\r\n' Purpose   : A lightweight function to verify that the ribbon add-in is active.\r\n'           : (It may get turned off if Access is opened in administrator mode.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub VerifyRibbon()\r\n    Dim objAddIn As COMAddIn\r\n    Set objAddIn = GetCOMAddIn\r\n    If Not objAddIn Is Nothing Then\r\n        ' Activate the add-in if it is not currently active\r\n        If Not objAddIn.Connect Then\r\n            LogUnhandledErrors\r\n            On Error Resume Next\r\n            objAddIn.Connect = True\r\n            CatchAny eelError, T(\"Unable to load Ribbon COM add-in\"), ModuleName & \".LoadAddIn\", False\r\n        End If\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UninstallComAddIn\r\n' Author    : Adam Waller\r\n' Date      : 4/5/2022\r\n' Purpose   : Unload, unregister, and remove COM add-in.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub UninstallComAddIn()\r\n\r\n    Dim strPath As String\r\n\r\n    ' Unload the add-in ribbon\r\n    UnloadAddIn\r\n\r\n    ' Unregister the DLL from the registry\r\n    DllUnregisterServer\r\n\r\n    ' Remove DLL file\r\n    RemoveComDll\r\n\r\n    ' Remove ribbon XML file\r\n    strPath = GetAddInPath & \"Ribbon.xml\"\r\n    If FSO.FileExists(strPath) Then DeleteFile strPath\r\n\r\n    ' Remove the json strings file\r\n    strPath = GetAddInPath & \"Ribbon.json\"\r\n    If FSO.FileExists(strPath) Then DeleteFile strPath\r\n\r\n    ' Update the list of COM add-ins\r\n    Application.COMAddIns.Update\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveComDll\r\n' Author    : Adam Waller\r\n' Date      : 4/8/2022\r\n' Purpose   : This can be a little tricky because if the COM add-in was loaded in\r\n'           : Microsoft Access, it may have a handle open that prevents us from deleting\r\n'           : the file. If we can't delete it, we can rename it to a temp file in the\r\n'           : current user's temp folder.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RemoveComDll()\r\n\r\n    Dim strPath As String\r\n    Dim strTemp As String\r\n\r\n    ' Build expected path for DLL\r\n    strPath = GetAddInPath & GetComAddInFileName\r\n    If FSO.FileExists(strPath) Then\r\n\r\n        ' Attempt to delete it first\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        DeleteFile strPath\r\n        If Catch(70) Then\r\n            ' File handle in use. Rename to temp file\r\n            strTemp = GetTempFile\r\n            DeleteFile strTemp\r\n            FSO.MoveFile strPath, strTemp\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAddInPath\r\n' Author    : Adam Waller\r\n' Date      : 3/11/2022\r\n' Purpose   : Return path to add-in installation folder\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetAddInPath() As String\r\n    GetAddInPath = GetInstallSettings.strInstallFolder & PathSep\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetComAddInFileName\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Return the file name for the COM add-in\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetComAddInFileName() As String\r\n    GetComAddInFileName = Replace(\"MSAccessVCSLib_winXX.dll\", \"XX\", GetOfficeBitness)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RegisterCOMAddIn\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Register the add-in in the Windows registry.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RegisterCOMAddIn() As Boolean\r\n\r\n    ' Register with list of Access add-ins\r\n    DllRegisterServer\r\n\r\n    ' Refresh the list of add-ins from the registry\r\n    Application.COMAddIns.Update\r\n\r\n    ' Return true if we can find the loaded object\r\n    RegisterCOMAddIn = Not (GetCOMAddIn Is Nothing)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UnloadAddIn\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Unload the COM add-in, if found.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub UnloadAddIn()\r\n    Dim addVCS As COMAddIn\r\n    Set addVCS = GetCOMAddIn\r\n    If Not addVCS Is Nothing Then addVCS.Connect = False\r\n    Application.COMAddIns.Update\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadAddIn\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Load (connect) the COM add-in\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub LoadAddIn()\r\n    Dim addVCS As COMAddIn\r\n    Set addVCS = GetCOMAddIn\r\n    If addVCS Is Nothing Then\r\n        ' Add-in not found. May need to be registered\r\n    Else\r\n        ' Load the add-in\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        addVCS.Connect = True\r\n        CatchAny eelError, T(\"Unable to load Ribbon COM add-in\"), ModuleName & \".LoadAddIn\", False\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetCOMAddIn\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Return a reference to the VCS COM Add-In, if available\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetCOMAddIn() As COMAddIn\r\n    Dim addIn As COMAddIn\r\n    For Each addIn In Application.COMAddIns\r\n        If addIn.Description = cstrAddinFriendlyName Then\r\n            Set GetCOMAddIn = addIn\r\n            Exit For\r\n        End If\r\n    Next addIn\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DllIsRegistered\r\n' Author    : Adam Waller\r\n' Date      : 4/7/2022\r\n' Purpose   : Checks for the CLSID registrations to verify that the COM DLL is properly\r\n'           : registered in the registry for the current user.\r\n'           : (This can change if the DLL is compiled in twinBASIC and registered to\r\n'           :  the compiled DLL instead of the installed one.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function DllIsRegistered() As Boolean\r\n\r\n    Dim strTest As String\r\n\r\n    ' Check HKLM registry key\r\n    With New IWshRuntimeLibrary.WshShell\r\n        ' We should have a value here if the install ran in the past.\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        ' Look up the class ID from the COM registration\r\n        strTest = .RegRead(\"HKCU\\SOFTWARE\\Classes\\MSAccessVCSLib.AddInRibbon\\CLSID\\\")\r\n        If strTest <> vbNullString Then\r\n            ' Read the file path for the registered DLL\r\n            strTest = .RegRead(\"HKCU\\SOFTWARE\\Classes\\CLSID\\\" & strTest & \"\\InProcServer32\\\")\r\n            ' See if it matches the installation folder\r\n            If strTest = GetAddInPath & GetComAddInFileName Then\r\n                ' Path matches. See if the file actually exists\r\n                DllIsRegistered = FSO.FileExists(strTest)\r\n            End If\r\n        End If\r\n        If Err Then Err.Clear\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DllRegisterServer\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Register the add-in with the list of available add-ins for Access\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub DllRegisterServer()\r\n    With New WshShell\r\n        .Exec \"regsvr32 /s \"\"\" & GetAddInPath & GetComAddInFileName & \"\"\"\"\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DllUnregisterServer\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Remove the add-in from the list\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub DllUnregisterServer()\r\n    If Not DllIsRegistered Then Exit Sub\r\n    With New WshShell\r\n        .Exec \"regsvr32 /u /s \"\"\" & GetAddInPath & GetComAddInFileName & \"\"\"\"\r\n    End With\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modConnect.bas",
    "content": "﻿Attribute VB_Name = \"modConnect\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modConnect\r\n' Author    : hecon5\r\n' Date      : 03/16/2021\r\n' Purpose   : Functions to deal with connection strings for tables, queries, etc.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\nPrivate Const ModuleName = \"modConnect\"\r\nPrivate m_dCachedConnections As Dictionary\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SanitizeConnectionString\r\n' Author    : Adam Waller\r\n' Date      : 2/26/2021\r\n' Purpose   : Sanitize the connection string by removing unneeded information and\r\n'           : converting database path to relative.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function SanitizeConnectionString(strConnection As String) As String\r\n\r\n    Dim lngPart As Long\r\n    Dim varParts As Variant\r\n    Dim strPart As String\r\n\r\n    If strConnection = vbNullString Then Exit Function\r\n\r\n    ' Create array of connection string parts\r\n    varParts = Split(strConnection, \";\")\r\n\r\n    ' Loop through parts, building new connection string\r\n    With New clsConcat\r\n        .AppendOnAdd = \";\"\r\n        For lngPart = 0 To UBound(varParts)\r\n            strPart = CStr(varParts(lngPart))\r\n            Select Case True\r\n\r\n                ' Check for username/password\r\n                Case StartsWith(strPart, \"UID=\", vbTextCompare), _\r\n                    StartsWith(strPart, \"PWD=\", vbTextCompare)\r\n                    ' These values are not needed when using a trusted connection.\r\n                    If (InStr(1, strConnection, \"Trusted_Connection=Yes\", vbTextCompare) = 0) _\r\n                        Or (Options.SanitizeLevel < eslStandard) Then\r\n                        ' Retain the values if not using trusted connection, or if\r\n                        ' using less than the normal sanitize level.\r\n                        .Add strPart\r\n                    End If\r\n\r\n                ' Check database path to convert to relative\r\n                Case StartsWith(strPart, \"DATABASE=\", vbTextCompare)\r\n                    .Add GetRelativeConnect(strPart)\r\n\r\n                ' Add all other sections\r\n                Case Else\r\n                    .Add strPart\r\n            End Select\r\n        Next lngPart\r\n\r\n        ' Remove final semicolon added with .AppendOnAdd\r\n        .Remove 1\r\n\r\n        ' Return sanitized string\r\n        SanitizeConnectionString = .GetStr\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetRelativeConnect\r\n' Author    : Adam Waller\r\n' Date      : 2/22/2021\r\n' Purpose   : Convert the connection string to a relative path if possible\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetRelativeConnect(strConnect As String) As String\r\n\r\n    Dim strPath As String\r\n    Dim strAdd As String\r\n\r\n    ' Extract the file path from the connection string\r\n    strPath = GetConnectPath(strConnect)\r\n\r\n    ' With linked text files, it may just be the folder path. If so, include\r\n    ' a final slash so it can correctly resolve to a relative path.\r\n    If strPath = GetUncPath(CurrentProject.Path) Then strAdd = PathSep\r\n\r\n    ' Prefix with the database property name, just in case the same string is used in\r\n    ' another part of the connection string.\r\n    GetRelativeConnect = Replace(strConnect, \"DATABASE=\" & strPath, _\r\n        \"DATABASE=\" & GetRelativePath(strPath & strAdd), , , vbTextCompare)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFullConnect\r\n' Author    : Adam Waller\r\n' Date      : 2/22/2021\r\n' Purpose   : Return the full path expanded from a relative one.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetFullConnect(strRelative As String) As String\r\n\r\n    Dim strPath As String\r\n    Dim strFull As String\r\n\r\n    ' Extract the file path from the connection string\r\n    strPath = GetConnectPath(strRelative)\r\n\r\n    ' Convert to full path\r\n    strFull = GetPathFromRelative(strPath)\r\n\r\n    ' Remove any trailing slash after conversion from relative.\r\n    If strFull <> strPath And Right$(strFull, 1) = PathSep Then\r\n        strFull = Left$(strFull, Len(strFull) - 1)\r\n    End If\r\n\r\n    ' Prefix with the database property name, just in case the same string is used in\r\n    ' another part of the connection string.\r\n    GetFullConnect = Replace(strRelative, \"DATABASE=\" & strPath, _\r\n        \"DATABASE=\" & strFull, , , vbTextCompare)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetConnectPath\r\n' Author    : Adam Waller\r\n' Date      : 2/22/2021\r\n' Purpose   : Return embedded connect path\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetConnectPath(strConnect As String) As String\r\n\r\n    Dim lngPos As Integer\r\n    Dim lngStart As String\r\n    Dim strPath As String\r\n\r\n    ' Look for path after \"Database\" connection property\r\n    lngPos = InStr(1, strConnect, \"Database=\", vbTextCompare)\r\n    If lngPos > 0 Then\r\n        lngStart = lngPos + Len(\"Database=\")\r\n        lngPos = InStr(lngStart, strConnect, \";\")\r\n        If lngPos > 0 Then\r\n            ' Stop at semi-colon\r\n            strPath = Mid$(strConnect, lngStart, lngPos - lngStart)\r\n        Else\r\n            ' Grab the rest of the string\r\n            strPath = Mid$(strConnect, lngStart)\r\n        End If\r\n    End If\r\n\r\n    ' Return path, if any\r\n    GetConnectPath = strPath\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CacheConnection\r\n' Author    : bclothier\r\n' Date      : 3/31/2023\r\n' Purpose   : Open an ODBC database to allow us to leverage Access' built-in caching\r\n'             and hopefully reduce the numbers of ODBC prompts. Because the connection\r\n'             may be incomplete, we will force a prompt for the user to then fill in\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function CacheConnection(strConnect As String) As Boolean\r\n    If Not (Left$(strConnect, 5) = \"ODBC;\") Then\r\n        Exit Function\r\n    End If\r\n\r\n    Dim qdf As DAO.QueryDef\r\n\r\n    If m_dCachedConnections Is Nothing Then\r\n        Set m_dCachedConnections = New Dictionary\r\n    End If\r\n\r\n    If m_dCachedConnections.Exists(strConnect) Then\r\n        CacheConnection = True\r\n    Else\r\n        ' We need to use the CurrentDb because it's the one that'll get stuff imported into. Otherwise,\r\n        ' we will get unwanted prompts during the import.\r\n        Set qdf = CurrentDb.CreateQueryDef\r\n\r\n        ' There is a bug where if Name property is left uninitialized or has a null string passed into it\r\n        ' the query will not behave correctly and gives strange error. Setting it to an initialized empty\r\n        ' string will avoid the bug.\r\n        qdf.Name = \"\"\r\n\r\n        ' We must provide a SQL statement. Every database engine understand this, right?\r\n        qdf.SQL = \"SELECT 1;\"\r\n        qdf.Connect = strConnect\r\n\r\n        On Error Resume Next\r\n        qdf.OpenRecordset\r\n        If Err.Number Then\r\n            Set qdf = Nothing\r\n        End If\r\n        On Error GoTo 0\r\n\r\n        If Not qdf Is Nothing Then\r\n            m_dCachedConnections.Add strConnect, qdf\r\n            CacheConnection = True\r\n        End If\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CacheConnection\r\n' Author    : bclothier\r\n' Date      : 3/31/2023\r\n' Purpose   : Closes all cached connections\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CloseCachedConnections()\r\n    If m_dCachedConnections Is Nothing Then\r\n        Exit Sub\r\n    End If\r\n\r\n    If m_dCachedConnections.Count Then\r\n        Dim qdf As DAO.QueryDef\r\n        Dim varKey As Variant\r\n\r\n        For Each varKey In m_dCachedConnections.Keys\r\n            Set qdf = m_dCachedConnections.Item(varKey)\r\n            qdf.Close\r\n            Set qdf = Nothing\r\n            m_dCachedConnections.Remove varKey\r\n        Next\r\n    End If\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modConstants.bas",
    "content": "﻿Attribute VB_Name = \"modConstants\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modConstants\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Constants and enums used throughout the project, but not exposed publicly.\r\n'           : If constants are only used in a single module, then they should be\r\n'           : declared in that module. If they are shared among multiple modules, they\r\n'           : should be declared here.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n' VBE Project Name for this Add-In\r\nPublic Const PROJECT_NAME As String = \"MSAccessVCS\"\r\n\r\n' Read/write chunks of text, rather than the whole thing at once for massive\r\n' performance gains when reading large files.\r\n' See https://docs.microsoft.com/is-is/sql/ado/reference/ado-api/readtext-method\r\nPublic Const CHUNK_SIZE As Long = 131072    ' (128K)\r\n\r\n' Whitespace indentation for outputing JSON content\r\nPublic Const JSON_WHITESPACE As Integer = 2\r\n\r\n' BOM characters for UTF-8/UTF-16 files\r\nPublic Const UTF8_BOM As String = \"ï»¿\"\r\n\r\n' Default hashing algorithm\r\nPublic Const DefaultHashAlgorithm As String = \"SHA256\"\r\n\r\n' This value seems to indicate that the theme was not used.\r\nPublic Const NO_THEME_INDEX As Integer = -1\r\n\r\n' Object types used when determining SQL modification date.\r\nPublic Enum eSqlObjectType\r\n    estView\r\n    estStoredProcedure\r\n    estTable\r\n    estTrigger\r\n    estOther\r\nEnd Enum\r\n\r\n' Types of objects that can be exported/imported from a database.\r\n' (Use corresponding constants wherever possible)\r\n' Be careful not to create collisions with two members sharing the\r\n' same value.\r\nPublic Enum eDatabaseComponentType\r\n    ' Standard database objects\r\n    edbForm = acForm\r\n    edbMacro = acMacro\r\n    edbModule = acModule\r\n    edbQuery = acQuery\r\n    edbReport = acReport\r\n    edbTableDef = acTable\r\n    edbTableDataMacro = acTableDataMacro\r\n    ' ADP specific\r\n    edbAdpTable\r\n    edbAdpFunction\r\n    edbAdpServerView\r\n    edbAdpStoredProcedure\r\n    edbAdpTrigger\r\n    ' Custom object types we are also handling.\r\n    edbTableData\r\n    edbRelation\r\n    edbDbsProperty\r\n    edbProjectProperty\r\n    edbSharedImage\r\n    edbDocument\r\n    edbHiddenAttribute\r\n    edbSavedSpec\r\n    edbImexSpec\r\n    edbNavPaneGroup\r\n    edbTheme\r\n    edbVbeForm\r\n    edbVbeProject\r\n    edbVbeReference\r\n    edbProject\r\n    edbConnection\r\n    edbCommandBar\r\n    ' Future implementation?\r\n    'edbLinkedTable\r\n    'edbFileProperty\r\n    [_Last]\r\nEnd Enum\r\n\r\n' Database server types for external databases\r\nPublic Enum eDatabaseServerType\r\n    estUnknown\r\n    estMsSql\r\n    estMySql\r\nEnd Enum\r\n\r\n' Applications used to interact with source code repository\r\n' (This list is also hard-coded as a drop-down in the options form)\r\nPublic Enum eRepositoryApp\r\n    eraUndefined = 0\r\n    eraGitHubDesktop = 1\r\n    eraVSCode = 2\r\n    eraSourceTree = 3\r\n    eraTortoiseGit = 4\r\nEnd Enum\r\n\r\n' Error levels used for logging and monitoring the status\r\n' of the current operation.\r\nPublic Enum eErrorLevel\r\n    eelNoError\r\n    eelWarning      ' Logged to file\r\n    eelError        ' Displayed and logged\r\n    eelCritical     ' Cancel operation\r\nEnd Enum\r\n\r\n' Compare mode for cloning dictionary object\r\n' See CloneDictionary function\r\nPublic Enum eCompareMethod2\r\n    ecmBinaryCompare = 0\r\n    ecmTextCompare = 1\r\n    ecmDatabaseCompare = 2\r\n    ' Added this to use original compare method\r\n    ecmSourceMethod = 3\r\nEnd Enum\r\n\r\n' Type of operation in progress\r\nPublic Enum eOperationType\r\n    eotExport = 1\r\n    eotBuild = 2\r\n    eotMerge = 3\r\nEnd Enum\r\n\r\n' Options for resolving file conflicts\r\nPublic Enum eResolveConflict\r\n    ercNone\r\n    ercSkip\r\n    ercOverwrite\r\n    ercDelete\r\nEnd Enum\r\n\r\n' Conflict types for import/export conflicts\r\nPublic Enum eConflictType\r\n    ectOrphanedSourceFile\r\n    ectNewerDatabaseObject\r\n    ectNewerSourceFile\r\nEnd Enum\r\n\r\n' Release type used when updating version\r\n' or deploying add-in\r\nPublic Enum eReleaseType\r\n    Major_Vxx = 0\r\n    Minor_xVx = 1\r\n    Build_xxV = 2\r\n    Same_Version = 3\r\nEnd Enum\r\n\r\n' Types of objects to include in GetContainers()\r\nPublic Enum eContainerFilter\r\n    ecfAllObjects\r\n    ecfVBAItems\r\n    ecfSchemas\r\nEnd Enum\r\n\r\n' Used for handling custom built-in command bar controls. See clsDbCommandBar for details.\r\nPublic Const strTemplateCommandBarName As String = \"MSAccessVCSCustomBuiltinCommandBarTemplate\"\r\n\r\n' Used for ImportCommandBars function; negative/zero result should be treated as an error.\r\nPublic Enum eImportCommandBarsResult\r\n    eicImportedNotVerified = -1\r\n    eicFailed = 0\r\n    eicImportedVerified = 1\r\n    eicImportedUnableToVerify = 2\r\nEnd Enum\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modDatabase.bas",
    "content": "﻿Attribute VB_Name = \"modDatabase\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modDatabase\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : General functions for interacting with the current database.\r\n'           : (See modVCSUtility for other functions more specific to this add-in.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\nPrivate Const ModuleName As String = \"modDatabase\"\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ProjectPath\r\n' Author    : Adam Waller\r\n' Date      : 1/25/2019\r\n' Purpose   : Path/Directory of the current database file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ProjectPath() As String\r\n    ProjectPath = CurrentProject.Path\r\n    If Right$(ProjectPath, 1) <> PathSep Then ProjectPath = ProjectPath & PathSep\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDBProperty\r\n' Author    : Adam Waller\r\n' Date      : 5/6/2021\r\n' Purpose   : Get a database property (Default to MDB version)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetDBProperty(strName As String, Optional dbs As DAO.Database) As Variant\r\n\r\n    Dim prp As Object ' DAO.Property\r\n    Dim oParent As Object\r\n\r\n    ' Check for database reference\r\n    If Not dbs Is Nothing Then\r\n        Set oParent = dbs.Properties\r\n    Else\r\n        If DatabaseFileOpen Then\r\n            ' Get parent container for properties\r\n            If CurrentProject.ProjectType = acADP Then\r\n                Set oParent = CurrentProject.Properties\r\n            Else\r\n                If dbs Is Nothing Then Set dbs = CurrentDb\r\n                Set oParent = dbs.Properties\r\n            End If\r\n        Else\r\n            ' No database open\r\n            GetDBProperty = vbNullString\r\n            Exit Function\r\n        End If\r\n    End If\r\n\r\n    ' Look for property by name\r\n    For Each prp In oParent\r\n        If prp.Name = strName Then\r\n            GetDBProperty = prp.Value\r\n            Exit For\r\n        End If\r\n    Next prp\r\n    Set prp = Nothing\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetDBProperty\r\n' Author    : Adam Waller\r\n' Date      : 9/1/2017\r\n' Purpose   : Set a database property\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetDBProperty(ByVal strName As String, ByVal varValue As Variant, Optional ByVal prpType As Long = dbText, Optional dbs As DAO.Database)\r\n\r\n    Dim prp As Object ' DAO.Property\r\n    Dim blnFound As Boolean\r\n    Dim oParent As Object\r\n\r\n    ' Properties set differently for databases and ADP projects\r\n    If CurrentProject.ProjectType = acADP Then\r\n        Set oParent = CurrentProject.Properties\r\n    Else\r\n        If dbs Is Nothing Then Set dbs = CurrentDb\r\n        Set oParent = dbs.Properties\r\n    End If\r\n\r\n    ' Look for property in collection\r\n    For Each prp In oParent\r\n        If prp.Name = strName Then\r\n            ' Check for matching type\r\n            If Not dbs Is Nothing Then\r\n                If prp.Type <> prpType Then\r\n                    ' Remove so we can add it back in with the correct type.\r\n                    dbs.Properties.Delete strName\r\n                    Exit For\r\n                End If\r\n            End If\r\n            blnFound = True\r\n            ' Skip set on matching value\r\n            If prp.Value = varValue Then\r\n                Set dbs = Nothing\r\n            Else\r\n                ' Update value\r\n                prp.Value = varValue\r\n            End If\r\n            Exit Sub\r\n        End If\r\n    Next prp\r\n\r\n    ' Add new property\r\n    If Not blnFound Then\r\n        If CurrentProject.ProjectType = acADP Then\r\n            CurrentProject.Properties.Add strName, varValue\r\n        Else\r\n            Set prp = dbs.CreateProperty(strName, prpType, varValue)\r\n            dbs.Properties.Append prp\r\n            Set dbs = Nothing\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetDAOProperty\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2020\r\n' Purpose   : Updates a DAO property, adding if it does not exist or is the wrong type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetDAOProperty(objParent As Object, intType As Integer, strName As String, varValue As Variant)\r\n\r\n    Dim prp As DAO.Property\r\n    Dim blnFound As Boolean\r\n\r\n    ' Look through existing properties.\r\n    For Each prp In objParent.Properties\r\n        If prp.Name = strName Then\r\n            blnFound = True\r\n            Exit For\r\n        End If\r\n    Next prp\r\n\r\n    ' Verify type, and update value if found.\r\n    If blnFound Then\r\n        If prp.Type <> intType Then\r\n            objParent.Properties.Delete strName\r\n            blnFound = False\r\n        Else\r\n            If objParent.Properties(strName).Value <> varValue Then\r\n                objParent.Properties(strName).Value = varValue\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' Add new property if needed\r\n    If Not blnFound Then\r\n        ' Create property, then append to collection\r\n        Set prp = objParent.CreateProperty(strName, intType, varValue)\r\n        objParent.Properties.Append prp\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetAccessObjectProperty\r\n' Author    : Adam Waller\r\n' Date      : 10/13/2017\r\n' Purpose   : Sets a custom access object property.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetAccessObjectProperty(objItem As AccessObject, strProperty As String, strValue As String)\r\n    Dim prp As AccessObjectProperty\r\n    For Each prp In objItem.Properties\r\n        If StrComp(prp.Name, strProperty, vbTextCompare) = 0 Then\r\n            ' Update value of property.\r\n            prp.Value = strValue\r\n            Exit Sub\r\n        End If\r\n    Next prp\r\n    ' Property not found. Create it.\r\n    objItem.Properties.Add strProperty, strValue\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAccessObjectProperty\r\n' Author    : Adam Waller\r\n' Date      : 10/13/2017\r\n' Purpose   : Get the value of a custom access property\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetAccessObjectProperty(objItem As AccessObject, strProperty As String, Optional strDefault As String) As Variant\r\n    Dim prp As AccessObjectProperty\r\n    For Each prp In objItem.Properties\r\n        If StrComp(prp.Name, strProperty, vbTextCompare) = 0 Then\r\n            GetAccessObjectProperty = prp.Value\r\n            Exit Function\r\n        End If\r\n    Next prp\r\n    ' Nothing found. Return default\r\n    GetAccessObjectProperty = strDefault\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsLoaded\r\n' Author    : Adam Waller\r\n' Date      : 9/22/2017\r\n' Purpose   : Returns true if the object is loaded and not in design view.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IsLoaded(intType As AcObjectType, strName As String, Optional blnAllowDesignView As Boolean = False) As Boolean\r\n\r\n    Dim frm As Form\r\n    Dim rpt As Report\r\n    Dim ctl As Control\r\n\r\n    If SysCmd(acSysCmdGetObjectState, intType, strName) <> adStateClosed Then\r\n        If blnAllowDesignView Then\r\n            IsLoaded = True\r\n        Else\r\n            Select Case intType\r\n                Case acForm\r\n                    ' Loop through forms collection, since this includes instances\r\n                    ' of add-in forms that cannot be referenced directly by name.\r\n                    For Each frm In Forms\r\n                        If StrComp(frm.Name, strName, vbTextCompare) = 0 Then\r\n                            IsLoaded = frm.CurrentView <> acCurViewDesign\r\n                            Exit For\r\n                        End If\r\n                    Next frm\r\n                Case acReport\r\n                    ' Loop through reports, looking for matching name.\r\n                    For Each rpt In Reports\r\n                        If StrComp(rpt.Name, strName, vbTextCompare) = 0 Then\r\n                            IsLoaded = rpt.CurrentView <> acCurViewDesign\r\n                            Exit For\r\n                        End If\r\n                    Next rpt\r\n                Case acServerView\r\n                    IsLoaded = CurrentData.AllViews(strName).CurrentView <> acCurViewDesign\r\n                Case acStoredProcedure\r\n                    IsLoaded = CurrentData.AllStoredProcedures(strName).CurrentView <> acCurViewDesign\r\n                Case Else\r\n                    ' Other unsupported object\r\n                    IsLoaded = True\r\n            End Select\r\n        End If\r\n    Else\r\n        ' Could be loaded as subform\r\n        If intType = acForm Then\r\n            For Each frm In Forms\r\n                For Each ctl In frm.Controls\r\n                    If TypeOf ctl Is SubForm Then\r\n                        If ctl.SourceObject = strName Then\r\n                            IsLoaded = True\r\n                            Exit For\r\n                        End If\r\n                    End If\r\n                Next ctl\r\n                If IsLoaded Then Exit For\r\n            Next frm\r\n        End If\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CloseDatabaseObjects\r\n' Author    : Adam Waller\r\n' Date      : 4/1/2022\r\n' Purpose   : Close any open database objects, returns true if no errors were\r\n'           : encountered. (This is run before a major operation like an export or\r\n'           : a merge.) ADP-specific items aren't particularly supported here.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function CloseDatabaseObjects() As Boolean\r\n\r\n    Dim blnSuccess As Boolean\r\n    Dim objItem As AccessObject\r\n\r\n    If DebugMode(True) Then On Error GoTo ErrHandler Else On Error GoTo ErrHandler\r\n\r\n    Perf.OperationStart \"Close Open Objects\"\r\n\r\n    ' Check forms and reports\r\n    blnSuccess = CloseAllFormsReports\r\n\r\n    ' If all forms and reports are closed, proceed with other object types.\r\n    If blnSuccess Then\r\n\r\n        ' Macros\r\n        For Each objItem In CurrentProject.AllMacros\r\n            If IsLoaded(acMacro, objItem.Name) Then DoCmd.Close acMacro, objItem.Name\r\n        Next objItem\r\n\r\n        ' Tables\r\n        For Each objItem In CurrentData.AllTables\r\n            If IsLoaded(acTable, objItem.Name) Then DoCmd.Close acTable, objItem.Name\r\n        Next objItem\r\n\r\n        ' Queries\r\n        For Each objItem In CurrentData.AllQueries\r\n            If IsLoaded(acQuery, objItem.Name) Then DoCmd.Close acQuery, objItem.Name\r\n        Next objItem\r\n\r\n    End If\r\n\r\n    Perf.OperationEnd\r\n    CloseDatabaseObjects = blnSuccess\r\n\r\n    Exit Function\r\n\r\nErrHandler:\r\n\r\n    blnSuccess = False\r\n\r\n    ' Handle any error message in calling function\r\n    CatchAny eelNoError, T(\"Unable to close database object\"), ModuleName & \".CloseDatabaseObjects\", False\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CloseAllFormsReports\r\n' Author    : Adam Waller\r\n' Date      : 1/25/2019\r\n' Purpose   : Close all open forms and reports. Returns true if successful.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function CloseAllFormsReports() As Boolean\r\n\r\n    Dim strName As String\r\n    Dim intOpened As Integer\r\n    Dim intItem As Integer\r\n\r\n    ' Get count of opened objects\r\n    intOpened = Forms.Count + Reports.Count\r\n    If intOpened > 0 Then\r\n        On Error GoTo ErrorHandler\r\n        ' Loop through forms\r\n        For intItem = Forms.Count - 1 To 0 Step -1\r\n            If Forms(intItem).Caption <> PROJECT_NAME Then\r\n                DoCmd.Close acForm, Forms(intItem).Name\r\n                DoEvents\r\n            End If\r\n            intOpened = intOpened - 1\r\n        Next intItem\r\n        ' Loop through reports\r\n        Do While Reports.Count > 0\r\n            strName = Reports(0).Name\r\n            DoCmd.Close acReport, strName\r\n            DoEvents\r\n            intOpened = intOpened - 1\r\n        Loop\r\n        If intOpened = 0 Then CloseAllFormsReports = True\r\n    Else\r\n        ' No forms or reports currently open.\r\n        CloseAllFormsReports = True\r\n    End If\r\n\r\n    Exit Function\r\n\r\nErrorHandler:\r\n    Debug.Print T(\"Error closing {0}: {1}\" & vbCrLf & \"{2}\", , , , strName, Err.Number, Err.Description)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ObjectExists\r\n' Author    : Adam Waller\r\n' Date      : 3/3/2023\r\n' Purpose   : See if the object exists in the current database/project\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ObjectExists(intType As AcObjectType, strName As String, Optional blnInCodeDb As Boolean = False) As Boolean\r\n\r\n    Dim objTest As Object\r\n    Dim objContainer As Object\r\n\r\n    Set objContainer = GetParentContainer(intType, blnInCodeDb)\r\n    If objContainer Is Nothing Then\r\n        Log.Error eelError, T(\"Parent container not supported for this object type: {0}\", var0:=intType), ModuleName & \".ObjectExists\"\r\n    Else\r\n        ' Attempt to reference the object by name\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        Set objTest = objContainer(strName)\r\n        ObjectExists = Not Catch(2467)\r\n        If Err Then Err.Clear\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetParentContainer\r\n' Author    : Adam Waller\r\n' Date      : 3/3/2023\r\n' Purpose   : Get the parent container collection for the object type. (Not all types\r\n'           : are supported.)\r\n'           : Also allows you to specify whether to use the CodeDb or CurrentDb\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetParentContainer(intType As AcObjectType, Optional blnInCodeDb As Boolean = False) As Object\r\n\r\n    Dim objHostData As CurrentData\r\n    Dim objHostProject As CurrentProject\r\n\r\n    ' Set the host objects\r\n    If blnInCodeDb Then\r\n        Set objHostData = CodeData\r\n        Set objHostProject = CodeProject\r\n    Else\r\n        Set objHostData = CurrentData\r\n        Set objHostProject = CurrentProject\r\n    End If\r\n\r\n    ' Return the associated parent container\r\n    Select Case intType\r\n\r\n        ' ADP Specific\r\n        Case acDiagram:             Set GetParentContainer = objHostData.AllDatabaseDiagrams\r\n        Case acFunction:            Set GetParentContainer = objHostData.AllFunctions\r\n        Case acServerView:          Set GetParentContainer = objHostData.AllViews\r\n        Case acStoredProcedure:     Set GetParentContainer = objHostData.AllStoredProcedures\r\n\r\n        ' Database objects\r\n        Case acForm:                Set GetParentContainer = objHostProject.AllForms\r\n        Case acMacro:               Set GetParentContainer = objHostProject.AllMacros\r\n        Case acModule:              Set GetParentContainer = objHostProject.AllModules\r\n        Case acQuery:               Set GetParentContainer = objHostData.AllQueries\r\n        Case acReport:              Set GetParentContainer = objHostProject.AllReports\r\n        Case acTable:               Set GetParentContainer = objHostData.AllTables\r\n\r\n        ' Unsupported\r\n        Case acTableDataMacro, acDatabaseProperties\r\n        Case Else\r\n\r\n    End Select\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ProjectIsSelected\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2015\r\n' Purpose   : Returns true if the base project is selected in the VBE\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ProjectIsSelected() As Boolean\r\n    ProjectIsSelected = (Application.VBE.SelectedVBComponent Is Nothing)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SelectionInActiveProject\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2015\r\n' Purpose   : Returns true if the current selection is in the active project\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function SelectionInActiveProject() As Boolean\r\n    SelectionInActiveProject = (Application.VBE.ActiveVBProject.FileName = GetUncPath(CurrentProject.FullName))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetCodeVBProject\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Get a reference to the VB Project for the running code.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetCodeVBProject() As VBProject\r\n    Set GetCodeVBProject = GetProjectByName(CodeProject.FullName)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetProjectByName\r\n' Author    : Adam Waller\r\n' Date      : 5/26/2020\r\n' Purpose   : Return the VBProject by file path. (Also supports network drives)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetProjectByName(ByVal strPath As String) As VBProject\r\n\r\n    Dim objProj As VBIDE.VBProject\r\n    Dim strUncPath As String\r\n\r\n    ' Use currently active project by default\r\n    Set GetProjectByName = VBE.ActiveVBProject\r\n\r\n    ' VBProject filenames are UNC paths\r\n    strUncPath = UCase(GetUncPath(strPath))\r\n\r\n    If UCase(VBE.ActiveVBProject.FileName) <> strUncPath Then\r\n        ' Search for project with matching filename.\r\n        For Each objProj In VBE.VBProjects\r\n            If UCase(objProj.FileName) = strUncPath Then\r\n                Set GetProjectByName = objProj\r\n                Exit For\r\n            End If\r\n        Next objProj\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RunInCurrentProject\r\n' Author    : Adam Waller\r\n' Date      : 4/22/2020\r\n' Purpose   : Use the Run command but make sure it is running in the context of the\r\n'           : current project, not the add-in file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub RunSubInCurrentProject(strSubName As String)\r\n\r\n    Dim strCmd As String\r\n\r\n    ' Don't need the parentheses after the sub name\r\n    strCmd = Replace(strSubName, \"()\", vbNullString)\r\n\r\n    ' Make sure we are not trying to run a function with arguments\r\n    If InStr(strCmd, \"(\") > 0 Then\r\n        MsgBox2 T(\"Unable to Run Command\"), _\r\n            T(\"Parameters are not supported for this command.\"), _\r\n            T(\"If you need to use parameters, please create a wrapper sub or function with\" & vbCrLf & _\r\n            \"no parameters that you can call instead of {0}.\", var0:=strSubName), vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Make sure procedure exists in current database\r\n    If Not GlobalProcExists(strSubName) Then\r\n        Log.Error eelError, T(\"The procedure \"\"{0}\"\" not found.\", var0:=strSubName), ModuleName & \".RunSubInCurrentProject\"\r\n        Log.Add T(\"The procedure must be declared as public in a standard module.\"), False\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Add project name so we can run it from the current datbase\r\n    strCmd = \"[\" & CurrentVBProject.Name & \"].\" & strCmd\r\n\r\n    ' Check for add-in project name\r\n    If StrComp(CurrentVBProject.Name, GetAddInProject.Name, vbTextCompare) = 0 Then\r\n        ' Temporarily rename the add-in project so the sub runs in the current project ... Not required: call with full name\r\n        Dim VbProjectFilenameWithoutFileExt As String\r\n        Const AddInFileExtension As String = \".accda\"\r\n        With CurrentVBProject\r\n            VbProjectFilenameWithoutFileExt = Left(.FileName, Len(.FileName) - Len(AddInFileExtension))\r\n            strCmd = Replace(strCmd, \"[\" & .Name & \"]\", VbProjectFilenameWithoutFileExt)\r\n        End With\r\n    End If\r\n\r\n    Application.Run strCmd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DatabaseFileOpen\r\n' Author    : Adam Waller\r\n' Date      : 7/14/2020\r\n' Purpose   : Returns true if a database (or ADP project) is currently open.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DatabaseFileOpen() As Boolean\r\n\r\n    Dim strTest As String\r\n\r\n    ' See if we have a reference to a CurrentProject object\r\n    If CurrentProject Is Nothing Then\r\n        DatabaseFileOpen = False\r\n    Else\r\n        ' For ADP projects, CurrentProject may be an invalid object reference\r\n        ' after the database file (adp) is closed.\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        strTest = CurrentProject.FullName\r\n        CatchAny eelNoError, vbNullString\r\n        DatabaseFileOpen = (strTest <> vbNullString)\r\n    End If\r\n\r\n    'DatabaseOpen = Workspaces(0).Databases.Count > 0   ' Another approach (Not ADP compatible)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TableExists\r\n' Author    : Adam Waller\r\n' Date      : 3/13/2023\r\n' Purpose   : Returns true if the table object is found in the dabase. (SQL version)\r\n'           : (Includes both local and linked tables, including system tables.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function TableExists(strName As String, Optional dbs As DAO.Database) As Boolean\r\n\r\n    Dim lngType As Long\r\n    Dim tdf As TableDef\r\n\r\n    ' Read type of table from internal system table\r\n    lngType = Nz(DLookup(\"Type\", \"MSysObjects\", \"Name=\"\"\" & strName & \"\"\" AND Type in (1,4,6)\"), 0)\r\n\r\n    ' Watch for errors connecting to linked tables\r\n    LogUnhandledErrors\r\n    On Error GoTo ErrHandler\r\n\r\n    Select Case lngType\r\n        Case 0  ' Does not exist\r\n            TableExists = False\r\n        Case 1  ' Local table\r\n            TableExists = True\r\n        Case Else\r\n            ' For linked tables, also check availability of target\r\n            If dbs Is Nothing Then Set dbs = CurrentDb\r\n            Set tdf = dbs.TableDefs(strName)\r\n            ' Return true if we find fields in the table\r\n            TableExists = (tdf.Fields.Count > 0)\r\n    End Select\r\n\r\n    Exit Function\r\n\r\nErrHandler:\r\n    ' Encountered a problem accessing the table\r\n    TableExists = False\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsLocalTable\r\n' Author    : Adam Waller\r\n' Date      : 3/13/2023\r\n' Purpose   : Returns true if the table exists as a local (not linked) table in the\r\n'           : current database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IsLocalTable(strName As String) As Boolean\r\n    IsLocalTable = Not (DCount(\"*\", \"MSysObjects\", \"Name=\"\"\" & strName & \"\"\" AND Type = 1\") = 0)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DeleteObjectIfExists\r\n' Author    : Adam Waller\r\n' Date      : 3/3/2023\r\n' Purpose   : Deletes the object if it exists. This gets really tricky if the object\r\n'           : exists in both the add-in database and the current database. Thankfully\r\n'           : we have a way to work around this for most objects by renaming to a\r\n'           : unique name that doesn't exist in the add-in, and deleting the renamed\r\n'           : object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DeleteObjectIfExists(intType As AcObjectType, strName As String) As Boolean\r\n\r\n    Dim blnExistsInAddIn As Boolean\r\n    Dim strTempName As String\r\n\r\n    ' If object does not exist in the current database, no need to go further\r\n    If Not ObjectExists(intType, strName) Then Exit Function\r\n\r\n    ' Check to see if the object exists in the add-in database. (See note above)\r\n    Select Case intType\r\n        ' Object types used in the add-in\r\n        Case acForm, acMacro, acModule, acQuery, acTable\r\n            blnExistsInAddIn = ObjectExists(intType, strName, True)\r\n    End Select\r\n\r\n    ' Trap errors when attempting to delete the object\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    If Not blnExistsInAddIn Then\r\n        ' Nice! We can use a simple call to delete the object\r\n        DoCmd.DeleteObject intType, strName\r\n    Else\r\n        ' This is where it gets fun... If you attempt to delete an object from the\r\n        ' VBA code in the add-in, it will default to operating on the add-in object\r\n        ' first, before looking in the current database. This can cause corruption\r\n        ' in the add-in, in addition to failing to delete the object from the\r\n        ' current database. To work around this, we will rename the object to something\r\n        ' random first, then delete it based on the randomized (non-matching) name.\r\n\r\n        ' Build a random temp name that will not collide with the add-in or any existing\r\n        ' object in the current database. (But leave a meaningful clue in the name, in\r\n        ' case something goes wrong and it ends up staying in the database.)\r\n        strTempName = strName & \"_DELETE_\" & GetStringHash(Perf.MicroTimer)\r\n\r\n        ' We need to avoid using DoCmd.Rename for the same reasons\r\n        Select Case intType\r\n            Case acForm\r\n                Log.Error eelError, T(\"Cannot delete a form with the same name as an add-in form.\"), _\r\n                    ModuleName & \".DeleteObjectIfExists\"\r\n                    Exit Function   ' (Returns False)\r\n            Case acMacro\r\n                ' The rename command seems to work on this object type... (At least\r\n                ' in Access 2010)\r\n                DoCmd.Rename strTempName, intType, strName\r\n            Case acModule\r\n                ' Rename the VBE object\r\n                CurrentVBProject.VBComponents(strName).Name = strTempName\r\n            Case acQuery\r\n                ' Rename the Query Definition object\r\n                CurrentDb.QueryDefs(strName).Name = strTempName\r\n            Case acTable\r\n                ' Rename the Table Definition object\r\n                CurrentDb.TableDefs(strName).Name = strTempName\r\n        End Select\r\n\r\n        ' Trap any errors involved in renaming the object\r\n        If Not CatchAny(eelError, T(\"Error renaming object: {0}\", var0:=strName), ModuleName & \".DeleteObjectIfExists\") Then\r\n            ' Delete object using the temp name\r\n            DoCmd.DeleteObject intType, strTempName\r\n        End If\r\n    End If\r\n\r\n    ' Catch any errors with deleting the object\r\n    CatchAny eelError, T(\"Error deleting object: {0}\", var0:=strName), ModuleName & \".DeleteObjectIfExists\"\r\n\r\n    ' Return success if the object no longer exists\r\n    DeleteObjectIfExists = Not ObjectExists(intType, strName)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DbVersion\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2021\r\n' Purpose   : Return the database version as an integer. Works in non-English locales\r\n'           : where CInt(CurrentDb.Version) doesn't work correctly.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DbVersion() As Integer\r\n    DbVersion = CInt(Split(CurrentDb.Version, \".\")(0))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FormLoaded\r\n' Author    : Adam Waller\r\n' Date      : 7/8/2021\r\n' Purpose   : Helps identify if a form has been closed, but is still running code\r\n'           : after the close event.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function FormLoaded(frmMe As Form) As Boolean\r\n    Dim strName As String\r\n    ' If no forms are open, we already have our answer.  :-)\r\n    If Forms.Count > 0 Then\r\n        ' We will throw an error accessing the name property if the form is closed\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        strName = frmMe.Name\r\n        ' Return true if we were able to read the name property\r\n        FormLoaded = strName <> vbNullString\r\n        If Err Then Err.Clear\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyFocus\r\n' Author    : Adam Waller\r\n' Date      : 7/8/2021\r\n' Purpose   : Verify that a control currently has the focus. (Is the active control)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function VerifyFocus(ctlWithFocus As Control) As Boolean\r\n\r\n    Dim frmParent As Form\r\n    Dim objParent As Object\r\n    Dim ctlCurrentFocus As Control\r\n\r\n    ' Determine parent form for control\r\n    Set objParent = ctlWithFocus\r\n    Do While Not TypeOf objParent Is Form\r\n        Set objParent = objParent.Parent\r\n    Loop\r\n    Set frmParent = objParent\r\n\r\n    ' Ignore any errors with Screen.* functions\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    ' Verify focus of parent form\r\n    Set frmParent = Screen.ActiveForm\r\n    If Not frmParent Is objParent Then\r\n        Set frmParent = objParent\r\n        frmParent.SetFocus\r\n        DoEvents\r\n    End If\r\n\r\n    ' Verify focus of control on form\r\n    Set ctlCurrentFocus = frmParent.ActiveControl\r\n    If Not ctlCurrentFocus Is ctlWithFocus Then\r\n        ctlWithFocus.SetFocus\r\n        DoEvents\r\n    End If\r\n\r\n    ' Return true if the control currently has the focus\r\n    VerifyFocus = frmParent.ActiveControl Is ctlWithFocus\r\n\r\n    ' Discard any errors\r\n    CatchAny eelNoError, vbNullString, , False\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAvailableConnectionCount\r\n' Author    : Adam Waller\r\n' Date      : 6/8/2022\r\n' Purpose   : Returns the number of available connections in the current database.\r\n'           : (Access has a maximum of 255 connections that can be made to the current\r\n'           :  database object.) See issue #338\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetAvailableConnectionCount()\r\n\r\n    Dim colDbs As Collection\r\n    Dim dbs As DAO.Database\r\n    Dim intCnt As Integer\r\n\r\n    Set colDbs = New Collection\r\n\r\n    On Error Resume Next\r\n    For intCnt = 1 To 300\r\n        Set dbs = CurrentDb\r\n        If Err Then\r\n            ' Probably cannot open any more databases\r\n            Err.Clear\r\n            Exit For\r\n        End If\r\n        colDbs.Add dbs\r\n    Next intCnt\r\n\r\n    Set dbs = Nothing\r\n\r\n    ' Return count of how many connections we were\r\n    ' able to create before hitting an error\r\n    GetAvailableConnectionCount = intCnt\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modEncoding.bas",
    "content": "﻿Attribute VB_Name = \"modEncoding\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modEncoding\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Functions for reading and converting file encodings (Unicode, UTF-8)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n' API call to determine active code page (default system encoding)\r\nPrivate Declare PtrSafe Function GetACP Lib \"kernel32\" () As Long\r\n\r\n\r\n' Cache the Ucs2 requirement for this database\r\nPrivate m_blnUcs2 As Boolean\r\nPrivate m_strDbPath As String\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RequiresUcs2\r\n' Author    : Adam Waller\r\n' Date      : 5/5/2020\r\n' Purpose   : Returns true if the current database requires objects to be converted\r\n'           : to Ucs2 format before importing. (Caching value for subsequent calls.)\r\n'           : While this involves creating a new querydef object each time, the idea\r\n'           : is that this would be faster than exporting a form if no queries exist\r\n'           : in the current database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function RequiresUcs2(Optional blnUseCache As Boolean = True) As Boolean\r\n\r\n    Dim strTempFile As String\r\n    Dim frm As Access.Form\r\n    Dim strName As String\r\n    Dim dbs As DAO.Database\r\n\r\n    ' See if we already have a cached value\r\n    If (m_strDbPath <> CurrentProject.FullName) Or Not blnUseCache Then\r\n\r\n        ' Get temp file name\r\n        strTempFile = GetTempFile\r\n\r\n        ' Can't create querydef objects in ADP databases, so we have to use something else.\r\n        If CurrentProject.ProjectType = acADP Then\r\n            ' Create and export a blank form object.\r\n            ' Turn of screen updates to improve performance and avoid flash.\r\n            DoCmd.Echo False\r\n            'strName = \"frmTEMP_UCS2_\" & Round(Timer)\r\n            Set frm = Application.CreateForm\r\n            strName = frm.Name\r\n            DoCmd.Close acForm, strName, acSaveYes\r\n            Perf.OperationStart \"App.SaveAsText()\"\r\n            Application.SaveAsText acForm, strName, strTempFile\r\n            Perf.OperationEnd\r\n            DoCmd.DeleteObject acForm, strName\r\n            DoCmd.Echo True\r\n        Else\r\n            ' Standard MDB database.\r\n            ' Create and export a querydef object. Fast and light.\r\n            strName = \"qryTEMP_UCS2_\" & Round(Timer)\r\n            Set dbs = CurrentDb\r\n            dbs.CreateQueryDef strName, \"SELECT 1\"\r\n            Perf.OperationStart \"App.SaveAsText()\"\r\n            Application.SaveAsText acQuery, strName, strTempFile\r\n            Perf.OperationEnd\r\n            dbs.QueryDefs.Delete strName\r\n        End If\r\n\r\n        ' Test and delete temp file\r\n        m_strDbPath = CurrentProject.FullName\r\n        m_blnUcs2 = HasUcs2Bom(strTempFile)\r\n        DeleteFile strTempFile, True\r\n\r\n    End If\r\n\r\n    ' Return cached value\r\n    RequiresUcs2 = m_blnUcs2\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ConvertUcs2Utf8\r\n' Author    : Adam Waller\r\n' Date      : 1/23/2019\r\n' Purpose   : Convert a UCS2-little-endian encoded file to UTF-8.\r\n'           : Typically the source file will be a temp file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ConvertUcs2Utf8(strSourceFile As String, strDestinationFile As String, _\r\n    Optional blnDeleteSourceFileAfterConversion As Boolean = True)\r\n\r\n    Dim cData As clsConcat\r\n    Dim blnIsAdp As Boolean\r\n    Dim intTristate As Tristate\r\n\r\n    ' Remove any existing file.\r\n    If FSO.FileExists(strDestinationFile) Then DeleteFile strDestinationFile, True\r\n\r\n    ' ADP Projects do not use the UCS BOM, but may contain mixed UTF-16 content\r\n    ' representing unicode characters.\r\n    blnIsAdp = (CurrentProject.ProjectType = acADP)\r\n\r\n    ' Check the first couple characters in the file for a UCS BOM.\r\n    If HasUcs2Bom(strSourceFile) Or blnIsAdp Then\r\n\r\n        ' Determine format\r\n        If blnIsAdp Then\r\n            ' Possible mixed UTF-16 content\r\n            intTristate = TristateMixed\r\n\r\n            ' Log performance\r\n            Perf.OperationStart \"Unicode Conversion\"\r\n\r\n            ' Read file contents and delete (temp) source file\r\n            Set cData = New clsConcat\r\n            With FSO.OpenTextFile(strSourceFile, ForReading, False, intTristate)\r\n                ' Read chunks of text, rather than the whole thing at once for massive\r\n                ' performance gains when reading large files.\r\n                ' See https://docs.microsoft.com/is-is/sql/ado/reference/ado-api/readtext-method\r\n                Do While Not .AtEndOfStream\r\n                    cData.Add .Read(CHUNK_SIZE)  ' 128K\r\n                Loop\r\n                .Close\r\n            End With\r\n\r\n            ' Write as UTF-8 in the destination file.\r\n            ' (Path will be verified before writing)\r\n            WriteFile cData.GetStr, strDestinationFile\r\n            Perf.OperationEnd\r\n\r\n        Else\r\n            ' Fully encoded as UTF-16\r\n            ReEncodeFile strSourceFile, \"utf-16\", strDestinationFile, \"utf-8\"\r\n        End If\r\n\r\n        ' Remove the source (temp) file if specified\r\n        If blnDeleteSourceFileAfterConversion Then DeleteFile strSourceFile, True\r\n    Else\r\n        ' No conversion needed, move/copy to destination.\r\n        VerifyPath strDestinationFile\r\n        If blnDeleteSourceFileAfterConversion Then\r\n            FSO.MoveFile strSourceFile, strDestinationFile\r\n        Else\r\n            FSO.CopyFile strSourceFile, strDestinationFile\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ConvertUtf8Ucs2\r\n' Author    : Adam Waller\r\n' Date      : 1/24/2019\r\n' Purpose   : Convert the file to old UCS-2 unicode format.\r\n'           : Typically the destination file will be a temp file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ConvertUtf8Ucs2(strSourceFile As String, strDestinationFile As String, _\r\n    Optional blnDeleteSourceFileAfterConversion As Boolean = True)\r\n\r\n    ' Make sure the path exists before we write a file.\r\n    VerifyPath strDestinationFile\r\n    If FSO.FileExists(strDestinationFile) Then DeleteFile strDestinationFile, True\r\n\r\n    If HasUcs2Bom(strSourceFile) Then\r\n        ' No conversion needed, move/copy to destination.\r\n        If blnDeleteSourceFileAfterConversion Then\r\n            FSO.MoveFile strSourceFile, strDestinationFile\r\n        Else\r\n            FSO.CopyFile strSourceFile, strDestinationFile\r\n        End If\r\n    Else\r\n        ' Encode as UCS2-LE (UTF-16 LE)\r\n        ReEncodeFile strSourceFile, \"utf-8\", strDestinationFile, \"utf-16\"\r\n\r\n        ' Remove original file if specified.\r\n        If blnDeleteSourceFileAfterConversion Then DeleteFile strSourceFile, True\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ConvertAnsiiUtf8\r\n' Author    : Adam Waller\r\n' Date      : 2/3/2021\r\n' Purpose   : Convert an ANSI encoded file to UTF-8. This allows extended characters\r\n'           : to properly display in diffs and other programs. See issue #154\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ConvertAnsiUtf8(strSourceFile As String, strDestinationFile As String, _\r\n    Optional blnDeleteSourceFileAfterConversion As Boolean = True)\r\n\r\n    ' Perform file conversion\r\n    ReEncodeFile strSourceFile, GetSystemEncoding, strDestinationFile, \"utf-8\", adSaveCreateOverWrite\r\n\r\n    ' Remove original file if specified.\r\n    If blnDeleteSourceFileAfterConversion Then DeleteFile strSourceFile\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ConvertUtf8Ansii\r\n' Author    : Adam Waller\r\n' Date      : 2/3/2021\r\n' Purpose   : Convert a UTF-8 file back to ANSI.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ConvertUtf8Ansi(strSourceFile As String, strDestinationFile As String, _\r\n    Optional blnDeleteSourceFileAfterConversion As Boolean = True)\r\n\r\n    ' Perform file conversion\r\n    ReEncodeFile strSourceFile, \"utf-8\", strDestinationFile, GetSystemEncoding, adSaveCreateOverWrite\r\n\r\n    ' Remove original file if specified.\r\n    If blnDeleteSourceFileAfterConversion Then DeleteFile strSourceFile\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasUtf8Bom\r\n' Author    : Adam Waller\r\n' Date      : 7/30/2020\r\n' Purpose   : Returns true if the file begins with a UTF-8 BOM\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function HasUtf8Bom(strFilePath As String) As Boolean\r\n    Dim bte() As Byte\r\n    bte = GetFileBytes(strFilePath, 3)\r\n    HasUtf8Bom = ((bte(0) = &HEF) And (bte(1) = &HBB) And (bte(2) = &HBF))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasUcs2Bom\r\n' Author    : Adam Waller\r\n' Date      : 8/1/2020\r\n' Purpose   : Returns true if the file begins with the bytes `FF FE` (ÿþ)\r\n'           : Note that these must be read as bytes, not compared to a string value\r\n'           : if the system is using the UTF-8 (beta) option in Windows 10. See #378\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function HasUcs2Bom(strFilePath As String) As Boolean\r\n    Dim bte() As Byte\r\n    bte = GetFileBytes(strFilePath, 2)\r\n    HasUcs2Bom = ((bte(0) = &HFF) And (bte(1) = &HFE))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StringHasExtendedASCII\r\n' Author    : Adam Waller\r\n' Date      : 3/6/2020\r\n' Purpose   : Returns true if the string contains non-ASCI characters.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function StringHasExtendedASCII(strText As String) As Boolean\r\n\r\n    Perf.OperationStart \"Extended Chars Check\"\r\n    With New VBScript_RegExp_55.RegExp\r\n        ' Include extended ASCII characters here.\r\n        .Pattern = \"[^\\u0000-\\u007F]\"\r\n        StringHasExtendedASCII = .Test(strText)\r\n    End With\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReEncodeFile\r\n' Author    : Adam Kauffman / Adam Waller\r\n' Date      : 3/4/2021\r\n' Purpose   : Change File Encoding. It reads and writes at the same time so the files must be different.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ReEncodeFile(strInputFile As String, strInputCharset As String, _\r\n    strOutputFile As String, strOutputCharset As String, _\r\n    Optional intOverwriteMode As SaveOptionsEnum = adSaveCreateOverWrite)\r\n\r\n    Dim objOutputStream As ADODB.Stream\r\n\r\n    ' Open streams and copy data\r\n    Perf.OperationStart \"Enc. \" & strInputCharset & \" as \" & strOutputCharset\r\n    Set objOutputStream = New ADODB.Stream\r\n    With New ADODB.Stream\r\n        .Open\r\n        .Type = adTypeBinary\r\n        Perf.OperationStart \"Load Stream from File\"\r\n        .LoadFromFile strInputFile\r\n        Perf.OperationEnd\r\n        .Type = adTypeText\r\n        .Charset = strInputCharset\r\n        objOutputStream.Open\r\n        objOutputStream.Charset = strOutputCharset\r\n        ' Copy from one stream to the other\r\n        Perf.OperationStart \"Copy Stream\"\r\n        .CopyTo objOutputStream\r\n        Perf.OperationEnd\r\n        .Close\r\n    End With\r\n\r\n    ' Save file and log performance\r\n    VerifyPath strOutputFile\r\n    Perf.OperationStart \"Save Stream to File\"\r\n    objOutputStream.SaveToFile strOutputFile, intOverwriteMode\r\n    Perf.OperationEnd\r\n    objOutputStream.Close\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSystemEncoding\r\n' Author    : Adam Waller\r\n' Date      : 7/1/2021\r\n' Purpose   : Return the current encoding type used for non-UTF-8 text files.\r\n'           : (Such as VBA code modules.)\r\n'           : https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers\r\n'           : https://documentation.help/MS-Office-VB/ofhowConstants.htm\r\n'           : * Note that using utf-8 as a default system encoding may not work\r\n'           : correctly with some extended characters in VBA code modules. The VBA IDE\r\n'           : does not support Unicode characters, and requires code pages to display\r\n'           : extended/non-English characters. See Issues #60, #186, #180, #246, #377\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSystemEncoding(Optional blnAllowUtf8 As Boolean = False) As String\r\n\r\n    Static lngEncoding As Long\r\n\r\n    ' Call API to determine active code page, caching return value.\r\n    If lngEncoding = 0 Then lngEncoding = GetACP\r\n    Select Case lngEncoding\r\n\r\n        ' Language encoding mappings are defined here, based on the following sources:\r\n        ' https://docs.microsoft.com/en-us/office/vba/api/office.msoencoding\r\n        ' https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=net-5.0\r\n        Case msoEncodingEBCDICUSCanada:                 GetSystemEncoding = \"IBM037\"\r\n        Case msoEncodingOEMUnitedStates:                GetSystemEncoding = \"IBM437\"\r\n        Case msoEncodingEBCDICInternational:            GetSystemEncoding = \"IBM500\"\r\n        Case msoEncodingArabicASMO:                     GetSystemEncoding = \"ASMO-708\"\r\n        Case msoEncodingArabicTransparentASMO:          GetSystemEncoding = \"DOS-720\"\r\n        Case msoEncodingOEMGreek437G:                   GetSystemEncoding = \"ibm737\"\r\n        Case msoEncodingOEMBaltic:                      GetSystemEncoding = \"ibm775\"\r\n        Case msoEncodingOEMMultilingualLatinI:          GetSystemEncoding = \"ibm850\"\r\n        Case msoEncodingOEMMultilingualLatinII:         GetSystemEncoding = \"ibm852\"\r\n        Case msoEncodingOEMCyrillic:                    GetSystemEncoding = \"IBM855\"\r\n        Case msoEncodingOEMTurkish:                     GetSystemEncoding = \"ibm857\"\r\n        Case msoEncodingOEMPortuguese:                  GetSystemEncoding = \"IBM860\"\r\n        Case msoEncodingOEMIcelandic:                   GetSystemEncoding = \"ibm861\"\r\n        Case msoEncodingOEMHebrew:                      GetSystemEncoding = \"DOS-862\"\r\n        Case msoEncodingOEMCanadianFrench:              GetSystemEncoding = \"IBM863\"\r\n        Case msoEncodingOEMArabic:                      GetSystemEncoding = \"IBM864\"\r\n        Case msoEncodingOEMNordic:                      GetSystemEncoding = \"IBM865\"\r\n        Case msoEncodingOEMCyrillicII:                  GetSystemEncoding = \"cp866\"\r\n        Case msoEncodingOEMModernGreek:                 GetSystemEncoding = \"ibm869\"\r\n        Case msoEncodingEBCDICMultilingualROECELatin2:  GetSystemEncoding = \"IBM870\"\r\n        Case msoEncodingThai:                           GetSystemEncoding = \"windows-874\"\r\n        Case msoEncodingEBCDICGreekModern:              GetSystemEncoding = \"cp875\"\r\n        Case msoEncodingJapaneseShiftJIS:               GetSystemEncoding = \"shift_jis\"\r\n        Case msoEncodingSimplifiedChineseGBK:           GetSystemEncoding = \"gb2312\"\r\n        Case msoEncodingKorean:                         GetSystemEncoding = \"ks_c_5601-1987\"\r\n        Case msoEncodingTraditionalChineseBig5:         GetSystemEncoding = \"big5\"\r\n        Case msoEncodingEBCDICTurkishLatin5:            GetSystemEncoding = \"IBM1026\"\r\n        Case msoEncodingUnicodeLittleEndian:            GetSystemEncoding = \"utf-16\"\r\n        Case msoEncodingUnicodeBigEndian:               GetSystemEncoding = \"unicodeFFFE\"\r\n        Case msoEncodingCentralEuropean:                GetSystemEncoding = \"windows-1250\"\r\n        Case msoEncodingCyrillic:                       GetSystemEncoding = \"windows-1251\"\r\n        Case msoEncodingWestern:                        GetSystemEncoding = \"Windows-1252\"\r\n        Case msoEncodingGreek:                          GetSystemEncoding = \"windows-1253\"\r\n        Case msoEncodingTurkish:                        GetSystemEncoding = \"windows-1254\"\r\n        Case msoEncodingHebrew:                         GetSystemEncoding = \"windows-1255\"\r\n        Case msoEncodingArabic:                         GetSystemEncoding = \"windows-1256\"\r\n        Case msoEncodingBaltic:                         GetSystemEncoding = \"windows-1257\"\r\n        Case msoEncodingVietnamese:                     GetSystemEncoding = \"windows-1258\"\r\n        Case msoEncodingKoreanJohab:                    GetSystemEncoding = \"Johab\"\r\n        Case msoEncodingMacRoman:                       GetSystemEncoding = \"macintosh\"\r\n        Case msoEncodingMacJapanese:                    GetSystemEncoding = \"x-mac-japanese\"\r\n        Case msoEncodingMacTraditionalChineseBig5:      GetSystemEncoding = \"x-mac-chinesetrad\"\r\n        Case msoEncodingMacKorean:                      GetSystemEncoding = \"x-mac-korean\"\r\n        Case msoEncodingMacArabic:                      GetSystemEncoding = \"x-mac-arabic\"\r\n        Case msoEncodingMacHebrew:                      GetSystemEncoding = \"x-mac-hebrew\"\r\n        Case msoEncodingMacGreek1:                      GetSystemEncoding = \"x-mac-greek\"\r\n        Case msoEncodingMacCyrillic:                    GetSystemEncoding = \"x-mac-cyrillic\"\r\n        Case msoEncodingMacSimplifiedChineseGB2312:     GetSystemEncoding = \"x-mac-chinesesimp\"\r\n        Case msoEncodingMacRomania:                     GetSystemEncoding = \"x-mac-romanian\"\r\n        Case msoEncodingMacUkraine:                     GetSystemEncoding = \"x-mac-ukrainian\"\r\n        Case msoEncodingMacLatin2:                      GetSystemEncoding = \"x-mac-ce\"\r\n        Case msoEncodingMacIcelandic:                   GetSystemEncoding = \"x-mac-icelandic\"\r\n        Case msoEncodingMacTurkish:                     GetSystemEncoding = \"x-mac-turkish\"\r\n        Case msoEncodingMacCroatia:                     GetSystemEncoding = \"x-mac-croatian\"\r\n        Case msoEncodingTaiwanCNS:                      GetSystemEncoding = \"x-Chinese-CNS\"\r\n        Case msoEncodingTaiwanTCA:                      GetSystemEncoding = \"x-cp20001\"\r\n        Case msoEncodingTaiwanEten:                     GetSystemEncoding = \"x-Chinese-Eten\"\r\n        Case msoEncodingTaiwanIBM5550:                  GetSystemEncoding = \"x-cp20003\"\r\n        Case msoEncodingTaiwanTeleText:                 GetSystemEncoding = \"x-cp20004\"\r\n        Case msoEncodingTaiwanWang:                     GetSystemEncoding = \"x-cp20005\"\r\n        Case msoEncodingIA5IRV:                         GetSystemEncoding = \"x-IA5\"\r\n        Case msoEncodingIA5German:                      GetSystemEncoding = \"x-IA5-German\"\r\n        Case msoEncodingIA5Swedish:                     GetSystemEncoding = \"x-IA5-Swedish\"\r\n        Case msoEncodingIA5Norwegian:                   GetSystemEncoding = \"x-IA5-Norwegian\"\r\n        Case msoEncodingUSASCII:                        GetSystemEncoding = \"us-ascii\"\r\n        Case msoEncodingT61:                            GetSystemEncoding = \"x-cp20261\"\r\n        Case msoEncodingISO6937NonSpacingAccent:        GetSystemEncoding = \"x-cp20269\"\r\n        Case msoEncodingEBCDICGermany:                  GetSystemEncoding = \"IBM273\"\r\n        Case msoEncodingEBCDICDenmarkNorway:            GetSystemEncoding = \"IBM277\"\r\n        Case msoEncodingEBCDICFinlandSweden:            GetSystemEncoding = \"IBM278\"\r\n        Case msoEncodingEBCDICItaly:                    GetSystemEncoding = \"IBM280\"\r\n        Case msoEncodingEBCDICLatinAmericaSpain:        GetSystemEncoding = \"IBM284\"\r\n        Case msoEncodingEBCDICUnitedKingdom:            GetSystemEncoding = \"IBM285\"\r\n        Case msoEncodingEBCDICJapaneseKatakanaExtended: GetSystemEncoding = \"IBM290\"\r\n        Case msoEncodingEBCDICFrance:                   GetSystemEncoding = \"IBM297\"\r\n        Case msoEncodingEBCDICArabic:                   GetSystemEncoding = \"IBM420\"\r\n        Case msoEncodingEBCDICGreek:                    GetSystemEncoding = \"IBM423\"\r\n        Case msoEncodingEBCDICHebrew:                   GetSystemEncoding = \"IBM424\"\r\n        Case msoEncodingEBCDICKoreanExtended:           GetSystemEncoding = \"x-EBCDIC-KoreanExtended\"\r\n        Case msoEncodingEBCDICThai:                     GetSystemEncoding = \"IBM-Thai\"\r\n        Case msoEncodingKOI8R:                          GetSystemEncoding = \"koi8-r\"\r\n        Case msoEncodingEBCDICIcelandic:                GetSystemEncoding = \"IBM871\"\r\n        Case msoEncodingEBCDICRussian:                  GetSystemEncoding = \"IBM880\"\r\n        Case msoEncodingEBCDICTurkish:                  GetSystemEncoding = \"IBM905\"\r\n        Case msoEncodingEBCDICSerbianBulgarian:         GetSystemEncoding = \"cp1025\"\r\n        Case msoEncodingKOI8U:                          GetSystemEncoding = \"koi8-u\"\r\n        Case msoEncodingISO88591Latin1:                 GetSystemEncoding = \"iso-8859-1\"\r\n        Case msoEncodingISO88592CentralEurope:          GetSystemEncoding = \"iso-8859-2\"\r\n        Case msoEncodingISO88593Latin3:                 GetSystemEncoding = \"iso-8859-3\"\r\n        Case msoEncodingISO88594Baltic:                 GetSystemEncoding = \"iso-8859-4\"\r\n        Case msoEncodingISO88595Cyrillic:               GetSystemEncoding = \"iso-8859-5\"\r\n        Case msoEncodingISO88596Arabic:                 GetSystemEncoding = \"iso-8859-6\"\r\n        Case msoEncodingISO88597Greek:                  GetSystemEncoding = \"iso-8859-7\"\r\n        Case msoEncodingISO88598Hebrew:                 GetSystemEncoding = \"iso-8859-8\"\r\n        Case msoEncodingISO88599Turkish:                GetSystemEncoding = \"iso-8859-9\"\r\n        Case msoEncodingISO885915Latin9:                GetSystemEncoding = \"iso-8859-15\"\r\n        Case msoEncodingEuropa3:                        GetSystemEncoding = \"x-Europa\"\r\n        Case msoEncodingISO88598HebrewLogical:          GetSystemEncoding = \"iso-8859-8-i\"\r\n        Case msoEncodingISO2022JPNoHalfwidthKatakana:   GetSystemEncoding = \"iso-2022-jp\"\r\n        Case msoEncodingISO2022JPJISX02021984:          GetSystemEncoding = \"csISO2022JP\"\r\n        Case msoEncodingISO2022JPJISX02011989:          GetSystemEncoding = \"iso-2022-jp\"\r\n        Case msoEncodingISO2022KR:                      GetSystemEncoding = \"iso-2022-kr\"\r\n        Case msoEncodingISO2022CNTraditionalChinese:    GetSystemEncoding = \"x-cp50227\"\r\n        Case msoEncodingEUCJapanese:                    GetSystemEncoding = \"euc-jp\"\r\n        Case msoEncodingEUCChineseSimplifiedChinese:    GetSystemEncoding = \"EUC-CN\"\r\n        Case msoEncodingEUCKorean:                      GetSystemEncoding = \"euc-kr\"\r\n        Case msoEncodingHZGBSimplifiedChinese:          GetSystemEncoding = \"hz-gb-2312\"\r\n        Case msoEncodingSimplifiedChineseGB18030:       GetSystemEncoding = \"GB18030\"\r\n        Case msoEncodingISCIIDevanagari:                GetSystemEncoding = \"x-iscii-de\"\r\n        Case msoEncodingISCIIBengali:                   GetSystemEncoding = \"x-iscii-be\"\r\n        Case msoEncodingISCIITamil:                     GetSystemEncoding = \"x-iscii-ta\"\r\n        Case msoEncodingISCIITelugu:                    GetSystemEncoding = \"x-iscii-te\"\r\n        Case msoEncodingISCIIAssamese:                  GetSystemEncoding = \"x-iscii-as\"\r\n        Case msoEncodingISCIIOriya:                     GetSystemEncoding = \"x-iscii-or\"\r\n        Case msoEncodingISCIIKannada:                   GetSystemEncoding = \"x-iscii-ka\"\r\n        Case msoEncodingISCIIMalayalam:                 GetSystemEncoding = \"x-iscii-ma\"\r\n        Case msoEncodingISCIIGujarati:                  GetSystemEncoding = \"x-iscii-gu\"\r\n        Case msoEncodingISCIIPunjabi:                   GetSystemEncoding = \"x-iscii-pa\"\r\n        Case msoEncodingUTF7:                           GetSystemEncoding = \"utf-7\"\r\n\r\n        ' In Windows 10, this is shown as a checkbox in Region settings for\r\n        ' \"Beta: Use Unicode UTF-8 for worldwide language support\"\r\n        Case msoEncodingUTF8:\r\n            If blnAllowUtf8 Then\r\n                GetSystemEncoding = \"utf-8\"\r\n            Else\r\n                ' If UTF-8 is not allowed (such as for code modules), then fall back\r\n                ' to most commonly used codepage, supporting most Western Euorpean\r\n                ' languages, but not Cyrillic. https://www.wikiwand.com/en/Windows-1252\r\n                GetSystemEncoding = \"Windows-1252\"\r\n            End If\r\n\r\n        ' Any other language encoding not defined above (should be very rare)\r\n        Case Else\r\n            ' Attempt to autodetect the language based on the content.\r\n            ' (Note that this does not work as well on code as it does\r\n            '  with normal written language. See issue #186)\r\n            GetSystemEncoding = \"_autodetect_all\"\r\n    End Select\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modErrorHandling.bas",
    "content": "﻿Attribute VB_Name = \"modErrorHandling\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modErrorHandling\r\n' Author    : Adam Waller\r\n' Date      : 5/13/2023\r\n' Purpose   : General error handling functions\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\nPrivate Const ModuleName As String = \"modErrorHandling\"\r\n\r\nPrivate Type udtThis\r\n    blnInError As Boolean   ' Monitor error state\r\nEnd Type\r\nPrivate this As udtThis\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DebugMode\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2023\r\n' Purpose   : Wrapper for use in error handling.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DebugMode(blnTrapUnhandledErrors As Boolean) As Boolean\r\n\r\n    ' Log any unhandled errors\r\n    If blnTrapUnhandledErrors Then LogUnhandledErrors\r\n\r\n    ' Don't reference the property this till we have loaded the options.\r\n    If OptionsLoaded Then DebugMode = Options.BreakOnError\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LogUnhandledErrors\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2023\r\n' Purpose   : Log any unhandled error condition, also breaking code execution if that\r\n'           : option is currently set. (Run this before any ON ERROR directive which\r\n'           : will siently reset any current VBA error condition.)\r\n'\r\n' Example   : See Sub `CatchTest` for example use.\r\n'\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LogUnhandledErrors(Optional ByRef CallingFunction As String = vbNullString)\r\n\r\n    ' Check for any unhandled errors\r\n    If (Err.Number <> 0) And Not this.blnInError Then\r\n\r\n        this.blnInError = True ' Set flag so we don't create a loop while logging the error\r\n\r\n        ' With the above flag, options will load in background and we don't depend on\r\n        ' flags outside of this routine.\r\n        ' Check current BreakOnError mode\r\n        If Options.BreakOnError Then\r\n            ' Stop the code here so we can investigate the source of the error.\r\n            Debug.Print \"Error \" & Err.Number & \": \" & Err.Description\r\n            Stop\r\n            '===========================================================================\r\n            '   NOTE: IF THE CODE STOPS HERE, PLEASE READ BEFORE CONTINUING\r\n            '===========================================================================\r\n            '   An unhandled error was (probably) found just before an `On Error ...`\r\n            '   statement. Since any existing errors are cleared when the On Error\r\n            '   statement is executed, this is your chance to identify the source of the\r\n            '   unhandled error.\r\n            '\r\n            '   Note that the error will typically be from the THIRD item in the call\r\n            '   stack, if the On Error statement is at the beginning of the calling\r\n            '   procedure. Use CTL+L to view the call stack. For example:\r\n            '\r\n            '   (1) MSAccessVCS.modErrorHandling.DebugMode  <--- This function\r\n            '   (2) MSAccessVCS.clsLog.Flush                <--- Calling function\r\n            '   (3) MSAccessVCS.clsLog.Add                  <--- Likely origin of error\r\n            '\r\n            '   You can use standard VBA debugging techniques to inspect variables and\r\n            '   step through code to pinpoint the source and cause of the error.\r\n            '   For additional information, please see the add-in wiki on GitHub at:\r\n            '   https://github.com/joyfullservice/msaccess-vcs-addin/wiki\r\n            '===========================================================================\r\n        Else\r\n            ' Log otherwise unhandled error\r\n            ' We don't know the procedure that it originated from, but we should at least\r\n            ' log that the error occurred. A review of the log file may help identify the source.\r\n            Log.Error eelError, \"Unhandled error, likely before `On Error` directive\", CallingFunction & \".Unknown.LogUnhandledErrors\"\r\n        End If\r\n        this.blnInError = False\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Catch\r\n' Author    : Adam Waller\r\n' Date      : 11/23/2020\r\n' Purpose   : Returns true if the last error matches any of the passed error numbers,\r\n'           : and clears the error object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Catch(ParamArray lngErrorNumbers()) As Boolean\r\n    Dim intCnt As Integer\r\n    For intCnt = LBound(lngErrorNumbers) To UBound(lngErrorNumbers)\r\n        If lngErrorNumbers(intCnt) = Err.Number Then\r\n            Err.Clear\r\n            Catch = True\r\n            Exit For\r\n        End If\r\n    Next intCnt\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CatchAny\r\n' Author    : Adam Waller\r\n' Date      : 12/3/2020\r\n' Purpose   : Generic error handler with logging.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function CatchAny(eLevel As eErrorLevel, strDescription As String, Optional strSource As String, _\r\n    Optional blnLogError As Boolean = True, Optional blnClearError As Boolean = True, _\r\n    Optional blnIncludeErrorWithDescription As Boolean = False) As Boolean\r\n    If Err Then\r\n        If blnLogError Then\r\n            this.blnInError = True\r\n            Log.Error eLevel, strDescription, strSource, blnIncludeErrorWithDescription\r\n            this.blnInError = False\r\n        End If\r\n        If blnClearError Then Err.Clear\r\n        CatchAny = True\r\n    End If\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modExportOnSaveHook.bas",
    "content": "﻿Attribute VB_Name = \"modExportOnSaveHook\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modExportOnSaveHook\r\n' Author    : bclothier\r\n' Date      : 3/28/2023\r\n' Purpose   : Handles hooking into and receiving callbacks from the hook DLL.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n' Must match the AccessObjectState definition used in hook's ObjectTracker module\r\nPrivate Enum AccessObjectState\r\n    ObjectIsNotOpenOrDoesntExist = 0\r\n    ObjectIsOpen = Access.acObjStateOpen\r\n    ObjectIsNew = Access.acObjStateNew\r\n    ObjectIsDirty = Access.acObjStateDirty\r\n    ObjectIsNewOrDirty = (Not ObjectIsOpen) ' Use in an AND operation and check if result is nonzero\r\nEnd Enum\r\n\r\n' Must match the HookConfiguration definition used in hook's DllGlobal module\r\nPrivate Type HookConfiguration\r\n    Size As Long\r\n    App As Access.Application\r\n    CallbackProject As VBIDE.VBProject\r\n    AfterSaveRequestDelayMilliseconds As Long\r\n    AfterSaveCallbackProcedureName As LongPtr\r\n    LogFilePath As LongPtr\r\nEnd Type\r\n\r\n' Must match the ObjectData definition used in hook's ObjectTracker module\r\nPrivate Type ObjectData\r\n    Index As Integer\r\n    Cancelled As Boolean\r\n    ObjectType As Access.AcObjectType\r\n    NewObjectType As Access.AcObjectType\r\n    InitialObjectState As AccessObjectState\r\n    DesignerhWnd As LongPtr\r\n    OriginalName As String * 64\r\n    NewName As String * 64\r\nEnd Type\r\n\r\n' HMODULE LoadLibraryExW(\r\n'   [in] LPCWSTR lpLibFileName,\r\n'        HANDLE  hFile,\r\n'   [in] DWORD   dwFlags\r\n' );\r\nPrivate Declare PtrSafe Function LoadLibraryExW _\r\nLib \"Kernel32.dll\" ( _\r\n    ByVal lpLibFileName As LongPtr, _\r\n    ByVal hFile As LongPtr, _\r\n    ByVal dwFlags As Long _\r\n) As LongPtr\r\n\r\n' BOOL FreeLibrary(\r\n'   [in] HMODULE hLibModule\r\n' );\r\nPrivate Declare PtrSafe Function FreeLibrary _\r\nLib \"Kernel32.dll\" ( _\r\n    ByVal hLibModule As LongPtr _\r\n) As Long\r\n\r\n#If Win64 Then\r\nPrivate Declare PtrSafe Function StartHook _\r\nLib \"MSAccessVCSHook_win64.dll\" ( _\r\n    ByRef Config As HookConfiguration _\r\n) As Boolean\r\n\r\nPrivate Declare PtrSafe Function StopHook _\r\nLib \"MSAccessVCSHook_win64.dll\" () As Boolean\r\n\r\n#Else\r\n\r\nPrivate Declare PtrSafe Function StartHook _\r\nLib \"MSAccessVCSHook_win32.dll\" ( _\r\n    ByRef Config As HookConfiguration _\r\n) As Boolean\r\n\r\nPrivate Declare PtrSafe Function StopHook _\r\nLib \"MSAccessVCSHook_win32.dll\" () As Boolean\r\n\r\n#End If\r\n\r\nPrivate ptrLibraryHandle As LongPtr\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetHookFileName\r\n' Author    : bclothier\r\n' Date      : 3/28/2023\r\n' Purpose   : This is path where the hook DLL would be installed.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetHookFileName() As String\r\n    GetHookFileName = Replace(\"MSAccessVCSHook_winXX.dll\", \"XX\", GetOfficeBitness)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyHook\r\n' Author    : bclothier\r\n' Date      : 3/28/2023\r\n' Purpose   : Verify that the hook is installed and the latest version.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub VerifyHook()\r\n\r\n    Dim strPath As String\r\n    Dim strFile As String\r\n    Dim strKey As String\r\n    Dim strHash As String\r\n    Dim blnInstall As Boolean\r\n\r\n    ' Hook\r\n    strPath = GetInstallSettings.strInstallFolder & PathSep\r\n    strFile = FSO.BuildPath(strPath, GetHookFileName)\r\n    strKey = \"Hook x\" & GetOfficeBitness()\r\n\r\n    ' Verify add-in file\r\n    If Not FSO.FileExists(strFile) Then\r\n        blnInstall = True\r\n    Else\r\n        ' Compare to embedded resource file\r\n        strHash = modResource.GetResourceHash(strKey)\r\n        If strHash <> vbNullString Then\r\n            ' Reinstall if the file is different\r\n            If strHash <> GetFileHash(strFile) Then blnInstall = True\r\n        End If\r\n    End If\r\n\r\n    ' Install/reinstall if needed\r\n    If blnInstall Then\r\n        ' Extract the new file from the resources table\r\n        modResource.ExtractResource strKey, strPath\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ActivateHook\r\n' Author    : bclothier\r\n' Date      : 3/28/2023\r\n' Purpose   : Enables the hook to support automatic export after save\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ActivateHook(Optional ExportRequestDelayMilliseconds As Long = 500) As Boolean\r\n    Dim Config As HookConfiguration\r\n\r\n    If ptrLibraryHandle = 0 Then\r\n        VerifyHook\r\n        ptrLibraryHandle = LoadLibraryExW(StrPtr(FSO.BuildPath(GetInstallSettings.strInstallFolder, GetHookFileName)), &H0, &H0)\r\n        If ptrLibraryHandle Then\r\n            Config.Size = LenB(Config)\r\n            Set Config.App = Application\r\n            Set Config.CallbackProject = GetAddInProject\r\n            If Config.CallbackProject Is Nothing Then\r\n                With Application.WizHook\r\n                    .Key = 51488399\r\n                    Set Config.CallbackProject = .DbcVbProject\r\n                End With\r\n            End If\r\n            Config.AfterSaveCallbackProcedureName = StrPtr(\"HandleExportCallback\")\r\n            Config.AfterSaveRequestDelayMilliseconds = ExportRequestDelayMilliseconds\r\n            Config.LogFilePath = StrPtr(CodeProject.Path & \"\\MSAccessVCSHook.log\")\r\n            If StartHook(Config) = False Then\r\n                FreeLibrary (ptrLibraryHandle)\r\n                ptrLibraryHandle = 0\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ActivateHook = (ptrLibraryHandle <> 0)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DeactivateHook\r\n' Author    : bclothier\r\n' Date      : 3/28/2023\r\n' Purpose   : Deactivates the hook and unloads the library\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DeactivateHook() As Boolean\r\n    If ptrLibraryHandle Then\r\n        If StopHook() Then\r\n            If FreeLibrary(ptrLibraryHandle) Then\r\n                ptrLibraryHandle = 0\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    DeactivateHook = (ptrLibraryHandle = 0)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HandleExportCallback\r\n' Author    : bclothier\r\n' Date      : 3/28/2023\r\n' Purpose   : Callback function from the hook DLL to provide information about modified\r\n'           : objects that were saved.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub HandleExportCallback(UpperBound As Long, ObjectDataArray() As ObjectData)\r\n    On Error GoTo ErrHandler\r\n\r\n    Dim dAccessObjects As Dictionary\r\n    Dim oAccessObject As Access.AccessObject\r\n\r\n    Dim strName As String\r\n\r\n    Dim lngIndex As Long\r\n\r\n    Set dAccessObjects = New Dictionary\r\n\r\n    For lngIndex = 0 To UpperBound\r\n        With ObjectDataArray(lngIndex)\r\n            If .Cancelled = False Then\r\n                If Len(Trim$(Replace$(.NewName, vbNullChar, vbNullString))) Then\r\n                    strName = Trim$(.NewName)\r\n                Else\r\n                    strName = Trim$(.OriginalName)\r\n                End If\r\n                Select Case .NewObjectType\r\n                    Case acTable\r\n                        Set oAccessObject = CurrentData.AllTables(strName)\r\n                    Case acTableDataMacro\r\n                        Set oAccessObject = CurrentData.AllTables(strName)\r\n                    Case acQuery\r\n                        Set oAccessObject = CurrentData.AllQueries(strName)\r\n                    Case acForm\r\n                        Set oAccessObject = CurrentProject.AllForms(strName)\r\n                    Case acReport\r\n                        Set oAccessObject = CurrentProject.AllReports(strName)\r\n                    Case acMacro\r\n                        Set oAccessObject = CurrentProject.AllMacros(strName)\r\n                    Case acModule\r\n                        Set oAccessObject = CurrentProject.AllModules(strName)\r\n                End Select\r\n\r\n                dAccessObjects.Add .NewObjectType & \"|\" & strName, oAccessObject\r\n            End If\r\n        End With\r\n    Next\r\n\r\n    modImportExport.ExportMultipleObjects dAccessObjects, False\r\n\r\nExitProc:\r\n    Exit Sub\r\n\r\nErrHandler:\r\n    If DebugMode(True) Then\r\n        Stop ' Use the unreachable Resume to return to the original line that caused error.\r\n    End If\r\n    Resume ExitProc\r\n    Resume ' Use for debugging only\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modFileAccess.bas",
    "content": "﻿Attribute VB_Name = \"modFileAccess\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modFileAccess\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : General functions for reading and writing files, building and verifying\r\n'           : paths, and parsing file names.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\nPrivate Const ModuleName As String = \"modFileAccess\"\r\n\r\nPrivate Declare PtrSafe Function getTempPath Lib \"kernel32\" Alias \"GetTempPathA\" ( _\r\n    ByVal nBufferLength As Long, _\r\n    ByVal lpBuffer As String) As Long\r\n\r\nPrivate Declare PtrSafe Function getTempFileName Lib \"kernel32\" Alias \"GetTempFileNameA\" ( _\r\n    ByVal lpszPath As String, _\r\n    ByVal lpPrefixString As String, _\r\n    ByVal wUnique As Long, _\r\n    ByVal lpTempFileName As String) As Long\r\n\r\nPrivate Declare PtrSafe Function SHCreateDirectoryEx Lib \"shell32\" Alias \"SHCreateDirectoryExW\" ( _\r\n    ByVal hwnd As LongPtr, _\r\n    ByVal pszPath As LongPtr, _\r\n    ByVal psa As Any) As Long\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTempFile\r\n' Author    : Adapted by Adam Waller\r\n' Date      : 1/23/2019\r\n' Purpose   : Generate Random / Unique temporary file name. (Also creates the file)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetTempFile(Optional strPrefix As String = \"VBA\") As String\r\n\r\n    Dim strPath As String * 512\r\n    Dim strName As String * 576\r\n    Dim lngReturn As Long\r\n\r\n    lngReturn = getTempPath(512, strPath)\r\n    lngReturn = getTempFileName(strPath, strPrefix, 0, strName)\r\n    If lngReturn <> 0 Then GetTempFile = Left$(strName, InStr(strName, vbNullChar) - 1)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTempFolder\r\n' Author    : Adam Waller\r\n' Date      : 9/24/2021\r\n' Purpose   : Get a random unique folder name and create the folder.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetTempFolder(Optional strPrefix As String = \"VBA\") As String\r\n\r\n    Dim strPath As String\r\n    Dim strFile As String\r\n    Dim strFolder As String\r\n\r\n    ' Generate a random temporary file name, and delete the temp file\r\n    strPath = GetTempFile(strPrefix)\r\n    DeleteFile strPath\r\n\r\n    ' Change path to use underscore instead of period.\r\n    strFile = PathSep & FSO.GetFileName(strPath)\r\n    strFolder = Replace(strFile, \".\", \"_\")\r\n    strPath = Replace(strPath, strFile, strFolder)\r\n\r\n    If FSO.FolderExists(strPath) Then\r\n        ' Oops, this folder already exists. Try again.\r\n        GetTempFolder = GetTempFolder(strPrefix)\r\n    Else\r\n        ' Create folder and return path\r\n        FSO.CreateFolder strPath\r\n        GetTempFolder = strPath\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReadFile\r\n' Author    : Adam Waller / Indigo\r\n' Date      : 11/4/2020\r\n' Purpose   : Read text file.\r\n'           : Read in UTF-8 encoding, removing a BOM if found at start of file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ReadFile(strPath As String, Optional strCharset As String = \"utf-8\") As String\r\n\r\n    Dim cData As clsConcat\r\n\r\n    Set cData = New clsConcat\r\n\r\n    If FSO.FileExists(strPath) Then\r\n        Perf.OperationStart \"Read File\"\r\n        With New ADODB.Stream\r\n            .Charset = strCharset\r\n            .Open\r\n            .LoadFromFile strPath\r\n            ' Read chunks of text, rather than the whole thing at once for massive\r\n            ' performance gains when reading large files.\r\n            ' See https://docs.microsoft.com/is-is/sql/ado/reference/ado-api/readtext-method\r\n            Do While Not .EOS\r\n                cData.Add .ReadText(CHUNK_SIZE) ' 128K\r\n            Loop\r\n            .Close\r\n        End With\r\n        Perf.OperationEnd\r\n    End If\r\n\r\n    ' Return text contents of file.\r\n    ReadFile = cData.GetStr\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : WriteFile\r\n' Author    : Adam Waller\r\n' Date      : 1/23/2019\r\n' Purpose   : Save string variable to text file. (Building the folder path if needed)\r\n'           : Saves in UTF-8 encoding, adding a BOM if extended or unicode content\r\n'           : is found in the file. https://stackoverflow.com/a/53036838/4121863\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub WriteFile(strText As String, strPath As String, Optional strEncoding As String = \"utf-8\")\r\n\r\n    Perf.OperationStart \"Write File\"\r\n\r\n    ' Write to a UTF-8 eoncoded file\r\n    With New ADODB.Stream\r\n        .Type = adTypeText\r\n        .Open\r\n        .Charset = strEncoding\r\n        .WriteText strText\r\n        ' Ensure that we are ending the content with a vbcrlf\r\n        If Right(strText, 2) <> vbCrLf Then .WriteText vbCrLf\r\n        ' Write to disk\r\n        VerifyPath strPath\r\n        ' Watch out for possible write error\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        .SaveToFile strPath, adSaveCreateOverWrite\r\n        If Catch(3004) Then\r\n            ' File is locked. Try again after 1 second, just in case something\r\n            ' like Google Drive momentarily locked the file.\r\n            Err.Clear\r\n            Pause 1\r\n            .SaveToFile strPath, adSaveCreateOverWrite\r\n        End If\r\n        CatchAny eelError, \"Error writing file: \" & strPath, ModuleName & \".WriteFile\"\r\n        .Close\r\n    End With\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : WriteFileNoBom\r\n' Author    : Adam Waller\r\n' Date      : 11/7/2024\r\n' Purpose   : Save a string to a text file without a BOM. (Needed for exporting *.po\r\n'           : files for translation since editors are not expecting a UTF-8 BOM.)\r\n'           : See: https://stackoverflow.com/a/31436631/4121863\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub WriteFileNoBom(strText As String, strPath As String, Optional strEncoding As String = \"utf-8\")\r\n\r\n    Dim stmNoBom As ADODB.Stream\r\n\r\n    ' Write to a UTF-8 eoncoded file\r\n    With New ADODB.Stream\r\n        .Type = adTypeText\r\n        .Open\r\n        .Charset = strEncoding\r\n        .WriteText strText\r\n        ' Ensure that we are ending the content with a vbcrlf\r\n        If Right(strText, 2) <> vbCrLf Then .WriteText vbCrLf\r\n\r\n        ' Now, create a new BINARY stream and copy over the content.\r\n        Set stmNoBom = New ADODB.Stream\r\n        stmNoBom.Type = adTypeBinary\r\n        stmNoBom.Open\r\n        .Position = 3\r\n        .CopyTo stmNoBom\r\n\r\n        ' Write to disk\r\n        VerifyPath strPath\r\n        ' Watch out for possible write error\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        stmNoBom.SaveToFile strPath, adSaveCreateOverWrite\r\n        If Catch(3004) Then\r\n            ' File is locked. Try again after 1 second, just in case something\r\n            ' like Google Drive momentarily locked the file.\r\n            Err.Clear\r\n            Pause 1\r\n            stmNoBom.SaveToFile strPath, adSaveCreateOverWrite\r\n        End If\r\n        CatchAny eelError, \"Error writing file: \" & strPath, ModuleName & \".WriteFile\"\r\n        .Close\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileBytes\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2020\r\n' Purpose   : Returns a byte array of the file contents.\r\n'           : This function supports Unicode paths, unlike VBA's Open statement.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetFileBytes(strPath As String, Optional lngBytes As Long = adReadAll) As Byte()\r\n    Perf.OperationStart \"Read File Bytes\"\r\n    With New ADODB.Stream\r\n        .Type = adTypeBinary\r\n        .Open\r\n        .LoadFromFile strPath\r\n        GetFileBytes = .Read(lngBytes)\r\n        .Close\r\n    End With\r\n    Perf.OperationEnd\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : WriteBinaryFile\r\n' Author    : Adam Waller\r\n' Date      : 7/9/2021\r\n' Purpose   : Writes the file bytes to a file (with Unicode path support)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function WriteBinaryFile(strPath As String, bteArray() As Byte)\r\n    Perf.OperationStart \"Write Binary File\"\r\n    With New ADODB.Stream\r\n        .Type = adTypeBinary\r\n        .Open\r\n        .Write bteArray\r\n        VerifyPath strPath\r\n        .SaveToFile strPath, adSaveCreateOverWrite\r\n        .Close\r\n    End With\r\n    Perf.OperationEnd\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DeleteFile\r\n' Author    : Adam Waller\r\n' Date      : 11/5/2020\r\n' Purpose   : Wrapper to delete file while monitoring performance.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub DeleteFile(strFile As String, Optional blnForce As Boolean = True)\r\n    Perf.OperationStart \"Delete File\"\r\n    FSO.DeleteFile strFile, blnForce\r\n    Perf.OperationEnd\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MoveFileIfExists\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Moves a file to a specified destination folder, creating the destination\r\n'           : folder if it does not exist.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MoveFileIfExists(strFilePath As String, strToFolder As String)\r\n    Dim strNewPath As String\r\n    If FSO.FileExists(strFilePath) Then\r\n        Perf.OperationStart \"Move File\"\r\n        VerifyPath strToFolder\r\n        strNewPath = StripSlash(strToFolder) & PathSep & FSO.GetFileName(strFilePath)\r\n        If FSO.FileExists(strNewPath) Then DeleteFile strNewPath\r\n        FSO.MoveFile strFilePath, strNewPath\r\n        Perf.OperationEnd\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MoveFolderIfExists\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Move a folder to a new location, replacing any existing folder.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MoveFolderIfExists(strFolderPath As String, strToParentFolder As String)\r\n    Dim strNewPath As String\r\n    If FSO.FolderExists(strFolderPath) Then\r\n        Perf.OperationStart \"Move Folder\"\r\n        VerifyPath strToParentFolder\r\n        strNewPath = StripSlash(strToParentFolder) & PathSep & FSO.GetFolder(strFolderPath).Name\r\n        If FSO.FolderExists(strNewPath) Then FSO.DeleteFolder strNewPath, True\r\n        FSO.MoveFolder strFolderPath, strNewPath\r\n        Perf.OperationEnd\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : clearfilesbyextension\r\n' Author    : Adam Waller\r\n' Date      : 1/25/2019\r\n' Purpose   : Erase all *.`ext` files in `Path`.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ClearFilesByExtension(ByVal strFolder As String, strExt As String)\r\n\r\n    Dim oFile As Scripting.File\r\n    Dim strFolderNoSlash As String\r\n\r\n    ' While the Dir() function would be simpler, it does not support Unicode.\r\n    Perf.OperationStart \"Clear Files by Ext\"\r\n    strFolderNoSlash = StripSlash(strFolder)\r\n    If FSO.FolderExists(strFolderNoSlash) Then\r\n        For Each oFile In FSO.GetFolder(strFolderNoSlash).Files\r\n            If StrComp(FSO.GetExtensionName(oFile.Name), strExt, vbTextCompare) = 0 Then\r\n                ' Found at least one matching file. Use the wildcard delete.\r\n                DeleteFile FSO.BuildPath(strFolderNoSlash, \"*.\" & strExt)\r\n                Exit Sub\r\n            End If\r\n        Next\r\n    End If\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n' ----------------------------------------------------------------\r\n' Procedure : VerifyPath (Renamed from EnsurePathExists)\r\n' Date      : 8/15/2022, 10/24/2024, 3/25/2025\r\n' Author    : Mike Wolfe, hecon5, Adam Waller\r\n' Source    : https://nolongerset.com/ensurepathexists/\r\n' Purpose   : Unicode-safe method to ensure a folder exists and\r\n'           : create the folder (and all subfolders) if it does not.\r\n'           : Added in additional error handling and logging.\r\n' ----------------------------------------------------------------\r\nPublic Function VerifyPath(PathToCheck As String, Optional EnableLongPath As Boolean = True) As Boolean\r\n\r\n    Const FunctionName As String = ModuleName & \".VerifyPath\"\r\n\r\n    Const ERROR_SUCCESS As Long = &H0\r\n    Const ERROR_ACCESS_DENIED As Long = &H5         'Could not create directory; access denied.\r\n    Const ERROR_BAD_PATHNAME As Long = &HA1         'The pszPath parameter was set to a relative path.\r\n    Const ERROR_FILENAME_EXCED_RANGE As Long = &HCE 'The path pointed to by pszPath is too long.\r\n    Const ERROR_FILE_EXISTS As Long = &H50          'The directory exists.\r\n    Const ERROR_ALREADY_EXISTS As Long = &HB7       'The directory exists.\r\n    Const ERROR_CANCELLED As Long = &H4C7           'The user canceled the operation.\r\n    Const ERROR_INVALID_NAME As Long = &H7B         'Unicode path passed when SHCreateDirectoryEx passes PathToCheck as string.\r\n\r\n    Const LONG_PATH_PREFIX As String = \"\\\\?\\\"\r\n    Const LONG_PATH_UNC_PREFIX As String = \"\\\\?\\UNC\\\"\r\n\r\n    Dim lngReturnCode As Long\r\n    Dim strFolder As String\r\n\r\n    LogUnhandledErrors FunctionName\r\n    On Error Resume Next\r\n\r\n    Perf.OperationStart FunctionName\r\n\r\n    If PathToCheck = vbNullString Then GoTo Exit_Here\r\n\r\n    If Right$(PathToCheck, 1) = PathSep Then\r\n        ' Folder name. (Folder names can contain periods)\r\n        strFolder = Left$(PathToCheck, Len(PathToCheck) - 1)\r\n    Else\r\n        ' File name\r\n        strFolder = FSO.GetParentFolderName(PathToCheck)\r\n    End If\r\n\r\n    ' Expand any environment variables\r\n    If InStr(2, strFolder, \"%\") > 0 Then strFolder = ExpandEnvironmentVariables(strFolder)\r\n\r\n    ' Because enabling long paths disables automatic folder expansion (i.e. \"\\..\\\") we don't want to use this\r\n    ' unless we are actually dealing with a path that exceeds the normal MAX_PATH limit. See issue #612\r\n    If EnableLongPath And Len(strFolder) > 260 And Not StartsWith(strFolder, \".\") Then  ' Can't use relative paths for LongPaths.\r\n        If StartsWith(strFolder, \"\\\\\") Then\r\n            ' Use UNC style long path prefix\r\n            lngReturnCode = SHCreateDirectoryEx(ByVal 0&, StrPtr(LONG_PATH_UNC_PREFIX & Mid(strFolder, 3)), ByVal 0&)\r\n        Else\r\n            ' Standard drive letter prefix\r\n            lngReturnCode = SHCreateDirectoryEx(ByVal 0&, StrPtr(LONG_PATH_PREFIX & strFolder), ByVal 0&)\r\n        End If\r\n    Else\r\n        ' Call API without long path prefix\r\n        lngReturnCode = SHCreateDirectoryEx(ByVal 0&, StrPtr(strFolder), ByVal 0&)\r\n    End If\r\n\r\n    ' Check return code from API call\r\n    Select Case lngReturnCode\r\n        Case ERROR_SUCCESS, _\r\n             ERROR_FILE_EXISTS, _\r\n             ERROR_ALREADY_EXISTS\r\n            VerifyPath = True\r\n        Case ERROR_ACCESS_DENIED: Log.Error eelError, \"Could not create path: Access denied. Path: \" & PathToCheck\r\n        Case ERROR_BAD_PATHNAME: Log.Error eelError, \"Cannot use relative path. Path: \" & PathToCheck, FunctionName\r\n        Case ERROR_FILENAME_EXCED_RANGE: Log.Error eelError, \"Path too long. Path: \" & PathToCheck, FunctionName\r\n        Case ERROR_CANCELLED: Log.Error eelError, \"User cancelled CreateDirectory operation. Path: \" & PathToCheck, FunctionName\r\n        Case ERROR_INVALID_NAME: Log.Error eelError, \"Invalid path name. Path: \" & PathToCheck, FunctionName\r\n        Case Else: Log.Error eelError, \"Unexpected error verifying path. Return Code: \" & CStr(lngReturnCode) & vbNewLine & vbNewLine & \"Path: \" & PathToCheck, FunctionName\r\n    End Select\r\n\r\nExit_Here:\r\n    CatchAny eelError, \"Unexpected Error verifying path: \" & vbNewLine & vbNewLine & PathToCheck, FunctionName\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ProgramFilesFolder\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2015\r\n' Purpose   : Returns the program files folder on the OS. (32 or 64 bit)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ProgramFilesFolder() As String\r\n    Dim strFolder As String\r\n    strFolder = Environ$(\"PROGRAMFILES\")\r\n    ' Should always work, but just in case!\r\n    If strFolder = vbNullString Then strFolder = \"C:\\Program Files (x86)\"\r\n    ProgramFilesFolder = strFolder & PathSep\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFilePathsInFolder\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Returns a collection containing the full paths of files in a folder.\r\n'           : Wildcards are supported.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetFilePathsInFolder(strFolder As String, Optional strFilePattern As String = \"*.*\") As Dictionary\r\n\r\n    Dim oFile As Scripting.File\r\n    Dim strBaseFolder As String\r\n\r\n    strBaseFolder = StripSlash(strFolder)\r\n    Set GetFilePathsInFolder = New Dictionary\r\n\r\n    Perf.OperationStart \"Get File List\"\r\n    If FSO.FolderExists(strBaseFolder) Then\r\n        For Each oFile In FSO.GetFolder(strBaseFolder).Files\r\n            ' Add files that match the pattern.\r\n            If oFile.Name Like strFilePattern Then GetFilePathsInFolder.Add oFile.Path, vbNullString\r\n        Next oFile\r\n    End If\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSubfolderPaths\r\n' Author    : Adam Waller\r\n' Date      : 7/30/2020\r\n' Purpose   : Return a collection of subfolders inside a folder.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSubfolderPaths(strPath As String) As Dictionary\r\n\r\n    Dim strBase As String\r\n    Dim oFolder As Scripting.Folder\r\n\r\n    Set GetSubfolderPaths = New Dictionary\r\n\r\n    strBase = StripSlash(strPath)\r\n    If FSO.FolderExists(strBase) Then\r\n        For Each oFolder In FSO.GetFolder(strBase).SubFolders\r\n            GetSubfolderPaths.Add oFolder.Path, vbNullString\r\n        Next oFolder\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RegRead\r\n' Author    : Adam Waller\r\n' Date      : 2/10/2025\r\n' Purpose   : Simple wrapper to read a registry value.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function RegRead(strPath As String) As String\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    With New IWshRuntimeLibrary.WshShell\r\n        RegRead = .RegRead(strPath)\r\n    End With\r\n    If Err Then Err.Clear\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReadJsonFile\r\n' Author    : Adam Waller\r\n' Date      : 5/5/2020\r\n' Purpose   : Reads a Json file into a dictionary object\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ReadJsonFile(strPath As String) As Dictionary\r\n\r\n    Dim strText As String\r\n    strText = ReadFile(strPath)\r\n\r\n    ' If it looks like json content, then parse into a dictionary object.\r\n    If Left$(strText, 1) = \"{\" Then\r\n        Set ReadJsonFile = ParseJson(strText)\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildPath2\r\n' Author    : Adam Waller\r\n' Date      : 3/3/2021\r\n' Purpose   : Like FSO.BuildPath, but with unlimited arguments)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function BuildPath2(ParamArray Segments())\r\n    Dim lngPart As Long\r\n    Dim strSegment As String\r\n\r\n    With New clsConcat\r\n        For lngPart = LBound(Segments) To UBound(Segments)\r\n            strSegment = Segments(lngPart)\r\n            Do\r\n                ' Remove leading & trailing slashes that may be included in the segment\r\n                If Left$(strSegment, 1) = PathSep Then\r\n                    If Right$(strSegment, 1) = PathSep Then\r\n                        strSegment = Mid$(strSegment, 2, Len(strSegment) - 2)\r\n                    Else\r\n                        strSegment = Mid$(strSegment, 2)\r\n                    End If\r\n                ElseIf Right$(strSegment, 1) = PathSep Then\r\n                    strSegment = Left$(strSegment, Len(strSegment) - 1)\r\n                Else\r\n                    Exit Do\r\n                End If\r\n            Loop\r\n            .Add CStr(strSegment)\r\n            If lngPart < UBound(Segments) Then\r\n                .Add PathSep\r\n            End If\r\n        Next lngPart\r\n    BuildPath2 = .GetStr\r\n    End With\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetRelativePath\r\n' Author    : Adam Waller\r\n' Date      : 5/11/2020\r\n' Purpose   : Returns a path relative to a specified folder. If folder path is omitted,\r\n'           : the current database's path is assumed.\r\n'           : If a relative path is not possible, it returns the original full path.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetRelativePath(strPath As String, Optional strFolder As String = vbNullString) As String\r\n\r\n    Dim strUncPath As String\r\n    Dim strUncTest As String\r\n    Dim strRelative As String\r\n\r\n    ' Check for matching parent folder as relative to the project path.\r\n    If Len(strFolder) = 0 Then\r\n        strFolder = GetUncPath(CurrentProject.Path) & PathSep\r\n    End If\r\n\r\n    ' Default to original path if no relative path could be resolved.\r\n    strRelative = strPath\r\n\r\n    ' Compare strPath to the current project path\r\n    If InStr(1, strPath, strFolder, vbTextCompare) = 1 Then\r\n        ' In export folder or subfolder. Simple replacement\r\n        strRelative = \"rel:\" & Mid$(strPath, Len(strFolder) + 1)\r\n    Else\r\n        ' Make sure we have a path, not just a file name.\r\n        If InStr(1, strRelative, PathSep) > 0 Then\r\n            ' Check UNC path for network drives\r\n            strUncPath = GetUncPath(strPath)\r\n            If StrComp(strUncPath, strPath, vbTextCompare) <> 0 Then\r\n                ' We are dealing with a network drive\r\n                strUncTest = GetRelativePath(strUncPath)\r\n                If StrComp(strUncPath, strUncTest, vbTextCompare) <> 0 Then\r\n                    ' Resolved to relative UNC path\r\n                    strRelative = strUncTest\r\n                End If\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' Return relative (or original) path\r\n    GetRelativePath = strRelative\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetPathFromRelative\r\n' Author    : Adam Waller\r\n' Date      : 5/11/2020\r\n' Purpose   : Expands a relative path out to the full path.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetPathFromRelative(strPath As String, Optional strFolder As String = vbNullString) As String\r\n    If Len(strFolder) = 0 Then\r\n        strFolder = CurrentProject.Path\r\n    End If\r\n    If IsRelativePath(strPath) Then\r\n        GetPathFromRelative = FSO.BuildPath(strFolder, Mid$(strPath, 5))\r\n    Else\r\n        ' No relative path used.\r\n        GetPathFromRelative = strPath\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsRelativePath\r\n' Author    : Adam Waller\r\n' Date      : 10/29/2021\r\n' Purpose   : Returns true if the specified path is stored as relative.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IsRelativePath(strPath As String) As Boolean\r\n    IsRelativePath = (Left$(strPath, 4) = \"rel:\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetUncPath\r\n' Author    : Adam Waller, hecon5\r\n' Date      : 7/14/2020, 2022 Sept 27\r\n' Purpose   : Returns the UNC path for a network location (if applicable)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetUncPath(ByRef PathIn As String)\r\n\r\n    Const FunctionName As String = ModuleName & \".GetUncPath\"\r\n    Dim DriveLetter As String\r\n    Dim UNCPath As String\r\n\r\n    LogUnhandledErrors FunctionName\r\n    On Error Resume Next\r\n\r\n    Perf.OperationStart FunctionName\r\n    UNCPath = PathIn\r\n\r\nRetry:\r\n    DriveLetter = FSO.GetDriveName(PathIn)\r\n    If Catch(68) Then GoTo HandleDriveLoss\r\n    CatchAny eelError, \"Issue getting drive paths.\", FunctionName\r\n    With FSO.GetDrive(DriveLetter)\r\n        If Catch(68) Then GoTo HandleDriveLoss\r\n        If .DriveType = Remote Then\r\n            If .IsReady Then\r\n                UNCPath = Replace(PathIn, DriveLetter, .ShareName, , 1, vbTextCompare)\r\n            Else\r\n                GoTo HandleDriveLoss\r\n            End If\r\n        End If\r\n    End With\r\n    GetUncPath = UNCPath\r\n\r\nExit_Here:\r\n    Perf.OperationEnd\r\n    CatchAny eelError, \"Issue getting drive paths.\", FunctionName\r\n    Exit Function\r\n\r\nHandleDriveLoss:\r\n    ' This was borrowed from our applicaion, which has more error handling, so we're doing this in two steps now.\r\n    Log.Error eelError, \"Your drive isn't ready! Reconnect \" & DriveLetter & \" to continue.\" _\r\n                        , FunctionName\r\n\r\n    Select Case MsgBox2(\"Click [Retry] AFTER reconnecting drive \" & DriveLetter & \" to continue.\" _\r\n                        , \"This usually just means you need to simply open the drive in File Explorer. \" _\r\n                        , \"Click Cancel to stop operation.\" _\r\n                        , vbRetryCancel + vbDefaultButton1 + vbExclamation _\r\n                        , \"Drive not ready.\")\r\n\r\n        Case vbRetry\r\n            GoTo Retry\r\n\r\n        Case Else\r\n            ' Log error, quit operation.\r\n            GoTo Exit_Here\r\n    End Select\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetLastModifiedDate\r\n' Author    : Adam Waller\r\n' Date      : 7/30/2020\r\n' Purpose   : Get the last modified date on a folder or file with Unicode support.\r\n'           : Returns 0 (blank date) if the file is not found.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetLastModifiedDate(strPath As String) As Date\r\n\r\n    Dim oFile As Scripting.File\r\n    Dim oFolder As Scripting.Folder\r\n\r\n    Perf.OperationStart \"Get Modified Date\"\r\n    If FSO.FileExists(strPath) Then\r\n        Set oFile = FSO.GetFile(strPath)\r\n        GetLastModifiedDate = oFile.DateLastModified\r\n    ElseIf FSO.FolderExists(strPath) Then\r\n        Set oFolder = FSO.GetFolder(strPath)\r\n        GetLastModifiedDate = oFolder.DateLastModified\r\n    End If\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StripSlash\r\n' Author    : Adam Waller\r\n' Date      : 1/25/2019\r\n' Purpose   : Strip the trailing slash (or other path separator)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function StripSlash(strText As String) As String\r\n    If Right$(strText, 1) = PathSep Then\r\n        StripSlash = Left$(strText, Len(strText) - 1)\r\n    Else\r\n        StripSlash = strText\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddSlash\r\n' Author    : Adam Waller\r\n' Date      : 7/28/2023\r\n' Purpose   : Ensure that the string or path ends with a slash (or path separator)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddSlash(strText As String) As String\r\n    If Right$(strText, 1) = PathSep Then\r\n        AddSlash = strText\r\n    Else\r\n        AddSlash = strText & PathSep\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PathSep\r\n' Author    : Adam Waller\r\n' Date      : 3/3/2021\r\n' Purpose   : Return the current path separator, based on language settings.\r\n'           : Caches value to avoid extra calls to FSO object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function PathSep() As String\r\n    Static strSeparator As String\r\n    If strSeparator = vbNullString Then strSeparator = Mid$(FSO.BuildPath(\"a\", \"b\"), 2, 1)\r\n    PathSep = strSeparator\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modFileWinAPI.bas",
    "content": "﻿Attribute VB_Name = \"modFileWinAPI\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modFileScan\r\n' Author    : Adam Waller\r\n' Date      : 7/25/2023\r\n' Purpose   : Functions for extremely fast file system scan utilizing the Windows API.\r\n'           : Other functions to accurately return/set file modified dates with dates\r\n'           : that correctly convert for time zone/daylight savings time for historical\r\n'           : files in other years.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\nPrivate Declare PtrSafe Function FindFirstFileW Lib \"kernel32\" (ByVal lpFileName As LongPtr, ByVal lpFindFileData As LongPtr) As LongPtr\r\nPrivate Declare PtrSafe Function FindNextFileW Lib \"kernel32\" (ByVal hFindFile As LongPtr, ByVal lpFindFileData As LongPtr) As LongPtr\r\nPrivate Declare PtrSafe Function FindClose Lib \"kernel32\" (ByVal hFindFile As LongPtr) As Long\r\nPrivate Declare PtrSafe Function FileTimeToLocalFileTime Lib \"kernel32\" (lpFileTime As FILETIME, lpLocalFileTime As FILETIME) As LongPtr\r\nPrivate Declare PtrSafe Sub GetSystemTime Lib \"kernel32\" (lpSystemTime As SYSTEMTIME)\r\nPrivate Declare PtrSafe Sub GetLocalTime Lib \"kernel32\" (lpSystemTime As SYSTEMTIME)\r\n\r\n' Used to relaunch Access as an administrator to install the addin.\r\nPrivate Declare PtrSafe Function ShellExecute Lib \"shell32.dll\" Alias \"ShellExecuteA\" ( _\r\n    ByVal hwnd As LongPtr, _\r\n    ByVal lpOperation As String, _\r\n    ByVal lpFile As String, _\r\n    ByVal lpParameters As String, _\r\n    ByVal lpDirectory As String, _\r\n    ByVal nShowCmd As Long) As LongPtr\r\n\r\n' Time zone conversions\r\nPrivate Declare PtrSafe Function GetTimeZoneInformation Lib \"kernel32\" (lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Long\r\nPrivate Declare PtrSafe Function FileTimeToSystemTime Lib \"kernel32\" (lpFileTime As FILETIME, lpSystemTime As SYSTEMTIME) As Long\r\nPrivate Declare PtrSafe Function SystemTimeToFileTime Lib \"kernel32\" (lpSystemTime As SYSTEMTIME, lpFileTime As FILETIME) As Long\r\nPrivate Declare PtrSafe Function TzSpecificLocalTimeToSystemTime Lib \"kernel32\" (lpTimeZoneInformation As TIME_ZONE_INFORMATION, lpLocalTime As SYSTEMTIME, lpUniversalTime As SYSTEMTIME) As LongPtr\r\nPrivate Declare PtrSafe Function SystemTimeToTzSpecificLocalTime Lib \"kernel32\" (lpTimeZoneInformation As TIME_ZONE_INFORMATION, lpUniversalTime As SYSTEMTIME, lpLocalTime As SYSTEMTIME) As LongPtr\r\n\r\n' Set file time\r\nPrivate Declare PtrSafe Function GetFileTime Lib \"kernel32\" (ByVal hFile As LongPtr, lpCreationTime As FILETIME, lpLastAccessTime As FILETIME, lpLastWriteTime As FILETIME) As Long\r\nPrivate Declare PtrSafe Function SetFileTime Lib \"kernel32\" (ByVal hFile As LongPtr, lpCreationTime As FILETIME, lpLastAccessTime As FILETIME, lpLastWriteTime As FILETIME) As Long\r\nDeclare PtrSafe Function CloseHandle Lib \"kernel32\" (ByVal hObject As LongPtr) As Long\r\n\r\n'lpSecurityAttributes As SECURITY_ATTRIBUTES,\r\nPrivate Declare PtrSafe Function CreateFile Lib \"kernel32\" Alias \"CreateFileA\" ( _\r\n    ByVal lpFileName As String, _\r\n    ByVal dwDesiredAccess As Long, _\r\n    ByVal dwShareMode As Long, _\r\n    lpSecurityAttributes As Any, _\r\n    ByVal dwCreationDisposition As Long, _\r\n    ByVal dwFlagsAndAttributes As Long, _\r\n    ByVal hTemplateFile As LongPtr) As LongPtr\r\n\r\n' Constants for CreateFile (used when changing modified date)\r\nPrivate Const OPEN_EXISTING = &H3\r\nPrivate Const FILE_SHARE_READ = &H1\r\nPrivate Const FILE_SHARE_WRITE = &H2\r\nPrivate Const CREATE_ALWAYS = &H2\r\nPrivate Const OPEN_ALWAYS = &H4\r\nPrivate Const INVALID_HANDLE_VALUE = -1\r\nPrivate Const ERROR_ALREADY_EXISTS = &HB7\r\nPrivate Const GENERIC_ALL = &H10000000\r\nPrivate Const GENERIC_EXECUTE = &H20000000\r\nPrivate Const GENERIC_READ = &H80000000\r\nPrivate Const GENERIC_WRITE = &H40000000\r\n\r\n' Other constants\r\nPrivate Const MAX_PATH  As Long = 260\r\nPrivate Const ALTERNATE As Long = 14\r\nPrivate Const FILE_ATTRIBUTE_DIRECTORY As Long = 16 '0x10\r\n\r\nPrivate Type FILETIME\r\n    dwLowDateTime  As Long\r\n    dwHighDateTime As Long\r\nEnd Type\r\n\r\nPrivate Type SECURITY_ATTRIBUTES\r\n    nLength As Long\r\n    lpSecurityDescriptor As LongPtr\r\n    bInheritHandle As Long\r\nEnd Type\r\n\r\nPrivate Type SYSTEMTIME\r\n    wYear As Integer\r\n    wMonth As Integer\r\n    wDayOfWeek As Integer\r\n    wDay As Integer\r\n    wHour As Integer\r\n    wMinute As Integer\r\n    wSecond As Integer\r\n    wMilliseconds As Integer\r\nEnd Type\r\n\r\nPrivate Type TIME_ZONE_INFORMATION\r\n    Bias As Long\r\n    StandardName(0 To 31) As Integer\r\n    StandardDate As SYSTEMTIME\r\n    StandardBias As Long\r\n    DaylightName(0 To 31) As Integer\r\n    DaylightDate As SYSTEMTIME\r\n    DaylightBias As Long\r\nEnd Type\r\n\r\nPrivate Enum TIME_ZONE\r\n    TIME_ZONE_ID_INVALID = 0\r\n    TIME_ZONE_STANDARD = 1\r\n    TIME_ZONE_DAYLIGHT = 2\r\nEnd Enum\r\n\r\n' Can be used with either W or A functions\r\n' Pass VarPtr(wfd) to W or simply wfd to A\r\nPrivate Type WIN32_FIND_DATA\r\n    dwFileAttributes As Long\r\n    ftCreationTime   As FILETIME\r\n    ftLastAccessTime As FILETIME\r\n    ftLastWriteTime  As FILETIME\r\n    nFileSizeHigh    As Long\r\n    nFileSizeLow     As Long\r\n    dwReserved0      As Long\r\n    dwReserved1      As Long\r\n    cFileName        As String * MAX_PATH\r\n    cAlternate       As String * ALTERNATE\r\nEnd Type\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ShellEx\r\n' Author    : Adam Waller\r\n' Date      : 2/8/2025\r\n' Purpose   : Wrapper for the ShellExecute API call (resolves file names automatically)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ShellEx(strFile As String, Optional strParams As String, _\r\n    Optional strOperation As String = \"open\", Optional blnVisible As Boolean = True)\r\n    ShellExecute 0, strOperation, strFile, strParams, vbNullString, IIf(blnVisible, 1, 0)\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileList\r\n' Author    : Adam Waller\r\n' Date      : 7/25/2023\r\n' Purpose   : Return a list of files from the specified folder. Returns a dictionary\r\n'           : with the filename as the key, and the modified date as the value.\r\n'           : (Could be extended in the future to return other values)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetFileList(strFolder As String, Optional strPattern As String = \"*.*\", Optional blnAsLocalTime As Boolean = True) As Dictionary\r\n\r\n    Dim dList As Dictionary\r\n    Dim pFileHandle As LongPtr\r\n    Dim strSearchPath As String\r\n    Dim tFileData As WIN32_FIND_DATA\r\n    Dim strName As String\r\n\r\n    Perf.OperationStart \"Get File Listing (API)\"\r\n    Set dList = New Dictionary\r\n\r\n    ' Build full search path\r\n    strSearchPath = AddSlash(strFolder) & strPattern\r\n\r\n    ' Attempt to find first file\r\n    pFileHandle = FindFirstFileW(StrPtr(strSearchPath), VarPtr(tFileData))\r\n    If pFileHandle <> INVALID_HANDLE_VALUE Then\r\n        Do\r\n            ' Get file name from API call\r\n            strName = Left$(tFileData.cFileName, InStr(tFileData.cFileName, vbNullChar) - 1)\r\n            If strName = \".\" Or strName = \"..\" Then\r\n                ' Skip meta directories\r\n            ElseIf tFileData.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY Then\r\n                ' Skip subfolders\r\n            Else\r\n                ' Save file to list\r\n                dList.Add strName, FileTimeToDate(tFileData.ftLastWriteTime, blnAsLocalTime)\r\n            End If\r\n        Loop While FindNextFileW(pFileHandle, VarPtr(tFileData))\r\n    End If\r\n\r\n    ' Close handle\r\n    FindClose pFileHandle\r\n    Perf.OperationEnd\r\n\r\n    ' Return listing of files\r\n    Set GetFileList = dList\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetFileDate\r\n' Author    : Adam Waller\r\n' Date      : 7/28/2023\r\n' Purpose   : This is WAY more complicated than it might first appear. In Windows 7 and\r\n'           : newer, Windows Explorer attempts to display file modified dates as\r\n'           : relative to the Daylight Savings Time offset in effect at the time the\r\n'           : file was modified. Setting a file date to match what you see in Windows\r\n'           : explorer requires converting the local date/time to a UTC time using the\r\n'           : same DST rules used by Windows. (Hence the additional API calls required\r\n'           : to make this conversion.)\r\n'           : Further Reading: https://stackoverflow.com/q/66615978/4121863\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetFileDate(strFile As String, dteDate As Date, blnAsLocalTime As Boolean)\r\n\r\n    Dim lngHandle As LongPtr\r\n    Dim stNewDate As SYSTEMTIME\r\n    Dim stUtc As SYSTEMTIME\r\n    Dim ftUtc As FILETIME\r\n    Dim ftBlank As FILETIME\r\n    Dim lngResult As LongPtr\r\n    Dim strFullPath As String\r\n\r\n    Perf.OperationStart \"Set file modified date\"\r\n\r\n    ' Support long paths\r\n    strFullPath = \"\\\\?\\\" & strFile\r\n\r\n    ' Don't attempt this if the file does not exist\r\n    If Not FSO.FileExists(strFile) Then Exit Sub\r\n\r\n    ' Open a handle to the existing file with write access\r\n    lngHandle = CreateFile(strFullPath, GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0&, 0&)\r\n    If lngHandle = INVALID_HANDLE_VALUE Then\r\n        'Debug.Print GetSystemErrorMessageText(Err.LastDllError)\r\n        'Log.Error eelError, \"Unable to set file date for \" & strFile & \". (Unable to write to file)\", ModuleName & \"SetFileDate\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Convert the date to a SYSTEMTIME\r\n    stNewDate = DateToSystemTime(dteDate)\r\n\r\n    ' See if we are converting this from a local time\r\n    If blnAsLocalTime Then\r\n        ' Convert to UTC using an API that is able to translate Timezone/DST to UTC\r\n        ' This is SUPPOSED to default to the local TZ if null is provided, but this was not the case\r\n        ' in my testing, so we are passing the current time zone information just to be safe.\r\n        lngResult = TzSpecificLocalTimeToSystemTime(GetLocalTimeZoneInfo, stNewDate, stUtc)\r\n        stNewDate = stUtc\r\n    End If\r\n\r\n    ' Convert the UTC system time to a FILETIME\r\n    lngResult = SystemTimeToFileTime(stNewDate, ftUtc)\r\n\r\n    ' Set the file date using the converted UTC time\r\n    lngResult = SetFileTime(lngHandle, ftBlank, ftBlank, ftUtc)\r\n\r\n    ' Close the file handle\r\n    CloseHandle lngHandle\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileDateEx\r\n' Author    : Adam Waller\r\n' Date      : 7/28/2023\r\n' Purpose   : Return the actual date displayed in Windows Explorer (DST aware for\r\n'           : historical dates), not just the FSO LastModified date, which may not be\r\n'           : accurate for dates outside the current DST settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetFileModifiedDateEx(strFile As String) As Date\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FileTimeToDate\r\n' Author    : Adam Waller\r\n' Date      : 7/25/2023\r\n' Purpose   : Convert a Win32 API FileTime to a VBA Datetime value\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function FileTimeToDate(tFileTime As FILETIME, blnAsLocalTime As Boolean) As Date\r\n\r\n    Dim tReturnTime As SYSTEMTIME\r\n    Dim tUtcTime As SYSTEMTIME\r\n    Dim lngResult As LongPtr\r\n\r\n    ' Get UTC file time\r\n    FileTimeToSystemTime tFileTime, tUtcTime\r\n\r\n    ' Perform local time conversion, if requested\r\n    If blnAsLocalTime Then\r\n        lngResult = SystemTimeToTzSpecificLocalTime(GetLocalTimeZoneInfo, tUtcTime, tReturnTime)\r\n    Else\r\n        tReturnTime = tUtcTime\r\n    End If\r\n\r\n    ' Convert to a VBA date value\r\n    With tReturnTime\r\n        FileTimeToDate = DateSerial(.wYear, .wMonth, .wDay) + TimeSerial(.wHour, .wMinute, .wSecond)\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DateToSystemTime\r\n' Author    : Adam Waller\r\n' Date      : 7/28/2023\r\n' Purpose   : Convert a VBA date to a systemtime structure\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function DateToSystemTime(dteDate) As SYSTEMTIME\r\n    With DateToSystemTime\r\n        .wYear = Year(dteDate)\r\n        .wMonth = Month(dteDate)\r\n        .wDay = Day(dteDate)\r\n        .wDayOfWeek = Weekday(dteDate) - 1 ' Adjust to expected format\r\n        .wHour = Hour(dteDate)\r\n        .wMinute = Minute(dteDate)\r\n        .wSecond = Second(dteDate)\r\n        .wMilliseconds = 0  ' Not used with VBA dates\r\n    End With\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetLocalTimeZoneInfo\r\n' Author    : Adam Waller\r\n' Date      : 7/28/2023\r\n' Purpose   : A function to return a copy of the current time zone information\r\n'           : (Cached for performance reasons)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetLocalTimeZoneInfo() As TIME_ZONE_INFORMATION\r\n    Static blnCached As Boolean\r\n    Static tzLocal As TIME_ZONE_INFORMATION\r\n    If Not blnCached Then\r\n        GetTimeZoneInformation tzLocal\r\n        blnCached = True\r\n    End If\r\n    GetLocalTimeZoneInfo = tzLocal\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modFunctions.bas",
    "content": "﻿Attribute VB_Name = \"modFunctions\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modFunctions\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : General functions that don't fit more specifically into another module.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n' API function to pause processing\r\nPrivate Declare PtrSafe Sub Sleep Lib \"kernel32\" (ByVal dwMilliseconds As LongPtr)\r\n\r\n' API calls to change window style\r\nPrivate Const GWL_STYLE = -16\r\nPrivate Const WS_SIZEBOX = &H40000\r\nPrivate Declare PtrSafe Function IsWindowUnicode Lib \"user32\" (ByVal hwnd As LongPtr) As Long\r\n#If Win64 Then\r\n    ' 64-bit versions of Access\r\n    Private Declare PtrSafe Function GetWindowLongPtr Lib \"user32\" Alias \"GetWindowLongPtrW\" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As LongPtr\r\n    Private Declare PtrSafe Function SetWindowLongPtr Lib \"user32\" Alias \"SetWindowLongPtrW\" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr\r\n#Else\r\n    ' 32-bit versions of Access\r\n    Private Declare PtrSafe Function GetWindowLongPtr Lib \"user32\" Alias \"GetWindowLongW\" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As LongPtr\r\n    Private Declare PtrSafe Function SetWindowLongPtr Lib \"user32\" Alias \"SetWindowLongW\" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr\r\n#End If\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : InCollection\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2015\r\n' Purpose   : Returns true if the item value is found in the collection\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function InCollection(ByVal MyCol As Collection, ByVal MyValue As Variant) As Boolean\r\n    Dim intCnt As Integer\r\n    For intCnt = 1 To MyCol.Count\r\n        If MyCol(intCnt) = MyValue Then\r\n            InCollection = True\r\n            Exit For\r\n        End If\r\n    Next intCnt\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergeCollection\r\n' Author    : Adam Waller\r\n' Date      : 4/23/2020\r\n' Purpose   : Adds a collection into another collection.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MergeCollection(ByRef colOriginal As Collection, ByVal colToAdd As Collection)\r\n    Dim varItem As Variant\r\n    For Each varItem In colToAdd\r\n        colOriginal.Add varItem\r\n    Next varItem\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergeDictionary\r\n' Author    : Adam Waller\r\n' Date      : 7/31/2023\r\n' Purpose   : Merge a dictionary into another dictionary. Existing values are replaced\r\n'           : from the incoming dictionary.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MergeDictionary(ByRef dOriginal As Dictionary, ByVal dToAdd As Dictionary)\r\n    Dim varKey As Variant\r\n    For Each varKey In dToAdd.Keys\r\n        If IsObject(dToAdd(varKey)) Then\r\n            Set dOriginal(varKey) = dToAdd(varKey)\r\n        Else\r\n            dOriginal(varKey) = dToAdd(varKey)\r\n        End If\r\n    Next varKey\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetVBEExtByType\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2015\r\n' Purpose   : Return a standardized VBE component extension by type\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetVBEExtByType(cmp As VBComponent) As String\r\n    Dim strExt As String\r\n    Select Case cmp.Type\r\n        Case vbext_ct_StdModule:    strExt = \".bas\"\r\n        Case vbext_ct_MSForm:       strExt = \".frm\" ' (not used in Microsoft Access)\r\n        Case Else ' vbext_ct_Document, vbext_ct_ActiveXDesigner, vbext_ct_ClassModule\r\n            strExt = \".cls\"\r\n    End Select\r\n    GetVBEExtByType = strExt\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSafeFileName\r\n' Author    : Adam Waller\r\n' Date      : 1/14/2019\r\n' Purpose   : Replace illegal filename characters with URL encoded substitutes\r\n'           : Sources: http://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSafeFileName(strName As String) As String\r\n\r\n    Dim strSafe As String\r\n\r\n    ' Use URL encoding for these characters\r\n    ' https://www.w3schools.com/tags/ref_urlencode.asp\r\n    strSafe = Replace(strName, \"%\", \"%25\")  ' This should be done first.\r\n    strSafe = Replace(strSafe, \"<\", \"%3C\")\r\n    strSafe = Replace(strSafe, \">\", \"%3E\")\r\n    strSafe = Replace(strSafe, \":\", \"%3A\")\r\n    strSafe = Replace(strSafe, \"\"\"\", \"%22\")\r\n    strSafe = Replace(strSafe, \"/\", \"%2F\")\r\n    strSafe = Replace(strSafe, \"\\\", \"%5C\")\r\n    strSafe = Replace(strSafe, \"|\", \"%7C\")\r\n    strSafe = Replace(strSafe, \"?\", \"%3F\")\r\n    strSafe = Replace(strSafe, \"*\", \"%2A\")\r\n\r\n    ' Return the sanitized file name.\r\n    GetSafeFileName = strSafe\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetObjectNameFromFileName\r\n' Author    : Adam Waller\r\n' Date      : 2/27/2025\r\n' Purpose   : Return the name or path after translating the HTML encoding back to normal\r\n'           : file name characters.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetOriginalFromSafeName(strSafeName As String) As String\r\n\r\n    Dim strName As String\r\n\r\n    strName = strSafeName\r\n    ' Make sure the following list matches the one above.\r\n    strName = Replace(strName, \"%3C\", \"<\")\r\n    strName = Replace(strName, \"%3E\", \">\")\r\n    strName = Replace(strName, \"%3A\", \":\")\r\n    strName = Replace(strName, \"%22\", \"\"\"\")\r\n    strName = Replace(strName, \"%2F\", \"/\")\r\n    strName = Replace(strName, \"%5C\", \"\\\")\r\n    strName = Replace(strName, \"%7C\", \"|\")\r\n    strName = Replace(strName, \"%3F\", \"?\")\r\n    strName = Replace(strName, \"%2A\", \"*\")\r\n    strName = Replace(strName, \"%25\", \"%\")  ' This should be done last.\r\n\r\n    ' Return the object name\r\n    GetOriginalFromSafeName = strName\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetObjectNameFromFileName\r\n' Author    : Adam Waller\r\n' Date      : 5/6/2020\r\n' Purpose   : Return the object name after translating the HTML encoding back to normal\r\n'           : file name characters.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetObjectNameFromFileName(strFile As String) As String\r\n    GetObjectNameFromFileName = FSO.GetBaseName(GetOriginalFromSafeName(strFile))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DatesClose\r\n' Author    : Adam Waller\r\n' Date      : 10/13/2017\r\n' Purpose   : Returns true if the dates are within the threshhold.\r\n'           : (Used when dates are very similar, but not exact)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DatesClose(dte1 As Date, dte2 As Date, Optional lngMaxDiffSeconds As Long = 3) As Boolean\r\n    DatesClose = (Abs(DateDiff(\"s\", dte1, dte2)) < lngMaxDiffSeconds)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MultiReplace\r\n' Author    : Adam Waller\r\n' Date      : 1/18/2019\r\n' Purpose   : Does a string replacement of multiple items in one call.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function MultiReplace(ByVal strText As String, ParamArray varPairs()) As String\r\n    Dim intPair As Integer\r\n    For intPair = 0 To UBound(varPairs) Step 2\r\n        strText = Replace(strText, varPairs(intPair), varPairs(intPair + 1))\r\n    Next intPair\r\n    MultiReplace = strText\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ShowIDE\r\n' Author    : Adam Waller\r\n' Date      : 5/18/2015\r\n' Purpose   : Show the VBA code editor (used in autoexec macro)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ShowIDE() As Boolean\r\n    DoCmd.RunCommand acCmdVisualBasicEditor\r\n    DoEvents\r\n    ShowIDE = True\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MsgBox2\r\n' Author    : Adam Waller\r\n' Date      : 1/27/2017\r\n' Purpose   : Alternate message box with bold prompt on first line.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function MsgBox2(strBold As String, Optional strLine1 As String, Optional strLine2 As String, _\r\n    Optional intButtons As VbMsgBoxStyle = vbOKOnly, Optional strTitle As String, Optional intDefaultResult As VbMsgBoxResult = vbOK) As VbMsgBoxResult\r\n\r\n    Dim strMsg As String\r\n    Dim varLines(0 To 3) As String\r\n    Dim intCursor As Integer\r\n\r\n    ' Turn off any hourglass\r\n    intCursor = Screen.MousePointer\r\n    If intCursor > 0 Then Screen.MousePointer = 0\r\n\r\n    ' Escape single quotes by doubling them.\r\n    varLines(0) = Replace(strBold, \"'\", \"''\")\r\n    varLines(1) = Replace(strLine1, \"'\", \"''\")\r\n    varLines(2) = Replace(strLine2, \"'\", \"''\")\r\n    varLines(3) = Replace(strTitle, \"'\", \"''\")\r\n\r\n    ' Check interaction mode\r\n    If InteractionMode = eimNormal Then\r\n        ' Normal user interaction with MsgBox\r\n        If varLines(3) = vbNullString Then varLines(3) = Application.VBE.ActiveVBProject.Name\r\n        strMsg = \"MsgBox('\" & varLines(0) & \"@\" & varLines(1) & \"@\" & varLines(2) & \"@',\" & intButtons & \",'\" & varLines(3) & \"')\"\r\n        Perf.PauseTiming\r\n        MsgBox2 = Eval(strMsg)\r\n        Perf.ResumeTiming\r\n    Else\r\n        ' Silent mode. Don't display any message, but log it instead.\r\n        With New clsConcat\r\n            .AppendOnAdd = vbCrLf\r\n            .Add \"[**MessageBox Not Displayed**]\"\r\n            If Len(strTitle) Then .Add \"Title: \" & strTitle\r\n            If Len(strBold) Then .Add strBold\r\n            If Len(strLine1) Then .Add strLine1\r\n            If Len(strLine2) Then .Add strLine2\r\n            If intButtons <> vbOKOnly Then .Add \"Buttons Flag: \" & intButtons\r\n            Log.Add .GetStr\r\n        End With\r\n        ' Return default (unattended) result\r\n        MsgBox2 = intDefaultResult\r\n    End If\r\n\r\n    ' Restore MousePointer (if needed)\r\n    If intCursor > 0 Then Screen.MousePointer = intCursor\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : dNZ\r\n' Author    : Adam Waller\r\n' Date      : 3/23/2020\r\n' Purpose   : Like the NZ function but for dictionary elements\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function dNZ(dObject As Dictionary, strPath As String, Optional strDelimiter As String = \"\\\") As String\r\n\r\n    Dim varPath As Variant\r\n    Dim intCnt As Integer\r\n    Dim dblVal As Double\r\n    Dim strKey As String\r\n    Dim varSegment As Variant\r\n\r\n    ' Split path into parts\r\n    varPath = Split(strPath, strDelimiter)\r\n    Set varSegment = dObject\r\n\r\n    For intCnt = LBound(varPath) To UBound(varPath)\r\n\r\n        strKey = varPath(intCnt)\r\n        If dObject Is Nothing Then\r\n            ' No object found\r\n            Exit For\r\n        ElseIf TypeOf varSegment Is Collection Then\r\n            ' Expect index (integer)\r\n            If IsNumeric(strKey) Then\r\n                ' Looks like an array index\r\n                dblVal = CDbl(strKey)\r\n                ' Do a couple more checks to see if this looks like a valid index\r\n                If dblVal < 1 Or dblVal > 32000 Or dblVal <> CInt(dblVal) Then Exit For\r\n                ' See if this is the last segment\r\n                If intCnt = UBound(varPath) Then\r\n                    If TypeOf varSegment(dblVal) Is Dictionary Then\r\n                        ' Need a named key\r\n                        Exit For\r\n                    Else\r\n                        ' Could be an array of values\r\n                        dNZ = Nz(varSegment(dblVal))\r\n                    End If\r\n                Else\r\n                    ' Move out to next segment\r\n                    Set varSegment = varSegment(dblVal)\r\n                End If\r\n            End If\r\n        ElseIf TypeOf varSegment Is Dictionary Then\r\n            ' Expect key (string)\r\n            If intCnt = UBound(varPath) Then\r\n                ' Reached last segment\r\n                If varSegment.Exists(strKey) Then\r\n                    If TypeOf varSegment Is Dictionary Then\r\n                        dNZ = Nz(varSegment(strKey))\r\n                    Else\r\n                        ' Might be array\r\n                        Exit For\r\n                    End If\r\n                End If\r\n            Else\r\n                ' Move out to next segment\r\n                If varSegment.Exists(strKey) Then\r\n                    If Not IsEmpty(varSegment(strKey)) Then\r\n                        Set varSegment = varSegment(strKey)\r\n                    Else\r\n                        ' Empty value\r\n                        Exit For\r\n                    End If\r\n                Else\r\n                    ' Path not found\r\n                    Exit For\r\n                End If\r\n            End If\r\n        End If\r\n    Next intCnt\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : KeyExists\r\n' Author    : Adam Waller\r\n' Date      : 5/28/2021\r\n' Purpose   : Returns true if the specified nested segment is found to exist.\r\n'           : Note that this currently only supports nested child dictionary objects,\r\n'           : not nested collections.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function KeyExists(dDictionary As Dictionary, ParamArray varSegmentKeys()) As Boolean\r\n\r\n    Dim intSegment As Integer\r\n    Dim dBase As Dictionary\r\n\r\n    ' Bail out if no valid dictionary passed\r\n    If dDictionary Is Nothing Then Exit Function\r\n\r\n    ' Start with based dictionary\r\n    Set dBase = dDictionary\r\n    KeyExists = True\r\n\r\n    ' Loop through segments, confirming that each one exists\r\n    For intSegment = 0 To UBound(varSegmentKeys)\r\n        If dBase.Exists(varSegmentKeys(intSegment)) Then\r\n            If intSegment < UBound(varSegmentKeys) Then\r\n                Set dBase = dBase(varSegmentKeys(intSegment))\r\n            End If\r\n        Else\r\n            KeyExists = False\r\n            Exit For\r\n        End If\r\n    Next intSegment\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SortCollection\r\n' Author    : Adam Waller\r\n' Date      : 11/14/2020\r\n' Purpose   : Sort a collection of items by value. (Returns a new sorted collection)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function SortCollectionByValue(colSource As Collection) As Collection\r\n\r\n    Dim colSorted As Collection\r\n    Dim varItems() As Variant\r\n    Dim lngCnt As Long\r\n\r\n    ' Don't need to sort empty collection or single item\r\n    If colSource.Count < 2 Then\r\n        Set SortCollectionByValue = colSource\r\n        Exit Function\r\n    End If\r\n\r\n    ' Build and sort array of keys\r\n    ReDim varItems(0 To colSource.Count - 1)\r\n    For lngCnt = 0 To UBound(varItems)\r\n        varItems(lngCnt) = colSource(lngCnt + 1)\r\n    Next lngCnt\r\n    QuickSort varItems\r\n\r\n    ' Build and return new collection using sorted values\r\n    Set colSorted = New Collection\r\n    For lngCnt = 0 To UBound(varItems)\r\n        colSorted.Add varItems(lngCnt)\r\n    Next lngCnt\r\n    Set SortCollectionByValue = colSorted\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SortDictionaryByKeys\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2020\r\n' Purpose   : Rebuilds a dictionary object by adding all the items to a new dictionary\r\n'           : sorted by keys.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function SortDictionaryByKeys(dSource As Dictionary) As Dictionary\r\n\r\n    Dim dSorted As Dictionary\r\n    Dim varKeys() As Variant\r\n    Dim varKey As Variant\r\n    Dim lngCnt As Long\r\n\r\n    ' Don't need to sort empty dictionary or single item\r\n    If dSource.Count < 2 Then\r\n        Set SortDictionaryByKeys = dSource\r\n        Exit Function\r\n    End If\r\n\r\n    ' Build and sort array of keys\r\n    ReDim varKeys(0 To dSource.Count - 1)\r\n    For Each varKey In dSource.Keys\r\n        varKeys(lngCnt) = varKey\r\n        lngCnt = lngCnt + 1\r\n    Next varKey\r\n\r\n    QuickSort varKeys\r\n\r\n    ' Build and return new dictionary using sorted keys\r\n    Set dSorted = New Dictionary\r\n    dSorted.CompareMode = dSource.CompareMode\r\n    For lngCnt = 0 To dSource.Count - 1\r\n        dSorted.Add varKeys(lngCnt), dSource(varKeys(lngCnt))\r\n    Next lngCnt\r\n\r\n    Set SortDictionaryByKeys = dSorted\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : QuickSort\r\n' Author    : Stack Overflow\r\n' Date      : 5/8/2020\r\n' Purpose   : Adapted from https://stackoverflow.com/a/152325/4121863\r\n' Usage     : QuickSort MyArray\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub QuickSort(ByRef vArray As Variant, Optional ByVal inLow, Optional ByVal inHi)\r\n\r\n    Dim pivot   As Variant\r\n    Dim tmpSwap As Variant\r\n    Dim tmpLow  As Long\r\n    Dim tmpHi   As Long\r\n\r\n    If IsMissing(inLow) Then inLow = LBound(vArray)\r\n    If IsMissing(inHi) Then inHi = UBound(vArray)\r\n\r\n    tmpLow = inLow\r\n    tmpHi = inHi\r\n\r\n    pivot = vArray((inLow + inHi) \\ 2)\r\n\r\n    While (tmpLow <= tmpHi)\r\n        While (vArray(tmpLow) < pivot And tmpLow < inHi)\r\n            tmpLow = tmpLow + 1\r\n        Wend\r\n\r\n        While (pivot < vArray(tmpHi) And tmpHi > inLow)\r\n            tmpHi = tmpHi - 1\r\n        Wend\r\n\r\n        If (tmpLow <= tmpHi) Then\r\n            tmpSwap = vArray(tmpLow)\r\n            vArray(tmpLow) = vArray(tmpHi)\r\n            vArray(tmpHi) = tmpSwap\r\n            tmpLow = tmpLow + 1\r\n            tmpHi = tmpHi - 1\r\n        End If\r\n    Wend\r\n\r\n    If (inLow < tmpHi) Then QuickSort vArray, inLow, tmpHi\r\n    If (tmpLow < inHi) Then QuickSort vArray, tmpLow, inHi\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DictionaryEqual\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2020\r\n' Purpose   : Returns true if the two dictionary objects are equal in values to each\r\n'           : other, including nested values. Testing the quickest comparisons first\r\n'           : to make the function as performant as possible.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DictionaryEqual(dOne As Dictionary, dTwo As Dictionary) As Boolean\r\n\r\n    Dim strOne As String\r\n    Dim strTwo As String\r\n    Dim blnEqual As Boolean\r\n\r\n    Perf.OperationStart \"Compare Dictionary\"\r\n    If dOne Is Nothing And dTwo Is Nothing Then\r\n        ' Neither object set.\r\n        blnEqual = True\r\n    ElseIf Not dOne Is Nothing And Not dTwo Is Nothing Then\r\n        ' Both are objects. Check count property.\r\n        If dOne.Count = dTwo.Count Then\r\n            strOne = ConvertToJson(dOne)\r\n            strTwo = ConvertToJson(dTwo)\r\n            ' Compare string length\r\n            If Len(strOne) = Len(strTwo) Then\r\n                ' Perform a binary (case-sensitive) comparison of strings.\r\n                blnEqual = (StrComp(strOne, strTwo, vbBinaryCompare) = 0)\r\n            End If\r\n        End If\r\n    End If\r\n    Perf.OperationEnd\r\n\r\n    ' Return comparison result\r\n    DictionaryEqual = blnEqual\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CloneDictionary\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Recursive function to deep-clone a dictionary object, including nested\r\n'           : dictionaries.\r\n'           : NOTE: All other object types are cloned as a reference to the same object\r\n'           : referenced by the original dictionary, not a new object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function CloneDictionary(dSource As Dictionary, _\r\n    Optional Compare As eCompareMethod2 = ecmSourceMethod) As Dictionary\r\n\r\n    Dim dNew As Dictionary\r\n    Dim dChild As Dictionary\r\n    Dim varKey As Variant\r\n\r\n    ' No object returned if source is nothing\r\n    If dSource Is Nothing Then Exit Function\r\n\r\n    ' Create new dictionary object and set compare mode\r\n    Set dNew = New Dictionary\r\n    If Compare = ecmSourceMethod Then\r\n        ' Use the same compare mode as the original dictionary.\r\n        dNew.CompareMode = dSource.CompareMode\r\n    Else\r\n        dNew.CompareMode = Compare\r\n    End If\r\n\r\n    ' Loop through keys\r\n    For Each varKey In dSource.Keys\r\n        If TypeOf dSource(varKey) Is Dictionary Then\r\n            ' Call this function recursively to add nested dictionary\r\n            Set dChild = dSource(varKey)\r\n            dNew.Add varKey, CloneDictionary(dChild, Compare)\r\n        Else\r\n            ' Add key to dictionary\r\n            dNew.Add varKey, dSource(varKey)\r\n        End If\r\n    Next varKey\r\n\r\n    ' Return new dictionary\r\n    Set CloneDictionary = dNew\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Pause\r\n' Author    : Adam Waller\r\n' Date      : 6/3/2020\r\n' Purpose   : Pause the code execution for x seconds.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Pause(sngSeconds As Single)\r\n    If sngSeconds > 0.1 Then Perf.OperationStart \"Pause execution\"\r\n    Sleep sngSeconds * 1000\r\n    If sngSeconds > 0.1 Then Perf.OperationEnd\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Largest\r\n' Author    : Adam Waller\r\n' Date      : 12/2/2020\r\n' Purpose   : Return the largest of an array of values\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Largest(ParamArray varValues()) As Variant\r\n\r\n    Dim varLargest As Variant\r\n    Dim intCnt As Integer\r\n\r\n    For intCnt = LBound(varValues) To UBound(varValues)\r\n        If varLargest < varValues(intCnt) Then varLargest = varValues(intCnt)\r\n    Next intCnt\r\n    Largest = varLargest\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ZN\r\n' Author    : Adam Waller\r\n' Date      : 12/2/2020\r\n' Purpose   : Opposite of the NZ function, where we convert an empty string or 0 to null.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ZN(varValue As Variant) As Variant\r\n    If varValue = vbNullString Or varValue = 0 Then\r\n        ZN = Null\r\n    Else\r\n        ZN = varValue\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ZNDate\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Return null for an empty date value\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ZNDate(varValue As Variant) As Variant\r\n    Dim blnDateValue As Boolean\r\n    If IsDate(varValue) Then blnDateValue = (CDate(varValue) <> 0)\r\n    If blnDateValue Then\r\n        ZNDate = varValue\r\n    Else\r\n        ZNDate = Null\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Nz2\r\n' Author    : Adam Waller\r\n' Date      : 2/18/2021\r\n' Purpose   : Extend the NZ function to also include 0 or empty string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Nz2(varValue, Optional varIfNull) As Variant\r\n    Select Case varValue\r\n        Case vbNullString, 0\r\n            If IsMissing(varIfNull) Then\r\n                Nz2 = vbNullString\r\n            Else\r\n                Nz2 = varIfNull\r\n            End If\r\n        Case Else\r\n            If IsNull(varValue) Then\r\n                Nz2 = varIfNull\r\n            Else\r\n                Nz2 = varValue\r\n            End If\r\n    End Select\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : InArray\r\n' Author    : Adam Waller\r\n' Date      : 5/16/2023\r\n' Purpose   : Returns true if the matching value is found in the array.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function InArray(varArray, varValue, Optional intCompare As VbCompareMethod = vbBinaryCompare) As Boolean\r\n\r\n    Dim lngCnt As Long\r\n\r\n    ' Guard clauses\r\n    If Not IsArray(varArray) Then Exit Function\r\n    If IsEmptyArray(varArray) Then Exit Function\r\n\r\n    ' Loop through array items, looking for a match\r\n    For lngCnt = LBound(varArray) To UBound(varArray)\r\n        If TypeName(varValue) = \"String\" Then\r\n            ' Use specified compare method\r\n            If StrComp(varArray(lngCnt), varValue, intCompare) = 0 Then\r\n                InArray = True\r\n                Exit For\r\n            End If\r\n        Else\r\n            ' Compare non-string value\r\n            If varValue = varArray(lngCnt) Then\r\n                ' Found exact match\r\n                InArray = True\r\n                Exit For\r\n            End If\r\n        End If\r\n    Next lngCnt\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddToArray\r\n' Author    : Adam Waller\r\n' Date      : 5/8/2023\r\n' Purpose   : Extends the array by one, and adds the new element to the last segment.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddToArray(ByRef varArray As Variant, varNewElement As Variant)\r\n    ' See if we have defined an index yet\r\n    If IsEmptyArray(varArray) Then\r\n        ' Add first index to array\r\n        ReDim varArray(0)\r\n    Else\r\n        ' Expand array by one element while preserving existing values\r\n        ReDim Preserve varArray(LBound(varArray) To UBound(varArray) + 1)\r\n    End If\r\n    varArray(UBound(varArray)) = varNewElement\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsEmptyArray\r\n' Author    : Adam Waller\r\n' Date      : 3/25/2025\r\n' Purpose   : Return true if the passed array is empty, meaning it does not have any\r\n'           : indexes defined. (Unfortunately we have to use on error resume next to\r\n'           : trap the error when accessing the index.)\r\n'           : This also has the side affect that it creates an array index for object\r\n'           : arrays. See issue #610 for details.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function IsEmptyArray(varArray As Variant) As Boolean\r\n\r\n    Dim lngLowerBound As Long\r\n    Dim lngUpperBound As Long\r\n\r\n    ' Exit (returning False) if we are not dealing with an array variable\r\n    If Not IsArray(varArray) Then Exit Function\r\n\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    ' Attempt to read the lower bound of the array\r\n    lngLowerBound = 0\r\n    lngUpperBound = -1\r\n    lngLowerBound = LBound(varArray)\r\n    lngUpperBound = UBound(varArray)\r\n\r\n    ' Clear any error thrown while attempting to read LBound()\r\n    If Err Then Err.Clear\r\n\r\n    ' If the above assignment fails, we have an empty array\r\n    ' (In the case of empty object arrays, this may be reflected\r\n    '  as Ubound = -1, LBound = 0\r\n    IsEmptyArray = (lngUpperBound < lngLowerBound)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BitSet\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2020\r\n' Purpose   : Returns true if the flag is set.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function BitSet(lngFlags As Long, lngValue As Long) As Boolean\r\n    BitSet = CBool((lngFlags And lngValue) = lngValue)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Repeat\r\n' Author    : Adam Waller\r\n' Date      : 4/29/2021\r\n' Purpose   : Repeat a string a specified number of times\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Repeat(strText As String, lngTimes As Long) As String\r\n    Repeat = Replace$(Space$(lngTimes), \" \", strText)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Coalesce\r\n' Author    : Adam Waller\r\n' Date      : 5/15/2021\r\n' Purpose   : Return the first non-empty string from an array of string values\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Coalesce(ParamArray varStrings()) As String\r\n    Dim intString As Integer\r\n    For intString = 0 To UBound(varStrings)\r\n        If Nz(varStrings(intString)) <> vbNullString Then\r\n            Coalesce = varStrings(intString)\r\n            Exit For\r\n        End If\r\n    Next intString\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DblQ\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Double any single or double quotes in the string (Used for SQL output)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DblQ(strText As String) As String\r\n    DblQ = MultiReplace(strText, \"'\", \"''\", \"\"\"\", \"\"\"\"\"\")\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DeDupString\r\n' Author    : Adam Waller\r\n' Date      : 9/10/2022\r\n' Purpose   : Removes consecutive duplicates of a string within a string.\r\n'           : (Some logic built in for efficiency and to prevent infinite loops)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function DeDupString(strText As String, strDuplicated As String) As String\r\n\r\n    Dim lngCnt As Long\r\n    Dim strNew As String\r\n\r\n    strNew = strText\r\n\r\n    ' See if the searched string exists before attempting to replace\r\n    If InStr(1, strText, strDuplicated) > 0 Then\r\n        For lngCnt = 10 To 2 Step -1\r\n            strNew = Replace(strNew, Repeat(strDuplicated, lngCnt), strDuplicated)\r\n        Next lngCnt\r\n    End If\r\n\r\n    ' Return deduplicated string\r\n    DeDupString = strNew\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StartsWith\r\n' Author    : Adam Waller\r\n' Date      : 11/5/2020\r\n' Purpose   : See if a string begins with a specified string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function StartsWith(strText As String, strStartsWith As String, Optional Compare As VbCompareMethod = vbBinaryCompare) As Boolean\r\n    StartsWith = (InStr(1, strText, strStartsWith, Compare) = 1)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : EndsWith\r\n' Author    : Adam Waller\r\n' Date      : 4/29/2021\r\n' Purpose   : See if a string ends with a specified string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function EndsWith(strText As String, strEndsWith As String, Optional Compare As VbCompareMethod = vbBinaryCompare) As Boolean\r\n    EndsWith = (StrComp(Right$(strText, Len(strEndsWith)), strEndsWith, Compare) = 0)\r\n    'EndsWith = (InStr(1, strText, strEndsWith, Compare) = len(strtext len(strendswith) 1)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SwapExtension\r\n' Author    : Adam Waller\r\n' Date      : 8/9/2023\r\n' Purpose   : Return the file path with a different file extension.\r\n'           : I.e.  c:\\test.bas > c:\\test.cls\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function SwapExtension(strFilePath As String, strNewExtensionWithoutDelimiter As String) As String\r\n    Dim strCurrentExt As String\r\n    strCurrentExt = FSO.GetExtensionName(strFilePath)\r\n    SwapExtension = Left(strFilePath, Len(strFilePath) - Len(strCurrentExt)) & strNewExtensionWithoutDelimiter\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LikeAny\r\n' Author    : Adam Waller\r\n' Date      : 3/14/2023\r\n' Purpose   : Returns true if strTest is LIKE any of the array of expressions.\r\n'           : (Short ciruits to first matching expression)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function LikeAny(strTest As String, ParamArray varLikeThis()) As Boolean\r\n    Dim lngCnt As Long\r\n    For lngCnt = 0 To UBound(varLikeThis)\r\n        If strTest Like varLikeThis(lngCnt) Then\r\n            LikeAny = True\r\n            Exit For\r\n        End If\r\n    Next lngCnt\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ModuleName\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Dynamically return the class name from a class object.\r\n'           : (This way we don't have to maintain name constants in class modules.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ModuleName(clsMe As Object) As String\r\n    ModuleName = TypeName(clsMe)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetOfficeBitness\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Returns \"32\" or \"64\" as the bitness of Microsoft Office (not Windows)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetOfficeBitness() As String\r\n    #If Win64 Then\r\n        ' 64-bit add-in (Office x64)\r\n        GetOfficeBitness = \"64\"\r\n    #Else\r\n        ' 32-bit add-in\r\n        GetOfficeBitness = \"32\"\r\n    #End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MakeDialogResizable\r\n' Author    : Adam Waller\r\n' Date      : 5/16/2023\r\n' Purpose   : Change the window style of an existing dialog window to make it resizable.\r\n'           : (This allows you to use the acDialog argument when opening a form, but\r\n'           :  still have the form resizable by the user.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MakeDialogResizable(frmMe As Form)\r\n\r\n    Dim lngHwnd As LongPtr\r\n    Dim lngFlags As LongPtr\r\n    Dim lngResult As LongPtr\r\n\r\n    ' Get handle for form\r\n    lngHwnd = frmMe.hwnd\r\n\r\n    ' Debug.Print IsWindowUnicode(lngHwnd) - Testing indicates that the windows are\r\n    ' Unicode, so we are using the Unicode versions of the GetWindowLong functions.\r\n\r\n    ' Get the current window style\r\n    lngFlags = GetWindowLongPtr(lngHwnd, GWL_STYLE)\r\n\r\n    ' Set resizable flag and apply updated style\r\n    lngFlags = lngFlags Or WS_SIZEBOX\r\n    lngResult = SetWindowLongPtr(lngHwnd, GWL_STYLE, lngFlags)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ScaleColumns\r\n' Author    : Adam Waller\r\n' Date      : 5/16/2023\r\n' Purpose   : Size the datasheet columns evenly to fill the available width, minus an\r\n'           : allotment for the width of the vertical scroll bar.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ScaleColumns(frmDatasheet As Form, Optional lngScrollWidthTwips As Long = 300, _\r\n    Optional varFixedControlNameArray As Variant)\r\n\r\n    Dim lngTotal As Long\r\n    Dim lngCurrent As Long\r\n    Dim lngSizeable As Long\r\n    Dim lngFixed As Long\r\n    Dim lngWidth As Long\r\n    Dim dblRatio As Double\r\n    Dim ctl As Control\r\n    Dim colResize As Collection\r\n\r\n    lngTotal = frmDatasheet.InsideWidth - lngScrollWidthTwips\r\n    Set colResize = New Collection\r\n\r\n    ' Loop through the columns twice, once to get the current widths, then to set them.\r\n    For Each ctl In frmDatasheet.Controls\r\n        Select Case ctl.ControlType\r\n            Case acTextBox, acComboBox\r\n                If ctl.Visible Then\r\n                    ' Get column width\r\n                    lngWidth = ctl.ColumnWidth\r\n                    If lngWidth < 0 Then\r\n                        ' Set to not hidden to get the actual width of the column\r\n                        ' -1 = Default Width\r\n                        ' -2 = Fit to Text\r\n                        ctl.ColumnHidden = False\r\n                        lngWidth = ctl.ColumnWidth\r\n                    End If\r\n                    lngCurrent = lngCurrent + lngWidth\r\n                    If Not InArray(varFixedControlNameArray, ctl.Name, vbTextCompare) Then\r\n                        lngSizeable = lngSizeable + lngWidth\r\n                        colResize.Add ctl\r\n                    End If\r\n                End If\r\n        End Select\r\n    Next ctl\r\n\r\n    ' Exit if we have no sizable controls\r\n    If lngSizeable = 0 Then Exit Sub\r\n\r\n    ' Get ratio for new sizes (Scales resizable controls proportionately)\r\n    lngFixed = lngCurrent - lngSizeable\r\n    dblRatio = (lngTotal - lngFixed) / lngSizeable\r\n\r\n    ' Resize each control\r\n    For Each ctl In colResize\r\n        ctl.ColumnWidth = ctl.ColumnWidth * dblRatio\r\n    Next ctl\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExpandEnvironmentVariables\r\n' Author    : Adam Waller\r\n' Date      : 2/12/2024\r\n' Purpose   : Expand out environment variables in a string.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ExpandEnvironmentVariables(strString) As String\r\n\r\n    Dim lngPos As Long\r\n    Dim lngEnd As Long\r\n    Dim strVariable As String\r\n    Dim strNew As String\r\n    Dim strValue As String\r\n\r\n    ' Prepare return value\r\n    strNew = strString\r\n\r\n    ' Find pairs of % characters\r\n    Do\r\n        lngPos = InStr(lngPos + 1, strString, \"%\")\r\n        If lngPos = 0 Then\r\n            Exit Do\r\n        Else\r\n            lngEnd = InStr(lngPos + 2, strString, \"%\")\r\n            If lngEnd > 0 Then\r\n                ' Found a pair of delimiters. Check the value\r\n                strVariable = Mid$(strString, lngPos + 1, (lngEnd - lngPos) - 1)\r\n                strValue = Environ$(strVariable)\r\n                If Len(strValue) Then\r\n                    ' Replace with expanded value\r\n                    strNew = Replace(strNew, \"%\" & strVariable & \"%\", strValue)\r\n                End If\r\n            Else\r\n                lngEnd = lngPos\r\n            End If\r\n        End If\r\n        lngPos = lngEnd\r\n    Loop\r\n\r\n    ' Return string with any changes\r\n    ExpandEnvironmentVariables = strNew\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modHash.bas",
    "content": "﻿Attribute VB_Name = \"modHash\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modHash\r\n' Author    : Adam Waller, Erik A, 2019; hecon5, 2021\r\n' Date      : 12/4/2020, 4/9/2020; Revised and adapted Jan. 21, 2021\r\n' Purpose   : Build hashes for content comparison.\r\n'           :\r\n'           : Adapted from: https://stackoverflow.com/questions/61929229/creating-secure-password-hash-in-php-but-checking-access-vba\r\n'           :\r\n'           : Removes dependancy on .NET 3.5 and others for hashing and securing data.\r\n'           : This also has the ancilliary benefit of being able to use OS-level optimizations\r\n'           : and hardware accelerators (if present).\r\n'           :\r\n'           : References: https://docs.microsoft.com/en-us/windows/win32/seccng/cng-algorithm-identifiers\r\n'           : https://docs.microsoft.com/en-us/windows/win32/seccng/cng-portal\r\n'           :\r\n'           : See also: https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Encryption\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\nPrivate Declare PtrSafe Function BCryptOpenAlgorithmProvider Lib \"BCrypt.dll\" ( _\r\n                            ByRef phAlgorithm As LongPtr, _\r\n                            ByVal pszAlgId As LongPtr, _\r\n                            ByVal pszImplementation As LongPtr, _\r\n                            ByVal dwFlags As Long) As Long\r\n\r\nPrivate Declare PtrSafe Function BCryptCloseAlgorithmProvider Lib \"BCrypt.dll\" ( _\r\n                            ByVal hAlgorithm As LongPtr, _\r\n                            ByVal dwFlags As Long) As Long\r\n\r\nPrivate Declare PtrSafe Function BCryptCreateHash Lib \"BCrypt.dll\" ( _\r\n                            ByVal hAlgorithm As LongPtr, _\r\n                            ByRef phHash As LongPtr, pbHashObject As Any, _\r\n                            ByVal cbHashObject As Long, _\r\n                            ByVal pbSecret As LongPtr, _\r\n                            ByVal cbSecret As Long, _\r\n                            ByVal dwFlags As Long) As Long\r\n\r\nPrivate Declare PtrSafe Function BCryptHashData Lib \"BCrypt.dll\" ( _\r\n                            ByVal hHash As LongPtr, _\r\n                            pbInput As Any, _\r\n                            ByVal cbInput As Long, _\r\n                            Optional ByVal dwFlags As Long = 0) As Long\r\n\r\nPrivate Declare PtrSafe Function BCryptFinishHash Lib \"BCrypt.dll\" ( _\r\n                            ByVal hHash As LongPtr, _\r\n                            pbOutput As Any, _\r\n                            ByVal cbOutput As Long, _\r\n                            ByVal dwFlags As Long) As Long\r\n\r\nPrivate Declare PtrSafe Function BCryptDestroyHash Lib \"BCrypt.dll\" (ByVal hHash As LongPtr) As Long\r\n\r\nPrivate Declare PtrSafe Function BCryptGetProperty Lib \"BCrypt.dll\" ( _\r\n                            ByVal hObject As LongPtr, _\r\n                            ByVal pszProperty As LongPtr, _\r\n                            ByRef pbOutput As Any, _\r\n                            ByVal cbOutput As Long, _\r\n                            ByRef pcbResult As Long, _\r\n                            ByVal dfFlags As Long) As Long\r\n\r\nPrivate Const ModuleName As String = \"modHash\"\r\n\r\n\r\nPrivate Function NGHash(pData As LongPtr, lenData As Long, Optional HashingAlgorithm As String = DefaultHashAlgorithm) As Byte()\r\n\r\n    'Erik A, 2019, adapted by Adam Waller\r\n    'Hash data by using the Next Generation Cryptography API\r\n    'Loosely based on https://docs.microsoft.com/en-us/windows/desktop/SecCNG/creating-a-hash-with-cng\r\n    'Allowed algorithms:  https://docs.microsoft.com/en-us/windows/desktop/SecCNG/cng-algorithm-identifiers. Note: only hash algorithms, check OS support\r\n    'Error messages not implemented\r\n    '\r\n    On Error GoTo VBErrHandler\r\n    Dim errorMessage As String\r\n\r\n    Dim hAlg As LongPtr\r\n    Dim algId As String\r\n\r\n    'Open crypto provider\r\n    algId = HashingAlgorithm & vbNullChar\r\n    If BCryptOpenAlgorithmProvider(hAlg, StrPtr(algId), 0, 0) Then GoTo ErrHandler\r\n\r\n    'Determine hash object size, allocate memory\r\n    Dim bHashObject() As Byte\r\n    Dim cmd As String\r\n    cmd = \"ObjectLength\" & vbNullString\r\n    Dim Length As Long\r\n    If BCryptGetProperty(hAlg, StrPtr(cmd), Length, LenB(Length), 0, 0) <> 0 Then GoTo ErrHandler\r\n    ReDim bHashObject(0 To Length - 1)\r\n\r\n    'Determine digest size, allocate memory\r\n    Dim HashLength As Long\r\n    cmd = \"HashDigestLength\" & vbNullChar\r\n    If BCryptGetProperty(hAlg, StrPtr(cmd), HashLength, LenB(HashLength), 0, 0) <> 0 Then GoTo ErrHandler\r\n    Dim bHash() As Byte\r\n    ReDim bHash(0 To HashLength - 1)\r\n\r\n    'Create hash object\r\n    Dim hHash As LongPtr\r\n    If BCryptCreateHash(hAlg, hHash, bHashObject(0), Length, 0, 0, 0) <> 0 Then GoTo ErrHandler\r\n\r\n    'Hash data\r\n    If BCryptHashData(hHash, ByVal pData, lenData) <> 0 Then GoTo ErrHandler\r\n    If BCryptFinishHash(hHash, bHash(0), HashLength, 0) <> 0 Then GoTo ErrHandler\r\n\r\n    'Return result\r\n    NGHash = bHash\r\n\r\nExitHandler:\r\n    'Cleanup\r\n    If hAlg <> 0 Then BCryptCloseAlgorithmProvider hAlg, 0\r\n    If hHash <> 0 Then BCryptDestroyHash hHash\r\n    Exit Function\r\n\r\nVBErrHandler:\r\n    errorMessage = \"VB Error \" & Err.Number & \": \" & Err.Description\r\n\r\nErrHandler:\r\n    CatchAny eelCritical, \"Error hashing! \" & errorMessage & \". Algorithm: \" & HashingAlgorithm, ModuleName & \".NGHash\", True, True\r\n    GoTo ExitHandler\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HashBytes\r\n' Author    : Adam Waller\r\n' Date      : 1/21/2021\r\n' Purpose   : Wrappers for NGHash functions\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function HashBytes(Data() As Byte, Optional HashingAlgorithm As String = DefaultHashAlgorithm) As Byte()\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    HashBytes = NGHash(VarPtr(Data(LBound(Data))), UBound(Data) - LBound(Data) + 1, HashingAlgorithm)\r\n    If Catch(9) Then HashBytes = NGHash(VarPtr(Null), UBound(Data) - LBound(Data) + 1, HashingAlgorithm)\r\n    CatchAny eelCritical, \"Error hashing data!\", ModuleName & \".HashBytes\", True, True\r\nEnd Function\r\n\r\nPrivate Function HashString(str As String, Optional HashingAlgorithm As String = DefaultHashAlgorithm) As Byte()\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    HashString = NGHash(StrPtr(str), Len(str) * 2, HashingAlgorithm)\r\n    If Catch(9) Then HashString = NGHash(StrPtr(vbNullString), Len(str) * 2, HashingAlgorithm)\r\n    CatchAny eelCritical, \"Error hashing string!\", ModuleName & \".HashString\", True, True\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetStringHash\r\n' Author    : Adam Waller\r\n' Date      : 11/30/2020\r\n' Purpose   : Convert string to byte array, and return a hash. Optionally include the\r\n'           : UTF-8 BOM. (Useful when comparing to a file hash)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetStringHash(ByVal strText As String, Optional blnWithBom As Boolean = False) As String\r\n    If blnWithBom Then\r\n        ' Ensure that we are ending the content with a vbCrLf\r\n        ' (To match the behavior of the WriteFile function)\r\n        If Right(strText, 2) <> vbCrLf Then strText = strText & vbCrLf\r\n    End If\r\n    GetStringHash = GetHash(GetUTF8Bytes(strText, blnWithBom))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileHash\r\n' Author    : Adam Waller\r\n' Date      : 11/30/2020\r\n' Purpose   : Return a hash from a file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetFileHash(strPath As String) As String\r\n    GetFileHash = GetHash(GetFileBytes(strPath))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetBytesHash\r\n' Author    : Adam Waller\r\n' Date      : 11/1/2021\r\n' Purpose   : Return hash from byte array\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetBytesHash(bteData() As Byte) As String\r\n    GetBytesHash = GetHash(bteData())\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetDictionaryHash\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2020\r\n' Purpose   : Wrapper to get a hash from a dictionary object (converted to json)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetDictionaryHash(dSource As Dictionary) As String\r\n    GetDictionaryHash = GetStringHash(ConvertToJson(dSource))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetHash\r\n' Author    : Adam Waller\r\n' Date      : 11/30/2020\r\n' Purpose   : Create a hash from the byte array\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetHash(bteContent() As Byte) As String\r\n\r\n    Dim bteHash As Variant\r\n    Dim strHash As String\r\n    Dim intPos As Integer\r\n    Dim intLength As Integer\r\n    Dim strAlgorithm As String\r\n\r\n    ' Get hashing options\r\n    strAlgorithm = Nz2(Options.HashAlgorithm, DefaultHashAlgorithm)\r\n    If Options.UseShortHash Then intLength = 7\r\n\r\n    ' Start performance timer and compute the hash\r\n    Perf.OperationStart \"Compute \" & strAlgorithm\r\n    bteHash = HashBytes(bteContent, strAlgorithm)\r\n\r\n    ' Create string buffer to avoid concatenation\r\n    strHash = Space(LenB(bteHash) * 2)\r\n\r\n    ' Convert full hash to hexidecimal string\r\n    For intPos = 1 To LenB(bteHash)\r\n        Mid$(strHash, (intPos * 2) - 1, 2) = LCase(Right(\"0\" & Hex(AscB(MidB(bteHash, intPos, 1))), 2))\r\n    Next\r\n\r\n    ' Return hash, truncating if needed.\r\n    If intLength > 0 And intLength < Len(strHash) Then\r\n        GetHash = Left$(strHash, intLength)\r\n    Else\r\n        GetHash = strHash\r\n    End If\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetCodeModuleHash\r\n' Author    : Adam Waller\r\n' Date      : 11/30/2020\r\n' Purpose   : Return a hash from the VBA code module behind an object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetCodeModuleHash(intType As eDatabaseComponentType, strName As String) As String\r\n\r\n    Dim strHash As String\r\n    Dim cmpItem As VBComponent\r\n    Dim strPrefix As String\r\n    Dim proj As VBProject\r\n    Dim blnNoCode As Boolean\r\n    Dim strInstancingFlag As String\r\n\r\n    Perf.OperationStart \"Get VBA Hash\"\r\n    Select Case intType\r\n        Case edbForm:   strPrefix = \"Form_\"\r\n        Case edbReport: strPrefix = \"Report_\"\r\n        Case edbModule, edbVbeForm\r\n        Case Else\r\n            ' No code module\r\n            blnNoCode = True\r\n    End Select\r\n\r\n    ' Get the hash from the VBA code module content.\r\n    If Not blnNoCode Then\r\n\r\n        ' Get a reference for the VBProject in the current (not code) database.\r\n        Set proj = CurrentVBProject\r\n\r\n        ' Attempt to locate the object in the VBComponents collection\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        Set cmpItem = proj.VBComponents(strPrefix & strName)\r\n        Catch 9 ' Component not found. (Could be an object with no code module)\r\n        CatchAny eelError, \"Error accessing VBComponent for '\" & strPrefix & strName & \"'\", ModuleName & \".GetCodeModuleHash\"\r\n        If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n        ' Output the hash\r\n        If Not cmpItem Is Nothing Then\r\n            With cmpItem\r\n                ' Check for class module\r\n                If .Type = vbext_ct_ClassModule Then\r\n                    ' Save instancing property as a flag to include with hash\r\n                    strInstancingFlag = CStr(.Properties(\"Instancing\"))\r\n                End If\r\n                ' Generate hash from code and instancing flag (if applicable)\r\n                strHash = GetStringHash(.CodeModule.Lines(1, 999999) & strInstancingFlag)\r\n            End With\r\n        End If\r\n\r\n    End If\r\n\r\n    ' Return hash (if any)\r\n    GetCodeModuleHash = strHash\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetUTF8Bytes\r\n' Author    : Adam Waller\r\n' Date      : 11/2/2021\r\n' Purpose   : Return a UTF-8 (wide) byte array from a string. Optionally include the\r\n'           : UTF-8 BOM. (Useful when comparing to a file hash)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetUTF8Bytes(strText As String, Optional blnWithBom As Boolean = False) As Byte()\r\n\r\n    Dim stmBinary As ADODB.Stream\r\n\r\n    ' Check for empty string\r\n    If (Len(strText) = 0) And Not blnWithBom Then\r\n        GetUTF8Bytes = vbNullString\r\n        Exit Function\r\n    End If\r\n\r\n    ' Set up binary stream\r\n    Set stmBinary = New ADODB.Stream\r\n    stmBinary.Open\r\n    stmBinary.Charset = \"utf-8\"\r\n    stmBinary.Type = adTypeBinary\r\n\r\n    ' Load text into text stream\r\n    With New ADODB.Stream\r\n        .Open\r\n        .Charset = \"utf-8\"\r\n        .Type = adTypeText\r\n        .WriteText strText\r\n        .Position = 0\r\n        ' Copy to binary stream\r\n        .CopyTo stmBinary, adReadAll\r\n        If blnWithBom Then\r\n            ' Include BOM\r\n            stmBinary.Position = 0\r\n        Else\r\n            ' Move past BOM\r\n            stmBinary.Position = 3\r\n        End If\r\n        ' Return binary stream\r\n        GetUTF8Bytes = stmBinary.Read(adReadAll)\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SimpleHash\r\n' Author    : Adam Waller\r\n' Date      : 7/24/2023\r\n' Purpose   : Return a simple SHA256 hash from a file without any Windows API calls.\r\n'           : (This function can be ported to VBScript as a worker process)\r\n'           : Adapted from https://en.wikibooks.org/wiki/Visual_Basic_for_Applications/String_Hashing_in_VBA\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSimpleHash(strText As String) As String\r\n\r\n    Dim objEnc As Object\r\n    Dim objSHA256 As Object\r\n    Dim objDoc As Object\r\n    Dim bteText() As Byte\r\n    Dim bteHash() As Byte\r\n    Dim strHash As String\r\n\r\n    On Error Resume Next\r\n\r\n    ' Create objects\r\n    Set objEnc = CreateObject(\"System.Text.UTF8Encoding\")\r\n    Set objSHA256 = CreateObject(\"System.Security.Cryptography.SHA256Managed\")\r\n\r\n    ' Compute hash\r\n    bteText = objEnc.GetBytes_4(strText)\r\n    bteHash = objSHA256.ComputeHash_2((bteText))\r\n\r\n    ' Convert to hex string\r\n    Set objDoc = CreateObject(\"MSXML2.DOMDocument\")\r\n    objDoc.LoadXML \"<root />\"\r\n    With objDoc.DocumentElement\r\n        .DataType = \"bin.Hex\"\r\n        .nodeTypedValue = bteHash\r\n        strHash = Replace(.Text, vbLf, vbNullString)\r\n    End With\r\n\r\n    ' Return short hash\r\n    GetSimpleHash = Left(strHash, 7)\r\n\r\n    ' Clear any errors\r\n    If Err Then Err.Clear\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modImportExport.bas",
    "content": "﻿Attribute VB_Name = \"modImportExport\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modImportExport\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Main export/import/merge functions for add-in.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\nPrivate Const ModuleName As String = \"modImportExport\"\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportSource\r\n' Author    : Adam Waller\r\n' Date      : 4/24/2020\r\n' Purpose   : Export source files from the currently open database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExportSource(blnFullExport As Boolean, Optional intFilter As eContainerFilter = ecfAllObjects, Optional frmMain As Form_frmVCSMain)\r\n\r\n    Dim dCategories As Dictionary\r\n    Dim colCategories As Collection\r\n    Dim dCategory As Dictionary\r\n    Dim dObjects As Dictionary\r\n    Dim varCatKey As Variant\r\n    Dim varKey As Variant\r\n    Dim cCategory As IDbComponent\r\n    Dim cDbObject As IDbComponent\r\n    Dim lngCount As Long\r\n    Dim strTempFile As String\r\n\r\n    ' Use inline error handling functions to trap and log errors.\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Can't export without an open database\r\n    If Not DatabaseFileOpen Then Exit Sub\r\n\r\n    ' If we are running this from the current database, we need to run it a different\r\n    ' way to prevent file corruption issues. (This really shouldn't happen after v4.02)\r\n    If StrComp(CurrentProject.FullName, CodeProject.FullName, vbTextCompare) = 0 Then\r\n        MsgBox2 T(\"Unabled to Export Running Database\", \"Please launch the export using the add-in menu or ribbon\"), , vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Reload the project options and reset the logs\r\n    Set VCSIndex = Nothing\r\n    Set Options = Nothing\r\n    Options.LoadProjectOptions\r\n    Log.Clear\r\n    Log.OperationType = eotExport\r\n    Log.SourcePath = Options.GetExportFolder\r\n    Log.Active = True\r\n    Perf.StartTiming\r\n\r\n    ' Check error handling mode after loading project options\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' If options (or VCS version) have changed, a full export will be required\r\n    If (VCSIndex.OptionsHash <> Options.GetHash) Then blnFullExport = True\r\n\r\n    ' Display heading\r\n    With Log\r\n        .Spacer\r\n        .Add T(\"Beginning Export of Source Files\"), False\r\n        .Add CurrentProject.Name\r\n        .Add T(\"VCS Version {0}\", var0:=GetVCSVersion)\r\n        .Add T(\"Full Path: {0}\", var0:=CurrentProject.FullName), False\r\n        .Add T(\"Export Folder: {0}\", var0:=Options.GetExportFolder), False\r\n        .Add IIf(blnFullExport, T(\"Performing Full Export\"), T(\"Using Fast Save\"))\r\n        .Add Now\r\n        ' Save the log file path\r\n        If Not frmMain Is Nothing Then frmMain.strLastLogFilePath = .LogFilePath\r\n    End With\r\n\r\n    ' Check VBE Project protection\r\n    If CurrentVBProject.Protection = vbext_pp_locked Then\r\n        MsgBox2 T(\"Project Locked\"), _\r\n            T(\"Project is protected with a password.\"), _\r\n            T(\"Please unlock the project before using this tool.\"), vbExclamation\r\n        Log.Spacer\r\n        Log.Add T(\"Export Canceled\"), , , \"Red\", True\r\n        Log.Flush\r\n        Log.ErrorLevel = eelCritical\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Check project VCS version\r\n    Select Case Options.CompareLoadedVersion\r\n        Case evcNewerVersion\r\n            Log.Flush\r\n            If MsgBox2(T(\"Newer VCS Version Detected\"), _\r\n                T(\"This project uses VCS version {0}, but version {1} is currently installed.\" & _\r\n                    vbNewLine & \"Would you like to continue anyway?\", _\r\n                    var0:=Options.GetLoadedVersion, var1:=GetVCSVersion), _\r\n                T(\"Click YES to continue this operation, or NO to cancel.\"), _\r\n                vbExclamation + vbYesNo + vbDefaultButton2) <> vbYes Then\r\n                    Log.Spacer\r\n                    Log.Add T(\"Export Canceled\"), , , \"Red\", True\r\n                    Log.Flush\r\n                    Log.ErrorLevel = eelCritical\r\n                    Exit Sub\r\n            End If\r\n        Case evcOlderVersion\r\n            Log.Add T(\"Updated VCS ({0} -> {1})\", var0:=Options.GetLoadedVersion, var1:=GetVCSVersion), , , \"blue\"\r\n    End Select\r\n\r\n    ' Perform any needed upgrades to source files\r\n    If blnFullExport Then UpgradeSourceFiles\r\n\r\n    ' Run any custom sub before export\r\n    If Options.RunBeforeExport <> vbNullString Then\r\n        Log.Add T(\"Running {0}...\", var0:=Options.RunBeforeExport)\r\n        Log.Flush\r\n        Perf.OperationStart \"RunBeforeExport\"\r\n        RunSubInCurrentProject Options.RunBeforeExport\r\n        Perf.OperationEnd\r\n    End If\r\n\r\n    ' Close any open database objects.\r\n    If Not CloseDatabaseObjects Then\r\n        MsgBox2 T(\"Please close all database objects\"), _\r\n            T(\"All database objects (i.e.forms, reports, tables, queries, etc...) must be closed to export source code.\"), _\r\n            , vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Export any external database schemas\r\n    ExportSchemas blnFullExport\r\n    If Log.ErrorLevel = eelCritical Then GoTo CleanUp\r\n\r\n    ' Finish header section\r\n    Log.Spacer\r\n    Log.Add IIf(blnFullExport, T(\"Scanning source files...\"), T(\"Scanning for changes...\"))\r\n    Log.Flush\r\n\r\n    ' Set up progress bar to show status on large projects\r\n    Set colCategories = GetContainers(intFilter)\r\n    Log.ProgressBar.Reset\r\n    Log.ProgressBar.Max = GetQuickObjectCount(colCategories) + GetQuickFileCount(colCategories)\r\n\r\n    ' Scan database objects for changes\r\n    Set dCategories = New Dictionary\r\n    VCSIndex.Conflicts.Initialize dCategories, eatExport\r\n    Perf.OperationStart \"Scan DB Objects\"\r\n    For Each cCategory In colCategories\r\n        Perf.CategoryStart cCategory.Category\r\n        Set dCategory = New Dictionary\r\n        dCategory.Add \"Class\", cCategory\r\n        ' Get collection of database objects (IDbComponent classes)\r\n        Set dObjects = cCategory.GetAllFromDB(Not blnFullExport)\r\n        If dObjects.Count = 0 Then\r\n            Log.Add IIf(blnFullExport, _\r\n                T(\"No {0} found in this database.\", var0:=T(LCase(cCategory.Category))), _\r\n                T(\"No modified {0} found in this database.\", var0:=T(LCase(cCategory.Category)))), _\r\n                Options.ShowDebug\r\n        End If\r\n        dCategory.Add \"Objects\", dObjects\r\n        dCategories.Add cCategory.Category, dCategory\r\n        VCSIndex.CheckExportConflicts dObjects\r\n        ' Clear any orphaned files in this category\r\n        ClearOrphanedSourceFiles cCategory\r\n        Perf.CategoryEnd 0\r\n        ' Handle critical error or cancel during scan\r\n        If Log.ErrorLevel = eelCritical Then\r\n            Log.Add vbNullString\r\n            Perf.OperationEnd   ' Scan DB Objects\r\n            GoTo CleanUp\r\n        End If\r\n    Next cCategory\r\n    Perf.OperationEnd\r\n    Log.ProgressBar.Reset\r\n\r\n    ' Check for any conflicts\r\n    With VCSIndex.Conflicts\r\n        If .Count > 0 Then\r\n            ' Show the conflicts resolution dialog\r\n            .ShowDialog\r\n            If .ApproveResolutions Then\r\n                Log.Add T(\"Resolving source conflicts\"), False\r\n                .Resolve\r\n            Else\r\n                ' Cancel export\r\n                Log.Spacer\r\n                Log.Add T(\"Export Canceled\"), , , \"Red\", True\r\n                Log.ErrorLevel = eelCritical\r\n                GoTo CleanUp\r\n            End If\r\n        End If\r\n    End With\r\n\r\n    ' Loop through all categories\r\n    For Each varCatKey In dCategories.Keys\r\n\r\n        ' Get category class and collection of items\r\n        Set dCategory = dCategories(varCatKey)\r\n        Set cCategory = dCategory(\"Class\")\r\n        Set dObjects = dCategory(\"Objects\")\r\n\r\n        ' Only show category details when it contains objects\r\n        lngCount = dObjects.Count\r\n        If lngCount > 0 Then\r\n\r\n            ' Show category header and clear out any orphaned files.\r\n            Log.Spacer Options.ShowDebug\r\n            Log.PadRight T(\"Exporting {0}...\", var0:=T(LCase(cCategory.Category))), , Options.ShowDebug\r\n            Log.ProgMax = lngCount\r\n            Perf.CategoryStart cCategory.Category\r\n\r\n            ' Loop through each object in this category.\r\n            For Each varKey In dObjects.Keys\r\n\r\n                ' Export object\r\n                Set cDbObject = dObjects(varKey)\r\n                Log.Add \"  \" & cDbObject.Name, Options.ShowDebug\r\n\r\n                ' If we have already exported this object while scanning for changes, use that copy.\r\n                strTempFile = Replace(cDbObject.SourceFile, Options.GetExportFolder, VCSIndex.GetTempExportFolder)\r\n                If FSO.FileExists(strTempFile) Then\r\n                    ' Move the temp file(s) over to the source export folder.\r\n                    cDbObject.MoveSource FSO.GetParentFolderName(strTempFile) & PathSep, cDbObject.BaseFolder\r\n                    ' Update the index with the values from the alternate export\r\n                    VCSIndex.UpdateFromAltExport cDbObject\r\n                Else\r\n                    ' Export a fresh copy\r\n                    cDbObject.Export\r\n                End If\r\n\r\n                ' Bail out if we hit a critical error.\r\n                CatchAny eelError, T(\"Error exporting {0}\", var0:=cDbObject.Name), ModuleName & \".ExportSource\", True, True\r\n                If Log.ErrorLevel = eelCritical Then Log.Add vbNullString: GoTo CleanUp\r\n                Log.Increment\r\n\r\n                ' Some kinds of objects are combined into a single export file, such\r\n                ' as database properties. For these, we just need to run the export once.\r\n                If cCategory.SingleFile Then Exit For\r\n\r\n            Next varKey\r\n\r\n            ' Show category wrap-up.\r\n            If Options.ShowDebug Then\r\n                Log.Add T(\"[{0}] {1} processed.\", var0:=lngCount, var1:=T(LCase(cCategory.Category)))\r\n            Else\r\n                Log.Add \"[\" & lngCount & \"]\"\r\n            End If\r\n            'Log.Flush  ' Gives smoother output, but slows down export.\r\n            Perf.CategoryEnd lngCount\r\n        End If\r\n\r\n    Next varCatKey\r\n\r\n    ' Ensure that we have created the .gitignore and .gitattributes files in Git environments.\r\n    CheckGitFiles\r\n\r\n    ' Run any custom sub after export\r\n    If Options.RunAfterExport <> vbNullString Then\r\n        Log.Add T(\"Running {0}...\", var0:=Options.RunAfterExport)\r\n        Perf.OperationStart \"RunAfterExport\"\r\n        RunSubInCurrentProject Options.RunAfterExport\r\n        Perf.OperationEnd\r\n        CatchAny eelError, T(\"Error running {0}\", var0:=Options.RunAfterExport), ModuleName & \".ExportSource\", True, True\r\n    End If\r\n\r\n    ' Show final output and save log\r\n    Log.Spacer\r\n    Log.Add T(\"Done. ({0} seconds)\", var0:=Round(Perf.TotalTime, 2)), , False, \"green\", True\r\n\r\nCleanUp:\r\n\r\n    ' Run any cleanup routines\r\n    VCSIndex.ClearTempExportFolder\r\n    RemoveThemeZipFiles\r\n\r\n    ' Add performance data to log file and save file\r\n    Perf.EndTiming\r\n    With Log\r\n        .Add vbNewLine & Perf.GetReports, False\r\n        .SaveFile\r\n        .Active = False\r\n        .Flush\r\n    End With\r\n\r\n    ' Check for VCS_ImportExport.bas (Used with other forks)\r\n    CheckForLegacyModules\r\n\r\n    ' Restore original fast save option, and save options with project\r\n    Options.SaveOptionsForProject\r\n\r\n    ' Save index file\r\n    With VCSIndex\r\n        .ExportDate = Now\r\n        If blnFullExport Then .FullExportDate = Now\r\n        .OptionsHash = Options.GetHash\r\n        .Save\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportSingleObject\r\n' Author    : Adam Waller\r\n' Date      : 2/22/2023\r\n' Purpose   : Export a single object (such as a selected item)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExportSingleObject(objItem As AccessObject, Optional frmMain As Form_frmVCSMain)\r\n\r\n    Dim dCategories As Dictionary\r\n    Dim dCategory As Dictionary\r\n    Dim dObjects As Dictionary\r\n    Dim cDbObject As IDbComponent\r\n    Dim strTempFile As String\r\n\r\n    ' Guard clause\r\n    If objItem Is Nothing Then Exit Sub\r\n\r\n    ' Use inline error handling functions to trap and log errors.\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Make sure the object is currently closed\r\n    With objItem\r\n        Select Case .Type\r\n            Case acForm, acMacro, acModule, acQuery, acReport, acTable\r\n                If SysCmd(acSysCmdGetObjectState, .Type, .Name) <> adStateClosed Then\r\n                    DoCmd.Close .Type, .Name, acSavePrompt\r\n                End If\r\n        End Select\r\n    End With\r\n\r\n    ' Reload the project options and reset the logs\r\n    Set VCSIndex = Nothing\r\n    Set Options = Nothing\r\n    Options.LoadProjectOptions\r\n    Log.Clear\r\n    Log.OperationType = eotExport\r\n    Log.SourcePath = Options.GetExportFolder\r\n    Log.Active = True\r\n    Perf.StartTiming\r\n\r\n    ' Check error handling mode after loading project options\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Display heading\r\n    With Log\r\n        .Spacer\r\n        .Add T(\"Beginning Export of Single Object\"), False\r\n        .Add CurrentProject.Name\r\n        .Add T(\"VCS Version {0}\", var0:=GetVCSVersion)\r\n        .Add T(\"Full Path: {0}\", var0:=CurrentProject.FullName), False\r\n        .Add T(\"Export Folder: {0}\", var0:=Options.GetExportFolder), False\r\n        .Add Now\r\n        .Spacer\r\n        .Add T(\"Exporting {0}...\", var0:=objItem.Name)\r\n        .Flush\r\n        ' Save export log file path\r\n        If Not frmMain Is Nothing Then frmMain.strLastLogFilePath = .LogFilePath\r\n    End With\r\n\r\n    ' Get a database component class from the item\r\n    Set cDbObject = GetClassFromObject(objItem)\r\n\r\n    ' Check for conflicts\r\n    Set dObjects = New Dictionary\r\n    Set dCategory = New Dictionary\r\n    Set dCategories = New Dictionary\r\n    dObjects.Add cDbObject.SourceFile, cDbObject\r\n    dCategory.Add \"Class\", cDbObject\r\n    dCategory.Add \"Objects\", dObjects\r\n    dCategories.Add cDbObject.Category, dCategory\r\n    VCSIndex.Conflicts.Initialize dCategories, eatExport\r\n    VCSIndex.CheckExportConflicts dObjects\r\n\r\n    ' Resolve any outstanding conflict, or allow user to cancel.\r\n    With VCSIndex.Conflicts\r\n        If .Count > 0 Then\r\n            ' Show the conflicts resolution dialog\r\n            .ShowDialog\r\n            If .ApproveResolutions Then\r\n                Log.Add T(\"Resolving source conflicts\"), False\r\n                .Resolve\r\n            Else\r\n                ' Cancel export\r\n                Log.Spacer\r\n                Log.Add T(\"Export Canceled\"), , , \"Red\", True\r\n                Log.ErrorLevel = eelCritical\r\n                GoTo CleanUp\r\n            End If\r\n        End If\r\n    End With\r\n\r\n    ' Check to see if we still have an item to export.\r\n    If dCategories.Count = 0 Then\r\n        Log.Add T(\"Skipped after conflict resolution.\"), , , \"blue\", True\r\n    Else\r\n        ' If we have already exported this object while scanning for changes, use that copy.\r\n        strTempFile = Replace(cDbObject.SourceFile, Options.GetExportFolder, VCSIndex.GetTempExportFolder)\r\n        If FSO.FileExists(strTempFile) Then\r\n            ' Move the temp file(s) over to the source export folder.\r\n            cDbObject.MoveSource FSO.GetParentFolderName(strTempFile) & PathSep, cDbObject.BaseFolder\r\n            ' Update the index with the values from the alternate export\r\n            VCSIndex.UpdateFromAltExport cDbObject\r\n        Else\r\n            ' Export a fresh copy\r\n            cDbObject.Export\r\n        End If\r\n    End If\r\n\r\n    ' Show final output and save log\r\n    Log.Spacer\r\n    Log.Add T(\"Done. ({0} seconds)\", var0:=Round(Perf.TotalTime, 2)), , False, \"green\", True\r\n\r\nCleanUp:\r\n\r\n    ' Run any cleanup routines\r\n    VCSIndex.ClearTempExportFolder\r\n\r\n    ' Add performance data to log file and save file\r\n    Perf.EndTiming\r\n    With Log\r\n        .Add vbNewLine & Perf.GetReports, False\r\n        .SaveFile\r\n        .Active = False\r\n        .Flush\r\n    End With\r\n\r\n    ' Save index file (don't change export date for single item export)\r\n    VCSIndex.Save\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportMultipleObjects\r\n' Author    : bclothier\r\n' Date      : 4/1/2023\r\n' Purpose   : Export multiple objects, passing a dictionary containing AccessObject.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExportMultipleObjects(objItems As Dictionary, Optional bolForceClose As Boolean = True)\r\n\r\n    Dim frm As Form_frmVCSMain\r\n\r\n    Dim dCategories As Dictionary\r\n    Dim dCategory As Dictionary\r\n    Dim dObjects As Dictionary\r\n    Dim cDbObject As IDbComponent\r\n    Dim objItem As Access.AccessObject\r\n    Dim strTempFile As String\r\n    Dim varKey As Variant\r\n    Dim varCategory As Variant\r\n    Dim varObject As Variant\r\n\r\n    ' Guard clause\r\n    If objItems Is Nothing Then Exit Sub\r\n    If objItems.Count = 0 Then Exit Sub\r\n\r\n    ' Use inline error handling functions to trap and log errors.\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Reset the log file\r\n    Log.Clear\r\n\r\n    ' Use the main form to display progress\r\n    DoCmd.OpenForm \"frmVCSMain\", , , , , acHidden\r\n    Set frm = Form_frmVCSMain   ' Connect to hidden instance\r\n    With frm\r\n\r\n        ' Prepare the UI screen\r\n        .cmdClose.SetFocus\r\n        .HideActionButtons\r\n        DoEvents\r\n        With .txtLog\r\n            .ScrollBars = 0\r\n            .Visible = True\r\n            .SetFocus\r\n        End With\r\n        Log.SetConsole .txtLog, .GetProgressBar\r\n        .strLastLogFilePath = Log.LogFilePath\r\n\r\n        ' Show the status\r\n        .SetStatusText T(\"Running...\"), T(\"Automatically exporting the saved source code\"), _\r\n            T(\"A summary of the export progress can be seen on this screen, and additional details are included in the log file.\")\r\n        .Visible = True\r\n    End With\r\n\r\n    ' Make sure the object is currently closed\r\n    If bolForceClose Then\r\n        For Each varKey In objItems.Keys\r\n            With objItems.Item(varKey)\r\n                Select Case .Type\r\n                    Case acForm, acMacro, acModule, acQuery, acReport, acTable\r\n                        If SysCmd(acSysCmdGetObjectState, .Type, .Name) <> adStateClosed Then\r\n                            DoCmd.Close .Type, .Name, acSavePrompt\r\n                        End If\r\n                End Select\r\n            End With\r\n        Next\r\n    End If\r\n\r\n    ' Reload the project options and reset the logs\r\n    Set VCSIndex = Nothing\r\n    Set Options = Nothing\r\n    Options.LoadProjectOptions\r\n    Log.Clear\r\n    Log.OperationType = eotExport\r\n    Log.SourcePath = Options.GetExportFolder\r\n    Log.Active = True\r\n    Perf.StartTiming\r\n\r\n    ' Check error handling mode after loading project options\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Display heading\r\n    With Log\r\n        .Spacer\r\n        .Add T(\"Beginning Export of Multiple Objects\"), False\r\n        .Add CurrentProject.Name\r\n        .Add T(\"VCS Version {0}\", var0:=GetVCSVersion)\r\n        .Add T(\"Full Path: {0}\", var0:=CurrentProject.FullName), False\r\n        .Add T(\"Export Folder: {0}\", var0:=Options.GetExportFolder), False\r\n        .Add Now\r\n        .Spacer\r\n        .Flush\r\n    End With\r\n\r\n    Set dCategories = New Dictionary\r\n\r\n    For Each varKey In objItems.Keys\r\n        Set objItem = objItems.Item(varKey)\r\n        Log.Add T(\"Exporting {0}...\", var0:=objItem.Name)\r\n        Log.Flush\r\n\r\n        ' FIXME: Hackish, need to figure a clean way of communicating types instead of encoding the key\r\n        Dim lngObjectType As Access.AcObjectType\r\n        On Error Resume Next\r\n        lngObjectType = CLng(Split(varKey, \"|\")(0))\r\n        On Error GoTo 0\r\n        If lngObjectType = acTableDataMacro Then\r\n            Set cDbObject = New clsDbTableDataMacro\r\n        Else\r\n            ' Get a database component class from the item\r\n            Set cDbObject = GetClassFromObject(objItem)\r\n        End If\r\n\r\n        ' Check for conflicts\r\n        If Not dCategories.Exists(cDbObject.Category) Then\r\n            Set dObjects = New Dictionary\r\n            Set dCategory = New Dictionary\r\n\r\n            dObjects.Add cDbObject.SourceFile, cDbObject\r\n            dCategory.Add \"Class\", cDbObject\r\n            dCategory.Add \"Objects\", dObjects\r\n            dCategories.Add cDbObject.Category, dCategory\r\n        Else\r\n            dCategories.Item(cDbObject.Category).Item(\"Objects\").Add cDbObject.SourceFile, cDbObject\r\n        End If\r\n\r\n        VCSIndex.Conflicts.Initialize dCategories, eatExport\r\n        VCSIndex.CheckExportConflicts dObjects\r\n    Next\r\n\r\n    ' Resolve any outstanding conflict, or allow user to cancel.\r\n    With VCSIndex.Conflicts\r\n        If .Count > 0 Then\r\n            ' Show the conflicts resolution dialog\r\n            .ShowDialog\r\n            If .ApproveResolutions Then\r\n                Log.Add T(\"Resolving source conflicts\"), False\r\n                .Resolve\r\n            Else\r\n                ' Cancel export\r\n                Log.Spacer\r\n                Log.Add T(\"Export Canceled\"), , , \"Red\", True\r\n                Log.ErrorLevel = eelCritical\r\n                GoTo CleanUp\r\n            End If\r\n        End If\r\n    End With\r\n\r\n    ' Check to see if we still have an item to export.\r\n    If dCategories.Count = 0 Then\r\n        Log.Add T(\"Skipped after conflict resolution.\"), , , \"blue\", True\r\n    Else\r\n        For Each varCategory In dCategories.Keys\r\n            Set dCategory = dCategories.Item(varCategory)\r\n            Set dObjects = dCategory.Item(\"Objects\")\r\n            For Each varObject In dObjects.Keys\r\n                Set cDbObject = dObjects.Item(varObject)\r\n\r\n                ' If we have already exported this object while scanning for changes, use that copy.\r\n                strTempFile = Replace(cDbObject.SourceFile, Options.GetExportFolder, VCSIndex.GetTempExportFolder)\r\n                If FSO.FileExists(strTempFile) Then\r\n                    ' Move the temp file(s) over to the source export folder.\r\n                    cDbObject.MoveSource FSO.GetParentFolderName(strTempFile) & PathSep, cDbObject.BaseFolder\r\n                    ' Update the index with the values from the alternate export\r\n                    VCSIndex.UpdateFromAltExport cDbObject\r\n                Else\r\n                    ' Export a fresh copy\r\n                    cDbObject.Export\r\n                End If\r\n            Next\r\n        Next\r\n    End If\r\n\r\n    ' Show final output and save log\r\n    Log.Spacer\r\n    Log.Add T(\"Done. ({0} seconds)\", var0:=Round(Perf.TotalTime, 2)), , False, \"green\", True\r\n\r\nCleanUp:\r\n\r\n    ' Run any cleanup routines\r\n    VCSIndex.ClearTempExportFolder\r\n\r\n    ' Add performance data to log file and save file\r\n    Perf.EndTiming\r\n    With Log\r\n        .Add vbNewLine & Perf.GetReports, False\r\n        .SaveFile\r\n        .Active = False\r\n        .Flush\r\n    End With\r\n\r\n    ' Save index file (don't change export date for multiple items export)\r\n    VCSIndex.Save\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportSchemas\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Export any external database schemas configured in options\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExportSchemas(blnFullExport As Boolean)\r\n\r\n    Dim varKey As Variant\r\n    Dim strName As String\r\n    Dim strType As String\r\n    Dim cSchema As IDbSchema\r\n    Dim dParams As Dictionary\r\n    Dim strFile As String\r\n    Dim lngCount As Long\r\n\r\n    ' Skip this section if there are no connections defined.\r\n    If Options.SchemaExports.Count = 0 Then Exit Sub\r\n\r\n    ' Loop through schemas\r\n    Log.Spacer\r\n    Log.Add T(\"Scanning external databases...\")\r\n    Perf.OperationStart \"Scan External Databases\"\r\n    For Each varKey In Options.SchemaExports.Keys\r\n        strName = varKey\r\n\r\n        ' Load parameters for initializing the connection\r\n        Set dParams = GetSchemaInitParams(strName)\r\n        If dParams(\"Enabled\") = False Then\r\n            Log.Add T(\" - {0} - Connection disabled\", var0:=strName), False\r\n        ElseIf dParams(\"Connect\") = vbNullString Then\r\n            Log.Add \" - \" & strName, False\r\n            Log.Add T(\"   No connection string found. (.env)\"), , , \"Red\", , True\r\n            Log.Error eelWarning, T(\"File not found: {0}\", var0:=strFile), ModuleName & \".ExportSchemas\"\r\n            Log.Add T(\"Set the connection string for this external database connection in VCS options to automatically create this file.\"), False\r\n            Log.Add T(\"(This file may contain authentication credentials and should be excluded from version control.)\"), False\r\n        Else\r\n            ' Show server type along with name\r\n            Select Case Options.SchemaExports(varKey)(\"DatabaseType\")\r\n                Case eDatabaseServerType.estMsSql\r\n                    strType = \" (MSSQL)\"\r\n                    Set cSchema = New clsSchemaMsSql\r\n                Case eDatabaseServerType.estMySql\r\n                    strType = \" (MySQL)\"\r\n                    Set cSchema = New clsSchemaMySql\r\n            End Select\r\n            Log.Add \" - \" & strName & strType\r\n            Perf.CategoryStart strName & strType\r\n            Log.Flush\r\n\r\n            ' Export/sync the server objects\r\n            cSchema.Initialize dParams\r\n            cSchema.Export blnFullExport\r\n            lngCount = cSchema.ObjectCount(True)\r\n        End If\r\n        Perf.CategoryEnd lngCount\r\n\r\n        ' Check for error\r\n        If Log.ErrorLevel = eelCritical Then Exit For\r\n    Next varKey\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Build (Full build or Merge Build)\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2020\r\n' Purpose   : Build the project from source files.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Build(strSourceFolder As String _\r\n                , blnFullBuild As Boolean _\r\n                , Optional intFilter As eContainerFilter = ecfAllObjects _\r\n                , Optional strAlternatePath As String)\r\n\r\n    Const FunctionName As String = ModuleName & \".Build\"\r\n\r\n    Dim strPath As String\r\n    Dim strBackup As String\r\n    Dim strCurrentDbFilename As String\r\n    Dim cCategory As IDbComponent\r\n    Dim dCategories As Dictionary\r\n    Dim varCategory As Variant\r\n    Dim dCategory As Dictionary\r\n    Dim dFiles As Dictionary\r\n    Dim varFile As Variant\r\n    Dim strType As String\r\n    Dim blnSuccess As Boolean\r\n\r\n    Dim strText As String   ' Remove later\r\n\r\n    LogUnhandledErrors FunctionName\r\n    On Error Resume Next\r\n\r\n    ' Close the previous cached connections, if any\r\n    CloseCachedConnections\r\n\r\n    ' The type of build will be used in various messages and log entries.\r\n    strType = IIf(blnFullBuild, T(\"Build\"), T(\"Merge\"))\r\n\r\n    ' We need to check the current db name later, so we need to cache it (especially for builds).\r\n    strCurrentDbFilename = CurrentProject.FullName\r\n\r\n    ' Make sure we can find the source files\r\n    If Not FolderHasVcsOptionsFile(strSourceFolder) Then\r\n        MsgBox2 T(\"Source files not found\") _\r\n            , T(\"Required source files were not found in the following folder:\"), strSourceFolder, vbExclamation\r\n        GoTo CleanUp\r\n    End If\r\n\r\n    ' Verify that the source files are being merged into the correct database.\r\n    strPath = GetOriginalDbFullPathFromSource(strSourceFolder)\r\n    If strPath = vbNullString Then\r\n        MsgBox2 T(\"Unable to determine database file name.\") _\r\n            , T(\"Required source files were not found or could not be parsed: \"), strSourceFolder, vbExclamation\r\n        GoTo CleanUp\r\n\r\n    ElseIf strCurrentDbFilename = vbNullString Then\r\n        ' No database currently open. Proceed with build\r\n\r\n    ElseIf StrComp(strPath, strCurrentDbFilename, vbTextCompare) <> 0 Then\r\n        If blnFullBuild Then\r\n            ' Full build allows you to use source file name.\r\n            If Not MsgBox2(T(\"Current Database filename does not match source filename.\"), _\r\n                    T(\"Do you want to {0} to the Source Defined Filename?\" & vbNewLine & vbNewLine & _\r\n                        \"Current: {1}\" & vbNewLine & _\r\n                        \"Source: {2}\", var0:=strType, var1:=strCurrentDbFilename, var2:=strPath), _\r\n                    T(\"[Ok] = Build with Source Configured Name\") & vbNewLine & vbNewLine & _\r\n                        T(\"Otherwise cancel and select 'Build As...' from the ribbon to change build name. \" & _\r\n                        \"Performing an export from this file name will also reset the file name, but will \" & _\r\n                        \"overwrite source. If this file stared as a copy of an existing source controlled \" & _\r\n                        \"database, select 'Build As...' to avoid overwriting.\"), _\r\n                    vbQuestion + vbOKCancel + vbDefaultButton1, _\r\n                    T(\"{0} Name Conflict\", var0:=strType), _\r\n                    vbOK) = vbOK Then\r\n\r\n                ' Launch the GUI form (it was closed a moment ago)\r\n                DoCmd.OpenForm \"frmVCSMain\"\r\n                Form_frmVCSMain.StartBuild blnFullBuild\r\n                Log.Error eelCritical, T(\"{0} aborted. Name mismatch.\", var0:=strType), FunctionName\r\n                GoTo CleanUp\r\n            End If\r\n        Else\r\n            MsgBox2 T(\"Cannot {0} to a different database.\", var0:=strType) _\r\n                , T(\"The database file name for the source files must match the currently open database.\") _\r\n                , T(\"Current: {0}\" & vbNewLine & _\r\n                    \"Source: {1}\", var0:=strCurrentDbFilename, var1:=strPath), vbExclamation _\r\n                , T(\"{0} Name Conflict\", var0:=strType) _\r\n                , vbOK\r\n            GoTo CleanUp\r\n        End If\r\n    End If\r\n\r\n    ' For full builds, close the current database if it is currently open.\r\n    If blnFullBuild Then\r\n        If DatabaseFileOpen Then\r\n            CloseCurrentDatabase2\r\n            If DatabaseFileOpen Then\r\n                MsgBox2 T(\"Unable to Close Database\"), _\r\n                    T(\"The current database must be closed to perform a full build.\"), , vbExclamation\r\n                GoTo CleanUp\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' Load options from project\r\n    Set Options = Nothing\r\n    Options.LoadOptionsFromFile StripSlash(strSourceFolder) & PathSep & \"vcs-options.json\"\r\n    ' Override the export folder when exporting to an alternate path.\r\n    If Len(strAlternatePath) Then Options.ExportFolder = strSourceFolder\r\n\r\n    ' Update VBA debug mode after loading options\r\n    LogUnhandledErrors FunctionName\r\n    On Error Resume Next\r\n\r\n    ' Build original file name for database\r\n    If blnFullBuild Then\r\n        ' Use alternate path if provided, otherwise extract the original database path from the source files.\r\n        strPath = Nz2(strAlternatePath, GetOriginalDbFullPathFromSource(strSourceFolder))\r\n        If strPath = vbNullString Then\r\n            MsgBox2 T(\"Unable to determine database file name\") _\r\n                , T(\"Required source files were not found or could not be parsed:\"), strSourceFolder, vbExclamation\r\n            GoTo CleanUp\r\n        End If\r\n    Else\r\n        ' Run any pre-merge instructions\r\n        strText = dNZ(Options.GitSettings, \"RunBeforeMerge\")\r\n        If strText <> vbNullString Then\r\n            Log.Add T(\"Running {0}...\", var0:=strText)\r\n            Perf.OperationStart \"RunBeforeMerge\"\r\n            RunSubInCurrentProject strText\r\n            Perf.OperationEnd\r\n        End If\r\n\r\n        ' Now, just to make sure all objects are closed and unloaded, we will\r\n        ' close and shift-open the database before merging source files into it.\r\n        Log.Add T(\"Closing and reopening current database before merge...\")\r\n        Perf.OperationStart \"Reopen DB before Merge\"\r\n        CloseCurrentDatabase2\r\n        ShiftOpenDatabase strPath\r\n        Perf.OperationEnd\r\n    End If\r\n\r\n    ' Start log and performance timers\r\n    Log.Clear\r\n    Log.OperationType = IIf(blnFullBuild, eotBuild, eotMerge)\r\n    Log.SourcePath = strSourceFolder\r\n    Log.Active = True\r\n    Perf.StartTiming\r\n\r\n    ' Launch the GUI form\r\n    DoCmd.OpenForm \"frmVCSMain\"\r\n    Form_frmVCSMain.StartBuild blnFullBuild\r\n\r\n    ' Display the build header.\r\n    DoCmd.Hourglass True\r\n    With Log\r\n        .Spacer\r\n        If blnFullBuild Then\r\n            .Add T(\"Beginning build from Source\"), False\r\n        Else\r\n            .Add T(\"Beginning merge from source\"), False\r\n        End If\r\n        .Add FSO.GetFileName(strPath)\r\n        .Add T(\"VCS Version {0}\", var0:=GetVCSVersion)\r\n        .Add T(\"Full Path: {0}\", var0:=strPath), False\r\n        .Add T(\"Export Folder: {0}\", var0:=strSourceFolder), False\r\n        .Add Now\r\n        .Spacer\r\n        .Flush\r\n    End With\r\n\r\n    ' Check project VCS version\r\n    If Options.CompareLoadedVersion = evcNewerVersion Then\r\n        If MsgBox2(T(\"Newer VCS Version Detected\"), _\r\n            T(\"This project uses VCS version {0} but version {1} is currently installed.\" & _\r\n                    vbNewLine & \"Would you like to continue anyway?\" _\r\n                , var0:=Options.GetLoadedVersion, var1:=GetVCSVersion), _\r\n            T(\"Click YES to continue this operation, or NO to cancel.\"), _\r\n            vbExclamation + vbYesNo + vbDefaultButton2) <> vbYes Then\r\n            Log.ErrorLevel = eelCritical\r\n            GoTo CleanUp\r\n        End If\r\n    End If\r\n\r\n    ' Rename original file as a backup\r\n    strBackup = GetBackupFileName(strPath)\r\n    If blnFullBuild Then\r\n        If FSO.FileExists(strPath) Then\r\n            Log.Add T(\"Saving backup of original database...\")\r\n            Name strPath As strBackup\r\n            If CatchAny(eelCritical, T(\"Unable to rename original file\"), FunctionName) Then GoTo CleanUp\r\n            Log.Add T(\"Saved as {0}.\", var0:=FSO.GetFileName(strBackup))\r\n        End If\r\n    Else\r\n        ' Backups for merge builds performed later,\r\n        ' but only if we have changes we are actually merging.\r\n    End If\r\n\r\n    ' Create a new database with the original name\r\n    If blnFullBuild Then\r\n        Perf.OperationStart \"Create new database\"\r\n        If LCase$(FSO.GetExtensionName(strPath)) = \"adp\" Then\r\n            ' ADP project\r\n            Application.NewAccessProject strPath\r\n        Else\r\n            ' Regular Access database\r\n            Application.NewCurrentDatabase strPath, GetFileFormat(strSourceFolder)\r\n        End If\r\n        Perf.OperationEnd\r\n        If DatabaseFileOpen Then\r\n            Log.Add T(\"Created blank database for import. (v{0})\", var0:=CurrentProject.FileFormat)\r\n        Else\r\n            CatchAny eelCritical, T(\"Unable to create database file\"), FunctionName\r\n            Log.Add T(\"This may occur when building an older database version if the \" & _\r\n                \"'New database sort order' (collation) option is not set to 'Legacy'\")\r\n            GoTo CleanUp\r\n        End If\r\n    End If\r\n\r\n    ' Now that we have a new database file, we can load the index.\r\n    Set VCSIndex = Nothing\r\n\r\n    If blnFullBuild Then\r\n        ' Remove any non-built-in references before importing from source.\r\n        Log.Add T(\"Removing non built-in references...\"), False\r\n        RemoveNonBuiltInReferences\r\n\r\n        ' Check for any RunBeforeBuild\r\n        If Options.RunBeforeBuild <> vbNullString Then\r\n            ' Run any pre-build bootstrapping code\r\n            PrepareRunBootstrap\r\n        End If\r\n    End If\r\n\r\n    ' Build collections of files to import/merge\r\n    Log.Add T(\"Scanning source files...\")\r\n    Log.Flush\r\n    Set dCategories = New Dictionary\r\n    VCSIndex.Conflicts.Initialize dCategories, eatImport\r\n    Perf.OperationStart \"Scan Source Files\"\r\n    For Each cCategory In GetContainers(intFilter)\r\n        Set dCategory = New Dictionary\r\n        dCategory.Add \"Class\", cCategory\r\n        ' Get collection of source files\r\n        If blnFullBuild Then\r\n            ' Return all the source files\r\n            dCategory.Add \"Files\", cCategory.GetFileList\r\n        Else\r\n            ' Merge build\r\n            If cCategory.ComponentType = edbTableData Then\r\n                ' Some component types are only imported on full build\r\n                Log.Add T(\"Not merging {0}. (Imported only on full build)\", _\r\n                    var0:=T(LCase(cCategory.Category))), Options.ShowDebug\r\n                dCategory.Add \"Files\", New Dictionary\r\n            Else\r\n                ' Return just the modified source files for merge, including source file paths\r\n                ' representing orphaned objects that no longer exist in the database.\r\n                dCategory.Add \"Files\", VCSIndex.GetModifiedSourceFiles(cCategory)\r\n            End If\r\n        End If\r\n        ' Check count of modified source files.\r\n        If dCategory(\"Files\").Count = 0 Then\r\n            Log.Add T(IIf(blnFullBuild, \"No {0} source files found.\", \"No modified {0} source files found.\"), _\r\n                var0:=T(LCase(cCategory.Category))), Options.ShowDebug\r\n        Else\r\n            dCategories.Add cCategory.Category, dCategory\r\n            ' For merge builds, check for import conflicts or orphaned database objects\r\n            If Not blnFullBuild Then\r\n                ' Record any conflicts for later review\r\n                VCSIndex.CheckMergeConflicts cCategory, dCategory(\"Files\")\r\n            End If\r\n        End If\r\n        ' Check for critical error or cancel\r\n        If Log.ErrorLevel = eelCritical Then\r\n            Log.Add vbNullString\r\n            Perf.OperationEnd\r\n            GoTo CleanUp\r\n        End If\r\n    Next cCategory\r\n    Perf.OperationEnd\r\n\r\n    ' Check for any conflicts\r\n    With VCSIndex.Conflicts\r\n        If .Count > 0 Then\r\n            ' Show the conflicts resolution dialog\r\n            .ShowDialog\r\n            If .ApproveResolutions Then\r\n                Log.Add T(\"Resolving source conflicts\"), False\r\n                .Resolve\r\n            Else\r\n                ' Cancel build/merge\r\n                Log.Spacer\r\n                Log.Add T(\"Build Canceled\")\r\n                Log.ErrorLevel = eelCritical\r\n                GoTo CleanUp\r\n            End If\r\n        End If\r\n    End With\r\n\r\n    ' A merge may not find any changed files\r\n    If dCategories.Count = 0 And Not blnFullBuild Then\r\n        Log.Add T(\"No changes found.\")\r\n    Else\r\n        ' Perform a backup if we have changes to merge\r\n        If Not blnFullBuild Then\r\n            LogUnhandledErrors\r\n            Log.Add T(\"Saving backup of original database...\")\r\n            FSO.CopyFile strPath, strBackup\r\n            If CatchAny(eelCritical, T(\"Unable to back up current database\"), FunctionName) Then GoTo CleanUp\r\n            Log.Add T(\"Saved as {0}.\", var0:=FSO.GetFileName(strBackup))\r\n        End If\r\n        Log.Spacer\r\n    End If\r\n\r\n    ' Loop through all categories\r\n    For Each varCategory In dCategories.Keys\r\n\r\n        ' Set reference to object category class and file list\r\n        Set cCategory = dCategories(varCategory)(\"Class\")\r\n        Set dFiles = dCategories(varCategory)(\"Files\")\r\n\r\n        ' Show category header\r\n        Log.Spacer Options.ShowDebug\r\n        Log.PadRight T(IIf(blnFullBuild, \"Importing {0}...\", \"Merging {0}...\"), _\r\n            var0:=T(LCase(cCategory.Category))), , Options.ShowDebug\r\n        Log.ProgMax = dFiles.Count\r\n        Perf.CategoryStart cCategory.Category\r\n\r\n        ' Loop through each file in this category.\r\n        For Each varFile In dFiles.Keys\r\n            ' Import/merge the file\r\n            Log.Increment\r\n            Log.Add \"  \" & FSO.GetFileName(varFile), Options.ShowDebug\r\n            If blnFullBuild Then\r\n                cCategory.Import CStr(varFile)\r\n            Else\r\n                cCategory.Merge CStr(varFile)\r\n            End If\r\n            CatchAny eelError, T(IIf(blnFullBuild, \"Build error in: {0}\", \"Merge error in: {0}\"), _\r\n                var0:=varFile), FunctionName, True, True\r\n\r\n            ' Bail out if we hit a critical error.\r\n            If Log.ErrorLevel = eelCritical Then Log.Add vbNullString: GoTo CleanUp\r\n\r\n        Next varFile\r\n\r\n        ' Show category wrap-up.\r\n        If Options.ShowDebug Then\r\n            Log.Add T(\"[{0}] {1} processed.\", var0:=dFiles.Count, var1:=T(LCase(cCategory.Category)))\r\n        Else\r\n            Log.Add \"[\" & dFiles.Count & \"]\"\r\n        End If\r\n        Perf.CategoryEnd dFiles.Count\r\n\r\n    Next varCategory\r\n\r\n    ' Check for merge items that might affect other components\r\n    If Not blnFullBuild Then\r\n        ' Check for any object visible in the object navigation pane that might have a description property.\r\n        If ContainerHasAnyObject(dCategories, _\r\n            edbAdpFunction, edbAdpServerView, edbAdpStoredProcedure, edbAdpTable, edbAdpTrigger, _\r\n            edbForm, edbMacro, edbModule, edbQuery, edbReport, edbTableData, edbTableDataMacro, edbTableDef) Then\r\n            ' Merge any changes to the document properties (i.e. description)\r\n            Log.Add T(\"Merging any changed document properties...\"), Options.ShowDebug\r\n            MergeIfChanged edbDocument\r\n        End If\r\n    End If\r\n\r\n    ' Reopen the database so the themes are loaded\r\n    If ContainerHasObject(dCategories, edbTheme) Then\r\n        Log.Add T(\"Reopening database...\")\r\n        Log.Flush\r\n        StageMainForm\r\n        CloseCurrentDatabase2\r\n        ShiftOpenDatabase strPath\r\n        RestoreMainForm\r\n    End If\r\n\r\n    ' Initialize forms to ensure that the colors/themes are rendered properly\r\n    ' (This must be done after all objects are imported, since subforms/subreports\r\n    '  may be involved, and must already exist in the database.)\r\n    If ContainerHasObject(dCategories, edbForm) Then\r\n        Log.Add T(\"Initializing forms...\")\r\n        InitializeForms dCategories\r\n    End If\r\n\r\n    ' Run any post-build/merge instructions\r\n    If blnFullBuild Then\r\n        If Options.RunAfterBuild <> vbNullString Then\r\n            Log.Add T(\"Running {0}...\", var0:=Options.RunAfterBuild)\r\n            Perf.OperationStart \"RunAfterBuild\"\r\n            RunSubInCurrentProject Options.RunAfterBuild\r\n            Perf.OperationEnd\r\n        End If\r\n    Else\r\n        ' Merge build\r\n        If Options.RunAfterMerge <> vbNullString Then\r\n            Log.Add T(\"Running {0}...\", Options.RunAfterMerge)\r\n            Perf.OperationStart \"RunAfterMerge\"\r\n            RunSubInCurrentProject Options.RunAfterMerge\r\n            Perf.OperationEnd\r\n        End If\r\n    End If\r\n\r\n    ' Log any errors after build/merge\r\n    CatchAny eelError, T(\"Error running {0}\", var0:=CallByName(Options, \"RunAfter\" & strType, VbGet)), FunctionName, True, True\r\n\r\n    ' Show final output and save log\r\n    Log.Spacer\r\n    Log.Add T(\"Done. ({0} seconds)\", var0:=Round(Perf.TotalTime, 2)), , False, \"green\", True\r\n    blnSuccess = True\r\n\r\nCleanUp:\r\n\r\n    ' Close the cached connections, if any\r\n    CloseCachedConnections\r\n\r\n    ' Add performance data to log file and save file.\r\n    Perf.EndTiming\r\n    With Log\r\n        .Add vbNewLine & Perf.GetReports, False\r\n        .SaveFile\r\n        .Active = False\r\n    End With\r\n\r\n    ' Show message if build failed\r\n    If Log.ErrorLevel = eelCritical Or Not blnSuccess Then\r\n        Log.Spacer\r\n        Log.Add T(\"Build Failed.\"), , , \"red\", True\r\n        Log.Flush\r\n    End If\r\n\r\n    ' Wrap up build.\r\n    DoCmd.Hourglass False\r\n    If Forms.Count > 0 Then\r\n        ' Finish up on GUI\r\n        Form_frmVCSMain.FinishBuild blnFullBuild, blnSuccess\r\n    Else\r\n        ' Allow navigation pane to refresh list of objects.\r\n        DoEvents\r\n    End If\r\n\r\n    ' Save index file after build is complete, or discard index for \"Build As...\"\r\n    ' discard update if build failed.\r\n    If strAlternatePath = vbNullString And blnSuccess Then\r\n        If blnFullBuild Then\r\n            ' NOTE: Add a couple seconds since some items may still be in the process of saving.\r\n            VCSIndex.FullBuildDate = DateAdd(\"s\", 2, Now)\r\n        Else\r\n            VCSIndex.MergeBuildDate = DateAdd(\"s\", 2, Now)\r\n        End If\r\n        VCSIndex.Save strSourceFolder\r\n    End If\r\n    Set VCSIndex = Nothing\r\n\r\n    ' Show MessageBox if not using GUI for build.\r\n    If Forms.Count = 0 And blnSuccess Then\r\n        ' Show message box when build is complete.\r\n        MsgBox2 T(\"Build Complete for '{0}'\", var0:=CurrentProject.Name), _\r\n            T(\"Note that some settings may not take effect until this database is reopened.\"), _\r\n            T(\"A backup of the previous build was saved as '{0}'.\", var0:=FSO.GetFileName(strBackup)), vbInformation\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadSingleObject\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2023\r\n' Purpose   : Reload a single object from source files.\r\n'           : NOTE: Be very careful to release all references to the object you\r\n'           : are attempting to import.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadSingleObject(cComponentClass As IDbComponent, strName As String, strSourceFilePath As String)\r\n\r\n    Dim dCategories As Dictionary\r\n    Dim dCategory As Dictionary\r\n    Dim dSourceFiles As Dictionary\r\n\r\n    ' Guard clauses\r\n    If cComponentClass Is Nothing Then Exit Sub\r\n    If Not FSO.FileExists(strSourceFilePath) Then Exit Sub\r\n\r\n    ' Use inline error handling functions to trap and log errors.\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Make sure the object is currently closed. (This is really important, since we\r\n    ' will be deleting the object before adding it from source.)\r\n    With cComponentClass\r\n        Select Case .ComponentType\r\n            Case acForm, acMacro, acModule, acQuery, acReport, acTable\r\n                If SysCmd(acSysCmdGetObjectState, .ComponentType, strName) <> adStateClosed Then\r\n                    DoCmd.Close .ComponentType, strName, acSavePrompt\r\n                End If\r\n        End Select\r\n    End With\r\n\r\n    ' Reload the project options and reset the logs\r\n    Set VCSIndex = Nothing\r\n    Set Options = Nothing\r\n    Options.LoadProjectOptions\r\n    Log.Clear\r\n    Log.OperationType = eotMerge\r\n    Log.SourcePath = Options.GetExportFolder\r\n    Log.Active = True\r\n    Perf.StartTiming\r\n\r\n    ' Check error handling mode after loading project options\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Display heading\r\n    With Log\r\n        .Spacer\r\n        .Add T(\"Beginning Import of Single Object\"), False\r\n        .Add CurrentProject.Name\r\n        .Add T(\"VCS Version {0}\", var0:=GetVCSVersion)\r\n        .Add T(\"Full Path: {0}\", var0:=CurrentProject.FullName), False\r\n        .Add T(\"Export Folder: {0}\", var0:=Options.GetExportFolder), False\r\n        .Add Now\r\n        .Spacer\r\n        .Add T(\"Importing {0}...\", var0:=strName)\r\n        .Flush\r\n    End With\r\n\r\n    ' Check for conflicts\r\n    Set dSourceFiles = New Dictionary\r\n    Set dCategory = New Dictionary\r\n    Set dCategories = New Dictionary\r\n    dSourceFiles.Add strSourceFilePath, vbNullString\r\n    dCategory.Add \"Class\", cComponentClass\r\n    dCategory.Add \"Files\", dSourceFiles\r\n    dCategories.Add cComponentClass, dCategory\r\n    VCSIndex.Conflicts.Initialize dCategories, eatImport\r\n    VCSIndex.CheckMergeConflicts cComponentClass, dSourceFiles\r\n\r\n    ' Resolve any outstanding conflict, or allow user to cancel.\r\n    With VCSIndex.Conflicts\r\n        If .Count > 0 Then\r\n            ' Show the conflicts resolution dialog\r\n            .ShowDialog\r\n            If .ApproveResolutions Then\r\n                Log.Add T(\"Resolving source conflicts\"), False\r\n                .Resolve\r\n            Else\r\n                ' Cancel export\r\n                Log.Spacer\r\n                Log.Add T(\"Import Canceled\"), , , \"Red\", True\r\n                Log.ErrorLevel = eelCritical\r\n                GoTo CleanUp\r\n            End If\r\n        End If\r\n    End With\r\n\r\n    ' Check to see if we still have an item to import.\r\n    If dCategories.Count = 0 Then\r\n        Log.Add T(\"Skipped after conflict resolution.\"), , , \"blue\", True\r\n    Else\r\n        ' TODO: Maybe copy the existing object to the recycle bin, just in case\r\n        ' the user makes a mistake. (Similar to how GitHub Desktop works)\r\n\r\n        ' Replace the existing object with the source file\r\n        cComponentClass.Merge strSourceFilePath\r\n    End If\r\n\r\n    ' Show final output and save log\r\n    Log.Spacer\r\n    Log.Add T(\"Done. ({0} seconds)\", var0:=Round(Perf.TotalTime, 2)), , False, \"green\", True\r\n\r\nCleanUp:\r\n\r\n    ' Run any cleanup routines\r\n    VCSIndex.ClearTempExportFolder\r\n\r\n    ' Add performance data to log file and save file\r\n    Perf.EndTiming\r\n    With Log\r\n        .Add vbNewLine & Perf.GetReports, False\r\n        .SaveFile\r\n        .Active = False\r\n        .Flush\r\n    End With\r\n\r\n    ' Save index file (don't change export date for single item export)\r\n    VCSIndex.Save\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergeAllSource\r\n' Author    : Adam Waller\r\n' Date      : 5/16/2023\r\n' Purpose   : Forcibly merge all source files into the current database. This is used\r\n'           : in testing to confirm that we can successfully merge all types of source\r\n'           : files into the database. (Not something an end user would normally use.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MergeAllSource()\r\n\r\n    Dim dCategories As Dictionary\r\n    Dim dCategory As Dictionary\r\n    Dim cCategory As IDbComponent\r\n    Dim varCategory As Variant\r\n    Dim dFiles As Dictionary\r\n    Dim varFile As Variant\r\n\r\n    ' Use inline error handling functions to trap and log errors.\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Make sure all database objects are currently closed (This is really important,\r\n    ' since we will be deleting most objects before importing them from source.)\r\n    CloseDatabaseObjects\r\n\r\n    ' Reload the project options and reset the logs\r\n    Set VCSIndex = Nothing\r\n    Set Options = Nothing\r\n    Options.LoadProjectOptions\r\n    Log.Clear\r\n    Log.OperationType = eotMerge\r\n    Log.SourcePath = Options.GetExportFolder\r\n    Log.Active = True\r\n    Perf.StartTiming\r\n\r\n    ' Check error handling mode after loading project options\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Display heading\r\n    With Log\r\n        .Spacer\r\n        .Add T(\"Beginning Merge of All Source Files\"), False\r\n        .Add CurrentProject.Name\r\n        .Add T(\"VCS Version {0}\", var0:=GetVCSVersion)\r\n        .Add T(\"Full Path: {0}\", var0:=CurrentProject.FullName), False\r\n        .Add T(\"Export Folder: {0}\", var0:=Options.GetExportFolder), False\r\n        .Add Now\r\n        .Spacer\r\n        .Add T(\"Scanning source files...\")\r\n        .Flush\r\n    End With\r\n\r\n    ' Check VBE Project protection\r\n    If CurrentVBProject.Protection = vbext_pp_locked Then\r\n        MsgBox2 T(\"Project Locked\"), _\r\n            T(\"Project is protected with a password.\"), _\r\n            T(\"Please unlock the project before using this tool.\"), vbExclamation\r\n        Log.Spacer\r\n        Log.Add T(\"Merge Canceled\"), , , \"Red\", True\r\n        Log.Flush\r\n        Log.ErrorLevel = eelCritical\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Build collections of files to import/merge\r\n    Set dCategories = New Dictionary\r\n    Perf.OperationStart \"Scan Source Files\"\r\n    For Each cCategory In GetContainers\r\n        Set dCategory = New Dictionary\r\n        dCategory.Add \"Class\", cCategory\r\n        dCategory.Add \"Files\", cCategory.GetFileList\r\n        dCategories.Add cCategory, dCategory\r\n    Next cCategory\r\n    Perf.OperationEnd\r\n\r\n\r\n    ' Loop through all categories\r\n    Log.Spacer\r\n    For Each varCategory In dCategories.Keys\r\n\r\n        ' Set reference to object category class\r\n        Set cCategory = varCategory\r\n        Set dFiles = dCategories(varCategory)(\"Files\")\r\n\r\n        ' Only show category details when source files are found\r\n        If dFiles.Count = 0 Then\r\n            Log.Spacer Options.ShowDebug\r\n            Log.Add T(\"No {0} source files found.\", var0:=LCase(cCategory.Category)), Options.ShowDebug\r\n        Else\r\n            ' Show category header\r\n            Log.Spacer Options.ShowDebug\r\n            Log.PadRight T(\"Merging \") & LCase(cCategory.Category) & \"...\", , Options.ShowDebug\r\n            Log.ProgMax = dFiles.Count\r\n            Perf.CategoryStart cCategory.Category\r\n\r\n            ' Loop through each file in this category.\r\n            For Each varFile In dFiles.Keys\r\n                ' Import/merge the file\r\n                Log.Increment\r\n                Log.Add \"  \" & FSO.GetFileName(varFile), Options.ShowDebug\r\n                cCategory.Merge CStr(varFile)\r\n                CatchAny eelError, T(\"Merge error in: {0}\", var0:=varFile), ModuleName & \".MergeAllSource\", True, True\r\n\r\n                ' Bail out if we hit a critical error.\r\n                If Log.ErrorLevel = eelCritical Then Log.Add vbNullString: GoTo CleanUp\r\n            Next varFile\r\n\r\n            ' Show category wrap-up.\r\n            Log.Add \"[\" & dFiles.Count & \"]\" & IIf(Options.ShowDebug, \" \" & LCase(cCategory.Category) & T(\" processed.\"), vbNullString)\r\n            Perf.CategoryEnd dFiles.Count\r\n        End If\r\n    Next varCategory\r\n\r\n    ' Show final output and save log\r\n    Log.Spacer\r\n    Log.Add T(\"Done. ({0} seconds)\", var0:=Round(Perf.TotalTime, 2)), , False, \"green\", True\r\n\r\nCleanUp:\r\n\r\n    ' Run any cleanup routines\r\n    VCSIndex.ClearTempExportFolder\r\n\r\n    ' Add performance data to log file and save file\r\n    Perf.EndTiming\r\n    With Log\r\n        .Add vbNewLine & Perf.GetReports, False\r\n        .SaveFile\r\n        .Active = False\r\n        .Flush\r\n    End With\r\n\r\n    ' Save index file (don't change export date for single item export)\r\n    VCSIndex.Save\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetBackupFileName\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2020\r\n' Purpose   : Return an unused filename for the database backup befor build\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetBackupFileName(strPath As String) As String\r\n\r\n    Const cstrSuffix As String = \"_VCSBackup\"\r\n\r\n    Dim strFile As String\r\n    Dim intCnt As Integer\r\n    Dim strTest As String\r\n    Dim strBase As String\r\n    Dim strExt As String\r\n    Dim strFolder As String\r\n    Dim strIncrement As String\r\n\r\n    strFolder = FSO.GetParentFolderName(strPath) & PathSep\r\n    strFile = FSO.GetFileName(strPath)\r\n    strBase = FSO.GetBaseName(strFile) & cstrSuffix\r\n    strExt = \".\" & FSO.GetExtensionName(strFile)\r\n\r\n    ' Attempt up to 500 versions of the file name. (i.e. Database_VSBackup45.accdb)\r\n    For intCnt = 1 To 500\r\n        strTest = strFolder & strBase & strIncrement & strExt\r\n        If FSO.FileExists(strTest) Then\r\n            ' Try next number\r\n            strIncrement = CStr(intCnt)\r\n        Else\r\n            ' Return file name\r\n            GetBackupFileName = strTest\r\n            Exit Function\r\n        End If\r\n    Next intCnt\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetFileFormat\r\n' Author    : Adam Waller\r\n' Date      : 5/7/2021\r\n' Purpose   : Return the file format version from the source files, or 0 if not found.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetFileFormat(strSourcePath As String) As Long\r\n\r\n    Dim strPath As String\r\n\r\n    ' Attempt to read the file format version from the CurrentProject export\r\n    strPath = StripSlash(strSourcePath) & PathSep & \"project.json\"\r\n    GetFileFormat = dNZ(ReadJsonFile(strPath), \"Items\\FileFormat\")\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveThemeZipFiles\r\n' Author    : Adam Waller\r\n' Date      : 6/3/2020\r\n' Purpose   : Removes any existing theme zip files. We don't run this inline because\r\n'           : the extraction runs asychrously through the OS and we don't want to slow\r\n'           : down the export waiting for each extraction to complete.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub RemoveThemeZipFiles()\r\n    Dim strFolder As String\r\n    If Options.ExtractThemeFiles Then\r\n        strFolder = Options.GetExportFolder & \"themes\" & PathSep\r\n        If FSO.FolderExists(strFolder) Then ClearFilesByExtension strFolder, \"zip\"\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckForLegacyModules\r\n' Author    : Adam Waller\r\n' Date      : 7/16/2020\r\n' Purpose   : Informs the user if the database contains a legacy module from another\r\n'           : fork of this project. (Some users might not realize that these are not\r\n'           : needed anymore.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub CheckForLegacyModules()\r\n\r\n    ' Check for legacy file\r\n    If Options.ShowVCSLegacy Then\r\n        If FSO.FileExists(Options.GetExportFolder & FSO.BuildPath(\"modules\", \"VCS_ImportExport.bas\")) Then\r\n            MsgBox2 T(\"Legacy Files not Needed\"), _\r\n                T(\"Other forks of the MSAccessVCS project used additional VBA modules to export code.\") & vbNewLine & _\r\n                T(\"This is no longer needed when using the installed Version Control Add-in.\") & vbNewLine & vbNewLine & _\r\n                T(\"Feel free to remove the legacy VCS_* modules from your database project and enjoy\" & vbNewLine & _\r\n                \"a simpler, cleaner code base for ongoing development.  :-)\"), _\r\n                T(\"NOTE: This message can be disabled in 'Options -> Show Legacy Prompt'.\"), _\r\n                vbInformation, T(\"Just a Suggestion...\")\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UpgradeSourceFiles\r\n' Author    : Adam Waller\r\n' Date      : 8/7/2024\r\n' Purpose   : Removes any legacy file formats used by earlier versions of this add-in.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub UpgradeSourceFiles()\r\n\r\n    Dim strBase As String\r\n\r\n    strBase = Options.GetExportFolder\r\n\r\n    ' Remove legacy files by extension\r\n    ClearFilesByExtension strBase & \"sqltables\", \"tdf\"\r\n    ClearFilesByExtension strBase & \"relations\", \"txt\"      ' Relationships (pre-json)\r\n    ClearFilesByExtension strBase & \"report\", \"pv\"          ' Print vars text file (pre-json)\r\n    ClearFilesByExtension strBase & \"tbldefs\", \"LNKD\"       ' Formerly used for linked tables\r\n    ClearFilesByExtension strBase & \"tbldefs\", \"bas\"        ' Moved to XML format\r\n    ClearFilesByExtension strBase & \"tbldefs\", \"tdf\"\r\n\r\n    ' Clear any print settings files if not using this option\r\n    If Not Options.SavePrintVars Then\r\n        ClearFilesByExtension \"forms\", \"json\"\r\n        ClearFilesByExtension \"reports\", \"json\"\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PrepareRunBootstrap\r\n' Author    : Adam Waller\r\n' Date      : 4/21/2021\r\n' Purpose   : Prepares the database to run the RunBeforeBuild code by loading all\r\n'           : GUID references and importing the module specified in RunBeforeBuild.\r\n'           : The bootstrap module (and any other objects) will get replaced from\r\n'           : source during the main build, but this allows any custom functions to\r\n'           : run before the main build, such as copying missing library files into\r\n'           : the same folder as the database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub PrepareRunBootstrap()\r\n\r\n    Dim strModule As String\r\n    Dim strName As String\r\n    Dim varFile As Variant\r\n\r\n    ' Update output since there may be some delays\r\n    Log.Add T(\"Loading bootstrap...\")\r\n    Log.Flush\r\n    Perf.OperationStart \"Bootstrap\"\r\n\r\n    ' Load all GUID references to support early binding in bootstrap sub\r\n    With New clsDbVbeReference\r\n        .ImportReferences .Parent.SourceFile, True\r\n    End With\r\n\r\n    ' Identify and load module for bootstrap code\r\n    strModule = Split(Options.RunBeforeBuild, \".\")(0)\r\n    With New clsDbModule\r\n        With .Parent\r\n            For Each varFile In .GetFileList\r\n                ' Look for matching name\r\n                strName = GetObjectNameFromFileName(CStr(varFile))\r\n                If StrComp(strName, strModule, vbTextCompare) = 0 Then\r\n                    ' This is the module we need to import\r\n                    Log.Add T(\"Importing bootstrap module '{0}'\", var0:=strName), False\r\n                    .Import CStr(varFile)\r\n                    Exit For\r\n                End If\r\n            Next varFile\r\n        End With\r\n    End With\r\n\r\n    ' Make sure we actually have a module before we attempt to run the code\r\n    If CurrentProject.AllModules.Count = 0 Then\r\n        ' Could not find source file\r\n        Log.Error eelError, T(\"Could not find source file for {0}\", var0:=strModule), ModuleName & \".PrepareRunBootstrap\"\r\n    Else\r\n        ' Important: We need to Run Project.Sub not Project.Module.Sub\r\n        strName = Split(Options.RunBeforeBuild, \".\")(1)\r\n\r\n        ' Run any pre-build bootstrapping code\r\n        Log.Add T(\"Running {0}\", var0:=Options.RunBeforeBuild)\r\n        Perf.OperationStart \"RunBeforeBuild\"\r\n        RunSubInCurrentProject strName\r\n        Perf.OperationEnd\r\n    End If\r\n\r\n    ' Now go back and remove all the non built-in references so they come\r\n    ' back in the correct order, just in case a library was at a higher level.\r\n    Log.Add T(\"Removing non built-in references after running bootstrap\"), False\r\n    RemoveNonBuiltInReferences\r\n\r\n    Perf.OperationEnd   ' Bootstrap\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : InitializeForms\r\n' Author    : Adam Waller\r\n' Date      : 7/2/2021\r\n' Purpose   : Opens and closes each form in design view to complete the process of\r\n'           : fully rendering the colors and applying the theme. (This is needed to\r\n'           : provide a consistent output after importing from source.)\r\n'           : Pass this function the dictionary of container of objects being\r\n'           : imported into the database. (All object types)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub InitializeForms(cContainers As Dictionary)\r\n\r\n    Dim cont As IDbComponent\r\n    Dim frm As IDbComponent\r\n    Dim dForms As Dictionary\r\n    Dim cAllForms As IDbComponent\r\n    Dim varKey As Variant\r\n\r\n    ' Trap any errors that may occur when opening forms\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    ' See if we imported any forms\r\n    For Each cont In cContainers\r\n        If cont.ComponentType = edbForm Then\r\n\r\n            ' Loop through the forms in the current database\r\n            Set cAllForms = New clsDbForm\r\n            Set dForms = cAllForms.GetAllFromDB\r\n            Log.ProgMax = dForms.Count\r\n            For Each varKey In dForms.Keys\r\n\r\n                ' See if this form matches one of the files we just imported\r\n                Set frm = dForms(varKey)\r\n                If cContainers(cont)(\"Files\").Exists(frm.SourceFile) Then\r\n\r\n                    ' Don't attempt to initialize add-in main form\r\n                    ' (Likely not needed, and would require staging)\r\n                    If frm.Name <> \"frmVCSMain\" Then\r\n\r\n                        ' Open each form in design view\r\n                        Perf.OperationStart \"Initialize Forms\"\r\n                        DoCmd.OpenForm frm.Name, acDesign, , , , acHidden\r\n                        DoEvents\r\n                        DoCmd.Close acForm, frm.Name, acSaveNo\r\n                        Perf.OperationEnd\r\n                    End If\r\n                    Log.Increment\r\n\r\n                    ' Log any errors\r\n                    CatchAny eelError, T(\"Error while initializing form {0}\", var0:=frm.Name), ModuleName & \".InitializeForms\"\r\n\r\n                    ' Update the index, since the save date has changed, but reuse the code hash\r\n                    ' since we just calculated it after importing the form.\r\n                    With VCSIndex.Item(frm)\r\n                        VCSIndex.Update frm, eatImport, .FileHash, .OtherHash\r\n                    End With\r\n\r\n                End If\r\n            Next varKey\r\n        End If\r\n    Next cont\r\n\r\n    ' Check for any unhandled errors\r\n    CatchAny eelError, \"Unhandled error while initializing forms\", ModuleName & \".InitializeForms\"\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modInstall.bas",
    "content": "﻿Attribute VB_Name = \"modInstall\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modInstall\r\n' Author    : Adam Waller\r\n' Date      : 2/4/2021\r\n' Purpose   : This module contains the logic for installing/updating/removing/deploying\r\n'           : the add-in.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n' Registry hive\r\nPrivate Enum eHive\r\n    ehHKLM\r\n    ehHKCU\r\nEnd Enum\r\n\r\n' Used to determine if Access is running as administrator. (Required for installing the add-in)\r\nPrivate Declare PtrSafe Function IsUserAnAdmin Lib \"shell32\" () As Long\r\n\r\nPrivate Const ModuleName As String = \"modInstall\"\r\n\r\n' Used to add a trusted location for the add-in path (when necessary)\r\nPrivate Const mcstrTrustedLocationName = PROJECT_NAME & \" Version Control\"\r\n\r\n' Use a private type to manage install settings.\r\nPublic Type udtInstallSettings\r\n    blnTrustAddInFolder As Boolean\r\n    blnUseRibbonAddIn As Boolean\r\n    blnOpenAfterInstall As Boolean\r\n    strInstallFolder As String\r\n    blnSettingsLoaded As Boolean\r\nEnd Type\r\nPrivate this As udtInstallSettings\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AutoRun\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : This code runs when the add-in file is opened directly. It provides the\r\n'           : user an easy way to update the add-in on their system.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AutoRun() As Boolean\r\n\r\n    ' See if the we are opening the file from the installed location.\r\n    If CodeProject.FullName = GetAddInFileName Then\r\n\r\n        ' Opening the file from add-in location, which would normally be unusual unless we are trying to remove\r\n        ' legacy registry entries, or to trust the file after install.\r\n        If IsUserAnAdmin = 1 Then RemoveLegacyInstall\r\n\r\n        ' Adding a message box to here to autoclose the addin once the prompt is cleared.\r\n        ' This handles the last step of the install for users that just installed the file.\r\n        ' Since no code will run until the \"Trust Document/Enable\" is completed, this allows for the trust\r\n        ' process to complete then close itself (if desired).\r\n\r\n        ' For users that need to open the add-in file to trust it, show the confirmation\r\n        ' message that the add-in has been installed successfully.\r\n        MsgBox2 \"Installation Complete!\", _\r\n            \"You did it! Add-in version \" & AppVersion & \" is now installed.\", _\r\n            \"Please reopen any instances of Microsoft Access before using the add-in.\" & vbCrLf & _\r\n            \"This instance of Microsoft Access will now close.\", vbInformation\r\n        DoCmd.Quit\r\n\r\n    Else\r\n        ' Could be running it from another location, such as after downloading\r\n        ' an updated version of the addin, or building from source.\r\n        VerifyResources\r\n\r\n        ' Open installer form\r\n        Form_frmVCSInstall.Visible = True\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : InstallVCSAddin\r\n' Author    : Adam Waller\r\n' Date      : 10/19/2020\r\n' Purpose   : Installs/updates the add-in for the current user.\r\n'           : Returns true if successful.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub InstallVCSAddin(blnTrustFolder As Boolean, blnUseRibbon As Boolean, blnOpenAfterInstall As Boolean, strInstallFolder As String, _\r\n            Optional ByVal blnCreateCompiledVersion As Boolean = False)\r\n\r\n    Const OPEN_MODE_OPTION As String = \"Default Open Mode for Databases\"\r\n\r\n    Dim strSource As String\r\n    Dim strDest As String\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Load install settings from registry, then update with parameter values\r\n    GetInstallSettings\r\n    With this\r\n        .blnUseRibbonAddIn = blnUseRibbon\r\n        .blnOpenAfterInstall = blnOpenAfterInstall\r\n        .blnTrustAddInFolder = blnTrustFolder\r\n        If .strInstallFolder <> strInstallFolder Then\r\n            ' Attempt to migrate any saved user settings files\r\n            MigrateUserFiles strInstallFolder, GetFilePathsInFolder(.strInstallFolder)\r\n            ' Update install folder to new path\r\n            .strInstallFolder = strInstallFolder\r\n        End If\r\n   End With\r\n\r\n    ' Save the updated settings to the registry.\r\n    SaveInstallSettings\r\n\r\n    ' Load some path values\r\n    strSource = CodeProject.FullName\r\n    strDest = GetAddInFileName\r\n    VerifyPath strDest\r\n\r\n    ' We can't replace a file with itself.  :-)\r\n    If strSource = strDest Then\r\n        MsgBox2 \"Unable to Install\", \"You can't install the add-in over itself.\", _\r\n            \"Please run from a different location to update.\", , vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Check default database open mode.\r\n    If Application.GetOption(OPEN_MODE_OPTION) = 1 Then\r\n        If MsgBox2(\"Default Open Mode set to Exclusive\", _\r\n            \"The default open mode option for Microsoft Access is currently set to open databases in Exclusive mode by default. \" & vbCrLf & _\r\n            \"This add-in needs to be opened in shared mode in order to install successfully.\", _\r\n            \"Change the default open mode to 'Shared'?\", vbYesNo + vbExclamation) = vbYes Then\r\n            Application.SetOption OPEN_MODE_OPTION, 0\r\n            MsgBox2 \"Default Option Changed\", _\r\n                \"Please restart Microsoft Access and run the install again.\", , vbInformation\r\n        End If\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Run any applicable upgrades\r\n    RunUpgrades\r\n\r\n    ' Verify the trusted location\r\n    If this.blnTrustAddInFolder Then VerifyTrustedLocation\r\n\r\n    ' Copy the add-in file\r\n    If Not UpdateAddInFile(blnCreateCompiledVersion) Then Exit Sub\r\n\r\n    ' Install the ribbon\r\n    If this.blnUseRibbonAddIn Then\r\n        ' Ensure that the ribbon is installed\r\n        modCOMAddIn.VerifyComAddIn\r\n    Else\r\n        ' Remove if currently installed\r\n        modCOMAddIn.UninstallComAddIn\r\n    End If\r\n\r\n    ' Register the Menu controls\r\n    RegisterMenuItem \"&VCS Open\", \"=AddInMenuItemLaunch()\"\r\n    RegisterMenuItem \"&VCS Options\", \"=AddInOptionsLaunch()\"\r\n    RegisterMenuItem \"&VCS Export All Source\", \"=AddInMenuItemExport()\"\r\n\r\n    ' Update installed version number\r\n    InstalledVersion = AppVersion\r\n\r\n    ' Warn the user if ActiveX is disabled\r\n    VerifyActivexNotDisabled\r\n\r\n    ' Show install confirmation message\r\n    MsgBox2 \"Success!\", \"Version Control System add-in has been updated to \" & AppVersion & \".\", _\r\n        \"The installer will now close. Please restart any open instances\" & vbCrLf & _\r\n        \"of Microsoft Access before using the add-in.\", vbInformation, \"Version Control Add-in\"\r\n\r\n    ' Open add-in from installed location if required.\r\n    If this.blnOpenAfterInstall Then OpenAddinFile GetAddInFileName, CodeProject.FullName\r\n\r\n    ' Close Access after installation is complete.\r\n    DoCmd.Quit\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UninstallVCSAddin\r\n' Author    : Adam Kauffman\r\n' Date      : 5/27/2020\r\n' Purpose   : Removes the add-in for the current user.\r\n'           : Returns true if successful.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub UninstallVCSAddin()\r\n\r\n    Dim intResponse As VbMsgBoxResult\r\n    Dim blnSaveSettings As Boolean\r\n\r\n    ' Ask the user if they want to preserve their user settings.\r\n    intResponse = MsgBox2(\"Save User Settings\", \"Would you like your user settings/options preserved?\", _\r\n        \"Click YES to save these items so they can be used if you reinstall the add-in,\" & vbCrLf & _\r\n        \"Or click NO to remove all settings related to this add-on.\", vbQuestion + vbYesNoCancel)\r\n\r\n    ' Allow user to cancel if they are not sure how to answer the above prompt.\r\n    If intResponse = vbCancel Then Exit Sub\r\n\r\n    ' Note if the user wants to save/migrate their existing settings.\r\n    If intResponse = vbYes Then blnSaveSettings = True\r\n\r\n    ' Close all database objects\r\n    If IsLoaded(acForm, \"frmVCSOptions\") Then DoCmd.Close acForm, \"frmVCSOptions\"\r\n    If IsLoaded(acForm, \"frmVCSMain\") Then DoCmd.Close acForm, \"frmVCSMain\"\r\n\r\n    ' Remove the add-in Menu controls\r\n    RemoveMenuItem \"&VCS Open\"\r\n    RemoveMenuItem \"&VCS Options\"\r\n    RemoveMenuItem \"&VCS Export All Source\"\r\n\r\n    ' Remove any legacy menu items.\r\n    RemoveMenuItem \"&Version Control\"\r\n    RemoveMenuItem \"&Version Control Options\"\r\n    RemoveMenuItem \"&Export All Source\"\r\n\r\n    ' Remove registry entries\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    If blnSaveSettings Then\r\n        ' Delete keys that don't contain settings\r\n        DeleteSetting PROJECT_NAME, \"Build\"\r\n        DeleteSetting PROJECT_NAME, \"Add-In\"\r\n        DeleteSetting PROJECT_NAME, \"Timer\"\r\n    Else\r\n        ' Remove entire application key\r\n        DeleteSetting PROJECT_NAME\r\n    End If\r\n\r\n    ' Resume normal error handling\r\n    If DebugMode(False) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Remove trusted location added by this add-in. (if found)\r\n    RemoveTrustedLocation\r\n\r\n    ' Remove COM add-in\r\n    modCOMAddIn.UninstallComAddIn\r\n\r\n    ' Remove On Save hook\r\n    'modExportOnSaveHook.Uninstall\r\n\r\n    ' Notify the user of the completion of the uninstall process.\r\n    MsgBox2 \"Success!\", \"Version Control System has now been uninstalled.\", _\r\n        \"Microsoft Access will be closed to remove the remaining files.\", _\r\n        vbInformation, \"Version Control Add-in\"\r\n\r\n    ' Use the worker script to actually remove the add-in files.\r\n    ' (They cannot be removed when they are in use, such as when procesing the uninstall.)\r\n    Worker.Run_UninstallAddin\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : UpdateAddInFile\r\n' Author    : Adam Waller\r\n' Date      : 5/22/2023\r\n' Purpose   : Update the add-in database file. Return true if successful.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function UpdateAddInFile(ByVal blnCreateCompiledVersion As Boolean) As Boolean\r\n\r\n    ' Make sure the destination folder exists\r\n    VerifyPath GetAddInFileName\r\n\r\n    ' Update the file\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    If FSO.FileExists(GetAddInFileName) Then DeleteFile GetAddInFileName, True\r\n\r\n    If blnCreateCompiledVersion Then\r\n        CreateAccde CodeProject.FullName, GetAddInFileName\r\n    Else\r\n        FSO.CopyFile CodeProject.FullName, GetAddInFileName, True\r\n    End If\r\n\r\n    If Err Then\r\n        MsgBox2 \"Unable to Update File\", _\r\n            \"Encountered error \" & Err.Number & \": \" & Err.Description & \" when copying file.\", _\r\n            \"Is the Version Control Add-in loaded in another instance of Microsoft Access?\" & vbCrLf & _\r\n            \"Please check to be sure that the following file is not in use:\" & _\r\n            vbCrLf & GetAddInFileName, vbExclamation\r\n        Err.Clear\r\n    Else\r\n        ' Copied file with no errors.\r\n        UpdateAddInFile = True\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CreateAccde\r\n' Author    : Josef Poetzl\r\n' Date      : 2/21/2025\r\n' Purpose   : Create a compiled Access file\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub CreateAccde(ByVal strSourceFilePath As String, ByVal strDestFilePath As String)\r\n\r\n    Const acSysCmdCompile As Long = 603 ' Added in later versions of Access\r\n    Dim strFileToCompile As String\r\n\r\n    strFileToCompile = strDestFilePath & \".accdb\"\r\n    FSO.CopyFile strSourceFilePath, strFileToCompile, True\r\n\r\n    ' use new Access instance to create accde\r\n    With New Access.Application\r\n        .Visible = True\r\n        .SysCmd acSysCmdCompile, (strFileToCompile), (strDestFilePath)\r\n    End With\r\n\r\n    FSO.DeleteFile strFileToCompile, True\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MigrateUserFiles\r\n' Author    : Adam Waller\r\n' Date      : 5/22/2023\r\n' Purpose   : Migrate a file, unless a destination file exists that is different from\r\n'           : the source file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub MigrateUserFiles(strToFolder As String, colNames As Dictionary)\r\n\r\n    Dim varKey As Variant\r\n    Dim strFile As String\r\n    Dim strSource As String\r\n    Dim strDest As String\r\n\r\n    ' Loop through file names\r\n    For Each varKey In colNames.Keys\r\n        strSource = varKey\r\n        strFile = FSO.GetFileName(strSource)\r\n        strDest = BuildPath2(strToFolder, strFile)\r\n        Select Case True\r\n            ' Define exceptions to skip\r\n            Case strFile Like PROJECT_NAME & \".*accda\"  ' Add-in or lock file\r\n            Case strFile Like \"*.dll\"   ' COM dlls\r\n            Case strFile Like \"*.vbs\"   ' Worker script\r\n            Case Else\r\n                ' Migrate other files\r\n                If FSO.FileExists(strSource) Then\r\n                    If FSO.FileExists(strDest) Then\r\n                        ' Check hash of file content\r\n                        If GetFileHash(strSource) = GetFileHash(strDest) Then\r\n                            ' File is identical in content. Remove source file.\r\n                            DeleteFile strSource\r\n                        Else\r\n                            ' Leave existing file if they don't match.\r\n                        End If\r\n                    Else\r\n                        ' If destination file does not exist, move from source.\r\n                        FSO.MoveFile strSource, strDest\r\n                    End If\r\n                End If\r\n        End Select\r\n    Next varKey\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAddinFileName\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : This is where the add-in would be installed.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetAddInFileName() As String\r\n    GetAddInFileName = FSO.BuildPath(GetInstallSettings.strInstallFolder, CodeProject.Name)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : DefaultAddInFolderPath\r\n' Author    : Adam Waller\r\n' Date      : 5/22/2023\r\n' Purpose   : Returns the default installation folder path.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function DefaultAddInFolderPath() As String\r\n    DefaultAddInFolderPath = BuildPath2(Environ$(\"AppData\"), PROJECT_NAME)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddinLoaded\r\n' Author    : Adam Waller\r\n' Date      : 11/10/2020\r\n' Purpose   : Returns true if the VCS add-in is currently loaded as a VBE Project.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function AddinLoaded() As Boolean\r\n    AddinLoaded = Not GetAddInProject Is Nothing\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAddinRegPath\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : Return the registry path to the addin menu items\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetAddinRegPath(Optional Hive As eHive = ehHKCU) As String\r\n\r\n    Dim strHive As String\r\n\r\n    Select Case Hive\r\n        Case ehHKCU: strHive = \"HKCU\\\"\r\n        Case ehHKLM: strHive = \"HKLM\\\"\r\n    End Select\r\n\r\n    GetAddinRegPath = strHive & \"SOFTWARE\\Microsoft\\Office\\\" & _\r\n            Application.Version & \"\\Access\\Menu Add-Ins\\\"\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RegisterMenuItem\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : Add the menu item through the registry (Normally HKCU hive)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RegisterMenuItem(ByVal strName As String, Optional ByVal strFunction As String = \"=LaunchMe()\")\r\n\r\n    Dim strPath As String\r\n\r\n    ' We need to create/update three registry keys for each item.\r\n    strPath = GetAddinRegPath & strName & \"\\\"\r\n    With New IWshRuntimeLibrary.WshShell\r\n        .RegWrite strPath & \"Expression\", strFunction, \"REG_SZ\"\r\n        .RegWrite strPath & \"Library\", GetAddInFileName, \"REG_SZ\"\r\n        .RegWrite strPath & \"Version\", 3, \"REG_DWORD\"\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveMenuItem\r\n' Author    : Adam Kauffman\r\n' Date      : 5/27/2020\r\n' Purpose   : Remove the menu item through the registry\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RemoveMenuItem(ByVal strName As String, Optional Hive As eHive = ehHKCU)\r\n\r\n    Dim strPath As String\r\n\r\n    ' We need to remove three registry keys for each item.\r\n    strPath = GetAddinRegPath(Hive) & strName & \"\\\"\r\n    With New IWshRuntimeLibrary.WshShell\r\n        ' Just in case someone changed some of the keys...\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        .RegDelete strPath & \"Expression\"\r\n        .RegDelete strPath & \"Library\"\r\n        .RegDelete strPath & \"Version\"\r\n        .RegDelete strPath\r\n        On Error GoTo 0\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RelaunchAsAdmin\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : Launch the addin file with admin privileges so the user can uninstall it.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RelaunchAsAdmin()\r\n    ShellEx FSO.BuildPath(SysCmd(acSysCmdAccessDir), \"msaccess.exe\"), \"\"\"\" & GetAddInFileName & \"\"\"\", \"runas\"\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Deploy\r\n' Author    : Adam Waller\r\n' Date      : 4/21/2020\r\n' Purpose   : Increments the build version and updates the project description.\r\n'           : This can be run from the debug window when making updates to the project.\r\n'           : (More significant updates to the version number can be made using the\r\n'           :  `AppVersion` property defined below.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub Deploy(Optional ReleaseType As eReleaseType = Same_Version)\r\n\r\n    Const cstrSpacer As String = \"--------------------------------------------------------------\"\r\n\r\n    Dim strBinaryFile As String\r\n\r\n    If Not IsCompiled Then\r\n        MsgBox2 \"Please Compile and Save Project\", _\r\n            \"The project needs to be compiled and saved before deploying.\", _\r\n            \"I would do this for you, but it seems to cause memory heap corruption\" & vbCrLf & _\r\n            \"when this is run via VBA code during the deployment process.\" & vbCrLf & _\r\n            \"(This can be fixed by rebuilding from source.)\", vbInformation\r\n        Exit Sub\r\n        ' Save all code modules\r\n        'DoCmd.RunCommand acCmdCompileAndSaveAllModules\r\n    End If\r\n\r\n    If AddinLoaded Then\r\n        MsgBox2 \"Add-in Currently Loaded\", _\r\n            \"The add-in file cannot be updated when it is currently in use.\", _\r\n            \"Please close Microsoft Access and open this file again to deploy.\", vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Make sure we don't run ths function while it is loaded in another project.\r\n    If CodeProject.FullName <> CurrentProject.FullName Then\r\n        Debug.Print \"This can only be run from a top-level project.\"\r\n        Debug.Print \"Please open \" & CodeProject.FullName & \" and try again.\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Increment build number\r\n    IncrementAppVersion ReleaseType\r\n\r\n    ' List project and new build number\r\n    Debug.Print cstrSpacer\r\n\r\n    ' Update project description\r\n    VBE.ActiveVBProject.Description = \"Version \" & AppVersion & \" deployed on \" & Date\r\n\r\n    ' Save copy to zip folder\r\n    strBinaryFile = FSO.BuildPath(CodeProject.Path, \"Version_Control_v\" & AppVersion & \".zip\")\r\n    If FSO.FileExists(strBinaryFile) Then DeleteFile strBinaryFile, True\r\n    CreateZipFile strBinaryFile\r\n    CopyFileToZip CodeProject.FullName, strBinaryFile\r\n\r\n    ' Deploy latest version on this machine\r\n    If Not UpdateAddInFile(False) Then Exit Sub\r\n\r\n    ' Use the newly installed add-in to Export the project to version control.\r\n    modAPI.HandleRibbonCommand \"btnExport\"\r\n\r\n    ' Finish with success message if the latest version was installed.\r\n    Debug.Print \"Version \" & AppVersion & \" installed.\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RunUpgrades\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2020\r\n' Purpose   : Process upgrade transitions and remove legacy components\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RunUpgrades()\r\n\r\n    Dim strName As String\r\n    Dim strOldPath As String\r\n    Dim strNewPath As String\r\n    Dim strTest As String\r\n    Dim objShell As IWshRuntimeLibrary.WshShell\r\n\r\n    If DebugMode(True) Then On Error GoTo 0 Else On Error Resume Next\r\n\r\n    ' Legacy HKLM install\r\n    If InstalledVersion < \"3.2.0\" Then\r\n        ' Check for installation in HKLM hive.\r\n        strOldPath = GetAddinRegPath(ehHKLM) & \"&Version Control\\Library\"\r\n        Set objShell = New IWshRuntimeLibrary.WshShell\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        strTest = objShell.RegRead(strOldPath)\r\n        If Err Then Err.Clear\r\n        On Error GoTo 0\r\n        If strTest <> vbNullString Then\r\n            If MsgBox2(\"Remove Legacy Version?\", \"Way back in the old days, this install required admin rights \" & _\r\n                \"and added some keys to the HKLM registry. We don't need those anymore \" & _\r\n                \"because the add-in is now installed for the current user with no special \" & _\r\n                \"privileges required.\" _\r\n                , \"Can we go ahead and clean those up now? (Requires admin to remove the registry keys.)\" _\r\n                , vbQuestion + vbYesNo) = vbYes Then\r\n                RelaunchAsAdmin\r\n            End If\r\n        End If\r\n    End If\r\n\r\n    ' Install in Microsoft\\AddIns\\ folder\r\n    If InstalledVersion < \"3.3.0\" Then\r\n\r\n        ' Check for install in AddIns folder (before we used the dedicated install folder)\r\n        strOldPath = BuildPath2(Environ$(\"AppData\"), \"Microsoft\", \"AddIns\", CodeProject.Name)\r\n\r\n        ' Remove add-in from legacy location\r\n        If FSO.FileExists(strOldPath) Then DeleteFile strOldPath\r\n\r\n        ' Migrate settings json file to new location\r\n        strOldPath = Replace(strOldPath, \".accda\", \".json\", , , vbTextCompare)\r\n        If FSO.FileExists(strOldPath) Then\r\n            ' Check for settings file in new location\r\n            strNewPath = Replace(GetAddInFileName, \".accda\", \".json\", , , vbTextCompare)\r\n            If FSO.FileExists(strNewPath) Then\r\n                ' Leave new settings file, and delete old one.\r\n                DeleteFile strOldPath\r\n            Else\r\n                ' Move settings to new location\r\n                VerifyPath strNewPath\r\n                FSO.MoveFile strOldPath, strNewPath\r\n            End If\r\n        End If\r\n\r\n        ' Remove any Legacy Menu controls\r\n        RemoveMenuItem \"&Version Control\"\r\n        RemoveMenuItem \"&Version Control Options\"\r\n        RemoveMenuItem \"&Export All Source\"\r\n\r\n        ' Remove custom trusted location for Office AddIns folder.\r\n        strName = \"Office Add-ins\"\r\n        If HasTrustedLocationKey(strName) Then RemoveTrustedLocation strName\r\n    End If\r\n\r\n    ' Use standardized options folder (5/7/2021)\r\n    strOldPath = FSO.BuildPath(CodeProject.Path, FSO.GetBaseName(CodeProject.Name)) & \".json\"\r\n    strNewPath = FSO.BuildPath(CodeProject.Path, \"vcs-options.json\")\r\n    If FSO.FileExists(strOldPath) Then\r\n        If FSO.FileExists(strNewPath) Then\r\n            ' Remove leftover legacy file\r\n            DeleteFile strOldPath\r\n        Else\r\n            ' Rename to new name\r\n            Name strOldPath As strNewPath\r\n        End If\r\n    End If\r\n\r\n    ' Handle any uncaught errors\r\n    CatchAny eelError, \"Running upgrades before install\", ModuleName & \".RunUpgrades\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveLegacyInstall\r\n' Author    : Adam Waller\r\n' Date      : 5/27/2020\r\n' Purpose   : Remove the installation that required admin rights in favor of the\r\n'           : per-user installation method.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub RemoveLegacyInstall()\r\n\r\n    ' These registry keys require admin access to remove\r\n    RemoveMenuItem \"&Version Control\", ehHKLM\r\n    RemoveMenuItem \"&Export All Source\", ehHKLM\r\n\r\n    MsgBox2 \"Legacy Items Removed\", \"Thanks for getting those cleaned up!\" _\r\n        , \"Microsoft Access will now close so you can continue.\", vbInformation\r\n    DoCmd.Quit\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetInstallSettings\r\n' Author    : Adam Waller\r\n' Date      : 5/22/2023\r\n' Purpose   : Return the install settings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetInstallSettings(Optional blnUseCache As Boolean = True) As udtInstallSettings\r\n\r\n    ' Load install settings from registry\r\n    With this\r\n        If Not (.blnSettingsLoaded And blnUseCache) Then\r\n            .blnTrustAddInFolder = GetSetting(PROJECT_NAME, \"Install\", \"Trust Folder\", CInt(True))\r\n            .blnUseRibbonAddIn = GetSetting(PROJECT_NAME, \"Install\", \"Use Ribbon\", True)\r\n            .blnOpenAfterInstall = GetSetting(PROJECT_NAME, \"Install\", \"Open File\", CInt(False))\r\n            .strInstallFolder = GetSetting(PROJECT_NAME, \"Install\", \"Install Folder\", DefaultAddInFolderPath)\r\n            .blnSettingsLoaded = True\r\n        End If\r\n    End With\r\n    GetInstallSettings = this\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveInstallSettings\r\n' Author    : Adam Waller\r\n' Date      : 5/22/2023\r\n' Purpose   : Saves current install settings to the registry.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function SaveInstallSettings()\r\n    With this\r\n        ' Basic settings\r\n        SaveSetting PROJECT_NAME, \"Install\", \"Trust Folder\", CInt(.blnTrustAddInFolder)\r\n        SaveSetting PROJECT_NAME, \"Install\", \"Use Ribbon\", CInt(.blnUseRibbonAddIn)\r\n        SaveSetting PROJECT_NAME, \"Install\", \"Open File\", CInt(.blnOpenAfterInstall)\r\n        ' Special handling\r\n        If .strInstallFolder = DefaultAddInFolderPath Then\r\n            ' This value should only be saved if using a non-standard path.\r\n            If GetSetting(PROJECT_NAME, \"Install\", \"Install Folder\") <> vbNullString Then\r\n                ' Remove custom folder path setting when it matches the default.\r\n                DeleteSetting PROJECT_NAME, \"Install\", \"Install Folder\"\r\n            End If\r\n        Else\r\n            ' Save the custom path\r\n            SaveSetting PROJECT_NAME, \"Install\", \"Install Folder\", .strInstallFolder\r\n        End If\r\n    End With\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyTrustedLocation\r\n' Author    : Adam Waller\r\n' Date      : 1/12/2021\r\n' Purpose   : The location of the add-in must be trusted, or the user will be unable\r\n'           : to run the add-in. This function ensures that the path has been added\r\n'           : as a trusted location after confirming this with the user. If the user\r\n'           : declines to add as a trusted location, it warns them that the add-in may\r\n'           : not function correctly.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function VerifyTrustedLocation() As Boolean\r\n\r\n    Dim strPath As String\r\n    Dim strTrusted As String\r\n\r\n    ' Get registry path for trusted locations\r\n    strPath = GetTrustedLocationRegPath\r\n    strTrusted = FSO.GetParentFolderName(GetAddInFileName) & PathSep\r\n\r\n    ' Use Windows Scripting Shell to read/write to registry\r\n    With New IWshRuntimeLibrary.WshShell\r\n\r\n        ' Check for existing value\r\n        If HasTrustedLocationKey Then\r\n\r\n            ' Found trusted location with this name.\r\n            VerifyTrustedLocation = True\r\n\r\n        Else\r\n            ' Get permission from user to add trusted location\r\n            If MsgBox2(\"Add Trusted Location?\", _\r\n                \"To function correctly, this add-in needs to be \"\"trusted\"\" by Microsoft Access.\" & vbCrLf & _\r\n                \"Typically this is accomplished by adding the add-in folder as a trusted location\" & vbCrLf & _\r\n                \"in your security settings. More information is available on the GitHub wiki for\" & vbCrLf & _\r\n                \"this add-in project.\", _\r\n                \"<<PLEASE CONFIRM>> Add the following path as a trusted location?\" & vbCrLf & vbCrLf & strTrusted _\r\n                , vbQuestion + vbOKCancel + vbDefaultButton2) = vbOK Then\r\n\r\n                ' Add trusted location\r\n                .RegWrite strPath & \"Path\", strTrusted\r\n                .RegWrite strPath & \"Date\", Now()\r\n                .RegWrite strPath & \"Description\", mcstrTrustedLocationName\r\n                .RegWrite strPath & \"AllowSubfolders\", 0, \"REG_DWORD\"\r\n\r\n                ' Verify it was actually set.\r\n                If HasTrustedLocationKey Then\r\n                    VerifyTrustedLocation = True\r\n                Else\r\n                    ' Could not find registry entry.\r\n                    MsgBox2 \"Hmm... Something didn't work\", _\r\n                        \"The new trusted location entry was not found in the registry.\", _\r\n                        \"Please open an issue on GitHub if the issue persists.\", vbExclamation\r\n                End If\r\n\r\n            Else\r\n                MsgBox2 \"Location NOT Added\", _\r\n                    \"No problem. You can always run the installer again\" & vbCrLf & _\r\n                    \"if you change your mind.\", _\r\n                    \"Note that the add-in may not function correctly.\", vbInformation\r\n            End If\r\n        End If\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveTrustedLocation\r\n' Author    : Adam Waller\r\n' Date      : 1/12/2021\r\n' Purpose   : Remove trusted location entry.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub RemoveTrustedLocation(Optional strName As String)\r\n\r\n    Dim strPath As String\r\n\r\n    ' Get registry path for trusted locations\r\n    strPath = GetTrustedLocationRegPath(strName)\r\n\r\n    With New IWshRuntimeLibrary.WshShell\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        .RegDelete strPath & \"Path\"\r\n        .RegDelete strPath & \"Date\"\r\n        .RegDelete strPath & \"Description\"\r\n        .RegDelete strPath & \"AllowSubfolders\"\r\n        .RegDelete strPath\r\n        On Error GoTo 0\r\n    End With\r\n\r\n    ' Make sure it was removed\r\n    If HasTrustedLocationKey Then\r\n        MsgBox2 \"Error Removing Trusted Location\", _\r\n            \"You may need to manually remove the trusted location\" & vbCrLf & _\r\n            \"in the Microsoft Access Security settings.\", _\r\n            \"Please open an issue on GitHub if the issue persists.\", vbExclamation\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetTrustedLocationRegPath\r\n' Author    : Adam Waller\r\n' Date      : 1/12/2021\r\n' Purpose   : Return the trusted location registry path. (Added to trusted locations)\r\n'---------------------------------------------------------------------------------------\r\n'\r\n'\r\nPrivate Function GetTrustedLocationRegPath(Optional ByVal strName As String) As String\r\n\r\n    ' If no (other) name was specified, default to the standard one.\r\n    If strName = vbNullString Then strName = mcstrTrustedLocationName\r\n\r\n    ' Return the full registry path to the trusted location\r\n    GetTrustedLocationRegPath = \"HKEY_CURRENT_USER\\Software\\Microsoft\\Office\\\" & _\r\n        Application.Version & \"\\Access\\Security\\Trusted Locations\\\" & strName & \"\\\"\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : HasTrustedLocationKey\r\n' Author    : Adam Waller\r\n' Date      : 1/13/2021\r\n' Purpose   : Returns true if we find the trusted location added by this add-in.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function HasTrustedLocationKey(Optional strName As String) As Boolean\r\n    With New IWshRuntimeLibrary.WshShell\r\n        LogUnhandledErrors\r\n        On Error Resume Next\r\n        HasTrustedLocationKey = Nz(.RegRead(GetTrustedLocationRegPath(strName) & \"Path\")) <> vbNullString\r\n        If Err Then Err.Clear\r\n    End With\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OpenAddinFile\r\n' Author    : hecon5\r\n' Date      : 1/15/2021\r\n' Purpose   : runs a script to complete the addin trusting process. Once a trusted\r\n'           : location is set, the file needs to be opened to trust it in many\r\n'           : Corporate / Government environments due to security concerns.\r\n'           : This will complete the process without the user needing to know\r\n'           : where the file resides.\r\n'           : It waits for two files to close (the \"installer\" and the \"addin\".\r\n'           : This should hopefully ensure Access was closed prior to relaunch and\r\n'           : significantly reduces instance of the application\r\n'           : The subroutine is private because if you have called the addin from\r\n'           : somewhere (aka, you're not installing it), opening the same file twice\r\n'           : will cause headaches and likely corrupt the file.\r\n'---------------------------------------------------------------------------------------\r\nPublic Sub OpenAddinFile(strAddInFileName As String, _\r\n                            strInstallerFileName As String)\r\n\r\n    Dim strScriptFile As String\r\n    Dim strExt As String\r\n    Dim lockFilePathAddin As String\r\n    Dim lockFilePathInstaller As String\r\n\r\n    ' Build file paths for lock files and batch script\r\n    strExt = \".\" & FSO.GetExtensionName(strInstallerFileName)\r\n    lockFilePathAddin = Replace(strAddInFileName, strExt, \".laccdb\", , , vbTextCompare)\r\n    lockFilePathInstaller = Replace(strInstallerFileName, strExt, \".laccdb\", , , vbTextCompare)\r\n    strScriptFile = Replace(strAddInFileName, strExt, \".cmd\", , , vbTextCompare)\r\n\r\n    ' Build batch script content\r\n    With New clsConcat\r\n        .AppendOnAdd = vbCrLf\r\n        .Add \"@Echo Off\"\r\n        .Add \"setlocal ENABLEDELAYEDEXPANSION\"\r\n        .Add \"ECHO Waiting for Addin file to copy over...\"\r\n        .Add \":WAITFORADDIN\"\r\n        .Add \"ping 127.0.0.1 -n 1 -w 100 > nul\"\r\n        .Add \"SET /a counter+=1\"\r\n        .Add \"IF !counter!==300 GOTO DONE\"\r\n        .Add \"IF NOT EXIST \"\"\", strAddInFileName, \"\"\" GOTO WAITFORADDIN\"\r\n        .Add \"ECHO Waiting for Access to close...\"\r\n        .Add \"SET /a counter=0\"\r\n        .Add \":WAITCLOSEINSTALLER\"\r\n        .Add \"ping 127.0.0.1 -n 1 -w 100 > nul\"\r\n        .Add \"SET /a counter+=1\"\r\n        .Add \"IF !counter!==30 GOTO WAITCLOSEADDIN\"\r\n        .Add \"IF EXIST \"\"\", lockFilePathInstaller, \"\"\" GOTO WAITCLOSEINSTALLER\"\r\n        .Add \":WAITCLOSEADDIN\"\r\n        .Add \"ping 127.0.0.1 -n 1 -w 100 > nul\"\r\n        .Add \"IF !counter!==40 GOTO MOVEON\"\r\n        .Add \"IF EXIST \"\"\", lockFilePathAddin, \"\"\" GOTO WAITCLOSEADDIN\"\r\n        .Add \":OPENADDIN\"\r\n        .Add \"ECHO Opening Add-in to finish installation...\"\r\n        .Add \"ECHO (This window will automatically close when complete.)\"\r\n        .Add \"\"\"\", strAddInFileName, \"\"\"\"\r\n        .Add \"GOTO DONE\"\r\n        .Add \":MOVEON\"\r\n        .Add \"Del \"\"\", lockFilePathAddin, \"\"\"\"\r\n        .Add \"Del \"\"\", lockFilePathInstaller, \"\"\"\"\r\n        .Add \"GOTO OPENADDIN\"\r\n        .Add \":DONE\"\r\n        .Add \"Del \"\"\", strScriptFile, \"\"\"\"\r\n\r\n        ' Write to file\r\n        WriteFile .GetStr, strScriptFile\r\n    End With\r\n\r\n    ' Execute script\r\n    Shell strScriptFile, vbNormalFocus\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyActivexNotDisabled\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2023\r\n' Purpose   : Verify that ActiveX has not been disabled in the registry, and warn the\r\n'           : user that the add-in may not be able to build from source without this.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub VerifyActivexNotDisabled()\r\n    If IsActivexDisabled Then\r\n        MsgBox2 \"ActiveX Disabled\", \"WARNING: ActiveX appears to be disabled in the \" & _\r\n            \"Microsoft Office Trust Center settings, or by a Group Policy setting. \" & _\r\n            \"Microsoft Access uses ActiveX when importing content from XML, so some features \" & _\r\n            \"of this add-in, such as building from source may not work \" & _\r\n            \"correctly without enabling ActiveX.\", _\r\n            \"You may need to review the ActiveX security settings  with your IT Department \" & _\r\n            \"or system administrator to determine the appropriate setting for your system.\", vbExclamation\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IsActivexDisabled\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2023\r\n' Purpose   : Returns true if ActiveX appears to be enabled on the current system.\r\n'           : (ActiveX is required to import XML files, such as table definitions when\r\n'           :  building a database from source.) See issue #396\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function IsActivexDisabled() As Boolean\r\n    IsActivexDisabled = Not ( _\r\n        CheckRegKey(\"HKCU\\SOFTWARE\\Policies\\Microsoft\\Office\\common\\security\\disableallactivex\", 0, Null) And _\r\n        CheckRegKey(\"HKCU\\SOFTWARE\\Microsoft\\Office\\Common\\Security\\disableallactivex\", 0, Null) And _\r\n        CheckRegKey(\"HKCU\\SOFTWARE\\Policies\\Microsoft\\Office\\\" & Application.Version & \"\\Common\\com categories\\checkofficeactivex\", 0, 1, Null) And _\r\n        CheckRegKey(\"HKCU\\SOFTWARE\\Microsoft\\Office\\\" & Application.Version & \"\\Common\\com categories\\checkofficeactivex\", 0, 1, Null))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckRegKey\r\n' Author    : Adam Waller\r\n' Date      : 4/14/2023\r\n' Purpose   : Check a registry key for specific allowed values, (including null)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function CheckRegKey(strPath As String, ParamArray AllowedValues() As Variant) As Boolean\r\n\r\n    Dim varValue As Variant\r\n    Dim intCnt As Integer\r\n\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    ' Attempt to read registry key\r\n    With New IWshRuntimeLibrary.WshShell\r\n        varValue = .RegRead(strPath)\r\n        ' A file not found error means the key did not exist.\r\n        If Catch(-2147024894) Then varValue = Null\r\n    End With\r\n\r\n    ' Compare to array of allowed values\r\n    For intCnt = 0 To UBound(AllowedValues)\r\n        If varValue = AllowedValues(intCnt) Or _\r\n            (IsNull(varValue) And IsNull(AllowedValues(intCnt))) Then\r\n            CheckRegKey = True\r\n            Exit For\r\n        End If\r\n    Next intCnt\r\n\r\n    If Err Then Err.Clear\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : IncrementAppVersion\r\n' Author    : Adam Waller\r\n' Date      : 1/6/2017\r\n' Purpose   : Increments the build version (1.0.12)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub IncrementAppVersion(Optional ReleaseType As eReleaseType = Build_xxV)\r\n\r\n    Dim varParts As Variant\r\n    Dim strFrom As String\r\n\r\n    If ReleaseType = Same_Version Then Exit Sub\r\n    strFrom = AppVersion\r\n    varParts = Split(AppVersion, \".\")\r\n    varParts(ReleaseType) = varParts(ReleaseType) + 1\r\n    If ReleaseType < Minor_xVx Then varParts(Minor_xVx) = 0\r\n    If ReleaseType < Build_xxV Then varParts(Build_xxV) = 0\r\n    AppVersion = Join(varParts, \".\")\r\n\r\n    ' Display old and new versions\r\n    Debug.Print \"Updated from \" & strFrom & \" to \" & AppVersion\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AppVersion\r\n' Author    : Adam Waller\r\n' Date      : 1/5/2017\r\n' Purpose   : Get the version from the database property.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get AppVersion() As String\r\n    Dim strVersion As String\r\n    strVersion = GetDBProperty(\"AppVersion\", CodeDb)\r\n    If strVersion = vbNullString Then strVersion = \"1.0.0\"\r\n    AppVersion = strVersion\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AppVersion\r\n' Author    : Adam Waller\r\n' Date      : 1/5/2017\r\n' Purpose   : Set version property in current database.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Let AppVersion(strVersion As String)\r\n    SetDBProperty \"AppVersion\", strVersion, , CodeDb\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : InstalledVersion\r\n' Author    : Adam Waller\r\n' Date      : 4/21/2020\r\n' Purpose   : Returns the installed version of the add-in from the registry.\r\n'           : (We are saving this in the user hive, since it requires admin rights\r\n'           :  to change the keys actually used by Access to register the add-in)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Let InstalledVersion(strVersion As String)\r\n    SaveSetting PROJECT_NAME, \"Add-in\", \"Installed Version\", strVersion\r\nEnd Property\r\nPublic Property Get InstalledVersion() As String\r\n    InstalledVersion = GetSetting(PROJECT_NAME, \"Add-in\", \"Installed Version\", vbNullString)\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modJsonConverter.bas",
    "content": "﻿Attribute VB_Name = \"modJsonConverter\"\r\n''\r\n' VBA-JSON v2.3.1\r\n' (c) Tim Hall - https://github.com/VBA-tools/VBA-JSON\r\n'\r\n' JSON Converter for VBA\r\n'\r\n' Errors:\r\n' 10001 - JSON parse error\r\n'\r\n' @class JsonConverter\r\n' @author tim.hall.engr@gmail.com\r\n' @license MIT (http://www.opensource.org/licenses/mit-license.php)\r\n'' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '\r\n'\r\n' Based originally on vba-json (with extensive changes)\r\n' BSD license included below\r\n'\r\n' JSONLib, http://code.google.com/p/vba-json/\r\n'\r\n' Copyright (c) 2013, Ryo Yokoyama\r\n' All rights reserved.\r\n'\r\n' Redistribution and use in source and binary forms, with or without\r\n' modification, are permitted provided that the following conditions are met:\r\n'     * Redistributions of source code must retain the above copyright\r\n'       notice, this list of conditions and the following disclaimer.\r\n'     * Redistributions in binary form must reproduce the above copyright\r\n'       notice, this list of conditions and the following disclaimer in the\r\n'       documentation and/or other materials provided with the distribution.\r\n'     * Neither the name of the <organization> nor the\r\n'       names of its contributors may be used to endorse or promote products\r\n'       derived from this software without specific prior written permission.\r\n'\r\n' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\r\n' ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r\n' WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r\n' DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\r\n' DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r\n' (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r\n' LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\r\n' ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r\n' SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '\r\nOption Compare Database\r\nOption Explicit\r\nOption Private Module\r\n\r\n' === VBA-UTC Headers\r\n#If Mac Then\r\n\r\n#If VBA7 Then\r\n\r\n' 64-bit Mac (2016)\r\nPrivate Declare PtrSafe Function utc_popen Lib \"/usr/lib/libc.dylib\" Alias \"popen\" ( _\r\n    ByVal utc_Command As String, ByVal utc_Mode As String) As LongPtr\r\nPrivate Declare PtrSafe Function utc_pclose Lib \"/usr/lib/libc.dylib\" Alias \"pclose\" ( _\r\n    ByVal utc_File As LongPtr) As LongPtr\r\nPrivate Declare PtrSafe Function utc_fread Lib \"/usr/lib/libc.dylib\" Alias \"fread\" ( _\r\n    ByVal utc_Buffer As String, ByVal utc_Size As LongPtr, ByVal utc_Number As LongPtr, ByVal utc_File As LongPtr) As LongPtr\r\nPrivate Declare PtrSafe Function utc_feof Lib \"/usr/lib/libc.dylib\" Alias \"feof\" ( _\r\n    ByVal utc_File As LongPtr) As LongPtr\r\n\r\n#Else\r\n\r\n' 32-bit Mac\r\nPrivate Declare Function utc_popen Lib \"libc.dylib\" Alias \"popen\" ( _\r\n    ByVal utc_Command As String, ByVal utc_Mode As String) As Long\r\nPrivate Declare Function utc_pclose Lib \"libc.dylib\" Alias \"pclose\" ( _\r\n    ByVal utc_File As Long) As Long\r\nPrivate Declare Function utc_fread Lib \"libc.dylib\" Alias \"fread\" ( _\r\n    ByVal utc_Buffer As String, ByVal utc_Size As Long, ByVal utc_Number As Long, ByVal utc_File As Long) As Long\r\nPrivate Declare Function utc_feof Lib \"libc.dylib\" Alias \"feof\" ( _\r\n    ByVal utc_File As Long) As Long\r\n\r\n#End If\r\n\r\n#ElseIf VBA7 Then\r\n\r\n' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724421.aspx\r\n' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724949.aspx\r\n' http://msdn.microsoft.com/en-us/library/windows/desktop/ms725485.aspx\r\nPrivate Declare PtrSafe Function utc_GetTimeZoneInformation Lib \"kernel32\" Alias \"GetTimeZoneInformation\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) As Long\r\nPrivate Declare PtrSafe Function utc_SystemTimeToTzSpecificLocalTime Lib \"kernel32\" Alias \"SystemTimeToTzSpecificLocalTime\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) As Long\r\nPrivate Declare PtrSafe Function utc_TzSpecificLocalTimeToSystemTime Lib \"kernel32\" Alias \"TzSpecificLocalTimeToSystemTime\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) As Long\r\n\r\n#Else\r\n\r\nPrivate Declare Function utc_GetTimeZoneInformation Lib \"kernel32\" Alias \"GetTimeZoneInformation\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) As Long\r\nPrivate Declare Function utc_SystemTimeToTzSpecificLocalTime Lib \"kernel32\" Alias \"SystemTimeToTzSpecificLocalTime\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) As Long\r\nPrivate Declare Function utc_TzSpecificLocalTimeToSystemTime Lib \"kernel32\" Alias \"TzSpecificLocalTimeToSystemTime\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) As Long\r\n\r\n#End If\r\n\r\n#If Mac Then\r\n\r\n#If VBA7 Then\r\nPrivate Type utc_ShellResult\r\n    utc_Output As String\r\n    utc_ExitCode As LongPtr\r\nEnd Type\r\n\r\n#Else\r\n\r\nPrivate Type utc_ShellResult\r\n    utc_Output As String\r\n    utc_ExitCode As Long\r\nEnd Type\r\n\r\n#End If\r\n\r\n#Else\r\n\r\nPrivate Type utc_SYSTEMTIME\r\n    utc_wYear As Integer\r\n    utc_wMonth As Integer\r\n    utc_wDayOfWeek As Integer\r\n    utc_wDay As Integer\r\n    utc_wHour As Integer\r\n    utc_wMinute As Integer\r\n    utc_wSecond As Integer\r\n    utc_wMilliseconds As Integer\r\nEnd Type\r\n\r\nPrivate Type utc_TIME_ZONE_INFORMATION\r\n    utc_Bias As Long\r\n    utc_StandardName(0 To 31) As Integer\r\n    utc_StandardDate As utc_SYSTEMTIME\r\n    utc_StandardBias As Long\r\n    utc_DaylightName(0 To 31) As Integer\r\n    utc_DaylightDate As utc_SYSTEMTIME\r\n    utc_DaylightBias As Long\r\nEnd Type\r\n\r\n#End If\r\n' === End VBA-UTC\r\n\r\nPrivate Type json_Options\r\n    ' VBA only stores 15 significant digits, so any numbers larger than that are truncated\r\n    ' This can lead to issues when BIGINT's are used (e.g. for Ids or Credit Cards), as they will be invalid above 15 digits\r\n    ' See: http://support.microsoft.com/kb/269370\r\n    '\r\n    ' By default, VBA-JSON will use String for numbers longer than 15 characters that contain only digits\r\n    ' to override set `JsonConverter.JsonOptions.UseDoubleForLargeNumbers = True`\r\n    UseDoubleForLargeNumbers As Boolean\r\n\r\n    ' The JSON standard requires object keys to be quoted (\" or '), use this option to allow unquoted keys\r\n    AllowUnquotedKeys As Boolean\r\n\r\n    ' The solidus (/) is not required to be escaped, use this option to escape them as \\/ in ConvertToJson\r\n    EscapeSolidus As Boolean\r\n\r\n    ' Before version 2.3.1 dates were converted to UTC in ConvertToJson method, but not when json was parsed.\r\n    ' Convert datetime values to UTC/ISO8601 (true, slower) or dont change local <-> global times (false, faster)\r\n    ConvertDateToIso As Boolean\r\n\r\n    ' Allow Unicode characters in JSON text. Set to True to use native Unicode or false for escaped values.\r\n    AllowUnicodeChars As Boolean\r\nEnd Type\r\nPublic JsonOptions As json_Options\r\n\r\n' ============================================= '\r\n' Public Methods\r\n' ============================================= '\r\n\r\n''\r\n' Convert JSON string to object (Dictionary/Collection)\r\n'\r\n' @method ParseJson\r\n' @param {String} json_String\r\n' @return {Object} (Dictionary or Collection)\r\n' @throws 10001 - JSON parse error\r\n''\r\nPublic Function ParseJson(ByVal JsonString As String) As Object\r\n    Dim json_Index As Long\r\n    json_Index = 1\r\n\r\n    Perf.OperationStart \"Parse JSON\"\r\n\r\n    ' Remove vbCr, vbLf, and vbTab from json_String\r\n    JsonString = VBA.Replace(VBA.Replace(VBA.Replace(JsonString, VBA.vbCr, vbNullString), VBA.vbLf, vbNullString), VBA.vbTab, vbNullString)\r\n\r\n    json_SkipSpaces JsonString, json_Index\r\n    Select Case VBA.Mid$(JsonString, json_Index, 1)\r\n    Case \"{\"\r\n        Set ParseJson = json_ParseObject(JsonString, json_Index)\r\n    Case \"[\"\r\n        Set ParseJson = json_ParseArray(JsonString, json_Index)\r\n    Case Else\r\n        ' Error: Invalid JSON string\r\n        Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(JsonString, json_Index, \"Expecting '{' or '['\")\r\n    End Select\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n''\r\n' Convert object (Dictionary/Collection/Array) to JSON\r\n'\r\n' @method ConvertToJson\r\n' @param {Variant} JsonValue (Dictionary, Collection, or Array)\r\n' @param {Integer|String} Whitespace \"Pretty\" print json with given number of spaces per indentation (Integer) or given string\r\n' @return {String}\r\n''\r\nPublic Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitespace As Variant, Optional ByVal json_CurrentIndentation As Long = 0) As String\r\n    Dim json_Buffer As String\r\n    Dim json_BufferPosition As Long\r\n    Dim json_BufferLength As Long\r\n    Dim json_Index As Long\r\n    Dim json_LBound As Long\r\n    Dim json_UBound As Long\r\n    Dim json_IsFirstItem As Boolean\r\n    Dim json_Index2D As Long\r\n    Dim json_LBound2D As Long\r\n    Dim json_UBound2D As Long\r\n    Dim json_IsFirstItem2D As Boolean\r\n    Dim json_Key As Variant\r\n    Dim json_Value As Variant\r\n    Dim json_DateStr As String\r\n    Dim json_Converted As String\r\n    Dim json_SkipItem As Boolean\r\n    Dim json_PrettyPrint As Boolean\r\n    Dim json_Indentation As String\r\n    Dim json_InnerIndentation As String\r\n\r\n    Perf.OperationStart \"Convert to JSON\"\r\n\r\n    json_LBound = -1\r\n    json_UBound = -1\r\n    json_IsFirstItem = True\r\n    json_LBound2D = -1\r\n    json_UBound2D = -1\r\n    json_IsFirstItem2D = True\r\n    json_PrettyPrint = Not IsMissing(Whitespace)\r\n\r\n    Select Case VBA.VarType(JsonValue)\r\n    Case VBA.vbNull\r\n        ConvertToJson = \"null\"\r\n\r\n    Case VBA.vbDate\r\n        ' Date\r\n        If JsonOptions.ConvertDateToIso Then\r\n            Perf.OperationStart \"Convert JSON Date to ISO\"\r\n            json_DateStr = ConvertToIsoTime(VBA.CDate(JsonValue))\r\n            Perf.OperationEnd\r\n        Else\r\n            json_DateStr = VBA.CStr(JsonValue)\r\n        End If\r\n        ConvertToJson = \"\"\"\" & json_DateStr & \"\"\"\"\r\n\r\n    Case VBA.vbString\r\n        ' String (or large number encoded as string)\r\n        If Not JsonOptions.UseDoubleForLargeNumbers And json_StringIsLargeNumber(JsonValue) Then\r\n            ConvertToJson = JsonValue\r\n        Else\r\n            ConvertToJson = \"\"\"\" & json_Encode(JsonValue) & \"\"\"\"\r\n        End If\r\n    Case VBA.vbBoolean\r\n        If JsonValue Then\r\n            ConvertToJson = \"true\"\r\n        Else\r\n            ConvertToJson = \"false\"\r\n        End If\r\n    Case VBA.vbArray To VBA.vbArray + VBA.vbByte\r\n        If json_PrettyPrint Then\r\n            If VBA.VarType(Whitespace) = VBA.vbString Then\r\n                json_Indentation = VBA.String$(json_CurrentIndentation + 1, Whitespace)\r\n                json_InnerIndentation = VBA.String$(json_CurrentIndentation + 2, Whitespace)\r\n            Else\r\n                json_Indentation = VBA.Space$((json_CurrentIndentation + 1) * Whitespace)\r\n                json_InnerIndentation = VBA.Space$((json_CurrentIndentation + 2) * Whitespace)\r\n            End If\r\n        End If\r\n\r\n        ' Array\r\n        json_BufferAppend json_Buffer, \"[\", json_BufferPosition, json_BufferLength\r\n\r\n        On Error Resume Next\r\n\r\n        json_LBound = LBound(JsonValue, 1)\r\n        json_UBound = UBound(JsonValue, 1)\r\n        json_LBound2D = LBound(JsonValue, 2)\r\n        json_UBound2D = UBound(JsonValue, 2)\r\n\r\n        If json_LBound >= 0 And json_UBound >= 0 Then\r\n            For json_Index = json_LBound To json_UBound\r\n                If json_IsFirstItem Then\r\n                    json_IsFirstItem = False\r\n                Else\r\n                    ' Append comma to previous line\r\n                    json_BufferAppend json_Buffer, \",\", json_BufferPosition, json_BufferLength\r\n                End If\r\n\r\n                If json_LBound2D >= 0 And json_UBound2D >= 0 Then\r\n                    ' 2D Array\r\n                    If json_PrettyPrint Then\r\n                        json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\r\n                    End If\r\n                    json_BufferAppend json_Buffer, json_Indentation & \"[\", json_BufferPosition, json_BufferLength\r\n\r\n                    For json_Index2D = json_LBound2D To json_UBound2D\r\n                        If json_IsFirstItem2D Then\r\n                            json_IsFirstItem2D = False\r\n                        Else\r\n                            json_BufferAppend json_Buffer, \",\", json_BufferPosition, json_BufferLength\r\n                        End If\r\n\r\n                        json_Converted = ConvertToJson(JsonValue(json_Index, json_Index2D), Whitespace, json_CurrentIndentation + 2)\r\n\r\n                        ' For Arrays/Collections, undefined (Empty/Nothing) is treated as null\r\n                        If json_Converted = vbNullString Then\r\n                            ' (nest to only check if converted = \"\")\r\n                            If json_IsUndefined(JsonValue(json_Index, json_Index2D)) Then\r\n                                json_Converted = \"null\"\r\n                            End If\r\n                        End If\r\n\r\n                        If json_PrettyPrint Then\r\n                            json_Converted = vbNewLine & json_InnerIndentation & json_Converted\r\n                        End If\r\n\r\n                        json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength\r\n                    Next json_Index2D\r\n\r\n                    If json_PrettyPrint Then\r\n                        json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\r\n                    End If\r\n\r\n                    json_BufferAppend json_Buffer, json_Indentation & \"]\", json_BufferPosition, json_BufferLength\r\n                    json_IsFirstItem2D = True\r\n                Else\r\n                    ' 1D Array\r\n                    json_Converted = ConvertToJson(JsonValue(json_Index), Whitespace, json_CurrentIndentation + 1)\r\n\r\n                    ' For Arrays/Collections, undefined (Empty/Nothing) is treated as null\r\n                    If json_Converted = vbNullString Then\r\n                        ' (nest to only check if converted = \"\")\r\n                        If json_IsUndefined(JsonValue(json_Index)) Then\r\n                            json_Converted = \"null\"\r\n                        End If\r\n                    End If\r\n\r\n                    If json_PrettyPrint Then\r\n                        json_Converted = vbNewLine & json_Indentation & json_Converted\r\n                    End If\r\n\r\n                    json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength\r\n                End If\r\n            Next json_Index\r\n        End If\r\n\r\n        On Error GoTo 0\r\n\r\n        If json_PrettyPrint Then\r\n            json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\r\n\r\n            If VBA.VarType(Whitespace) = VBA.vbString Then\r\n                json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)\r\n            Else\r\n                json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)\r\n            End If\r\n        End If\r\n\r\n        json_BufferAppend json_Buffer, json_Indentation & \"]\", json_BufferPosition, json_BufferLength\r\n\r\n        ConvertToJson = json_BufferToString(json_Buffer, json_BufferPosition)\r\n\r\n    ' Dictionary or Collection\r\n    Case VBA.vbObject\r\n        If json_PrettyPrint Then\r\n            If VBA.VarType(Whitespace) = VBA.vbString Then\r\n                json_Indentation = VBA.String$(json_CurrentIndentation + 1, Whitespace)\r\n            Else\r\n                json_Indentation = VBA.Space$((json_CurrentIndentation + 1) * Whitespace)\r\n            End If\r\n        End If\r\n\r\n        ' Dictionary\r\n        If VBA.TypeName(JsonValue) = \"Dictionary\" Then\r\n            json_BufferAppend json_Buffer, \"{\", json_BufferPosition, json_BufferLength\r\n            For Each json_Key In JsonValue.Keys\r\n                ' For Objects, undefined (Empty/Nothing) is not added to object\r\n                json_Converted = ConvertToJson(JsonValue(json_Key), Whitespace, json_CurrentIndentation + 1)\r\n                If json_Converted = vbNullString Then\r\n                    json_SkipItem = json_IsUndefined(JsonValue(json_Key))\r\n                Else\r\n                    json_SkipItem = False\r\n                End If\r\n\r\n                If Not json_SkipItem Then\r\n                    If json_IsFirstItem Then\r\n                        json_IsFirstItem = False\r\n                    Else\r\n                        json_BufferAppend json_Buffer, \",\", json_BufferPosition, json_BufferLength\r\n                    End If\r\n\r\n                    If json_PrettyPrint Then\r\n                        json_Converted = vbNewLine & json_Indentation & \"\"\"\" & json_Encode(json_Key) & \"\"\": \" & json_Converted\r\n                    Else\r\n                        json_Converted = \"\"\"\" & json_Encode(json_Key) & \"\"\":\" & json_Converted\r\n                    End If\r\n\r\n                    json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength\r\n                End If\r\n            Next json_Key\r\n\r\n            If json_PrettyPrint Then\r\n                json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\r\n\r\n                If VBA.VarType(Whitespace) = VBA.vbString Then\r\n                    json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)\r\n                Else\r\n                    json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)\r\n                End If\r\n            End If\r\n\r\n            json_BufferAppend json_Buffer, json_Indentation & \"}\", json_BufferPosition, json_BufferLength\r\n\r\n        ' Collection\r\n        ElseIf VBA.TypeName(JsonValue) = \"Collection\" Then\r\n            json_BufferAppend json_Buffer, \"[\", json_BufferPosition, json_BufferLength\r\n            For Each json_Value In JsonValue\r\n                If json_IsFirstItem Then\r\n                    json_IsFirstItem = False\r\n                Else\r\n                    json_BufferAppend json_Buffer, \",\", json_BufferPosition, json_BufferLength\r\n                End If\r\n\r\n                json_Converted = ConvertToJson(json_Value, Whitespace, json_CurrentIndentation + 1)\r\n\r\n                ' For Arrays/Collections, undefined (Empty/Nothing) is treated as null\r\n                If json_Converted = vbNullString Then\r\n                    ' (nest to only check if converted = \"\")\r\n                    If json_IsUndefined(json_Value) Then\r\n                        json_Converted = \"null\"\r\n                    End If\r\n                End If\r\n\r\n                If json_PrettyPrint Then\r\n                    json_Converted = vbNewLine & json_Indentation & json_Converted\r\n                End If\r\n\r\n                json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength\r\n            Next json_Value\r\n\r\n            If json_PrettyPrint Then\r\n                json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength\r\n\r\n                If VBA.VarType(Whitespace) = VBA.vbString Then\r\n                    json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)\r\n                Else\r\n                    json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)\r\n                End If\r\n            End If\r\n\r\n            json_BufferAppend json_Buffer, json_Indentation & \"]\", json_BufferPosition, json_BufferLength\r\n        End If\r\n\r\n        ConvertToJson = json_BufferToString(json_Buffer, json_BufferPosition)\r\n    Case VBA.vbInteger, VBA.vbLong, VBA.vbSingle, VBA.vbDouble, VBA.vbCurrency, VBA.vbDecimal\r\n        ' Number (use decimals for numbers)\r\n        ConvertToJson = VBA.Replace(JsonValue, \",\", \".\")\r\n    Case Else\r\n        ' vbEmpty, vbError, vbDataObject, vbByte, vbUserDefinedType\r\n        ' Use VBA's built-in to-string\r\n        On Error Resume Next\r\n        ConvertToJson = JsonValue\r\n        On Error GoTo 0\r\n    End Select\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Function\r\n\r\n' ============================================= '\r\n' Private Functions\r\n' ============================================= '\r\n\r\nPrivate Function json_ParseObject(json_String As String, ByRef json_Index As Long) As Dictionary\r\n    Dim json_Key As String\r\n    Dim json_NextChar As String\r\n\r\n    Set json_ParseObject = New Dictionary\r\n    json_SkipSpaces json_String, json_Index\r\n    If VBA.Mid$(json_String, json_Index, 1) <> \"{\" Then\r\n        Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting '{'\")\r\n    Else\r\n        json_Index = json_Index + 1\r\n\r\n        Do\r\n            json_SkipSpaces json_String, json_Index\r\n            If VBA.Mid$(json_String, json_Index, 1) = \"}\" Then\r\n                json_Index = json_Index + 1\r\n                Exit Function\r\n            ElseIf VBA.Mid$(json_String, json_Index, 1) = \",\" Then\r\n                json_Index = json_Index + 1\r\n                json_SkipSpaces json_String, json_Index\r\n            End If\r\n\r\n            json_Key = json_ParseKey(json_String, json_Index)\r\n            json_NextChar = json_Peek(json_String, json_Index)\r\n            If json_NextChar = \"[\" Or json_NextChar = \"{\" Then\r\n                Set json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)\r\n            Else\r\n                json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)\r\n            End If\r\n        Loop\r\n    End If\r\nEnd Function\r\n\r\nPrivate Function json_ParseArray(json_String As String, ByRef json_Index As Long) As Collection\r\n    Set json_ParseArray = New Collection\r\n\r\n    json_SkipSpaces json_String, json_Index\r\n    If VBA.Mid$(json_String, json_Index, 1) <> \"[\" Then\r\n        Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting '['\")\r\n    Else\r\n        json_Index = json_Index + 1\r\n\r\n        Do\r\n            json_SkipSpaces json_String, json_Index\r\n            If VBA.Mid$(json_String, json_Index, 1) = \"]\" Then\r\n                json_Index = json_Index + 1\r\n                Exit Function\r\n            ElseIf VBA.Mid$(json_String, json_Index, 1) = \",\" Then\r\n                json_Index = json_Index + 1\r\n                json_SkipSpaces json_String, json_Index\r\n            End If\r\n\r\n            json_ParseArray.Add json_ParseValue(json_String, json_Index)\r\n        Loop\r\n    End If\r\nEnd Function\r\n\r\nPrivate Function json_ParseValue(json_String As String, ByRef json_Index As Long) As Variant\r\n    json_SkipSpaces json_String, json_Index\r\n    Select Case VBA.Mid$(json_String, json_Index, 1)\r\n    Case \"{\"\r\n        Set json_ParseValue = json_ParseObject(json_String, json_Index)\r\n    Case \"[\"\r\n        Set json_ParseValue = json_ParseArray(json_String, json_Index)\r\n    Case \"\"\"\", \"'\"\r\n        json_ParseValue = json_ParseString(json_String, json_Index)\r\n    Case Else\r\n        If VBA.Mid$(json_String, json_Index, 4) = \"true\" Then\r\n            json_ParseValue = True\r\n            json_Index = json_Index + 4\r\n        ElseIf VBA.Mid$(json_String, json_Index, 5) = \"false\" Then\r\n            json_ParseValue = False\r\n            json_Index = json_Index + 5\r\n        ElseIf VBA.Mid$(json_String, json_Index, 4) = \"null\" Then\r\n            json_ParseValue = Null\r\n            json_Index = json_Index + 4\r\n        ElseIf VBA.InStr(\"+-0123456789\", VBA.Mid$(json_String, json_Index, 1)) Then\r\n            json_ParseValue = json_ParseNumber(json_String, json_Index)\r\n        Else\r\n            Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting 'STRING', 'NUMBER', null, true, false, '{', or '['\")\r\n        End If\r\n    End Select\r\nEnd Function\r\n\r\nPrivate Function json_ParseString(ByRef json_String As String _\r\n                                , ByRef json_Index As Long) As Variant\r\n\r\n    Dim json_Quote As String\r\n    Dim json_Char As String\r\n    Dim json_Code As String\r\n    Dim json_Buffer As String\r\n    Dim json_BufferPosition As Long\r\n    Dim json_BufferLength As Long\r\n\r\n    json_SkipSpaces json_String, json_Index\r\n\r\n    ' Store opening quote to look for matching closing quote\r\n    json_Quote = VBA.Mid$(json_String, json_Index, 1)\r\n    json_Index = json_Index + 1\r\n\r\n    Do While json_Index > 0 And json_Index <= Len(json_String)\r\n        json_Char = VBA.Mid$(json_String, json_Index, 1)\r\n\r\n        Select Case json_Char\r\n        Case \"\\\"\r\n            ' Escaped string, \\\\, or \\/\r\n            json_Index = json_Index + 1\r\n            json_Char = VBA.Mid$(json_String, json_Index, 1)\r\n\r\n            Select Case json_Char\r\n            Case \"\"\"\", \"\\\", \"/\", \"'\"\r\n                json_BufferAppend json_Buffer, json_Char, json_BufferPosition, json_BufferLength\r\n                json_Index = json_Index + 1\r\n            Case \"b\"\r\n                json_BufferAppend json_Buffer, vbBack, json_BufferPosition, json_BufferLength\r\n                json_Index = json_Index + 1\r\n            Case \"f\"\r\n                json_BufferAppend json_Buffer, vbFormFeed, json_BufferPosition, json_BufferLength\r\n                json_Index = json_Index + 1\r\n            Case \"n\"\r\n                'json_BufferAppend json_Buffer, vbCrLf, json_BufferPosition, json_BufferLength\r\n                ' This should be treated as a line feed, not CrLf. - Adam 7/24/2023\r\n                json_BufferAppend json_Buffer, vbLf, json_BufferPosition, json_BufferLength\r\n                json_Index = json_Index + 1\r\n            Case \"r\"\r\n                json_BufferAppend json_Buffer, vbCr, json_BufferPosition, json_BufferLength\r\n                json_Index = json_Index + 1\r\n            Case \"t\"\r\n                json_BufferAppend json_Buffer, vbTab, json_BufferPosition, json_BufferLength\r\n                json_Index = json_Index + 1\r\n            Case \"u\"\r\n                ' Unicode character escape (e.g. \\u00a9 = Copyright)\r\n                json_Index = json_Index + 1\r\n                json_Code = VBA.Mid$(json_String, json_Index, 4)\r\n                json_BufferAppend json_Buffer, VBA.ChrW$(VBA.Val(\"&h\" + json_Code)), json_BufferPosition, json_BufferLength\r\n                json_Index = json_Index + 4\r\n            End Select\r\n        Case json_Quote\r\n            json_ParseString = json_BufferToString(json_Buffer, json_BufferPosition)\r\n            If JsonOptions.ConvertDateToIso Then ' Only convert and test for condition if needed for speed boost.\r\n                If (json_ParseString Like \"####-##-##T##:##:##*\") Then\r\n                    Perf.OperationStart \"Parse JSON ISO Date\"\r\n                    json_ParseString = ParseIso(VBA.CStr$(json_ParseString)) ' Return as a date\r\n                    Perf.OperationEnd\r\n                End If\r\n            End If\r\n            json_Index = json_Index + 1\r\n            Exit Function\r\n        Case Else\r\n            json_BufferAppend json_Buffer, json_Char, json_BufferPosition, json_BufferLength\r\n            json_Index = json_Index + 1\r\n        End Select\r\n    Loop\r\nEnd Function\r\n\r\nPrivate Function json_ParseNumber(json_String As String, ByRef json_Index As Long) As Variant\r\n    Dim json_Char As String\r\n    Dim json_Value As String\r\n    Dim json_IsLargeNumber As Boolean\r\n\r\n    json_SkipSpaces json_String, json_Index\r\n\r\n    Do While json_Index > 0 And json_Index <= Len(json_String)\r\n        json_Char = VBA.Mid$(json_String, json_Index, 1)\r\n\r\n        If VBA.InStr(\"+-0123456789.eE\", json_Char) Then\r\n            ' Unlikely to have massive number, so use simple append rather than buffer here\r\n            json_Value = json_Value & json_Char\r\n            json_Index = json_Index + 1\r\n        Else\r\n            ' Excel only stores 15 significant digits, so any numbers larger than that are truncated\r\n            ' This can lead to issues when BIGINT's are used (e.g. for Ids or Credit Cards), as they will be invalid above 15 digits\r\n            ' See: http://support.microsoft.com/kb/269370\r\n            '\r\n            ' Fix: Parse -> String, Convert -> String longer than 15/16 characters containing only numbers and decimal points -> Number\r\n            ' (decimal doesn't factor into significant digit count, so if present check for 15 digits + decimal = 16)\r\n            json_IsLargeNumber = IIf(InStr(json_Value, \".\"), Len(json_Value) >= 17, Len(json_Value) >= 16)\r\n            If Not JsonOptions.UseDoubleForLargeNumbers And json_IsLargeNumber Then\r\n                json_ParseNumber = json_Value\r\n            Else\r\n                ' VBA.Val does not use regional settings, so guard for comma is not needed\r\n                json_ParseNumber = VBA.Val(json_Value)\r\n            End If\r\n            Exit Function\r\n        End If\r\n    Loop\r\nEnd Function\r\n\r\nPrivate Function json_ParseKey(json_String As String, ByRef json_Index As Long) As String\r\n    ' Parse key with single or double quotes\r\n    If VBA.Mid$(json_String, json_Index, 1) = \"\"\"\" Or VBA.Mid$(json_String, json_Index, 1) = \"'\" Then\r\n        json_ParseKey = json_ParseString(json_String, json_Index)\r\n    ElseIf JsonOptions.AllowUnquotedKeys Then\r\n        Dim json_Char As String\r\n        Do While json_Index > 0 And json_Index <= Len(json_String)\r\n            json_Char = VBA.Mid$(json_String, json_Index, 1)\r\n            If (json_Char <> \" \") And (json_Char <> \":\") Then\r\n                json_ParseKey = json_ParseKey & json_Char\r\n                json_Index = json_Index + 1\r\n            Else\r\n                Exit Do\r\n            End If\r\n        Loop\r\n    Else\r\n        Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting '\"\"' or '''\")\r\n    End If\r\n\r\n    ' Check for colon and skip if present or throw if not present\r\n    json_SkipSpaces json_String, json_Index\r\n    If VBA.Mid$(json_String, json_Index, 1) <> \":\" Then\r\n        Err.Raise 10001, \"JSONConverter\", json_ParseErrorMessage(json_String, json_Index, \"Expecting ':'\")\r\n    Else\r\n        json_Index = json_Index + 1\r\n    End If\r\nEnd Function\r\n\r\nPrivate Function json_IsUndefined(ByVal json_Value As Variant) As Boolean\r\n    ' Empty / Nothing -> undefined\r\n    Select Case VBA.VarType(json_Value)\r\n    Case VBA.vbEmpty\r\n        json_IsUndefined = True\r\n    Case VBA.vbObject\r\n        Select Case VBA.TypeName(json_Value)\r\n        Case \"Empty\", \"Nothing\"\r\n            json_IsUndefined = True\r\n        End Select\r\n    End Select\r\nEnd Function\r\n\r\nPrivate Function json_Encode(ByVal json_Text As Variant) As String\r\n    ' Reference: http://www.ietf.org/rfc/rfc4627.txt\r\n    ' Escape: \", \\, /, backspace, form feed, line feed, carriage return, tab\r\n    Dim json_Index As Long\r\n    Dim json_Char As String\r\n    Dim json_AscCode As Long\r\n    Dim json_Buffer As String\r\n    Dim json_BufferPosition As Long\r\n    Dim json_BufferLength As Long\r\n\r\n    For json_Index = 1 To VBA.Len(json_Text)\r\n        json_Char = VBA.Mid$(json_Text, json_Index, 1)\r\n        json_AscCode = VBA.AscW(json_Char)\r\n\r\n        ' When AscW returns a negative number, it returns the twos complement form of that number.\r\n        ' To convert the twos complement notation into normal binary notation, add 0xFFF to the return result.\r\n        ' https://support.microsoft.com/en-us/kb/272138\r\n        If json_AscCode < 0 Then\r\n            json_AscCode = json_AscCode + 65536\r\n        End If\r\n\r\n        ' From spec, \", \\, and control characters must be escaped (solidus is optional)\r\n\r\n        Select Case json_AscCode\r\n        Case 34\r\n            ' \" -> 34 -> \\\"\r\n            json_Char = \"\\\"\"\"\r\n        Case 92\r\n            ' \\ -> 92 -> \\\\\r\n            json_Char = \"\\\\\"\r\n        Case 47\r\n            ' / -> 47 -> \\/ (optional)\r\n            If JsonOptions.EscapeSolidus Then\r\n                json_Char = \"\\/\"\r\n            End If\r\n        Case 8\r\n            ' backspace -> 8 -> \\b\r\n            json_Char = \"\\b\"\r\n        Case 12\r\n            ' form feed -> 12 -> \\f\r\n            json_Char = \"\\f\"\r\n        Case 10\r\n            ' line feed -> 10 -> \\n\r\n            json_Char = \"\\n\"\r\n        Case 13\r\n            ' carriage return -> 13 -> \\r\r\n            json_Char = \"\\r\"\r\n        Case 9\r\n            ' tab -> 9 -> \\t\r\n            json_Char = \"\\t\"\r\n        Case 0 To 31\r\n            ' Non-ascii characters -> convert to 4-digit hex\r\n            json_Char = \"\\u\" & VBA.Right$(\"0000\" & VBA.Hex$(json_AscCode), 4)\r\n        Case 127 To 65535\r\n            ' Unicode character range\r\n            If Not JsonOptions.AllowUnicodeChars Then\r\n                json_Char = \"\\u\" & VBA.Right$(\"0000\" & VBA.Hex$(json_AscCode), 4)\r\n            End If\r\n        End Select\r\n\r\n        json_BufferAppend json_Buffer, json_Char, json_BufferPosition, json_BufferLength\r\n    Next json_Index\r\n\r\n    json_Encode = json_BufferToString(json_Buffer, json_BufferPosition)\r\nEnd Function\r\n\r\nPrivate Function json_Peek(json_String As String, ByVal json_Index As Long, Optional json_NumberOfCharacters As Long = 1) As String\r\n    ' \"Peek\" at the next number of characters without incrementing json_Index (ByVal instead of ByRef)\r\n    json_SkipSpaces json_String, json_Index\r\n    json_Peek = VBA.Mid$(json_String, json_Index, json_NumberOfCharacters)\r\nEnd Function\r\n\r\nPrivate Sub json_SkipSpaces(json_String As String, ByRef json_Index As Long)\r\n    ' Increment index to skip over spaces\r\n    Do While json_Index > 0 And json_Index <= VBA.Len(json_String) And VBA.Mid$(json_String, json_Index, 1) = \" \"\r\n        json_Index = json_Index + 1\r\n    Loop\r\nEnd Sub\r\n\r\nPrivate Function json_StringIsLargeNumber(json_String As Variant) As Boolean\r\n    ' Check if the given string is considered a \"large number\"\r\n    ' (See json_ParseNumber)\r\n\r\n    Dim json_Length As Long\r\n    Dim json_CharIndex As Long\r\n    json_Length = VBA.Len(json_String)\r\n\r\n    ' Length with be at least 16 characters and assume will be less than 100 characters\r\n    If json_Length >= 16 And json_Length <= 100 Then\r\n        Dim json_CharCode As String\r\n\r\n        json_StringIsLargeNumber = True\r\n\r\n        For json_CharIndex = 1 To json_Length\r\n            json_CharCode = VBA.Asc(VBA.Mid$(json_String, json_CharIndex, 1))\r\n            Select Case json_CharCode\r\n            ' Look for .|0-9|E|e\r\n            Case 46, 48 To 57, 69, 101\r\n                ' Continue through characters\r\n            Case Else\r\n                json_StringIsLargeNumber = False\r\n                Exit Function\r\n            End Select\r\n        Next json_CharIndex\r\n    End If\r\nEnd Function\r\n\r\nPrivate Function json_ParseErrorMessage(json_String As String, ByRef json_Index As Long, errorMessage As String) As Variant\r\n    ' Provide detailed parse error message, including details of where and what occurred\r\n    '\r\n    ' Example:\r\n    ' Error parsing JSON:\r\n    ' {\"abcde\":True}\r\n    '          ^\r\n    ' Expecting 'STRING', 'NUMBER', null, true, false, '{', or '['\r\n\r\n    Dim json_StartIndex As Long\r\n    Dim json_StopIndex As Long\r\n\r\n    ' Include 10 characters before and after error (if possible)\r\n    json_StartIndex = json_Index - 10\r\n    json_StopIndex = json_Index + 10\r\n    If json_StartIndex <= 0 Then\r\n        json_StartIndex = 1\r\n    End If\r\n    If json_StopIndex > VBA.Len(json_String) Then\r\n        json_StopIndex = VBA.Len(json_String)\r\n    End If\r\n\r\n    json_ParseErrorMessage = \"Error parsing JSON:\" & VBA.vbNewLine & _\r\n                             VBA.Mid$(json_String, json_StartIndex, json_StopIndex - json_StartIndex + 1) & VBA.vbNewLine & _\r\n                             VBA.Space$(json_Index - json_StartIndex) & \"^\" & VBA.vbNewLine & _\r\n                             errorMessage\r\nEnd Function\r\n\r\nPrivate Sub json_BufferAppend(ByRef json_Buffer As String, _\r\n                              ByRef json_Append As Variant, _\r\n                              ByRef json_BufferPosition As Long, _\r\n                              ByRef json_BufferLength As Long)\r\n    ' VBA can be slow to append strings due to allocating a new string for each append\r\n    ' Instead of using the traditional append, allocate a large empty string and then copy string at append position\r\n    '\r\n    ' Example:\r\n    ' Buffer: \"abc  \"\r\n    ' Append: \"def\"\r\n    ' Buffer Position: 3\r\n    ' Buffer Length: 5\r\n    '\r\n    ' Buffer position + Append length > Buffer length -> Append chunk of blank space to buffer\r\n    ' Buffer: \"abc       \"\r\n    ' Buffer Length: 10\r\n    '\r\n    ' Put \"def\" into buffer at position 3 (0-based)\r\n    ' Buffer: \"abcdef    \"\r\n    '\r\n    ' Approach based on cStringBuilder from vbAccelerator\r\n    ' http://www.vbaccelerator.com/home/VB/Code/Techniques/RunTime_Debug_Tracing/VB6_Tracer_Utility_zip_cStringBuilder_cls.asp\r\n    '\r\n    ' and clsStringAppend from Philip Swannell\r\n    ' https://github.com/VBA-tools/VBA-JSON/pull/82\r\n\r\n    Dim json_AppendLength As Long\r\n    Dim json_LengthPlusPosition As Long\r\n\r\n    json_AppendLength = VBA.Len(json_Append)\r\n    json_LengthPlusPosition = json_AppendLength + json_BufferPosition\r\n\r\n    If json_LengthPlusPosition > json_BufferLength Then\r\n        ' Appending would overflow buffer, add chunk\r\n        ' (double buffer length or append length, whichever is bigger)\r\n        Dim json_AddedLength As Long\r\n        json_AddedLength = IIf(json_AppendLength > json_BufferLength, json_AppendLength, json_BufferLength)\r\n\r\n        json_Buffer = json_Buffer & VBA.Space$(json_AddedLength)\r\n        json_BufferLength = json_BufferLength + json_AddedLength\r\n    End If\r\n\r\n    ' Note: Namespacing with VBA.Mid$ doesn't work properly here, throwing compile error:\r\n    ' Function call on left-hand side of assignment must return Variant or Object\r\n    Mid$(json_Buffer, json_BufferPosition + 1, json_AppendLength) = CStr(json_Append)\r\n    json_BufferPosition = json_BufferPosition + json_AppendLength\r\nEnd Sub\r\n\r\nPrivate Function json_BufferToString(ByRef json_Buffer As String, ByVal json_BufferPosition As Long) As String\r\n    If json_BufferPosition > 0 Then\r\n        json_BufferToString = VBA.Left$(json_Buffer, json_BufferPosition)\r\n    End If\r\nEnd Function\r\n\r\n\r\nPrivate Function ConvertToIso(utc_LocalDate As Date) As String\r\n    ConvertToIso = ConvertToUTCISO8601TimeStamp(utc_LocalDate)\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modLibReference.bas",
    "content": "﻿Attribute VB_Name = \"modLibReference\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modLibReference\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2022\r\n' Purpose   : Module for handling functions related to Access Library references\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LocalizeLibraryReferences\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2022\r\n' Purpose   : Ensures that referenced Microsoft Access Library databases are linked to\r\n'           : the version in the same folder as the parent database if a copy of the\r\n'           : file exists in that location. (Used in some deployment scenarios)\r\n'           :\r\n'           : Background:\r\n'           : One database can reference another database as a library reference. If\r\n'           : the library database is in the same folder as the parent database, it can\r\n'           : automatically find it when the parent database is first opened on a client\r\n'           : computer. This gives the parent database the ability to open without\r\n'           : compile errors.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LocalizeLibraryReferences(Optional blnAlwaysShowGUI As Boolean)\r\n\r\n    Dim strPath As String\r\n    Dim strFolder As String\r\n    Dim dRefs As Dictionary\r\n    Dim varKey As Variant\r\n    Dim frm As Form_frmVCSMain\r\n\r\n    ' Look up the references from the current database\r\n    Perf.StartTiming\r\n    Perf.OperationStart \"Scan references\"\r\n    Set dRefs = GetReferencesDictionary\r\n    Perf.OperationEnd\r\n\r\n    ' We may not need to show the GUI if no problems are found.\r\n    If dRefs(\"ProjCount\") = 0 And Not blnAlwaysShowGUI Then\r\n        Debug.Print \"Verified local library references\"\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Reset the log file\r\n    Log.Clear\r\n    strPath = CurrentProject.FullName\r\n    strFolder = CurrentProject.Path & PathSep\r\n\r\n    ' Use the main form to display progress\r\n    DoCmd.OpenForm \"frmVCSMain\", , , , , acHidden\r\n    Set frm = Form_frmVCSMain   ' Connect to hidden instance\r\n    With frm\r\n\r\n        ' Prepare the UI screen\r\n        .cmdClose.SetFocus\r\n        .HideActionButtons\r\n        DoEvents\r\n        With .txtLog\r\n            .ScrollBars = 0\r\n            .Visible = True\r\n            .SetFocus\r\n        End With\r\n        Log.SetConsole .txtLog, .GetProgressBar\r\n\r\n        ' Show the status\r\n        .SetStatusText \"Running...\", \"Localizing References\", _\r\n            \"Linking Access database library references to files in the current folder.\"\r\n        Log.Add \"Fixing \" & dRefs(\"RefCount\") & \" references in \" & dRefs(\"ProjCount\") & \" databases. \" & _\r\n            \"This may take several seconds to complete, so please be patient.\"\r\n        Log.Spacer\r\n        .Visible = True\r\n    End With\r\n\r\n    ' Loop through databases\r\n    For Each varKey In dRefs.Keys\r\n        Select Case varKey\r\n            Case \"ProjCount\", \"RefCount\"\r\n            Case Else\r\n                Log.Add FSO.GetFileName(varKey)\r\n                ShiftOpenDatabase strFolder & varKey\r\n                FixReferences dRefs(varKey)\r\n        End Select\r\n    Next varKey\r\n    Log.Spacer\r\n\r\n    ' Reopen the original database, if it is not already open\r\n    If CurrentProject.FullName <> strPath Then\r\n        Log.Add \"Opening original database...\"\r\n        Log.Flush\r\n        ShiftOpenDatabase strPath\r\n        DoEvents\r\n    End If\r\n\r\n    Log.Spacer\r\n    Log.Add \"Operation Complete\", , , , True\r\n\r\n    ' Save the log file\r\n    Perf.EndTiming\r\n    With Log\r\n        .Flush\r\n        .Add vbCrLf & Perf.GetReports, False\r\n        .SaveFile FSO.BuildPath(Options.GetExportFolder, \"References.log\")\r\n        .Active = False\r\n        .Clear\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetReferencesDictionary\r\n' Author    : Adam Waller\r\n' Date      : 2/17/2022\r\n' Purpose   : Return a dictionary object containing the databases and references\r\n'           : that should be localized. (Returns no items if all Access libraries are\r\n'           : already localized.)\r\n'           : TIP: Use `?ConvertToJson(GetReferencesDictionary,2)` to inspect dictionary\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetReferencesDictionary() As Dictionary\r\n\r\n    Dim proj As VBProject\r\n    Dim ref As VBIDE.Reference\r\n    Dim strFolder As String\r\n    Dim strFile As String\r\n    Dim strPath As String\r\n    Dim dProjects As Dictionary\r\n    Dim dRefs As Dictionary\r\n    Dim varKey As Variant\r\n    Dim strRefPath As String\r\n\r\n    ' Create dictionary and header info\r\n    Set dProjects = New Dictionary\r\n    dProjects.CompareMode = TextCompare\r\n    dProjects(\"ProjCount\") = 0\r\n    dProjects(\"RefCount\") = 0\r\n\r\n    ' Activate the VB Project for the current database\r\n    Set proj = CurrentVBProject\r\n    strFolder = FSO.GetParentFolderName(proj.FileName) & PathSep\r\n\r\n    ' Loop through all projects\r\n    For Each proj In VBE.VBProjects\r\n\r\n        ' Get name of project file, and see if it exists in the parent folder\r\n        'Debug.Print proj.Name & \" (\" & proj.FileName & \")\"\r\n        strFile = FSO.GetFileName(proj.FileName)\r\n        strPath = strFolder & strFile\r\n        If FSO.FileExists(strPath) Then\r\n\r\n            ' Set up dictionary of refs for this file\r\n            Set dRefs = New Dictionary\r\n\r\n            ' Loop through references, looking for projects (libraries)\r\n            For Each ref In proj.References\r\n                If ref.Type = vbext_rk_Project Then\r\n                    'Debug.Print \" - \" & ref.Name & \" (\" & ref.FullPath & \")\"\r\n                    strRefPath = strFolder & FSO.GetFileName(ref.FullPath)\r\n                    If FSO.FileExists(strRefPath) Then\r\n                        ' Target file exists in the parent folder. Check ref path.\r\n                        If StrComp(strRefPath, ref.FullPath, vbTextCompare) <> 0 Then\r\n                            ' We need to relink this reference\r\n                            dRefs.Add ref.Name, strRefPath\r\n                        End If\r\n                    End If\r\n                End If\r\n            Next ref\r\n\r\n            ' Add to list of databases if we found at least one reference to fix\r\n            If dRefs.Count > 0 Then\r\n\r\n                ' We might have multiple VB projects with the same name, but\r\n                ' pointing to different locations. Add ALL broken references\r\n                ' since we don't know which file has the broken reference.\r\n                If dProjects.Exists(strFile) Then\r\n                    For Each varKey In dRefs.Keys\r\n                        If Not dProjects(strFile).Exists(varKey) Then\r\n                            dProjects(strPath).Add varKey, dRefs(varKey)\r\n                            dProjects(\"RefCount\") = dProjects(\"RefCount\") + 1\r\n                        End If\r\n                    Next varKey\r\n                Else\r\n                    ' Add database and broken references\r\n                    dProjects.Add strFile, dRefs\r\n                    dProjects(\"ProjCount\") = dProjects(\"ProjCount\") + 1\r\n                    dProjects(\"RefCount\") = dProjects(\"RefCount\") + dRefs.Count\r\n                End If\r\n            End If\r\n        End If\r\n    Next proj\r\n\r\n    ' Return dictionary\r\n    Set GetReferencesDictionary = dProjects\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FixReferences\r\n' Author    : Adam Waller\r\n' Date      : 2/25/2022\r\n' Purpose   : Work through the dictionary of references to fix\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub FixReferences(dProject As Dictionary)\r\n\r\n    Dim proj As VBProject\r\n    Dim colExisting As Collection\r\n    Dim varItem As Variant\r\n    Dim ref As VBIDE.Reference\r\n\r\n    Set colExisting = New Collection\r\n    Set proj = CurrentVBProject\r\n\r\n    ' Build a collection of the existing library references\r\n    ' (We can't change the order, other than by removing and adding\r\n    '  back in, so we need to work from this list to preserve the\r\n    '  order of the references.)\r\n    ' This also assumes that the the library references are AFTER\r\n    ' any type libraries. (Which should generally be the case)\r\n    For Each ref In proj.References\r\n        If ref.Type = vbext_rk_Project Then\r\n            If colExisting.Count = 0 Then\r\n                ' Only need to remove and add the ones at and after\r\n                ' the point where we find the first broken one.\r\n                If dProject.Exists(ref.Name) Then colExisting.Add Array(ref.Name, ref.FullPath)\r\n            Else\r\n                ' Add all library references from this point on.\r\n                colExisting.Add Array(ref.Name, ref.FullPath)\r\n            End If\r\n        End If\r\n    Next ref\r\n    Set ref = Nothing\r\n\r\n    ' Now go through the list of saved references, and remove and add them back in.\r\n    For Each varItem In colExisting\r\n        proj.References.Remove proj.References(varItem(0))\r\n        If dProject.Exists(varItem(0)) Then\r\n            Log.Add \" - Fixing: \" & varItem(0), , , \"Blue\"\r\n            proj.References.AddFromFile dProject(varItem(0))\r\n        Else\r\n            Log.Add \" - Linking: \" & varItem(0)\r\n            proj.References.AddFromFile varItem(1)\r\n        End If\r\n    Next varItem\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modObjects.bas",
    "content": "﻿Attribute VB_Name = \"modObjects\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modObjects\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Wrapper functions for classes and other objects available globally.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\nPrivate Const ModuleName = \"modObjects\"\r\n\r\n' Use a private type to manage instances of object classes\r\nPrivate Type udtObjects\r\n    Perf As clsPerformance\r\n    Log As clsLog\r\n    Options As clsOptions\r\n    VCSIndex As clsVCSIndex\r\n    Worker As clsWorker\r\n    Git As clsGitIntegration\r\n    Translation As clsTranslation\r\n\r\n    ' Keep a persistent reference to file system object after initializing version control.\r\n    ' This way we don't have to recreate this object dozens of times while using VCS.\r\n    FSO As Scripting.FileSystemObject\r\nEnd Type\r\nPrivate this As udtObjects\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReleaseObjects\r\n' Author    : Adam Waller\r\n' Date      : 3/28/2022\r\n' Purpose   : Release references to objects for a clean exit.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ReleaseObjects()\r\n\r\n    Set this.Perf = Nothing\r\n    Set this.Log = Nothing\r\n    Set this.Options = Nothing\r\n    Set this.VCSIndex = Nothing\r\n    Set this.Worker = Nothing\r\n    Set this.Git = Nothing\r\n    Set this.FSO = Nothing\r\n    Set this.Translation = Nothing\r\n\r\n    Dim udtEmpty As udtObjects\r\n    ' Reassign \"this\" to blank, clearing any saved data.\r\n    LSet this = udtEmpty\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadOptions\r\n' Author    : Adam Waller\r\n' Date      : 4/15/2020\r\n' Purpose   : Loads the current options from defaults and this project.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function LoadOptions() As clsOptions\r\n    Dim Options As clsOptions\r\n    Set Options = New clsOptions\r\n    Options.LoadProjectOptions\r\n    Set LoadOptions = Options\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Options\r\n' Author    : Adam Waller\r\n' Date      : 5/2/2020\r\n' Purpose   : A global property to access options from anywhere in code.\r\n'           : (Avoiding a global state is better OO programming, but this approach keeps\r\n'           :  the coding simpler when you don't have to tie everything back to the\r\n'           :  primary object.)\r\n'           : To clear the current set of options, simply set the property to nothing.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Options() As clsOptions\r\n    If this.Options Is Nothing Then Set this.Options = LoadOptions\r\n    Set Options = this.Options\r\nEnd Property\r\nPublic Property Set Options(cNewOptions As clsOptions)\r\n    Set this.Options = cNewOptions\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OptionsLoaded\r\n' Author    : Adam Waller\r\n' Date      : 5/13/2023\r\n' Purpose   : Return true if the options object has been loaded. (It is loaded on first\r\n'           : access, but in some cases we might want to avoid loading the options if\r\n'           : they are not already loaded.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get OptionsLoaded() As Boolean\r\n    OptionsLoaded = (Not this.Options Is Nothing)\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Perf\r\n' Author    : Adam Waller\r\n' Date      : 11/3/2020\r\n' Purpose   : Wrapper for performance logging class\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Perf() As clsPerformance\r\n    If this.Perf Is Nothing Then Set this.Perf = New clsPerformance\r\n    Set Perf = this.Perf\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Translation\r\n' Author    : Adam Waller\r\n' Date      : 5/19/2021\r\n' Purpose   : Expose translation class\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Translation() As clsTranslation\r\n    If this.Translation Is Nothing Then Set this.Translation = New clsTranslation\r\n    Set Translation = this.Translation\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : T\r\n' Author    : Adam Waller\r\n' Date      : 3/19/2024\r\n' Purpose   : Wrapper function to translate to current language\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function T(strText As String, Optional strReference As String, _\r\n    Optional strContext As String, Optional strComments As String, _\r\n    Optional var0, Optional var1, Optional var2, Optional var3, Optional var4, _\r\n    Optional var5, Optional var6, Optional var7, Optional var8, Optional var9)\r\n    T = Translation.T(strText, strReference, strContext, strComments, _\r\n        var0, var1, var2, var3, var4, var5, var6, var7, var8, var9)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Log\r\n' Author    : Adam Waller\r\n' Date      : 4/28/2020\r\n' Purpose   : Wrapper for log file class\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function Log(Optional blnCreateInstance As Boolean = True) As clsLog\r\n    If this.Log Is Nothing Then If blnCreateInstance Then Set this.Log = New clsLog\r\n    Set Log = this.Log\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FSO\r\n' Author    : Adam Waller, hecon5\r\n' Date      : 1/18/2019, 10/24/2024\r\n' Purpose   : Wrapper for file system object. A property allows us to clear the object\r\n'           : reference when we have completed an export or import operation.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get FSO() As Scripting.FileSystemObject\r\n\r\n    Const FunctionName As String = ModuleName & \".FSO\"\r\n    Dim RetryCount As Long\r\n\r\n    LogUnhandledErrors FunctionName\r\n    On Error Resume Next\r\n\r\nRetry:\r\n    If this.FSO Is Nothing Then Set this.FSO = New Scripting.FileSystemObject\r\n    Set FSO = this.FSO\r\n    If CatchAny(eelError, \"Retry FSO Check\", FunctionName, False, True) And RetryCount < 2 Then\r\n        ' Some machines in some environments may fail to generate the FileSystemObject the first time.\r\n        ' 99% of retries the second attempt will work. This may be due to a race condition in the OS.\r\n        ' RetryCount prevents a permanent loop if for some reason the second attempt fails out, and in\r\n        ' those cases additional tries are also likely to fail.\r\n        RetryCount = RetryCount + 1\r\n        GoTo Retry\r\n    End If\r\n    CatchAny eelCritical, \"Unable to create Scripting.FileSystemObject\", FunctionName\r\n\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VSCIndex\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2020\r\n' Purpose   : Reference to the VCS Index class (saved state from vcs-index.json)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get VCSIndex() As clsVCSIndex\r\n    If this.VCSIndex Is Nothing Then\r\n        Set this.VCSIndex = New clsVCSIndex\r\n        this.VCSIndex.LoadFromFile\r\n    End If\r\n    Set VCSIndex = this.VCSIndex\r\nEnd Property\r\nPublic Property Set VCSIndex(cIndex As clsVCSIndex)\r\n    Set this.VCSIndex = cIndex\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Worker\r\n' Author    : Adam Waller\r\n' Date      : 3/2/2023\r\n' Purpose   : Expose worker class\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Worker() As clsWorker\r\n    If this.Worker Is Nothing Then Set this.Worker = New clsWorker\r\n    Set Worker = this.Worker\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Diff\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2022\r\n' Purpose   : Wrapper for class to view diff between string and file content\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Diff() As clsViewDiff\r\n    Static cDiff As clsViewDiff\r\n    If cDiff Is Nothing Then Set cDiff = New clsViewDiff\r\n    Set Diff = cDiff\r\nEnd Property\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : Git\r\n' Author    : Adam Waller\r\n' Date      : 3/10/2023\r\n' Purpose   : Return Git integration class\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Property Get Git() As clsGitIntegration\r\n    If this.Git Is Nothing Then Set this.Git = New clsGitIntegration\r\n    Set Git = this.Git\r\nEnd Property\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modOrphaned.bas",
    "content": "﻿Attribute VB_Name = \"modOrphaned\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modVCSUtility\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Functions relating to detecting and removing orphaned items in relation\r\n'           : to the source code files and existing database objects.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\nPrivate Const ModuleName = \"modOrphaned\"\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ClearOrphanedSourceFiles\r\n' Author    : Adam Waller\r\n' Date      : 2/23/2021\r\n' Purpose   : Clears existing source files that don't have a matching object in the\r\n'           : database.\r\n'           : Note that this function is integrated with the index, so deleted files\r\n'           : are removed from the index, and potential conflicts are logged as well.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ClearOrphanedSourceFiles(cType As IDbComponent)\r\n\r\n    Dim oFolder As Scripting.Folder\r\n    Dim oFile As Scripting.File\r\n    Dim dBaseNames As Dictionary\r\n    Dim dExtensions As Dictionary\r\n    Dim dItems As Dictionary\r\n    Dim varKey As Variant\r\n    Dim varExt As Variant\r\n    Dim cItem As IDbComponent\r\n\r\n    ' No orphaned files if the folder doesn't exist.\r\n    If Not FSO.FolderExists(cType.BaseFolder) Then Exit Sub\r\n\r\n    ' Set up dictionary objects for case-insensitive comparison\r\n    Set dBaseNames = New Dictionary\r\n    dBaseNames.CompareMode = TextCompare\r\n    Set dExtensions = New Dictionary\r\n    dExtensions.CompareMode = TextCompare\r\n\r\n    ' Cache a list of base source file names for actual database objects\r\n    Perf.OperationStart \"Clear Orphaned Files\"\r\n    Set dItems = cType.GetAllFromDB(False)\r\n    For Each varKey In dItems.Keys\r\n        Set cItem = dItems(varKey)\r\n        dBaseNames.Add FSO.GetBaseName(cItem.SourceFile), vbNullString\r\n        If cType.SingleFile Then Exit For\r\n    Next varKey\r\n\r\n    ' Build dictionary of included extensions\r\n    For Each varExt In cType.FileExtensions\r\n        dExtensions.Add varExt, vbNullString\r\n    Next varExt\r\n\r\n    ' Check for single-file exports with no items\r\n    If cType.SingleFile Then\r\n        If dItems.Count = 0 Then\r\n            ' No more of these items exist in the database.\r\n            ' (For example, IMEX specs)\r\n            If FSO.FileExists(cType.SourceFile) Then\r\n                ' Compare to index to check for any source changes.\r\n                CompareToIndex cType, cType.SourceFile, dExtensions, dBaseNames\r\n            End If\r\n        End If\r\n    Else\r\n        ' Loop through files in folder\r\n        Set oFolder = FSO.GetFolder(cType.BaseFolder)\r\n        For Each oFile In oFolder.Files\r\n            ' Compare to index to find orphaned source files.\r\n            CompareToIndex cType, oFile.Path, dExtensions, dBaseNames\r\n            ' Increment the progress bar as we scan through the files\r\n            Log.Increment\r\n        Next oFile\r\n\r\n        ' Remove base folder if we don't have any files in it\r\n        If oFolder.Files.Count = 0 Then oFolder.Delete True\r\n    End If\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CompareToIndex\r\n' Author    : Adam Waller\r\n' Date      : 12/5/2023\r\n' Purpose   : Compare the potential orphaned file to the index to determine if we need\r\n'           : to list this as a possible conflict item.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub CompareToIndex(cType As IDbComponent, strFilePath As String, dExtensions As Dictionary, dBaseNames As Dictionary)\r\n\r\n    Dim strFileName As String\r\n    Dim strBaseName As String\r\n    Dim strExt As String\r\n    Dim strHash As String\r\n\r\n    ' Get base name and file extension to build primary source file name\r\n    strFileName = FSO.GetFileName(strFilePath)\r\n    strBaseName = FSO.GetBaseName(strFileName)\r\n    strExt = Mid$(strFileName, Len(strBaseName) + 2)\r\n\r\n    ' See if extension exists in cached list\r\n    If dExtensions.Exists(strExt) Then\r\n\r\n        ' See if base file name exists in list of database objects\r\n        If Not dBaseNames.Exists(strBaseName) Then\r\n\r\n            ' See if this is the primary file extension for this component type\r\n            If StrComp(strExt, dExtensions(0), vbTextCompare) = 0 Then\r\n\r\n                ' Object not found in database. Check the index\r\n                If VCSIndex.Exists(cType, strFileName) Then\r\n\r\n                    ' If file is unchanged from the index, we can go ahead and delete it.\r\n                    ' (The source file matches the last version imported or exported)\r\n                    strHash = GetSourceFilesPropertyHash(cType, strFilePath)\r\n                    If VCSIndex.Item(cType, strFileName).FilePropertiesHash = strHash Then\r\n\r\n                        ' Remove file and index entry\r\n                        Log.Add \"  Removing orphaned file: \" & cType.BaseFolder & strFileName, Options.ShowDebug\r\n                        DeleteFile strFilePath, True\r\n                        VCSIndex.Remove cType, strFileName\r\n                    Else\r\n                        ' File properties different from index. Add as a conflict to resolve.\r\n                        ' (This can happen when the last export was during a different daylight savings time\r\n                        ' setting, as the past file modified date returned by FSO is not adjusted for DST.)\r\n                        Log.Add \"  Orphaned source file does not match last export: \" & strFilePath, Options.ShowDebug\r\n                        VCSIndex.Conflicts.Add cType, strFilePath, 0, GetSourceModifiedDate(cType, strFilePath), ercDelete, strFilePath, ercDelete\r\n                    End If\r\n                Else\r\n                    ' Object does not exist in the index. It might be a new file added\r\n                    ' by another developer. Don't delete it, as it may need to be merged\r\n                    ' into the database. (Defaults to skip deleting the file)\r\n                    Log.Add \"  Found new source file: \" & strFilePath, Options.ShowDebug\r\n                    VCSIndex.Conflicts.Add cType, strFilePath, 0, GetSourceModifiedDate(cType, strFilePath), ercDelete, strFilePath, ercSkip\r\n                End If\r\n\r\n            Else\r\n                ' Not the primary extension for this component type.\r\n                ' If the primary source file exists, we will let that file handle evaluate any conflicts\r\n                If Not FSO.FileExists(SwapExtension(strFilePath, CStr(dExtensions(0)))) Then\r\n                    ' The primary source file does not exist. Go ahead and delete this orphaned file.\r\n                    Log.Add \"  Removing orphaned file: \" & cType.BaseFolder & strFileName, Options.ShowDebug\r\n                    DeleteFile strFilePath, True\r\n                End If\r\n            End If\r\n        End If\r\n    End If\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modRepair.bas",
    "content": "﻿Attribute VB_Name = \"modRepair\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modRepair\r\n' Author    : Adam Waller\r\n' Date      : 6/8/2021\r\n' Purpose   : This module is for functions used to repair conditions in the host\r\n'           : database that may impair the functionality of the add-in.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RepairColorDefinitionBlocks\r\n' Author    : Adam Waller\r\n' Date      : 6/8/2021\r\n' Purpose   : Go through all the form objects and set the color properties so that\r\n'           : the theme index lines are correctly stored in the source files.\r\n'           : See issue #183 for more details.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function RepairColorDefinitionBlocks()\r\n\r\n    Dim obj As AccessObject\r\n    Dim frm As Form\r\n    Dim ctl As Control\r\n    Dim sec As Section\r\n    Dim intSec As Integer\r\n    ' Loop through all forms\r\n    For Each obj In CurrentProject.AllForms\r\n        ' Open in design view so we can make changes.\r\n        DoCmd.OpenForm obj.Name, acDesign, , , , acHidden\r\n        Set frm = Forms(obj.Name)\r\n        ' Form properties\r\n        SetColorProperties frm.Properties\r\n        ' Control properties\r\n        For Each ctl In frm.Controls\r\n            SetColorProperties ctl.Properties\r\n        Next ctl\r\n        ' Section properties (header, detail, footer, etc...)\r\n        For intSec = acDetail To 20 ' Max sections?\r\n            On Error Resume Next\r\n            Set sec = frm.Section(intSec)\r\n            If Err Then\r\n                ' Invalid section\r\n                Err.Clear\r\n            Else\r\n                SetColorProperties sec.Properties\r\n            End If\r\n        Next intSec\r\n        ' Save and close form\r\n        DoCmd.Close acForm, obj.Name, acSaveYes\r\n    Next obj\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetColorProperties\r\n' Author    : Adam Waller\r\n' Date      : 6/8/2021\r\n' Purpose   : Reapplies the existing color properties to update the internal color\r\n'           : definitions.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub SetColorProperties(prpCollection As Properties)\r\n\r\n    Dim prp As Property\r\n    Dim dItems As Dictionary\r\n    Dim dProp As Dictionary\r\n    Dim strBase As String\r\n    Dim varKey As Variant\r\n    Dim strProp As String\r\n    Dim lngGradient As Long\r\n\r\n    Set dItems = New Dictionary\r\n    lngGradient = -1\r\n\r\n    ' Loop through properties, collecting the color-related properties\r\n    For Each prp In prpCollection\r\n        With prp\r\n            If InStr(1, .Name, \"Color\") > 0 _\r\n                Or EndsWith(.Name, \"Shade\") _\r\n                Or EndsWith(.Name, \"Tint\") Then\r\n                ' Save this property value\r\n                ' Build base name of property\r\n                strBase = MultiReplace(.Name, _\r\n                    \"ThemeColorIndex\", vbNullString, _\r\n                    \"Color\", vbNullString, _\r\n                    \"Shade\", vbNullString, _\r\n                    \"Tint\", vbNullString)\r\n                ' Save in dictionary using base name as the key\r\n                '     Fore\r\n                '       |---- ForeColor = 12345\r\n                '       |---- ForeThemeColorIndex = 3\r\n                '       |---- ForeShade = 4\r\n                '       |---- ForeTint = 100\r\n                If Not dItems.Exists(strBase) Then\r\n                    Set dProp = New Dictionary\r\n                    dItems.Add strBase, dProp\r\n                End If\r\n                dItems(strBase)(.Name) = .Value\r\n\r\n            ElseIf .Name = \"Gradient\" Then\r\n                ' Save gradient value\r\n                lngGradient = .Value\r\n            End If\r\n        End With\r\n    Next prp\r\n    ' Now, with all the properties collected, we can check\r\n    ' for the presence of the required items to represent the color\r\n    For Each varKey In dItems.Keys\r\n        Set dProp = dItems(varKey)\r\n        strProp = varKey & \"ThemeColorIndex\"\r\n        If dProp.Exists(strProp) Then\r\n            ' Has index. Check value\r\n            If dProp(strProp) = -1 Then\r\n                ' Using absolute color, not theme\r\n                ReApplyValue prpCollection, dProp, varKey & \"Color\"\r\n            Else\r\n                ' Using theme color\r\n                ReApplyValue prpCollection, dProp, varKey & \"ThemeColorIndex\"\r\n                ReApplyValue prpCollection, dProp, varKey & \"Shade\"\r\n                ReApplyValue prpCollection, dProp, varKey & \"Tint\"\r\n            End If\r\n        Else\r\n            ' No theme index. Use color value\r\n            ReApplyValue prpCollection, dProp, varKey & \"Color\"\r\n        End If\r\n    Next varKey\r\n\r\n    ' Restore any gradient property\r\n    If lngGradient >= 0 Then prpCollection(\"Gradient\") = lngGradient\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReApplyValue\r\n' Author    : Adam Waller\r\n' Date      : 6/8/2021\r\n' Purpose   : Reapply the property value to ensure it has been saved.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ReApplyValue(colProps As Properties, dProps As Dictionary, strName As String)\r\n    If dProps.Exists(strName) Then\r\n        colProps(strName).Value = colProps(strName).Value\r\n    End If\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modResource.bas",
    "content": "﻿Attribute VB_Name = \"modResource\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modResource\r\n' Author    : Adam Waller\r\n' Date      : 2/28/2022\r\n' Purpose   : Manage the resource files (such as ribbon XML and COM add-in files) used\r\n'           : when installing/updating the add-in.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadResources\r\n' Author    : Adam Waller\r\n' Date      : 2/28/2022\r\n' Purpose   : Verify resource files in tblResources. (Run after building from source\r\n'           : or launching installer on a development computer.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub VerifyResources()\r\n\r\n    ' Ribbon XML and COM add-in for the ribbon\r\n    VerifyResource \"Ribbon XML\", \"\\Ribbon\\Ribbon.xml\"\r\n    VerifyResource \"COM Addin x32\", \"\\Ribbon\\Build\\MSAccessVCSLib_win32.dll\"\r\n    VerifyResource \"COM Addin x64\", \"\\Ribbon\\Build\\MSAccessVCSLib_win64.dll\"\r\n    VerifyResource \"Hook x32\", \"\\Hook\\Build\\MSAccessVCSHook_win32.dll\"\r\n    VerifyResource \"Hook x64\", \"\\Hook\\Build\\MSAccessVCSHook_win64.dll\"\r\n\r\n    ' Template .gitignore and .gitattributes files\r\n    VerifyResource \"Default .gitignore\", \"\\.gitignore.default\"\r\n    VerifyResource \"Default .gitattributes\", \"\\.gitattributes.default\"\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetResourceHash\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Returns the hash of the resource, or an empty string if the key is not\r\n'           : found.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetResourceHash(strKey As String) As String\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset2\r\n\r\n    ' Look for specified record in resources table\r\n    Set dbs = CodeDb\r\n    Set rst = dbs.OpenRecordset( _\r\n        \"select * from tblResources where ResourceName='\" & strKey & \"'\", dbOpenDynaset)\r\n\r\n    ' Return hash if we found a record\r\n    If Not rst.EOF Then GetResourceHash = GetRstResourceHash(rst)\r\n    rst.Close\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExtractResource\r\n' Author    : Adam Waller\r\n' Date      : 2/28/2022\r\n' Purpose   : Extract a resource to a specified folder\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExtractResource(strKey As String, strFolder As String)\r\n\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset2\r\n    Dim rstFiles As DAO.Recordset2\r\n    Dim strPath As String\r\n\r\n    Set dbs = CodeDb\r\n    Set rst = dbs.OpenRecordset( _\r\n        \"select * from tblResources where ResourceName='\" & strKey & \"'\", dbOpenDynaset)\r\n\r\n    ' Check for requested key\r\n    If Not rst.EOF Then\r\n\r\n        ' Get embedded recordset of files\r\n        Set rstFiles = rst.Fields(\"Content\").Value\r\n        With rstFiles\r\n            If Not .EOF Then\r\n                strPath = strFolder & .Fields(\"FileName\")\r\n                If FSO.FileExists(strPath) Then DeleteFile strPath\r\n                .Fields(\"FileData\").SaveToFile strPath\r\n            End If\r\n        End With\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : VerifyResource\r\n' Author    : Adam Waller\r\n' Date      : 2/28/2022\r\n' Purpose   : Verify a resource in the embedded resources table. (Compare hash to file)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub VerifyResource(strKey As String, strFile As String)\r\n\r\n    Dim strPath As String\r\n    Dim dbs As DAO.Database\r\n    Dim rst As DAO.Recordset2\r\n\r\n    ' Build full path to file using system path separator\r\n    strPath = Replace(CodeProject.Path & strFile, \"\\\", PathSep)\r\n\r\n    ' First check to make sure the file exists\r\n    If FSO.FileExists(strPath) Then\r\n\r\n        ' Look for specified record in resources table\r\n        Set dbs = CodeDb\r\n        Set rst = dbs.OpenRecordset( _\r\n            \"select * from tblResources where ResourceName='\" & strKey & \"'\", dbOpenDynaset)\r\n        If rst.EOF Then\r\n            ' Record does not exist. Add it (silently)\r\n            rst.AddNew\r\n                rst!ResourceName = strKey\r\n                LoadResource rst, strPath\r\n            rst.Update\r\n        Else\r\n            ' Compare the resource hash with the file hash to see if they match.\r\n            If GetFileHash(strPath) <> GetRstResourceHash(rst) Then\r\n                rst.Edit\r\n                    LoadResource rst, strPath\r\n                    MsgBox2 \"Updated Resource\", strKey & \" has been updated from source.\", , vbInformation\r\n                rst.Update\r\n            End If\r\n        End If\r\n    Else\r\n        ' Source file does not exist. No need to go any further. (Might be running\r\n        ' on a client computer during the installation process.)\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddResource\r\n' Author    : Adam Waller\r\n' Date      : 2/28/2022\r\n' Purpose   : Add a resource to the table\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub LoadResource(rst As DAO.Recordset2, strFile As String)\r\n    Dim rstFiles As Recordset2\r\n    Set rstFiles = rst.Fields(\"Content\").Value\r\n    With rstFiles\r\n        If .EOF Then\r\n            .AddNew\r\n        Else\r\n            .Edit\r\n        End If\r\n        .Fields(\"FileData\").LoadFromFile strFile\r\n        .Update\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetRstResourceHash\r\n' Author    : Adam Waller\r\n' Date      : 3/5/2022\r\n' Purpose   : Return a hash of the resource item. (After the header portion)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetRstResourceHash(rst As DAO.Recordset2)\r\n\r\n    Dim rstFiles As Recordset2\r\n    Dim bteContent() As Byte\r\n    Dim strExt As String\r\n\r\n    Set rstFiles = rst.Fields(\"Content\").Value\r\n    With rstFiles\r\n        If Not .EOF Then\r\n            strExt = .Fields(\"FileType\").Value\r\n            bteContent = .Fields(\"FileData\").Value\r\n            GetRstResourceHash = GetBytesHash(StripOLEHeader(strExt, bteContent))\r\n        End If\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StripOLEHeader\r\n' Author    : Adam Waller\r\n' Date      : 5/12/2020\r\n' Purpose   : Strip out the OLE header so we can return the raw binary data the way\r\n'           : it would be saved as a file. (First 20 bytes (10 chars) of the data)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function StripOLEHeader(strExt As String, bteData() As Byte) As Byte()\r\n\r\n    Dim strData As String\r\n\r\n    ' Convert to string\r\n    strData = bteData\r\n\r\n    ' Strip off header, and convert back to byte array\r\n    StripOLEHeader = Mid$(strData, 8 + Len(strExt))\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modRibbonStrings.bas",
    "content": "﻿Attribute VB_Name = \"modRibbonStrings\"\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\nPrivate m_dStrings As Dictionary\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetRibbonStrings\r\n' Author    : Adam Waller\r\n' Date      : 10/29/2024\r\n' Purpose   : Return a dictionary of strings for menu labels and descriptions.\r\n'           : Applies any applicable translations before returning strings.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetRibbonStrings() As Dictionary\r\n\r\n    ' Start with empty dictionary\r\n    Set m_dStrings = New Dictionary\r\n\r\n    ' Add a comment entry so translators don't attempt to edit the strings.\r\n    AddCtl \"PLEASE NOTE:\", \"This file is automatically generated by the add-in. Please do not translate or edit this file directly.\"\r\n\r\n    ' Ribbon tab\r\n    AddCtl \"tabVersionControl\", \"Version Control\"\r\n\r\n    ' Actions group\r\n    AddCtl \"grpActions\", \"Actions\"\r\n\r\n        AddCtl \"btnShow\", \"Open Add-in\", _\r\n            strSupertip:=\"Open the main form for the add-in. From the main form you can run an export or build a project from source.\"\r\n\r\n        AddCtl \"btnExport\", \"Export Source Files\", _\r\n            strSupertip:=\"Export all source files for the current project using the current options.\"\r\n\r\n        AddCtl \"btnExportVBA\", \"Export VBA Code\", _\r\n            strSupertip:=\"Export only the components that may contain VBA code. Includes forms, reports, and VBA code modules.\"\r\n\r\n        AddCtl \"btnExportSelected\", \"Export Selected\", _\r\n            strSupertip:=\"Export the database component currently selected in the navigation pane.\"\r\n\r\n        AddCtl \"btnBuild\", \"Build From Source\", _\r\n            strSupertip:=\"Build the current project from source files.\"\r\n\r\n        AddCtl \"btnMergeBuild\", \"Merge Build\", _\r\n            strSupertip:=\"Merge changed source files into the current database.\"\r\n\r\n        AddCtl \"btnBuildAs\", \"Build As... \", _\r\n            strSupertip:=\"Build from source files to a new name or location.\"\r\n\r\n        AddCtl \"btnLoadSelected\", \"Load Selected\", _\r\n            strSupertip:=\"Import from source files the database component currently selected in the navigation pane.\"\r\n\r\n    ' Options group\r\n    AddCtl \"grpOptions\", \"Options\"\r\n\r\n        AddCtl \"btnShowOptions\", \"View Options\", _\r\n            strSupertip:=\"Open the Options form to view or change options for the current project.\"\r\n\r\n    ' Tools group\r\n    AddCtl \"grpTools\", \"Tools\"\r\n\r\n        AddCtl \"btnOpenRepository\", \"Open Repository\", _\r\n            strSupertip:=\"Open the source code repository using the application specified in the add-in options\"\r\n\r\n        AddCtl \"btnOpenSourceFolder\", \"Source Folder\", _\r\n            strSupertip:=\"Open the source folder for the current project.\"\r\n\r\n        AddCtl \"btnOpenExportLog\", \"Export Log\", _\r\n            strSupertip:=\"Open the most recent export log file.\"\r\n\r\n        AddCtl \"btnOpenBuildLog\", \"Build Log\", _\r\n            strSupertip:=\"Open the most recent build log file.\"\r\n\r\n        ' Advanced Tools menu\r\n        AddCtl \"mnuAdvancedTools\", \"Advanced Tools\", _\r\n            strSupertip:=\"These tools are intended for more advanced development purposes. Please review the documentation for these tools before using them.\"\r\n\r\n            AddCtl \"btnGoToLinkAdvancedTools\", \"View Documentation\", _\r\n                strDescription:=\"See documentation on the purpose and usage of these custom tools...\"\r\n\r\n            AddCtl \"btnLocalizeLibraryReferences\", \"Localize Library References\", _\r\n                strDescription:=\"Relink add-in library databases to the files located in the same folder as the current database\"\r\n\r\n            AddCtl \"btnRepairColors\", \"Repair Colors\", _\r\n                strDescription:=\"Rebuild color definition blocks in form objects (See documentation for more info)\"\r\n\r\n            AddCtl \"btnSplitFiles\", \"Split Files\", _\r\n                strDescription:=\"Duplicate files while preserving Git line history\"\r\n\r\n            AddCtl \"btnActivateHook\", \"Activate Hook\", _\r\n                strDescription:=\"Enable Access Application hook to report saving of objects and enabling automatic export on save.\"\r\n\r\n            AddCtl \"btnReloadRibbon\", \"Reload Ribbon\", _\r\n                strDescription:=\"Refresh the add-in ribbon menu to reflect changes in XML source\"\r\n\r\n    ' GitHub Community group\r\n    AddCtl \"grpGitHub\", \"GitHub Community\"\r\n\r\n        AddCtl \"btnGoToLinkHome\", \"Project Home\", _\r\n            strSupertip:=\"View the add-in project home on GitHub.\"\r\n\r\n        AddCtl \"btnGoToLinkDocumentation\", \"Online Documentation\", _\r\n            strSupertip:=\"View the documentation Wiki on GitHub.\"\r\n\r\n        AddCtl \"btnGoToLinkSupport\", \"Support Issues\", _\r\n            strSupertip:=\"View the add-in project support issues on GitHub.\"\r\n\r\n        AddCtl \"btnGoToLinkDownload\", \"Download Latest Version\", _\r\n            strSupertip:=\"View the GitHub download page for this add-in project.\"\r\n\r\n    ' Return dictionary\r\n    Set GetRibbonStrings = m_dStrings\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AddCtl\r\n' Author    : Adam Waller\r\n' Date      : 10/29/2024\r\n' Purpose   : Add an entry to the dictionary of translated property strings\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub AddCtl(strControl As String, strLabel As String, _\r\n                        Optional strDescription As String, _\r\n                        Optional strSupertip As String)\r\n    Dim dControl As Dictionary\r\n    Set dControl = New Dictionary\r\n    With dControl\r\n        .Add \"Label\", T(strLabel, \"Ribbon.\" & strControl & \".Label\")\r\n        .Add \"Description\", T(strDescription, \"Ribbon.\" & strControl & \".Description\")\r\n        .Add \"Supertip\", T(strSupertip, \"Ribbon.\" & strControl & \".Supertip\")\r\n    End With\r\n    m_dStrings.Add strControl, dControl\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modSqlFunctions.bas",
    "content": "﻿Attribute VB_Name = \"modSqlFunctions\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modAdpFunctions\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Functions for interacting with SQL servers and ADP projects.\r\n'           : (ADP projects were discontinued after Microsoft Access 2010)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSQLObjectModifiedDate\r\n' Author    : Adam Waller\r\n' Date      : 10/11/2017\r\n' Purpose   : Get the last modified date for the SQL object\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSQLObjectModifiedDate(strName As String, eType As eSqlObjectType) As Date\r\n\r\n    ' Use static variables so we can avoid hundreds of repeated calls\r\n    ' for the same object type. Instead use a local array after\r\n    ' pulling the initial values.\r\n    ' (Makes a significant performance gain in complex databases)\r\n    Static colCache As Collection\r\n    Static strLastType As String\r\n    Static dteCacheDate As Date\r\n\r\n    Dim rst As ADODB.Recordset\r\n    Dim strSql As String\r\n    Dim strObject As String\r\n    Dim strTypeFilter As String\r\n    Dim intPos As Integer\r\n    Dim strSchema As String\r\n    Dim varItem As Variant\r\n    Dim strType As String\r\n\r\n    ' Shortcut to clear the cached variable\r\n    If strName = vbNullString And strType = vbNullString Then\r\n        Set colCache = Nothing\r\n        strLastType = vbNullString\r\n        dteCacheDate = 0\r\n        Exit Function\r\n    End If\r\n\r\n    ' Only try this on ADP projects\r\n    If CurrentProject.ProjectType <> acADP Then Exit Function\r\n\r\n    ' Simple validation on object name\r\n    strObject = Replace(strName, \";\", vbNullString)\r\n\r\n    ' Build schema filter if required\r\n    intPos = InStr(1, strObject, \".\")\r\n    If intPos > 0 Then\r\n        strObject = Mid$(strObject, intPos + 1)\r\n        strSchema = Left$(strName, intPos - 1)\r\n        'strSchemaFilter = \" AND [schema_id]=schema_id('\" & strSchema & \"')\"\r\n    Else\r\n        strSchema = \"dbo\"\r\n    End If\r\n\r\n    ' Build type filter\r\n    Select Case eType\r\n        Case estView: strType = \"V\"\r\n        Case estStoredProcedure: strType = \"P\"\r\n        Case estTable: strType = \"U\"\r\n        Case estTrigger: strType = \"TR\"\r\n    End Select\r\n    If strType <> vbNullString Then strTypeFilter = \" AND [type]='\" & strType & \"'\"\r\n\r\n    ' Check to see if we have already cached the results\r\n    If strType = strLastType And (DateDiff(\"s\", dteCacheDate, Now) < 5) And Not colCache Is Nothing Then\r\n        ' Look through cache to find matching date\r\n        For Each varItem In colCache\r\n            If varItem(0) = strName Then\r\n                GetSQLObjectModifiedDate = varItem(1)\r\n                Exit For\r\n            End If\r\n        Next varItem\r\n    Else\r\n        ' Look up from query, and cache results\r\n        Set colCache = New Collection\r\n        dteCacheDate = Now\r\n        strLastType = strType\r\n\r\n        ' Build SQL query to find object\r\n        strSql = \"SELECT [name], schema_name([schema_id]) as [schema], modify_date FROM sys.objects WHERE 1=1 \" & strTypeFilter\r\n        Set rst = New ADODB.Recordset\r\n        With rst\r\n            .Open strSql, CurrentProject.Connection, adOpenForwardOnly, adLockReadOnly\r\n            Do While Not .EOF\r\n                ' Return date when name matches. (But continue caching additional results)\r\n                If Nz(!Name) = strObject And Nz(!schema) = strSchema Then GetSQLObjectModifiedDate = Nz(!modify_date)\r\n                If Nz(!schema) = \"dbo\" Then\r\n                    colCache.Add Array(Nz(!Name), Nz(!modify_date))\r\n                Else\r\n                    ' Include schema name in object name\r\n                    colCache.Add Array(Nz(!schema) & \".\" & Nz(!Name), Nz(!modify_date))\r\n                End If\r\n                .MoveNext\r\n            Loop\r\n            .Close\r\n        End With\r\n        Set rst = Nothing\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSQLObjectDefinitionForADP\r\n' Author    : awaller\r\n' Date      : 12/12/2016\r\n' Purpose   : Returns the SQL definition for the ADP project item.\r\n'           : (Queries, Views, Tables, etc... are not stored in Access but on the\r\n'           :  SQL server.)\r\n'           : NOTE: This takes a simplistic approach, which does not guard againts\r\n'           : certain types of SQL injection attacks. Use at your own risk!\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSQLObjectDefinitionForADP(strName As String) As String\r\n\r\n    Dim rst As ADODB.Recordset\r\n    Dim strSql As String\r\n    Dim strObject As String\r\n\r\n    ' Only try this on ADP projects\r\n    If CurrentProject.ProjectType <> acADP Then Exit Function\r\n\r\n    ' Simple validation on object name\r\n    strObject = Replace(strName, \";\", vbNullString)\r\n\r\n    strSql = \"SELECT object_definition (OBJECT_ID(N'\" & Replace$(strObject, \"'\", \"''\") & \"'))\"\r\n    '@Ignore SetAssignmentWithIncompatibleObjectType\r\n    Set rst = CurrentProject.Connection.Execute(strSql)\r\n    If Not rst.EOF Then\r\n        ' Get SQL definition\r\n        GetSQLObjectDefinitionForADP = Nz(rst(0).Value)\r\n    End If\r\n\r\n    Set rst = Nothing\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StripDboPrefix\r\n' Author    : Adam Waller\r\n' Date      : 1/25/2019\r\n' Purpose   : Removes the dbo prefix, as sometimes encountered with ADP projects\r\n'           : depending on the sql permissions of the current user.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function StripDboPrefix(strName As String) As String\r\n    If Left$(strName, 4) = \"dbo.\" Then\r\n        StripDboPrefix = Mid$(strName, 5)\r\n    Else\r\n        StripDboPrefix = strName\r\n    End If\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modStaging.bas",
    "content": "﻿Attribute VB_Name = \"modStaging\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modStaging\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2022\r\n' Purpose   : Handle the staging and restoring of the main form.\r\n'           : (It may be automatically closed when closing the current database, or it\r\n'           :  may be closed intentionally before importing a form with the same name.)\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\nPrivate Type udtThis\r\n    strHeading As String\r\n    strSubHeading As String\r\n    strLogHtml As String\r\n    blnProgVisible As Boolean\r\n    dblProgMax As Double\r\n    dblProgValue As Double\r\nEnd Type\r\nPrivate this As udtThis\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : StageForm\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2022\r\n' Purpose   : Stage the content of the form so we can close it then restore it later.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub StageMainForm()\r\n\r\n    Dim blnLogActive As Boolean\r\n    Dim frm As Form_frmVCSMain\r\n\r\n    ' Make sure the form is actually open, just in case.\r\n    DoCmd.OpenForm \"frmVCSMain\", , , , , acHidden\r\n\r\n    ' Get reference to form instance\r\n    Set frm = Form_frmVCSMain\r\n\r\n    With this\r\n        ' Get headings and content\r\n        .strHeading = frm.lblHeading.Caption\r\n        .strSubHeading = frm.lblSubheading.Caption\r\n        .strLogHtml = Nz(frm.txtLog.Value)\r\n\r\n        ' Disconnect from logging class\r\n        If Not Log.ProgressBar Is Nothing Then\r\n            .blnProgVisible = frm.lblProgFront.Visible\r\n            .dblProgMax = Log.ProgressBar.Max\r\n            .dblProgValue = Log.ProgressBar.Value\r\n        End If\r\n        Log.ReleaseConsole\r\n    End With\r\n\r\n    ' Temporarily deactivate the log so we don't trigger warnings when closing the form.\r\n    blnLogActive = Log.Active\r\n    Log.Active = False\r\n\r\n    ' Close the form, if it is open\r\n    DoCmd.Close acForm, frm.Name\r\n    Set frm = Nothing\r\n\r\n    ' Restore active property to original value\r\n    Log.Active = blnLogActive\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RestoreForm\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2022\r\n' Purpose   : Restore the form to the staged values.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub RestoreMainForm()\r\n\r\n    Dim frm As Form_frmVCSMain\r\n\r\n    ' Open form in hidden mode, and get reference to instance\r\n    DoCmd.OpenForm \"frmVCSMain\", , , , , acHidden\r\n    Set frm = Form_frmVCSMain\r\n\r\n    ' Restore settings on form\r\n    With this\r\n        ' Get headings and content\r\n        frm.lblHeading.Caption = .strHeading\r\n        frm.lblSubheading.Caption = .strSubHeading\r\n        frm.txtLog.Value = .strLogHtml\r\n        frm.txtLog.Visible = True\r\n\r\n        ' Reconnect to logging class\r\n        Log.SetConsole frm.txtLog, frm.GetProgressBar\r\n        If .blnProgVisible Then\r\n            Log.ProgressBar.Max = .dblProgMax\r\n            Log.ProgressBar.Value = .dblProgValue\r\n        Else\r\n            Log.ProgressBar.Hide\r\n        End If\r\n\r\n        ' Assume that the action buttons should be hidden\r\n        frm.cmdClose.SetFocus\r\n        frm.HideActionButtons\r\n        frm.Visible = True\r\n        DoEvents\r\n    End With\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modTimer.bas",
    "content": "﻿Attribute VB_Name = \"modTimer\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modTimer\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : API timer functions for callbacks\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n' Windows API calls for Timer functionality\r\nPrivate Declare PtrSafe Function ApiSetTimer Lib \"user32\" Alias \"SetTimer\" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr\r\nPrivate Declare PtrSafe Function ApiKillTimer Lib \"user32\" Alias \"KillTimer\" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long\r\n\r\nPrivate m_lngTimerID As LongPtr\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : WinAPITimerCallback\r\n' Author    : Adam Waller\r\n' Date      : 2/25/2022\r\n' Purpose   : Generic callback function to handle timer requests to resume operations.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub WinAPITimerCallback()\r\n\r\n    Dim strParam1 As String\r\n    Dim strParam2 As String\r\n    Dim strCommand As String\r\n\r\n    ' First, make sure we kill the timer!\r\n    KillTimer\r\n\r\n    ' Read in parameter values\r\n    strCommand = GetSetting(PROJECT_NAME, \"Timer\", \"Operation\")\r\n    strParam1 = GetSetting(PROJECT_NAME, \"Timer\", \"Param1\")\r\n    strParam2 = GetSetting(PROJECT_NAME, \"Timer\", \"Param2\")\r\n\r\n    ' Clear values from registry (In case an operation sets another timer)\r\n    SaveSetting PROJECT_NAME, \"Timer\", \"Operation\", vbNullString\r\n    SaveSetting PROJECT_NAME, \"Timer\", \"Param1\", vbNullString\r\n    SaveSetting PROJECT_NAME, \"Timer\", \"Param2\", vbNullString\r\n\r\n    ' Now, run the desired operation\r\n    Select Case strCommand\r\n\r\n        Case \"HandleRibbonCommand\"\r\n            HandleRibbonCommand strParam1\r\n\r\n        Case \"Build\"\r\n            ' Build from source (full or merge build)\r\n            Build strParam1, CBool(strParam2)\r\n\r\n        Case Else\r\n            ' Use the Run command to execute the specified operation with supplied parameters\r\n            If strParam2 <> vbNullString Then\r\n                Application.Run strCommand, strParam1, strParam2\r\n            ElseIf strParam1 <> vbNullString Then\r\n                Application.Run strCommand, strParam1\r\n            Else\r\n                Application.Run strCommand\r\n            End If\r\n\r\n    End Select\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SetTimer\r\n' Author    : Adam Waller\r\n' Date      : 2/25/2022\r\n' Purpose   : Set the API timer to trigger the desired operation\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub SetTimer(strOperation As String, _\r\n    Optional strParam1 As String, Optional strParam2 As String, _\r\n    Optional sngSeconds As Single = 0.5)\r\n\r\n    ' Make sure we are not trying to stack timer operations\r\n    If m_lngTimerID <> 0 Then\r\n        MsgBox2 \"Failed to Set Callback Timer\", _\r\n            \"Multiple callback timers are not currently supported.\", _\r\n            \"Please ensure that any previous timer was completed or killed first.\", vbExclamation\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Save parameter values\r\n    SaveSetting PROJECT_NAME, \"Timer\", \"Param1\", strParam1\r\n    SaveSetting PROJECT_NAME, \"Timer\", \"Param2\", strParam2\r\n\r\n    ' Save ID to registry before setting the timer\r\n    SaveSetting PROJECT_NAME, \"Timer\", \"Operation\", strOperation\r\n    SaveSetting PROJECT_NAME, \"Timer\", \"TimerID\", m_lngTimerID\r\n    m_lngTimerID = ApiSetTimer(0, 0, 1000 * sngSeconds, AddressOf WinAPITimerCallback)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : KillTimer\r\n' Author    : Adam Waller\r\n' Date      : 2/25/2022\r\n' Purpose   : Kill any existing timer\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub KillTimer()\r\n    If m_lngTimerID = 0 Then m_lngTimerID = GetSetting(PROJECT_NAME, \"Timer\", \"TimerID\", 0)\r\n    If m_lngTimerID <> 0 Then\r\n        ApiKillTimer 0, m_lngTimerID\r\n        Debug.Print \"Killed API Timer \" & m_lngTimerID\r\n        m_lngTimerID = 0\r\n        SaveSetting PROJECT_NAME, \"Timer\", \"TimerID\", 0\r\n    End If\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modUIAutomation.bas",
    "content": "﻿Attribute VB_Name = \"modUIAutomation\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modUIAutomation\r\n' Author    : Adam Waller\r\n' Date      : 2/21/2023\r\n' Purpose   : Use UI Automation to access elements not available through the VBA\r\n'           : object model.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Explicit\r\n\r\nPrivate Const ModuleName = \"modUIAutomation\"\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSelectedNavPaneObject\r\n' Author    : Adam Waller\r\n' Date      : 2/14/2023\r\n' Purpose   : Return the item currently selected in the navigation pane.\r\n'           : Tip: The Accessibility Insights for Windows utility is a great way to\r\n'           : explore the UI elements in an application.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSelectedNavPaneObject() As AccessObject\r\n\r\n    Dim oClient As UIAutomationClient.CUIAutomation\r\n    Dim oSelected As UIAutomationClient.IUIAutomationElement\r\n    Dim oElement As UIAutomationClient.IUIAutomationElement\r\n    Dim oCondition As UIAutomationClient.IUIAutomationCondition\r\n\r\n    ' Create new automation client\r\n    Set oClient = New UIAutomationClient.CUIAutomation\r\n\r\n    ' Get currently selected element\r\n    Set oSelected = oClient.GetFocusedElement\r\n\r\n    ' Build condition for navigation pane item with keyboard focus\r\n    Set oCondition = oClient.CreateAndCondition( _\r\n        oClient.CreatePropertyCondition(UIA_HasKeyboardFocusPropertyId, True), _\r\n        oClient.CreatePropertyCondition(UIA_ClassNamePropertyId, \"NetUINavPaneItem\"))\r\n\r\n    ' Attempt to find the selected item (looking for keyboard focus)\r\n    ' In Access 2010, this will be several layers below. In newer versions of Access\r\n    ' the focused element may already be the desired navigation pane item.\r\n    Set oElement = oSelected.FindFirst(TreeScope_Subtree, oCondition)\r\n\r\n    ' If an item was found, the continue to drill down to get the name and type\r\n    If Not oElement Is Nothing Then\r\n        Set GetSelectedNavPaneObject = GetUnderlyingDbObjectFromButton(oClient, oElement)\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetUnderlyingDbObjectFromButton\r\n' Author    : Adam Waller\r\n' Date      : 2/21/2023\r\n' Purpose   : Return the database object from the UI button\r\n'           : Supported languages: English, German, French\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetUnderlyingDbObjectFromButton(oClient As CUIAutomation, oElement As IUIAutomationElement) As AccessObject\r\n\r\n    Dim strName As String\r\n    Dim strImage As String\r\n    Dim objItem As AccessObject\r\n\r\n    ' Read name from button name\r\n    strName = oElement.CurrentName\r\n\r\n    ' Get the object type from the image name\r\n    strImage = GetImageName(oClient, oElement)\r\n\r\n    ' Just in case something doesn't work right...\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n\r\n    ' Identify the item based on the image name\r\n    ' This sadly depends on the Access language\r\n    If LikeAny(strImage, \"Table*\", \"*Tabelle\") Then\r\n        Set objItem = CurrentData.AllTables(strName)\r\n    ElseIf LikeAny(strImage, \"*Query\", \"*Abfrage\", \"Requête*\") Then\r\n        Set objItem = CurrentData.AllQueries(strName)\r\n    ElseIf LikeAny(strImage, \"Form*\") Then\r\n        Set objItem = CurrentProject.AllForms(strName)\r\n    ElseIf LikeAny(strImage, \"Report\", \"Bericht\", \"État\") Then\r\n        Set objItem = CurrentProject.AllReports(strName)\r\n    ElseIf LikeAny(strImage, \"Macro\", \"Makro\") Then\r\n        Set objItem = CurrentProject.AllMacros(strName)\r\n    ElseIf LikeAny(strImage, \"*Modul*\") Then ' Module and Class Module\r\n        Set objItem = CurrentProject.AllModules(strName)\r\n    ElseIf LikeAny(strImage, \"Function\", \"Fonction\") Then ' ADP specific project items\r\n        Set objItem = CurrentData.AllFunctions(strName)\r\n    ElseIf LikeAny(strImage, \"StoredProcedure\", \"Procédure stockée\") Then ' ADP specific project items\r\n        Set objItem = CurrentData.AllStoredProcedures(strName)\r\n    ElseIf LikeAny(strImage, \"Diagram*\") Then ' ADP specific project items\r\n        Set objItem = CurrentData.AllDatabaseDiagrams(strName)\r\n    ElseIf strImage = vbNullString Then\r\n        ' No image name found\r\n    Else\r\n        ' Unrecognized name\r\n        Debug.Print \"Navigation pane item image name not recognized: \" _\r\n            & strImage & \" (for \" & strName & \")\"\r\n        Debug.Print \"If you are using a non-English version of Access, \" & _\r\n            \"please open an issue on GitHub to add support for your language.\"\r\n    End If\r\n\r\n    ' Report any errors retrieving underlying object\r\n    CatchAny eelError, \"Error getting underlying object for \" & strName, _\r\n        ModuleName & \".GetUnderlyingDbObjectFromButton\"\r\n\r\n    ' Return database object if we found a matching one\r\n    If Not objItem Is Nothing Then Set GetUnderlyingDbObjectFromButton = objItem\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetImageName\r\n' Author    : Adam Waller\r\n' Date      : 2/21/2023\r\n' Purpose   : Get the image name from the icon on the button\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function GetImageName(oClient As CUIAutomation, oElement As IUIAutomationElement) As String\r\n\r\n    Dim oImage As UIAutomationClient.IUIAutomationElement\r\n    Dim oCondition As UIAutomationClient.IUIAutomationCondition\r\n\r\n    ' Build condition for navigation pane item with keyboard focus\r\n    Set oCondition = oClient.CreateAndCondition( _\r\n        oClient.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ImageControlTypeId), _\r\n        oClient.CreatePropertyCondition(UIA_ClassNamePropertyId, \"NetUIImage\"))\r\n\r\n    ' Attempt to find the selected item (looking for keyboard focus)\r\n    Set oImage = oElement.FindFirst(TreeScope_Descendants, oCondition)\r\n\r\n    ' Return name of image, if found\r\n    If Not oImage Is Nothing Then GetImageName = oImage.CurrentName\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modUnitTesting.bas",
    "content": "﻿Attribute VB_Name = \"modUnitTesting\"\r\nOption Compare Database\r\nOption Explicit\r\nOption Private Module\r\n\r\nPrivate Const ModuleName As String = \"modUnitTesting\"\r\n\r\n'@TestModule\r\n'@Folder(\"Tests\")\r\n\r\nPrivate Assert As Object\r\nPrivate Fakes As Object\r\n\r\n'@ModuleInitialize\r\nPrivate Sub ModuleInitialize()\r\n    'this method runs once per module.\r\n    Set Assert = CreateObject(\"Rubberduck.AssertClass\")\r\n    Set Fakes = CreateObject(\"Rubberduck.FakesProvider\")\r\nEnd Sub\r\n\r\n'@ModuleCleanup\r\nPrivate Sub ModuleCleanup()\r\n    'this method runs once per module.\r\n    Set Assert = Nothing\r\n    Set Fakes = Nothing\r\nEnd Sub\r\n\r\n'@TestInitialize\r\nPrivate Sub TestInitialize()\r\n    'this method runs before every test in the module.\r\nEnd Sub\r\n\r\n'@TestCleanup\r\nPrivate Sub TestCleanup()\r\n    'this method runs after every test in the module.\r\nEnd Sub\r\n\r\n' Test shows that UCS-2 files exported by Access make round trip through our conversions.\r\n'@TestMethod(\"TextConversions\")\r\nPublic Sub TestUCS2toUTF8RoundTrip()\r\n    On Error GoTo TestFail\r\n\r\n    'Arrange:\r\n    Dim queryName As String\r\n    queryName = \"Temp_Test_Query_Delete_Me_Æ_ø_Å\"\r\n    Dim tempFileName As String\r\n    tempFileName = GetTempFile()\r\n\r\n    Dim UCStoUCS As String\r\n    Dim UCStoUTF As String\r\n    Dim UTFtoUTF As String\r\n    Dim UTFtoUCS As String\r\n    UCStoUCS = tempFileName & \"UCS-2toUCS-2\"\r\n    UCStoUTF = tempFileName & \"UCS-2toUTF-8\"\r\n    UTFtoUTF = tempFileName & \"UTF-8toUTF-8\"\r\n    UTFtoUCS = tempFileName & \"UTF-8toUCS-2\"\r\n\r\n    ' Use temporary query to export example file\r\n    CurrentDb.CreateQueryDef queryName, \"SELECT * FROM TEST WHERE TESTING='ÆØÅ'\"\r\n    Application.SaveAsText acQuery, queryName, tempFileName\r\n    CurrentDb.QueryDefs.Delete queryName\r\n\r\n    ' Read original export\r\n    Dim originalExport As String\r\n    With FSO.OpenTextFile(tempFileName, ForReading, False, TristateTrue)\r\n        originalExport = .ReadAll\r\n        .Close\r\n    End With\r\n\r\n    'Act:\r\n    ConvertUtf8Ucs2 tempFileName, UCStoUCS\r\n    ConvertUcs2Utf8 UCStoUCS, UCStoUTF\r\n    ConvertUcs2Utf8 UCStoUTF, UTFtoUTF\r\n    ConvertUtf8Ucs2 UTFtoUTF, UTFtoUCS\r\n\r\n    ' Read final file that went through all permutations of conversion\r\n    Dim finalFile As String\r\n    With FSO.OpenTextFile(UTFtoUCS, ForReading, False, TristateTrue)\r\n        finalFile = .ReadAll\r\n        .Close\r\n    End With\r\n\r\n    ' Cleanup temp files\r\n    'deletefile tempFileName\r\n    'deletefile UTFtoUCS\r\n\r\n    'Assert:\r\n    Assert.AreEqual originalExport, finalFile\r\n\r\n    GoTo TestExit\r\n\r\nTestFail:\r\n    Assert.Fail \"Test raised an error: #\" & Err.Number & \" - \" & Err.Description\r\n\r\nTestExit:\r\n\r\nEnd Sub\r\n\r\n'@TestMethod(\"TextConversion\")\r\nPrivate Sub TestParseSpecialCharsInJson()\r\n    On Error GoTo TestFail\r\n\r\n    'Arrange:\r\n    Dim strPath As String\r\n    Dim dict As Dictionary\r\n    Dim FSO As Object\r\n\r\n    strPath = GetTempFile\r\n\r\n    Set FSO = CreateObject(\"Scripting.FileSystemObject\")\r\n    With FSO.CreateTextFile(strPath, True)\r\n        .WriteLine \"{\"\"Test\"\":\"\"ÆØÅ are special?\"\"}\"\r\n        .Close\r\n    End With\r\n\r\n    Debug.Print strPath\r\n\r\n    'Act:\r\n    Set dict = modFileAccess.ReadJsonFile(strPath)\r\n\r\n    'Assert:\r\n    If dict Is Nothing Then\r\n        Assert.Fail \"Empty dictionary returned\"\r\n    Else\r\n        Debug.Print dict(\"Test\")\r\n        Assert.Succeed\r\n    End If\r\n\r\n\r\nTestExit:\r\n    Exit Sub\r\nTestFail:\r\n    Assert.Fail \"Test raised an error: #\" & Err.Number & \" - \" & Err.Description\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"Sorting\")\r\nPrivate Sub TestSortDictionaryByKeys()\r\n    On Error GoTo TestFail\r\n\r\n    'Arrange:\r\n    Dim dItems As Dictionary\r\n\r\n    Set dItems = New Dictionary\r\n    dItems.Add \"C\", \"C\"\r\n    dItems.Add \"A\", \"A\"\r\n    dItems.Add \"B\", \"B\"\r\n\r\n    'Act:\r\n    Set dItems = SortDictionaryByKeys(dItems)\r\n\r\n    'Assert:\r\n    Assert.AreEqual dItems.Items(0), \"A\"\r\n    Assert.AreEqual dItems.Items(1), \"B\"\r\n    Assert.AreEqual dItems.Items(2), \"C\"\r\n\r\nTestExit:\r\n    Exit Sub\r\nTestFail:\r\n    Assert.Fail \"Test raised an error: #\" & Err.Number & \" - \" & Err.Description\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"QuickSort\")\r\nPrivate Sub TestQuickSort()\r\n\r\n    Dim arr() As String\r\n    Dim result As String\r\n\r\n    arr = Split(\"u i a\")\r\n\r\n    QuickSort arr\r\n    result = Join(arr, \" \")\r\n    Assert.AreEqual result, \"a i u\"\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"Concat\")\r\nPrivate Sub TestConcat()\r\n\r\n    With New clsConcat\r\n        .SelfTest\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"SanitizeConnectionString\")\r\nPrivate Sub TestSanitizeConnectionString()\r\n\r\n    ' Verify semicolon placement matches original\r\n    Debug.Assert SanitizeConnectionString(\";test;test;\") = \";test;test;\"\r\n    Debug.Assert SanitizeConnectionString(\"test;test\") = \"test;test\"\r\n    Debug.Assert SanitizeConnectionString(\";test;test\") = \";test;test\"\r\n    Debug.Assert SanitizeConnectionString(\"test;test;\") = \"test;test;\"\r\n    Debug.Assert SanitizeConnectionString(\"test;test;\") = \"test;test;\"\r\n    Debug.Assert SanitizeConnectionString(\"test\") = \"test\"\r\n    Debug.Assert SanitizeConnectionString(vbNullString) = vbNullString\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"CloneDictionary\")\r\nPrivate Sub TestCloneDictionary()\r\n\r\n    Dim dFruit As Dictionary\r\n    Dim dApple As Dictionary\r\n    Dim dClone As Dictionary\r\n\r\n    Set dFruit = New Dictionary\r\n    Set dApple = New Dictionary\r\n\r\n    ' Create text compare dictionary\r\n    With dApple\r\n        .CompareMode = TextCompare\r\n        .Add \"SEED1\", \"Apple Seed\"\r\n        .Add \"seed2\", \"Apple Seed\"\r\n    End With\r\n\r\n    ' Create binary compare dictionary with nested dictionary\r\n    With dFruit\r\n        .CompareMode = BinaryCompare\r\n        .Add \"Apple\", dApple\r\n        .Add \"Orange\", \"Orange\"\r\n        .Add \"Pear\", \"Pear\"\r\n    End With\r\n\r\n    ' Clone the dictionary\r\n    Set dClone = CloneDictionary(dFruit, ecmSourceMethod)\r\n\r\n    ' Change some data in the cloned dictionary\r\n    dClone(\"Apple\")(\"Seed2\") = \"Pear Seed\"\r\n\r\n    ' Test the results to make sure it cloned correctly.\r\n    Debug.Assert dClone.Exists(\"APPLE\") = False\r\n    Debug.Assert dClone.Exists(\"Apple\") = True\r\n    Debug.Assert dClone.Exists(\"ORANGE\") = False\r\n    Debug.Assert dClone.Exists(\"Orange\") = True\r\n    Debug.Assert dClone.CompareMode = BinaryCompare\r\n    Debug.Assert dClone(\"Apple\").CompareMode = Scripting.CompareMethod.TextCompare\r\n    Debug.Assert dClone(\"Apple\").Exists(\"seed1\") = True\r\n    Debug.Assert dClone(\"Apple\").Exists(\"SEED1\") = True\r\n    Debug.Assert dClone(\"Apple\").Exists(\"Seed3\") = False\r\n    Debug.Assert dClone(\"Apple\")(\"Seed2\") = \"Pear Seed\"\r\n    Debug.Assert dFruit(\"Apple\")(\"Seed2\") = \"Apple Seed\"\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"IDbComponent Interface\")\r\nPrivate Sub TestComponentPropertyAccess()\r\n\r\n    Dim cnt As IDbComponent\r\n    Dim varTest As Variant\r\n\r\n    For Each cnt In GetContainers\r\n        ' Make sure none of the following throw an error\r\n        ' when the database object has not been set.\r\n        varTest = cnt.Name\r\n        varTest = cnt.DateModified\r\n        varTest = cnt.SourceFile\r\n        Debug.Assert cnt.DbObject Is Nothing\r\n    Next\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"IDbComponent Interface\")\r\nPrivate Sub TestUniqueComponentCategory()\r\n\r\n    Dim dList As Dictionary\r\n    Dim cnt As IDbComponent\r\n\r\n    Set dList = New Dictionary\r\n    For Each cnt In GetContainers\r\n        Debug.Assert Not dList.Exists(cnt.Category)\r\n        dList.Add cnt.Category, vbNullString\r\n    Next\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"IDbComponent Interface\")\r\nPrivate Sub TestUniqueComponentType()\r\n\r\n    Dim dList As Dictionary\r\n    Dim cnt As IDbComponent\r\n\r\n    Set dList = New Dictionary\r\n    For Each cnt In GetContainers\r\n        Debug.Assert Not dList.Exists(cnt.ComponentType)\r\n        dList.Add cnt.ComponentType, vbNullString\r\n    Next\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"IDbComponent Interface\")\r\nPrivate Sub TestUniqueBaseSubfolder()\r\n\r\n    Dim dList As Dictionary\r\n    Dim cnt As IDbComponent\r\n\r\n    Set dList = New Dictionary\r\n    For Each cnt In GetContainers\r\n        If Not cnt.SingleFile Then\r\n            Debug.Assert Not dList.Exists(cnt.BaseFolder)\r\n            dList.Add cnt.BaseFolder, vbNullString\r\n        End If\r\n    Next\r\n\r\nEnd Sub\r\n\r\n\r\n' Test the operation of a progress bar without using label objects\r\n' (Uses Access system progress meter instead)\r\nPublic Sub TestMeterProgressBar()\r\n\r\n    Dim intCnt As Integer\r\n\r\n    With New clsLblProg\r\n        .Max = 20\r\n        For intCnt = 1 To 30\r\n            'Pause 0.1\r\n            .Increment\r\n        Next intCnt\r\n        .Reset\r\n        .Clear\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"clsGitIntegration.GetRepositoryRoot\")\r\nPublic Sub TestGitRepositoryRoot()\r\n\r\n    With New clsGitIntegration\r\n\r\n        ' Verify repository root for this project\r\n        Debug.Assert .GetRepositoryRoot = CurrentProject.Path & PathSep\r\n\r\n        ' Resolve from subfolder\r\n        .WorkingFolder = CurrentProject.Path & \"\\Version Control.accda.src\\modules\\\"\r\n        Debug.Assert .GetRepositoryRoot = CurrentProject.Path & PathSep\r\n\r\n        ' Return working folder when not in a git repository\r\n        ' (Also tests returning final path separator)\r\n        .WorkingFolder = \"c:\\windows\"\r\n        Debug.Assert .GetRepositoryRoot = \"c:\\windows\\\"\r\n\r\n        ' Reflect change in working folder\r\n        .WorkingFolder = vbNullString\r\n         Debug.Assert .GetRepositoryRoot = CurrentProject.Path & PathSep\r\n\r\n        ' Return specified working folder, even if it doesn't exist\r\n        .WorkingFolder = \"c:\\Some Path that Doesn't Exist\"\r\n         Debug.Assert .GetRepositoryRoot = \"c:\\Some Path that Doesn't Exist\\\"\r\n\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"InArray\")\r\nPublic Sub TestInArray()\r\n    Dim varArray As Variant\r\n    varArray = Array(\"a\", \"b\", \"c\", 1, 2, 3, #1/1/2000#)\r\n    Debug.Assert InArray(varArray, \"b\")\r\n    Debug.Assert Not InArray(varArray, \"B\")\r\n    Debug.Assert InArray(varArray, \"B\", vbTextCompare)\r\n    Debug.Assert InArray(varArray, 2)\r\n    Debug.Assert InArray(varArray, #1/1/2000#)\r\n    Debug.Assert Not InArray(varArray, Null)\r\n    Debug.Assert Not InArray(Null, \"b\")\r\n    Debug.Assert Not InArray(Array(), \"b\")\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"GetFileHash\")\r\nPublic Sub TestStringFileHash()\r\n\r\n    Const cstrText As String = \"This is my text content.\"\r\n    Dim strTempFile As String\r\n\r\n    ' Make sure we get the same result when hashing a string as hashing a file.\r\n\r\n    ' Create a file, and write our content.\r\n    strTempFile = GetTempFile\r\n    WriteFile cstrText, strTempFile\r\n\r\n    ' Compare to known hash (without BOM)\r\n    Debug.Assert GetStringHash(cstrText) = \"f80a555\"        ' Without BOM\r\n    Debug.Assert GetStringHash(cstrText, True) = \"b628391\"  ' With UTF-8 BOM and trailing vbCrLf\r\n\r\n    ' Compare results of hashing file with hashing a string.\r\n    Debug.Assert GetFileHash(strTempFile) = GetStringHash(cstrText, True)\r\n\r\n    ' Remove temp file.\r\n    FSO.DeleteFile strTempFile\r\n\r\nEnd Sub\r\n\r\n\r\n'@TestMethod(\"GetComponentClass\")\r\nPublic Sub TestGetClassFromComponentType()\r\n\r\n    Dim intType As eDatabaseComponentType\r\n\r\n    ' Test the entire enum range of component types\r\n    ' to make sure they are all assigned to a class.\r\n    For intType = edbTableDataMacro To eDatabaseComponentType.[_Last] - 1\r\n        Debug.Assert Not GetComponentClass(intType) Is Nothing\r\n    Next intType\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TestJsonNewLineIssue\r\n' Author    : Adam Waller\r\n' Date      : 7/24/2023\r\n' Purpose   : Encountered an issue where vbCrLf strings are not parsed correctly when\r\n'           : converting to JSON and back to string values.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub TestJsonNewLineIssue()\r\n\r\n    Const cstrTest As String = \"Line1\" & vbCrLf & \"Line2\" & vbCr & \"Line3\" & vbLf & \"Line4\" & vbCrLf\r\n\r\n    Dim dTest As Dictionary\r\n    Dim strResult As String\r\n\r\n    Set dTest = New Dictionary\r\n\r\n    dTest(\"Multiline\") = cstrTest\r\n    Debug.Assert dTest(\"Multiline\") = cstrTest\r\n\r\n    ' Test round trip conversion\r\n    strResult = ParseJson(ConvertToJson(dTest, 2))(\"Multiline\")\r\n    Debug.Assert (strResult = cstrTest)\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TestSqlFormatter\r\n' Author    : Adam Waller\r\n' Date      : 8/16/2023\r\n' Purpose   : Self-test the SQL Formatter class\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub TestSqlFormatter()\r\n    With New clsSqlFormatter\r\n        .SelfTest\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TestCatch\r\n' Author    : hecon5\r\n' Date      : 10/20/2023\r\n' Purpose   : Validates that Catch operates correctly and that LogUnhandledErrors\r\n'           : doesn't create an infinite loop whether or not log exists.\r\n'           :\r\n'           : To use, run normally, after loading options / other core dependancies.\r\n'           : Then Stop the code (in VBA IDE) and then run again. Stopping code execution\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub TestCatch()\r\n\r\n    ' Specifiying a Const FunctionName allows copy/paste code and having the wrong FunctionName\r\n    ' names if (when) they change.\r\n    Const FunctionName As String = ModuleName & \".CatchTest\"\r\n\r\n    On Error Resume Next ' Clear out any errors that may happen, and continue on when errors happen.\r\n    Err.Raise 24601, \"Pre Log Test\"\r\n\r\n    ' This is the \"standard\" way of catching errors without losing them.\r\n    LogUnhandledErrors FunctionName\r\n    On Error Resume Next\r\n\r\n    ' \"Pretend\" code tossing an error.\r\n    Err.Raise 24602, \"Post Log Test\"\r\n    ' Checking for any issues post code execution.\r\n    CatchAny eelError, \"Catch Test Validation\", FunctionName\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : TestPathFunctions\r\n' Author    : Adam Waller\r\n' Date      : 4/12/2025\r\n' Purpose   : Ensure that VerifyPath is working correctly for different types of paths\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub TestPathFunctions()\r\n\r\n    ' This path may not work on all systems, but it should work in a normal dev environment\r\n    Const cstrUncBase As String = \"\\\\%computername%\\c$\\users\\%username%\\AppData\\Local\\Temp\\\"\r\n\r\n    Dim strBase As String\r\n    Dim strPath As String\r\n    Dim strTempPath As String\r\n    Dim intCnt As Integer\r\n\r\n    ' Test expansion of environment variable\r\n    strPath = ExpandEnvironmentVariables(\"%TEMP%\\test.tmp\")\r\n    Debug.Assert FSO.FolderExists(FSO.GetParentFolderName(strPath))\r\n\r\n    ' Test relative path\r\n    strBase = ExpandEnvironmentVariables(\"%TEMP%\\\")\r\n    strTempPath = strBase & \"\\subfolder\\level2\\\"\r\n    If FSO.FolderExists(strTempPath) Then FSO.DeleteFolder StripSlash(strTempPath)\r\n    Debug.Assert Not FSO.FolderExists(strTempPath)\r\n    Debug.Assert VerifyPath(strTempPath)\r\n    Debug.Assert FSO.FolderExists(strTempPath)\r\n    Debug.Assert GetRelativePath(strTempPath, strBase) = \"rel:\\subfolder\\level2\\\"\r\n    FSO.DeleteFolder strBase & \"\\subfolder\"\r\n\r\n    ' Test verify path with file name\r\n    strTempPath = strTempPath & \"test.tmp\"\r\n    Debug.Assert VerifyPath(strTempPath)\r\n    Debug.Assert FSO.FolderExists(FSO.GetParentFolderName(strTempPath))\r\n    FSO.DeleteFolder strBase & \"\\subfolder\"\r\n\r\n    ' Test UNC path (May not work on all systems)\r\n    strTempPath = ExpandEnvironmentVariables(cstrUncBase & \"subfolder\\level2\\test.tmp\")\r\n    Debug.Assert VerifyPath(strTempPath)\r\n    Debug.Assert FSO.FolderExists(FSO.GetParentFolderName(strTempPath))\r\n    FSO.DeleteFolder strBase & \"\\subfolder\"\r\n\r\n    ' LONG PATHS (> 260) (Requires OS support and newer version of Access)\r\n    'https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry\r\n    If Application.Version >= 16 Then\r\n\r\n        ' Test long path (On newer versions of Access)\r\n        strTempPath = strBase & \"\\\" & Repeat(\"subfolder\\\", 26)\r\n        Debug.Assert VerifyPath(strTempPath)\r\n        strPath = strBase & \"\\subfolder\"\r\n        If FSO.FolderExists(strPath) Then FSO.DeleteFolder strPath\r\n\r\n        ' Test long UNC path\r\n        strTempPath = cstrUncBase & Repeat(\"subfolder\\\", 26)\r\n        Debug.Assert VerifyPath(strTempPath)\r\n        strPath = strBase & \"\\subfolder\"\r\n        If FSO.FolderExists(strPath) Then FSO.DeleteFolder strPath\r\n    End If\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modUtcConverter.bas",
    "content": "﻿Attribute VB_Name = \"modUtcConverter\"\r\nPrivate Const ModuleName As String = \"modUtcConverter\"\r\n\r\n''\r\n' VBA-UTC v2.0.1\r\n' (c) Tim Hall - https://github.com/VBA-tools/VBA-UtcConverter\r\n' (c) hecon5 - 2022-08-30T16:00:20.540Z rewrites and updates.\r\n' UTC/ISO 8601 Converter for VBA\r\n'\r\n' Errors:\r\n' 10011 - UTC parsing error\r\n' 10012 - UTC conversion error\r\n' 10013 - ISO 8601 parsing error\r\n' 10014 - ISO 8601 conversion error\r\n'\r\n' @module UtcConverter\r\n' @author tim.hall.engr@gmail.com, hecon5\r\n' @license MIT (http://www.opensource.org/licenses/mit-license.php)\r\n'' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '\r\nOption Compare Text\r\nOption Explicit\r\n\r\n' Spec details which make parsing easier, instead of calling and / or doing math every time.\r\nPrivate Const TotalHoursInDay As Double = 24\r\nPrivate Const TotalMinutesInDay As Double = TotalHoursInDay * 60\r\nPrivate Const TotalSecondsInDay As Double = TotalMinutesInDay * 60\r\nPrivate Const TotalMillisecondsInDay As Double = TotalSecondsInDay * 1000\r\n\r\n\r\nPrivate Const DecimalSeparator As String = \".\"\r\nPrivate Const ISO8601DateDelimiter As String = \"-\"\r\nPrivate Const ISO8601DateTimeSeparator As String = \"T\"\r\nPrivate Const ISO8601TimeDelimiter As String = \":\"\r\nPrivate Const ISO8601UTCTimeZone As String = \"Z\"\r\n\r\n#If Mac Then\r\n#If VBA7 Then\r\n' 64-bit Mac (2016)\r\nPrivate Declare PtrSafe Function utc_popen Lib \"/usr/lib/libc.dylib\" Alias \"popen\" ( _\r\n    ByVal utc_Command As String, ByVal utc_Mode As String) As LongPtr\r\nPrivate Declare PtrSafe Function utc_pclose Lib \"/usr/lib/libc.dylib\" Alias \"pclose\" ( _\r\n    ByVal utc_File As LongPtr) As LongPtr\r\nPrivate Declare PtrSafe Function utc_fread Lib \"/usr/lib/libc.dylib\" Alias \"fread\" ( _\r\n    ByVal utc_Buffer As String, ByVal utc_Size As LongPtr, ByVal utc_Number As LongPtr, ByVal utc_File As LongPtr) As LongPtr\r\nPrivate Declare PtrSafe Function utc_feof Lib \"/usr/lib/libc.dylib\" Alias \"feof\" ( _\r\n    ByVal utc_File As LongPtr) As LongPtr\r\n\r\n#Else\r\n' 32-bit Mac\r\nPrivate Declare Function utc_popen Lib \"libc.dylib\" Alias \"popen\" ( _\r\n    ByVal utc_Command As String, ByVal utc_Mode As String) As Long\r\nPrivate Declare Function utc_pclose Lib \"libc.dylib\" Alias \"pclose\" ( _\r\n    ByVal utc_File As Long) As Long\r\nPrivate Declare Function utc_fread Lib \"libc.dylib\" Alias \"fread\" ( _\r\n    ByVal utc_Buffer As String, ByVal utc_Size As Long, ByVal utc_Number As Long, ByVal utc_File As Long) As Long\r\nPrivate Declare Function utc_feof Lib \"libc.dylib\" Alias \"feof\" ( _\r\n    ByVal utc_File As Long) As Long\r\n\r\n#End If\r\n' End of Mac\r\n#ElseIf VBA7 Then\r\n' Windows VBA7\r\n\r\nPrivate Declare PtrSafe Sub GetSystemTime Lib \"kernel32\" (lpSystemTime As utc_SYSTEMTIME)\r\nPrivate Declare PtrSafe Sub GetLocalTime Lib \"kernel32\" (lpSystemTime As utc_SYSTEMTIME)\r\n\r\n' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724421.aspx\r\n' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724949.aspx\r\n' http://msdn.microsoft.com/en-us/library/windows/desktop/ms725485.aspx\r\nPrivate Declare PtrSafe Function utc_GetTimeZoneInformation Lib \"kernel32\" Alias \"GetTimeZoneInformation\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) As Long\r\n\r\nPrivate Declare PtrSafe Function utc_SystemTimeToTzSpecificLocalTime Lib \"kernel32\" Alias \"SystemTimeToTzSpecificLocalTime\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) As Long\r\nPrivate Declare PtrSafe Function utc_TzSpecificLocalTimeToSystemTime Lib \"kernel32\" Alias \"TzSpecificLocalTimeToSystemTime\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) As Long\r\n\r\n' Dynamic Functions allow for past Time Zones to be accounted for. Above will work for \"now\".\r\n' https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-gettimezoneinformationforyear\r\n' From docs: the wYear is LOCAL time, so if the year converts over, you need to check the following (or prior) year.\r\n' to ensure you get the correct time zone detail.\r\n' Word of warning: https://devblogs.microsoft.com/oldnewthing/20110311-00/?p=11243\r\nPrivate Declare PtrSafe Function GetTimeZoneInformationForYear Lib \"kernel32\" ( _\r\n    wYear As Integer _\r\n    , ByRef lpDynamicTimeZoneInformation As DYNAMIC_TIME_ZONE_INFORMATION _\r\n    , ByRef lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) As Long\r\n\r\nPrivate Declare PtrSafe Function GetDynamicTimeZoneInformation Lib \"kernel32\" ( _\r\n    ByRef pTimeZoneInformation As DYNAMIC_TIME_ZONE_INFORMATION) As Long\r\n\r\nPrivate Declare PtrSafe Function SystemTimeToTzSpecificLocalTimeEx Lib \"kernel32\" ( _\r\n    ByRef lpDynamicTimeZoneInformation As DYNAMIC_TIME_ZONE_INFORMATION _\r\n    , ByRef lpUniversalTime As utc_SYSTEMTIME _\r\n    , ByRef lpLocalTime As utc_SYSTEMTIME) As Long\r\n\r\nPrivate Declare PtrSafe Function TzSpecificLocalTimeToSystemTimeEx Lib \"kernel32\" ( _\r\n    ByRef lpDynamicTimeZoneInformation As DYNAMIC_TIME_ZONE_INFORMATION _\r\n    , ByRef lpLocalTime As utc_SYSTEMTIME _\r\n    , ByRef lpUniversalTime As utc_SYSTEMTIME) As Long\r\n\r\n#Else\r\n' VBA 6 or less.\r\n\r\nPrivate Declare Function GetTimeZoneInformationForYear Lib \"kernel32\" ( _\r\n    wYear As Integer, _\r\n    lpDynamicTimeZoneInformation As DYNAMIC_TIME_ZONE_INFORMATION, _\r\n    lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION _\r\n) As Long\r\n\r\nPrivate Declare Function GetDynamicTimeZoneInformation Lib \"kernel32\" ( _\r\n    pTimeZoneInformation As DYNAMIC_TIME_ZONE_INFORMATION _\r\n) As Long\r\nPrivate Declare Function SystemTimeToTzSpecificLocalTimeEx Lib \"kernel32\" ( _\r\n    ByRef lpDynamicTimeZoneInformation As DYNAMIC_TIME_ZONE_INFORMATION _\r\n    , ByRef lpUniversalTime As utc_SYSTEMTIME _\r\n    , ByRef lpLocalTime As utc_SYSTEMTIME) As Long\r\n\r\nPrivate Declare Function TzSpecificLocalTimeToSystemTimeEx Lib \"kernel32\" ( _\r\n    lpDynamicTimeZoneInformation As DYNAMIC_TIME_ZONE_INFORMATION, _\r\n    lpLocalTime As utc_SYSTEMTIME, _\r\n    lpUniversalTime As utc_SYSTEMTIME _\r\n) As Long\r\n\r\nPrivate Declare Sub GetSystemTime Lib \"kernel32\" (lpSystemTime As utc_SYSTEMTIME)\r\nPrivate Declare Sub GetLocalTime Lib \"kernel32\" (lpSystemTime As utc_SYSTEMTIME)\r\n\r\nPrivate Declare Function utc_GetTimeZoneInformation Lib \"kernel32\" Alias \"GetTimeZoneInformation\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) As Long\r\nPrivate Declare Function utc_SystemTimeToTzSpecificLocalTime Lib \"kernel32\" Alias \"SystemTimeToTzSpecificLocalTime\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) As Long\r\nPrivate Declare Function utc_TzSpecificLocalTimeToSystemTime Lib \"kernel32\" Alias \"TzSpecificLocalTimeToSystemTime\" ( _\r\n    utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) As Long\r\n#End If\r\n\r\n\r\n' ============================================= '\r\n' Required types\r\n' ============================================= '\r\n\r\n#If Mac Then\r\n#If VBA7 Then\r\nPrivate Type utc_ShellResult\r\n    utc_Output As String\r\n    utc_ExitCode As LongPtr\r\nEnd Type\r\n\r\n#Else\r\nPrivate Type utc_ShellResult\r\n    utc_Output As String\r\n    utc_ExitCode As Long\r\nEnd Type\r\n\r\n#End If\r\n#Else\r\n' Windows time structures.\r\nPublic Enum TIME_ZONE\r\n    TIME_ZONE_ID_INVALID = 0\r\n    TIME_ZONE_STANDARD = 1\r\n    TIME_ZONE_DAYLIGHT = 2\r\nEnd Enum\r\n\r\nPublic Type utc_SYSTEMTIME\r\n    utc_wYear As Integer\r\n    utc_wMonth As Integer\r\n    utc_wDayOfWeek As Integer\r\n    utc_wDay As Integer\r\n    utc_wHour As Integer\r\n    utc_wMinute As Integer\r\n    utc_wSecond As Integer\r\n    utc_wMilliseconds As Integer\r\nEnd Type\r\n\r\nPrivate Type utc_TIME_ZONE_INFORMATION\r\n    utc_Bias As Long\r\n    utc_StandardName(0 To 31) As Integer\r\n    utc_StandardDate As utc_SYSTEMTIME\r\n    utc_StandardBias As Long\r\n    utc_DaylightName(0 To 31) As Integer\r\n    utc_DaylightDate As utc_SYSTEMTIME\r\n    utc_DaylightBias As Long\r\nEnd Type\r\n\r\nPrivate Type DYNAMIC_TIME_ZONE_INFORMATION\r\n    Bias As Long\r\n    StandardName(0 To 31) As Integer\r\n    StandardDate As utc_SYSTEMTIME\r\n    StandardBias As Long\r\n    DaylightName(0 To 31) As Integer\r\n    DaylightDate As utc_SYSTEMTIME\r\n    DaylightBias As Long\r\n    TimeZoneKeyName(0 To 127) As Integer\r\n    DynamicDaylightTimeDisabled As Integer\r\nEnd Type\r\n#End If\r\n\r\n\r\nPrivate Type StringBufferCache\r\n    String_Buffer As String\r\n    string_BufferPosition As Long\r\n    string_BufferLength As Long\r\nEnd Type\r\n\r\n\r\n' ============================================= '\r\n' Public Methods\r\n' ============================================= '\r\n\r\n''\r\n' Parse UTC date to local date\r\n'\r\n' @method ParseUtc\r\n' @param {Date} UtcDate\r\n' @return {Date} Local date\r\n' @throws 10011 - UTC parsing error\r\n\r\n' NOTE: Mac functions may or may not return the millisecond portion of the value; they're untested.\r\n'       Windows time parsing has been extensively tested to return the correct value.\r\n''\r\nPublic Function ParseUtc(utc_UtcDate As Date) As Date\r\n    ParseUtc = ConvertToLocalDate(utc_UtcDate)\r\nEnd Function\r\n\r\n\r\nPublic Function ConvertToLocalDate(ByVal utc_UtcDate As Date) As Date\r\n    On Error GoTo utc_ErrorHandling\r\n\r\n#If Mac Then\r\n    ConvertToLocalDate = utc_ConvertDate(utc_UtcDate)\r\n#Else\r\n    Dim utc_DynamicTimeZoneInfo As DYNAMIC_TIME_ZONE_INFORMATION\r\n    Dim UTCDateYear As Integer ' The year of UTC date.\r\n\r\n    Dim utc_UtcDateSysTime As utc_SYSTEMTIME ' Gets the year and month to compare.\r\n    Dim utc_TimeZoneInfo As utc_TIME_ZONE_INFORMATION\r\n\r\n    Dim utc_LocalDateSysTime As utc_SYSTEMTIME\r\n\r\n    ' Convert to SystemTime to facilitate more accurate date checking.\r\n    utc_UtcDateSysTime = utc_DateToSystemTime(utc_UtcDate)\r\n\r\n    UTCDateYear = utc_UtcDateSysTime.utc_wYear\r\n\r\nRecheck_Year:\r\n    ' Get the timezone data for that year.\r\n    GetDynamicTimeZoneInformation utc_DynamicTimeZoneInfo\r\n    GetTimeZoneInformationForYear UTCDateYear, utc_DynamicTimeZoneInfo, utc_TimeZoneInfo\r\n    SystemTimeToTzSpecificLocalTimeEx utc_DynamicTimeZoneInfo, utc_UtcDateSysTime, utc_LocalDateSysTime\r\n\r\n    If UTCDateYear <> utc_LocalDateSysTime.utc_wYear Then\r\n        UTCDateYear = utc_LocalDateSysTime.utc_wYear\r\n        GoTo Recheck_Year\r\n    End If\r\n\r\n    ConvertToLocalDate = utc_SystemTimeToDate(utc_LocalDateSysTime)\r\n#End If\r\n\r\n    Exit Function\r\n\r\nutc_ErrorHandling:\r\n    Err.Raise 10011, \"UtcConverter.ConvertToLocalDate\", \"UTC parsing error: \" & Err.Number & \" - \" & Err.Description\r\nEnd Function\r\n\r\n''\r\n' Convert local date to UTC date\r\n'\r\n' @method ConvertToUrc\r\n' @param {Date} utc_LocalDate\r\n' @return {Date} UTC date\r\n' @throws 10012 - UTC conversion error\r\n''\r\n'Public Function LocalToUTC(utc_LocalDate As Date) As Date\r\n'    LocalToUTC = ConvertToUtc(utc_LocalDate)\r\n'End Function\r\n\r\nPublic Function ConvertToUtc(utc_LocalDate As Date) As Date\r\n    On Error GoTo utc_ErrorHandling\r\n\r\n#If Mac Then\r\n    ConvertToUtc = utc_ConvertDate(utc_LocalDate, utc_ConvertToUtc:=True)\r\n#Else\r\n    Dim utc_DynamicTimeZoneInfo As DYNAMIC_TIME_ZONE_INFORMATION\r\n    Dim utc_TimeZoneInfo As utc_TIME_ZONE_INFORMATION\r\n    Dim utc_UtcDate As utc_SYSTEMTIME\r\n    Dim utc_LocalSystemTime As utc_SYSTEMTIME\r\n\r\n    utc_LocalSystemTime = utc_DateToSystemTime(utc_LocalDate)\r\n    GetDynamicTimeZoneInformation utc_DynamicTimeZoneInfo\r\n    GetTimeZoneInformationForYear utc_LocalSystemTime.utc_wYear, utc_DynamicTimeZoneInfo, utc_TimeZoneInfo\r\n    TzSpecificLocalTimeToSystemTimeEx utc_DynamicTimeZoneInfo, utc_LocalSystemTime, utc_UtcDate\r\n\r\n    ConvertToUtc = utc_SystemTimeToDate(utc_UtcDate)\r\n#End If\r\n\r\n    Exit Function\r\n\r\nutc_ErrorHandling:\r\n    Err.Raise 10012, \"UtcConverter.ConvertToUtc\", \"UTC conversion error: \" & Err.Number & \" - \" & Err.Description\r\nEnd Function\r\n\r\n\r\nPublic Function TimeStampDate(Optional LocalTimeStamp As Boolean = False) As Date\r\n\r\n    Dim TimeStampOut As Date\r\n\r\n#If Mac Then\r\n    ' I'm sure there's a way to do this better, but this works for now.\r\n    TimeStampOut = ConvertToUtc(VBA.Now())\r\n    If Not LocalTimeStamp Then TimeStampOut = ConvertToUtc(TimeStampOut)\r\n\r\n#Else\r\n    Dim tSysTime As utc_SYSTEMTIME\r\n\r\n    If Not LocalTimeStamp Then\r\n        GetSystemTime tSysTime\r\n        TimeStampOut = utc_SystemTimeToDate(tSysTime)\r\n\r\n    Else\r\n        GetLocalTime tSysTime\r\n        TimeStampOut = utc_SystemTimeToDate(tSysTime)\r\n    End If\r\n#End If\r\n\r\n    TimeStampDate = TimeStampOut\r\n\r\nEnd Function\r\n\r\n\r\n' NOTE: As of now, \"LocalTimeStamp\" does nothing on a Mac; need to build \"getTimeZoneOffset\" for Mac, and I don't have one.\r\n'       It will, however, output a UTC string that is correct for local time (eg, in the correct UTC for the given local time)\r\n'       I also don't know how to get millisecond values out of a Mac, so that'll return zero, as well.\r\nPublic Function ISO8601TimeStamp(Optional IncludeMilliseconds As Boolean = True _\r\n                                , Optional LocalTimeStamp As Boolean = False) As String\r\n\r\n    Dim CurrentTimeVB As Date\r\n    Dim tString_Buffer As StringBufferCache\r\n\r\n' Note: This varies slightly from ConvertToISO8601Time because it's faster to do on Windows if you have SYSTEMTIME\r\n#If Mac Then\r\n    ' I'm sure there's a way to do this better, but this works for now.\r\n    CurrentTimeVB = ConvertToUtc(VBA.Now())\r\n\r\n    String_BufferAppend tString_Buffer, VBA.Format(CurrentTimeVB, ISOTimeFormatStr)\r\n    If IncludeMilliseconds Then String_BufferAppend tString_Buffer, \".\" & VBA.Format(GetMilliseconds(CurrentTimeVB), \"000\")\r\n\r\n#Else\r\n    Dim tSysTime As utc_SYSTEMTIME\r\n\r\n    If Not LocalTimeStamp Then\r\n        GetSystemTime tSysTime\r\n        CurrentTimeVB = utc_SystemTimeToDate(tSysTime)\r\n    Else\r\n        GetLocalTime tSysTime\r\n        CurrentTimeVB = utc_SystemTimeToDate(tSysTime)\r\n    End If\r\n\r\n    String_BufferAppend tString_Buffer, VBA.Format(CurrentTimeVB, ISOTimeFormatStr)\r\n    If IncludeMilliseconds Then String_BufferAppend tString_Buffer, \".\" & VBA.Format(tSysTime.utc_wMilliseconds, \"000\")\r\n\r\n    If LocalTimeStamp Then\r\n        String_BufferAppend tString_Buffer, CurrentISOTimezoneOffset\r\n    Else\r\n        String_BufferAppend tString_Buffer, ISO8601UTCTimeZone\r\n    End If\r\n#End If\r\n\r\n    ISO8601TimeStamp = String_BufferToString(tString_Buffer)\r\nEnd Function\r\n\r\n' Wrappers to make it easier to use the below.\r\nPublic Function ParseISOTimeStampToUTC(utc_IsoString As String) As Date\r\n    ParseISOTimeStampToUTC = ParseIso(utc_IsoString, True)\r\nEnd Function\r\n\r\nPublic Function ParseISOTimeStampToLocal(utc_IsoString As String) As Date\r\n    ParseISOTimeStampToLocal = ParseIso(utc_IsoString)\r\nEnd Function\r\n\r\n' While this function may look silly, it is useful when converting disparate time zone stamps in a log to a common one when aligning user input data.\r\nPublic Function ParseISOTimeStampToISO8601TimeStamp(ByRef InVal As String _\r\n                                                , Optional LocalOut As Boolean = False) As String\r\n    Dim tDateTime As Date\r\n\r\n    tDateTime = ParseIso(InVal, True)\r\n    ParseISOTimeStampToISO8601TimeStamp = ConvertToISO8601Time(tDateTime, True, LocalOut, True)\r\nEnd Function\r\n\r\n''\r\n' Parse ISO 8601 date string to local date\r\n'\r\n' @method ParseIso\r\n' @param {Date} utc_IsoString\r\n' @return {Date} Local date\r\n' @throws 10013 - ISO 8601 parsing error\r\n'\r\nPublic Function ParseIso(utc_IsoString As String _\r\n                        , Optional ByVal OutputUTCDate As Boolean = False) As Date\r\n    On Error GoTo utc_ErrorHandling\r\n    Dim utc_Parts() As String\r\n    Dim utc_DateTimeOut As Date\r\n\r\n    If utc_IsoString = vbNullString Then Exit Function\r\n    utc_Parts = VBA.Split(utc_IsoString, ISO8601DateTimeSeparator)\r\n\r\n#If Mac Then\r\n' Mac doesn't have RegEx, so we can't map all of the dates, only date numbers, unlike RegEx which can support date names and most of the suite of\r\n' ISO8601 Date formatting.\r\n    Dim utc_DateParts() As String\r\n    Dim utc_TimeParts() As String\r\n    Dim utc_OffsetIndex As Long\r\n    Dim utc_HasOffset As Boolean\r\n    Dim utc_NegativeOffset As Boolean\r\n    Dim utc_OffsetParts() As String\r\n    Dim utc_Offset As Date\r\n\r\n    utc_DateParts = VBA.Split(utc_Parts(0), ISO8601DateDelimiter)\r\n    utc_DateTimeOut = VBA.DateSerial(VBA.CInt(utc_DateParts(0)), VBA.CInt(utc_DateParts(1)), VBA.CInt(utc_DateParts(2)))\r\n'TimeSerialDbl\r\n    If UBound(utc_Parts) > 0 Then\r\n        If VBA.InStr(utc_Parts(1), ISO8601UTCTimeZone) Then\r\n            utc_TimeParts = VBA.Split(VBA.Replace(utc_Parts(1), ISO8601UTCTimeZone, vbNullString), ISO8601TimeDelimiter)\r\n        Else\r\n            utc_OffsetIndex = VBA.InStr(1, utc_Parts(1), \"+\")\r\n            If utc_OffsetIndex = 0 Then\r\n                utc_NegativeOffset = True\r\n                utc_OffsetIndex = VBA.InStr(1, utc_Parts(1), \"-\")\r\n            End If\r\n\r\n            If utc_OffsetIndex > 0 Then\r\n                utc_HasOffset = True\r\n                utc_TimeParts = VBA.Split(VBA.Left$(utc_Parts(1), utc_OffsetIndex - 1), ISO8601TimeDelimiter)\r\n                utc_OffsetParts = VBA.Split(VBA.Right$(utc_Parts(1), Len(utc_Parts(1)) - utc_OffsetIndex), ISO8601TimeDelimiter)\r\n\r\n                Select Case UBound(utc_OffsetParts)\r\n                Case 0\r\n                    utc_Offset = TimeSerialDbl(VBA.CDbl(utc_OffsetParts(0)), 0, 0)\r\n                Case 1\r\n                    utc_Offset = TimeSerialDbl(VBA.CDbl(utc_OffsetParts(0)), VBA.CDbl(utc_OffsetParts(1)), 0)\r\n                Case 2\r\n                    ' VBA.Val does not use regional settings, use for seconds to avoid decimal/comma issues\r\n                    utc_Offset = TimeSerialDbl(VBA.CDbl(utc_OffsetParts(0)), VBA.CDbl(utc_OffsetParts(1)), VBA.CDbl(VBA.Val(utc_OffsetParts(2))))\r\n                End Select\r\n\r\n                If utc_NegativeOffset Then: utc_Offset = -utc_Offset\r\n            Else\r\n                utc_TimeParts = VBA.Split(utc_Parts(1), ISO8601TimeDelimiter)\r\n            End If\r\n        End If\r\n\r\n        Select Case UBound(utc_TimeParts)\r\n        Case 0\r\n            utc_DateTimeOut = utc_DateTimeOut + TimeSerialDbl(VBA.CInt(utc_TimeParts(0)), 0, 0)\r\n        Case 1\r\n            utc_DateTimeOut = utc_DateTimeOut + TimeSerialDbl(VBA.CInt(utc_TimeParts(0)), VBA.CInt(utc_TimeParts(1)), 0)\r\n        Case 2\r\n            ' VBA.Val does not use regional settings, use for seconds to avoid decimal/comma issues\r\n            utc_DateTimeOut = utc_DateTimeOut + TimeSerialDbl(VBA.CInt(utc_TimeParts(0)), VBA.CInt(utc_TimeParts(1)), Int(VBA.Val(utc_TimeParts(2))))\r\n        End Select\r\n\r\n        If OutputUTCDate Then utc_DateTimeOut = ConvertToLocalDate(utc_DateTimeOut)\r\n\r\n        If utc_HasOffset Then\r\n            ParseIso = utc_DateTimeOut - utc_Offset\r\n        End If\r\n    End If\r\n\r\n    Exit Function\r\n#Else\r\n    If UBound(utc_Parts) > 0 Then\r\n        utc_DateTimeOut = ConvDateUTC(utc_Parts(0)) + ConvTimeUTC(utc_Parts(1))\r\n        If Not OutputUTCDate Then\r\n            ParseIso = ConvertToLocalDate(utc_DateTimeOut)\r\n        Else\r\n            ParseIso = utc_DateTimeOut\r\n        End If\r\n    Else ' Assume any \"Date Only\" Text doesn't have a timezone (they aren't converted the other way, either)\r\n        ParseIso = ConvDateUTC(utc_Parts(0))\r\n    End If\r\n    Exit Function\r\n#End If\r\nutc_ErrorHandling:\r\n    Err.Raise 10013, \"UtcConverter.ParseIso\", \"ISO 8601 parsing error for \" & utc_IsoString & \": \" & Err.Number & \" - \" & Err.Description\r\nEnd Function\r\n\r\nPublic Function ConvertToUTCISO8601TimeStamp(ByVal LocalDateIn As Date) As String\r\n    ConvertToUTCISO8601TimeStamp = ConvertToISO8601Time(LocalDateIn, False, False, True)\r\nEnd Function\r\n\r\nPublic Function ConvertToLocalISO8601TimeStamp(ByVal UTCDateIn As Date) As String\r\n    ConvertToLocalISO8601TimeStamp = ConvertToISO8601Time(UTCDateIn, True, True, True)\r\nEnd Function\r\n\r\n''\r\n' Convert local date to ISO 8601 string\r\n'\r\n' @method ConvertToIso\r\n' @param {Date} utc_LocalDate\r\n' @return {Date} ISO 8601 string\r\n' @throws 10014 - ISO 8601 conversion error\r\n''\r\nPublic Function ConvertToIsoTime(utc_LocalDate As Date _\r\n                            , Optional OutputAsLocalDate As Boolean = False) As String\r\n\r\n    On Error GoTo utc_ErrorHandling\r\n    ConvertToIsoTime = ConvertToISO8601Time(utc_LocalDate, False, False, True)\r\n    Exit Function\r\n\r\nutc_ErrorHandling:\r\n    Err.Raise 10014, \"UtcConverter.ConvertToIso\", \"ISO 8601 conversion error: \" & Err.Number & \" - \" & Err.Description\r\nEnd Function\r\n\r\n\r\n' Convert to ISOTimeStamp\r\n' Converts a provided date into an ISO8601 formatted string.\r\n' By default, assumes you pass in a local date and outputs a UTC date string.\r\n' Set isUTC to True if you already have the UTC date.\r\n' Set OutputLocalString to true if you want to output a localized timestamp string.\r\n' This would be useful for instance if you want to know the geographic region an\r\n' action was performed by a user.\r\n' Prior versions of this function did not convert if it was a date only.\r\n' This is no longer true, all dates and times are always localaized.\r\n' To revert back to that behavior, set ConvertDateOnly to False\r\nPublic Function ConvertToISO8601Time(ByVal DateIn As Date _\r\n                                    , Optional isUTC As Boolean = False _\r\n                                    , Optional OutputLocalString As Boolean = False _\r\n                                    , Optional IncludeMilliseconds As Boolean = True) As String\r\n\r\n    Dim fStringBuffer As StringBufferCache\r\n\r\n    Dim tBias As Long\r\n    Dim OutputDate As Date\r\n    Dim MSCount As Long\r\n\r\n    If (isUTC And Not OutputLocalString) Then\r\n        tBias = 0\r\n        ' Don't need to convert.\r\n        OutputDate = DateIn\r\n    ElseIf (isUTC And OutputLocalString) Then\r\n        ' Convert UTC to local\r\n        OutputDate = ConvertToLocalDate(DateIn)\r\n        tBias = VBA.DateDiff(\"n\", OutputDate, DateIn)\r\n    ElseIf OutputLocalString Then\r\n        ' No conversi on needed; get bias.\r\n        OutputDate = DateIn\r\n        tBias = GetBiasForGivenLocalDate(OutputDate)\r\n    Else\r\n        OutputDate = ConvertToUtc(DateIn)\r\n        tBias = GetBiasForGivenLocalDate(OutputDate)\r\n    End If\r\n\r\n    Dim tString_Buffer As StringBufferCache\r\n\r\n    String_BufferAppend tString_Buffer, VBA.Format(OutputDate, ISOTimeFormatStr)\r\n\r\n    If IncludeMilliseconds Then\r\n        MSCount = GetMilliseconds(OutputDate)\r\n        String_BufferAppend tString_Buffer, \".\" & VBA.Format(MSCount, \"000\")\r\n    End If\r\n\r\n    If OutputLocalString Then\r\n        String_BufferAppend tString_Buffer, ISOTimezoneOffset(tBias)\r\n    Else\r\n        String_BufferAppend tString_Buffer, ISO8601UTCTimeZone\r\n    End If\r\n\r\n    ConvertToISO8601Time = String_BufferToString(tString_Buffer)\r\n\r\nEnd Function\r\n\r\n\r\n' Provides a format string to other functions that complies with ISO8601\r\nPublic Function ISOTimeFormatStr(Optional ByVal IncludeMilliseconds As Boolean = False _\r\n                                , Optional ByVal IncludeTimeZonePart As Boolean = False _\r\n                                , Optional ByVal IncludeLocalTimeZone As Boolean = False) As String\r\n\r\n    Static f_dFormatString As Scripting.Dictionary\r\n\r\n    Dim DictPosition As Long\r\n\r\n    If f_dFormatString Is Nothing Then Set f_dFormatString = New Scripting.Dictionary\r\n\r\n    DictPosition = (4 And IncludeMilliseconds) + (2 And IncludeTimeZonePart) + (1 And IncludeLocalTimeZone)\r\n\r\n    If Not f_dFormatString.Exists(DictPosition) Then\r\n        With New clsConcat\r\n            .Add \"yyyy-mm-ddTHH:mm:ss\"\r\n            If IncludeMilliseconds Then .Add \".000\"\r\n            If IncludeTimeZonePart And IncludeLocalTimeZone Then\r\n                .Add CurrentISOTimezoneOffset\r\n            ElseIf IncludeTimeZonePart Then\r\n                .Add ISO8601UTCTimeZone\r\n            End If\r\n            f_dFormatString.Add DictPosition, .GetStr\r\n        End With\r\n    End If\r\n\r\n    ISOTimeFormatStr = f_dFormatString.Item(DictPosition)\r\n\r\nEnd Function\r\n\r\n\r\nPrivate Function RoundUp(ByVal Value As Double) As Long\r\n    Dim lngVal As Long\r\n    Dim deltaValue As Double\r\n\r\n    lngVal = VBA.CLng(Value)\r\n    deltaValue = lngVal - Value\r\n\r\n    If deltaValue < 0 Then\r\n        RoundUp = lngVal + 1\r\n    Else\r\n        RoundUp = lngVal\r\n    End If\r\nEnd Function\r\nPrivate Function RoundDown(ByVal Value As Double) As Long\r\n    Dim lngVal As Long\r\n    Dim deltaValue As Double\r\n\r\n    lngVal = VBA.CLng(Value)\r\n    deltaValue = lngVal - Value\r\n\r\n    If deltaValue <= 0 Then\r\n        RoundDown = lngVal\r\n    Else\r\n        RoundDown = lngVal - 1\r\n    End If\r\nEnd Function\r\n\r\n\r\n' ============================================= '\r\n' Private Functions\r\n' ============================================= '\r\n\r\n#If Mac Then\r\n\r\nPrivate Function utc_ConvertDate(utc_Value As Double _\r\n                                , Optional utc_ConvertToUtc As Boolean = False) As Date\r\n    Dim utc_ShellCommand As String\r\n    Dim utc_Result As utc_ShellResult\r\n    Dim utc_Parts() As String\r\n    Dim utc_DateParts() As String\r\n    Dim utc_TimeParts() As String\r\n\r\n    If utc_ConvertToUtc Then\r\n        utc_ShellCommand = \"date -ur `date -jf '%Y-%m-%d %H:%M:%S' \" & _\r\n            \"'\" & VBA.Format$(utc_Value, \"yyyy-mm-dd HH:mm:ss\") & \"' \" & _\r\n            \" +'%s'` +'%Y-%m-%d %H:%M:%S'\"\r\n    Else\r\n        utc_ShellCommand = \"date -jf '%Y-%m-%d %H:%M:%S %z' \" & _\r\n            \"'\" & VBA.Format$(utc_Value, \"yyyy-mm-dd HH:mm:ss\") & \" +0000' \" & _\r\n            \"+'%Y-%m-%d %H:%M:%S'\"\r\n    End If\r\n\r\n    utc_Result = utc_ExecuteInShell(utc_ShellCommand)\r\n\r\n    If utc_Result.utc_Output = \"\" Then\r\n        Err.Raise 10015, \"UtcConverter.utc_ConvertDate\", \"'date' command failed\"\r\n    Else\r\n        utc_Parts = Split(utc_Result.utc_Output, \" \")\r\n        utc_DateParts = Split(utc_Parts(0), \"-\")\r\n        utc_TimeParts = Split(utc_Parts(1), \":\")\r\n\r\n        utc_ConvertDate = DateSerial(utc_DateParts(0), utc_DateParts(1), utc_DateParts(2)) + _\r\n            TimeSerial(utc_TimeParts(0), utc_TimeParts(1), utc_TimeParts(2))\r\n    End If\r\nEnd Function\r\n\r\n\r\nPrivate Function utc_ExecuteInShell(utc_ShellCommand As String) As utc_ShellResult\r\n#If VBA7 Then\r\n    ' 64bit Mac\r\n    Dim utc_File As LongPtr\r\n    Dim utc_Read As LongPtr\r\n#Else\r\n    Dim utc_File As Long\r\n    Dim utc_Read As Long\r\n#End If\r\n\r\n    Dim utc_Chunk As String\r\n\r\n    On Error GoTo utc_ErrorHandling\r\n    utc_File = utc_popen(utc_ShellCommand, \"r\")\r\n\r\n    If utc_File = 0 Then: Exit Function\r\n\r\n    Do While utc_feof(utc_File) = 0\r\n        utc_Chunk = VBA.Space$(50)\r\n        utc_Read = VBA.CLng(utc_fread(utc_Chunk, 1, VBA.Len(utc_Chunk) - 1, utc_File))\r\n        If utc_Read > 0 Then\r\n            utc_Chunk = VBA.Left$(utc_Chunk, VBA.CLng(utc_Read))\r\n            utc_ExecuteInShell.utc_Output = utc_ExecuteInShell.utc_Output & utc_Chunk\r\n        End If\r\n    Loop\r\n\r\nutc_ErrorHandling:\r\n    utc_ExecuteInShell.utc_ExitCode = CLng(utc_pclose(utc_File))\r\nEnd Function\r\n\r\n#Else\r\n' Windows\r\n\r\n\r\n' Pass in a date, this will return a Windows SystemTime structure with millisecond accuracy.\r\nPrivate Function utc_DateToSystemTime(ByRef utc_Value As Date) As utc_SYSTEMTIME ' \"Helper Functions\r\n\r\n    With utc_DateToSystemTime\r\n        .utc_wYear = VBA.Year(utc_Value)\r\n        .utc_wMonth = VBA.Month(utc_Value)\r\n        .utc_wDay = VBA.Day(utc_Value)\r\n        .utc_wHour = VBA.Hour(utc_Value)\r\n        .utc_wMinute = VBA.Minute(utc_Value)\r\n        .utc_wMilliseconds = GetMilliseconds(utc_Value)\r\n        If .utc_wMilliseconds >= 500 Then\r\n            .utc_wSecond = VBA.Second(utc_Value) - 1\r\n        Else\r\n            .utc_wSecond = VBA.Second(utc_Value)\r\n        End If\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\nPrivate Function utc_SystemTimeToDate(ByRef utc_Value As utc_SYSTEMTIME) As Date\r\n' \"Helper Function\" for Public Functions (below)\r\n\r\n    utc_SystemTimeToDate = DateSerial(utc_Value.utc_wYear _\r\n                                    , utc_Value.utc_wMonth _\r\n                                    , utc_Value.utc_wDay) + _\r\n                            TimeSerialDbl(utc_Value.utc_wHour _\r\n                                        , utc_Value.utc_wMinute _\r\n                                        , utc_Value.utc_wSecond _\r\n                                        , utc_Value.utc_wMilliseconds)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ConvDateUTC2\r\n' Author    : Adam Waller\r\n' Date      : 11/14/2023\r\n' Purpose   : Attempt a higher performance conversion first, then fall back to RegEx.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ConvDateUTC(ByRef InVal As String) As Date\r\n\r\n    Dim varParts As Variant\r\n\r\n    If InVal Like \"####-##-##\" Then\r\n        ' Use high-performance conversion to date\r\n        varParts = Split(InVal, \"-\")\r\n        ConvDateUTC = DateSerial(varParts(0), varParts(1), varParts(2))\r\n    Else\r\n        ' Fall back to slower RegEx function\r\n        ConvDateUTC = ConvDateUTC2(InVal)\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\nPrivate Function ConvDateUTC2(ByRef InVal As String) As Date\r\n\r\n    Dim RetVal As Variant\r\n    Dim RegEx As New RegExp ' Object\r\n\r\n'    Set RegEx = CreateObject(\"VBScript.RegExp\")\r\n    With RegEx\r\n        .Global = True\r\n        .Multiline = True\r\n        .IgnoreCase = False\r\n    End With\r\n\r\n    RegEx.Pattern = \"^(\\d{4})-?(\\d{2})?-?(\\d{1,2})?$|^(\\d{4})-?W(\\d{2})?-?(\\d)?$|^(\\d{4})-?(\\d{3})$\"\r\n    Dim Match As Object\r\n    Set Match = RegEx.Execute(InVal)\r\n\r\n    If Match.Count <> 1 Then Exit Function\r\n    With Match(0)\r\n        If Not IsEmpty(.SubMatches(0)) Then\r\n            'YYYY-MM-DD\r\n            If IsEmpty(.SubMatches(1)) Then  'YYYY\r\n                RetVal = DateSerial(CInt(.SubMatches(0)), 1, 1)\r\n            ElseIf IsEmpty(.SubMatches(2)) Then 'YYYY-MM\r\n                RetVal = DateSerial(CInt(.SubMatches(0)), CInt(.SubMatches(1)), 1)\r\n            Else 'YYYY-MM-DD or YYYY-MM-D\r\n                RetVal = DateSerial(CInt(.SubMatches(0)), CInt(.SubMatches(1)), CInt(.SubMatches(2)))\r\n            End If\r\n        ElseIf Not IsEmpty(.SubMatches(3)) Then\r\n            'YYYY-Www-D\r\n            RetVal = DateSerial(CInt(.SubMatches(3)), 1, 4) '4th of jan is always week 1\r\n            RetVal = RetVal - Weekday(RetVal, 2) 'subtract the weekday number of 4th of jan\r\n            RetVal = RetVal + 7 * (CInt(.SubMatches(4)) - 1) 'add 7 times the (weeknumber - 1)\r\n\r\n            If IsEmpty(.SubMatches(5)) Then 'YYYY-Www\r\n                RetVal = RetVal + 1 'choose monday of that week\r\n            Else 'YYYY-Www-D\r\n                RetVal = RetVal + CInt(.SubMatches(5)) 'choose day of that week 1-7 monday to sunday\r\n            End If\r\n        Else\r\n            'YYYY-DDD\r\n            RetVal = DateSerial(CInt(.SubMatches(6)), 1, 1) + CInt(.SubMatches(7)) - 1\r\n        End If\r\n    End With\r\n\r\n    ConvDateUTC2 = RetVal\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ConvTimeUTC\r\n' Author    : Adam Waller\r\n' Date      : 11/14/2023\r\n' Purpose   : Attempt a higher performance conversion first, then fall back to RegEx.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ConvTimeUTC(ByRef InVal As String) As Date\r\n\r\n    Dim varParts As Variant\r\n    Dim dblSeconds As Double\r\n\r\n    ' Check for standard ISO date format\r\n    If InVal Like \"##:##:##.###Z\" Then\r\n        ' Use high-performance conversion from string to date\r\n        varParts = Split(InVal, \":\")\r\n        ' Use Val() function to avoid regional decimal conversion issues\r\n        dblSeconds = Val(Mid(varParts(2), 1, Len(varParts(2)) - 1))\r\n        ConvTimeUTC = TimeSerialDbl(varParts(0), varParts(1), dblSeconds)\r\n    Else\r\n        ' Fall back to slower RegEx function\r\n        ConvTimeUTC = ConvTimeUTC2(InVal)\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ConvTimeUTC2\r\n' Author    : Adam Waller\r\n' Date      : 1/10/2024\r\n' Purpose   : Fallback date conversion using RegEx to parse ISO8601 dates\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function ConvTimeUTC2(ByRef InVal As String) As Date\r\n\r\n    Dim dblHours As Double\r\n    Dim dblMinutes As Double\r\n    Dim dblSeconds As Double\r\n    Dim dblMilliseconds As Double\r\n    Dim RegEx As New RegExp ' Object\r\n    'Set RegEx = CreateObject(\"VBScript.RegExp\")\r\n\r\n    With RegEx\r\n        .Global = True\r\n        .Multiline = False\r\n        .IgnoreCase = False\r\n    End With\r\n\r\n    ' Allowing for hours,minutes, and seconds to have partial amounts per ISO8601 standard.\r\n    RegEx.Pattern = \"^(\\d{0,2}[\\.\\,]?\\d*(?=[\\+\\-Z :]|$)):?(\\d{0,2}[\\.\\,]?\\d*(?=[\\+\\-Z :]|$))?:?(\\d{0,2}[\\.\\,]?\\d*(?=[\\+\\-Z :]|$))?(\\+|\\-|Z)?(\\d{1,2})?:?(\\d{1,2})?$\"\r\n\r\n    Dim Match As Object\r\n    Set Match = RegEx.Execute(InVal)\r\n\r\n    If Match.Count <> 1 Then Exit Function\r\n\r\n    With Match(0)\r\n        'hh:mm:ss.nnn detection\r\n        ' Load hours in, then detect if there's more to do.\r\n        dblHours = CDbl(NzEmpty(.SubMatches(0), 0))\r\n\r\n        If Not (IsEmpty(.SubMatches(3)) Or IsEmpty(.SubMatches(4)) Or NzEmpty(.SubMatches(3), ISO8601UTCTimeZone) = ISO8601UTCTimeZone) Then _\r\n            dblHours = dblHours - CDbl(NzEmpty(.SubMatches(3) & .SubMatches(4), vbNullString))\r\n\r\n        dblMinutes = CDbl(NzEmpty(.SubMatches(1), vbNullString))\r\n\r\n        If Not (IsEmpty(.SubMatches(3)) Or IsEmpty(.SubMatches(5)) Or NzEmpty(.SubMatches(3), ISO8601UTCTimeZone) = ISO8601UTCTimeZone) Then _\r\n            dblMinutes = dblMinutes - CDbl(NzEmpty(.SubMatches(3), vbNullString) & NzEmpty(.SubMatches(5), vbNullString))\r\n\r\n        ' Use Val() function to avoid regional decimal conversion issues\r\n        dblSeconds = Val(NzEmpty(.SubMatches(2), vbNullString))\r\n    End With\r\n\r\n    ConvTimeUTC2 = TimeSerialDbl(dblHours, dblMinutes, dblSeconds)\r\n\r\nEnd Function\r\n\r\n\r\nPrivate Function NzEmpty(ByVal Value As Variant, Optional ByVal value_when_null As Variant = 0) As Variant\r\n\r\n    Dim return_value As Variant\r\n    On Error Resume Next 'supress error handling\r\n\r\n    If IsEmpty(Value) Or IsNull(Value) Or (VarType(Value) = vbString And Value = vbNullString) Then\r\n        return_value = value_when_null\r\n    Else\r\n        return_value = Value\r\n    End If\r\n\r\n    Err.Clear 'clear any errors that might have occurred\r\n    On Error GoTo 0 'reinstate error handling\r\n\r\n    NzEmpty = return_value\r\n\r\nEnd Function\r\n#End If\r\n\r\n\r\n' Will return a Date type Double (specified as Double because it makes VBA less likely to \"help\")\r\nPublic Function TimeSerialDbl(ByVal HoursIn As Double _\r\n                            , ByVal MinutesIn As Double _\r\n                            , ByVal SecondsIn As Double _\r\n                            , Optional ByVal MillisecondsIn As Double = 0) As Double\r\n\r\n    Dim tMS As Double\r\n    Dim tSec As Double\r\n    Dim tSecTemp As Double\r\n\r\n    tSec = VBA.CDbl(RoundDown(SecondsIn))\r\n    tSecTemp = SecondsIn - tSec\r\n    tMS = (tSecTemp * (TotalMillisecondsInDay / TotalSecondsInDay)) \\ 1\r\n    tMS = tMS + MillisecondsIn\r\n    If (tSecTemp > 0.5) Then tSec = tSec - 1\r\n    If tMS = 500 Then tMS = tMS - 0.001 ' Shave a hair, because otherwise it'll round up too much.\r\n    TimeSerialDbl = (HoursIn / TotalHoursInDay) + (MinutesIn / TotalMinutesInDay) + CDbl((tSec / TotalSecondsInDay)) + (tMS / TotalMillisecondsInDay)\r\n\r\nEnd Function\r\n\r\n\r\n' If given a time double, will return the millisecond portion of the time.\r\nPrivate Function GetMilliseconds(ByRef TimeIn As Date) As Variant\r\n\r\n    Dim IntDatePart As Long\r\n    Dim DblTimePart As Double\r\n    Dim LngSeconds As Long ' Used to remove whole seconds.\r\n    Dim DblSecondsPart As Double\r\n\r\n    Dim DblMS As Double\r\n    Dim MSCount As Double\r\n\r\n    ' Get rid of the date portion\r\n    ' There is an annoying bug where VBA rounds up in certain cases when\r\n    ' using the \\ operator and dividing by 1. So, divide by 2 and double it.\r\n    ' this side steps the bug and ensures it always rounds down.\r\n    IntDatePart = RoundDown(TimeIn)\r\n    DblTimePart = TimeIn - IntDatePart\r\n\r\n    LngSeconds = RoundDown(TotalSecondsInDay * DblTimePart)\r\n    DblSecondsPart = LngSeconds / TotalSecondsInDay\r\n    DblMS = DblTimePart - DblSecondsPart\r\n    MSCount = ((DblMS * (TotalMillisecondsInDay))) \\ 1\r\n    If MSCount >= 1000 Then MSCount = 0\r\n    GetMilliseconds = MSCount\r\n\r\nEnd Function\r\n\r\n\r\nPublic Function CurrentLocalBiasFromUTC(Optional ByVal OutputAsHours As Boolean = False) As Long\r\n''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\r\n' This returns the CURRENT amount of time in minutes (if OutputAsHours is omitted or\r\n' false) or hours (if OutputAsHours is True) that should be added (or subtracted) to the\r\n' local time to get UTC. It should (untested on Mac as of yet) return the value\r\n' adjusted for DST if active.\r\n'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''\r\n    Dim tBias As Long\r\n\r\n#If Mac Then\r\n    ' While we could do this for the Windows version, too, the Windows functions are rock solid and\r\n    ' work (these work, too), and are certain to get the correct data. I'm reasonably sure these\r\n    ' work now, but don't have a Mac to test.\r\n    tBias = GetBiasForGivenLocalDate(VBA.Now(), OutputAsHours)\r\n#Else\r\n    Dim TZI As utc_TIME_ZONE_INFORMATION\r\n    Dim DST As TIME_ZONE\r\n    DST = utc_GetTimeZoneInformation(TZI)\r\n\r\n    If DST = TIME_ZONE_DAYLIGHT Then\r\n        tBias = TZI.utc_Bias + TZI.utc_DaylightBias\r\n    Else\r\n        tBias = TZI.utc_Bias + TZI.utc_StandardBias\r\n    End If\r\n\r\n    If OutputAsHours Then tBias = tBias / 60 ' This is already done in GetBiasForGivenLocalDate for Mac.\r\n#End If\r\n\r\n    CurrentLocalBiasFromUTC = tBias\r\n\r\nEnd Function\r\n\r\n\r\nPublic Function CurrentISOTimezoneOffset() As String\r\n    CurrentISOTimezoneOffset = ISOTimezoneOffset(CurrentLocalBiasFromUTC)\r\nEnd Function\r\n\r\n\r\nPublic Function GetBiasForGivenLocalDate(ByRef LocalDateIn As Date _\r\n                                        , Optional ByVal OutputAsHours As Boolean = False) As Long\r\n\r\n    Dim DateUTCNow As Date\r\n\r\n    DateUTCNow = ConvertToUtc(LocalDateIn)\r\n\r\n    ' I tried to get fancy here and retrieve the bias from the OS, but that turned into a huge amount of work.\r\n    ' unless your time zone is defined by change on a specific day, this is far simpler and easier\r\n    ' than chasing week numbers around.\r\n    If Not OutputAsHours Then\r\n        GetBiasForGivenLocalDate = VBA.DateDiff(\"n\", LocalDateIn, DateUTCNow)\r\n    Else\r\n        GetBiasForGivenLocalDate = VBA.DateDiff(\"h\", LocalDateIn, DateUTCNow)\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\nPublic Function ISOTimezoneOffsetOnDate(ByVal LocalDateIn As Date) As String\r\n    ISOTimezoneOffsetOnDate = ISOTimezoneOffset(GetBiasForGivenLocalDate(LocalDateIn))\r\nEnd Function\r\n\r\n\r\n' Provides the ISO Offset time from an input (or current offset if none is passed in) to build an ISO8601 output String\r\n'\r\nPrivate Function ISOTimezoneOffset(Optional TimeBias As Long = 0) As String\r\n\r\n    Dim OffsetLong As Long\r\n    Dim hourOffset As Long\r\n    Dim minOffset As Long\r\n\r\n    If TimeBias = 0 Then\r\n\r\n        ISOTimezoneOffset = ISO8601UTCTimeZone\r\n\r\n    Else\r\n        ' Counterintuitively, the Bias is postive (time ahead),\r\n        ' and the offset is the negative value of bias.\r\n        OffsetLong = TimeBias * -1\r\n        hourOffset = OffsetLong \\ 60\r\n        minOffset = OffsetLong Mod 60\r\n\r\n        With New clsConcat\r\n            If OffsetLong > 0 Then .Add \"+\"\r\n            .Add VBA.CStr(VBA.Format(hourOffset, \"00\"))\r\n            .Add ISO8601TimeDelimiter\r\n            .Add VBA.CStr(VBA.Format(minOffset, \"00\"))\r\n\r\n            ISOTimezoneOffset = .GetStr\r\n        End With\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n' String_BufferAppend\r\n' Based on VBA-Tools\\Jsonconverter's \"json_BufferAppend\" functions\r\n' To use, your calling routine needs to store the input variables to be handed back.\r\nPrivate Sub String_BufferAppend(ByRef StringBufferIn As StringBufferCache _\r\n                                , ByRef String_Append As Variant)\r\n    ' VBA can be slow to append strings due to allocating a new string for each append\r\n    ' Instead of using the traditional append, allocate a large empty string and then copy string at append position\r\n    '\r\n    ' Example:\r\n    ' Buffer: \"abc  \"\r\n    ' Append: \"def\"\r\n    ' Buffer Position: 3\r\n    ' Buffer Length: 5\r\n    '\r\n    ' Buffer position + Append length > Buffer length -> Append chunk of blank space to buffer\r\n    ' Buffer: \"abc       \"\r\n    ' Buffer Length: 10\r\n    '\r\n    ' Put \"def\" into buffer at position 3 (0-based)\r\n    ' Buffer: \"abcdef    \"\r\n    '\r\n    ' Approach based on cStringBuilder from vbAccelerator\r\n    ' http://www.vbaccelerator.com/home/VB/Code/Techniques/RunTime_Debug_Tracing/VB6_Tracer_Utility_zip_cStringBuilder_cls.asp\r\n    '\r\n    ' and clsStringAppend from Philip Swannell\r\n    ' https://github.com/VBA-tools/VBA-JSON/pull/82\r\n\r\n    Dim String_AppendLength As Long\r\n    Dim String_LengthPlusPosition As Long\r\n\r\n    String_AppendLength = VBA.Len(String_Append)\r\n    String_LengthPlusPosition = String_AppendLength + StringBufferIn.string_BufferPosition\r\n\r\n    If String_LengthPlusPosition > StringBufferIn.string_BufferLength Then\r\n        ' Appending would overflow buffer, add chunk\r\n        ' (double buffer length or append length, whichever is bigger)\r\n        Dim string_AddedLength As Long\r\n        string_AddedLength = IIf(String_AppendLength > StringBufferIn.string_BufferLength, String_AppendLength, StringBufferIn.string_BufferLength)\r\n\r\n        StringBufferIn.String_Buffer = StringBufferIn.String_Buffer & VBA.Space$(string_AddedLength)\r\n        StringBufferIn.string_BufferLength = StringBufferIn.string_BufferLength + string_AddedLength\r\n    End If\r\n\r\n    ' Note: Namespacing with VBA.Mid$ doesn't work properly here, throwing compile error:\r\n    ' Function call on left-hand side of assignment must return Variant or Object\r\n    If String_AppendLength > 0 Then\r\n        Mid$(StringBufferIn.String_Buffer, StringBufferIn.string_BufferPosition + 1, String_AppendLength) = CStr(String_Append)\r\n    End If\r\n    StringBufferIn.string_BufferPosition = StringBufferIn.string_BufferPosition + String_AppendLength\r\nEnd Sub\r\n\r\nPrivate Function String_BufferToString(ByRef StringBufferIn As StringBufferCache) As String\r\n    If StringBufferIn.string_BufferPosition > 0 Then\r\n        String_BufferToString = VBA.Left$(StringBufferIn.String_Buffer, StringBufferIn.string_BufferPosition)\r\n    End If\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modVCSUtility.bas",
    "content": "﻿Attribute VB_Name = \"modVCSUtility\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modVCSUtility\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Utility functions specific to the VCS project but not publicly exposed.\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n' Control the interaction mode for the add-in\r\nPublic InteractionMode As eInteractionMode\r\n\r\nPrivate Declare PtrSafe Function SetFocus Lib \"user32\" (ByVal hwnd As LongPtr) As LongPtr\r\nPrivate Declare PtrSafe Function SetKeyboardState Lib \"user32\" (lppbKeyState As Any) As Long\r\nPrivate Declare PtrSafe Function GetKeyboardState Lib \"user32\" (pbKeyState As Any) As Long\r\nPrivate Declare PtrSafe Function GetWindowThreadProcessId Lib \"user32\" (ByVal hwnd As LongPtr, ByRef lpdwProcessId As LongPtr) As Long\r\nPrivate Declare PtrSafe Function AttachThreadInput Lib \"user32\" (ByVal idAttach As Long, ByVal idAttachTo As Long, ByVal fAttach As Long) As Long\r\nPrivate Declare PtrSafe Function SetForegroundWindow Lib \"user32\" (ByVal hwnd As LongPtr) As Long\r\n\r\nPrivate Const ModuleName = \"modVCSUtility\"\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAllContainers\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2020\r\n' Purpose   : Return a collection of all containers.\r\n'           : NOTE: The order doesn't matter for export, but is VERY important\r\n'           : when building the project from source.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetContainers(Optional intFilter As eContainerFilter = ecfAllObjects) As Collection\r\n\r\n    Dim blnADP As Boolean\r\n    Dim blnMDB As Boolean\r\n\r\n    blnADP = (CurrentProject.ProjectType = acADP)\r\n    blnMDB = (CurrentProject.ProjectType = acMDB)\r\n\r\n    Set GetContainers = New Collection\r\n    With GetContainers\r\n        Select Case intFilter\r\n\r\n            ' Primary case for processing all objects\r\n            Case ecfAllObjects\r\n                If blnMDB Then\r\n                    ' Handle the connections early as possible but only for MDB formats\r\n                    .Add New clsDbConnection\r\n                End If\r\n\r\n                ' Shared objects in both MDB and ADP formats\r\n                .Add New clsDbProject\r\n                .Add New clsDbVbeProject\r\n                .Add New clsDbVbeReference\r\n                .Add New clsDbVbeForm\r\n                .Add New clsDbProjProperty\r\n                .Add New clsDbSavedSpec\r\n                .Add New clsDbModule\r\n                .Add New clsDbCommandBar\r\n                If blnADP Then\r\n                    ' Some types of objects only exist in ADP projects\r\n                    .Add New clsAdpFunction\r\n                    .Add New clsAdpServerView\r\n                    .Add New clsAdpProcedure\r\n                    .Add New clsAdpTable\r\n                    .Add New clsAdpTrigger\r\n                ElseIf blnMDB Then\r\n                    ' These objects only exist in DAO databases\r\n                    .Add New clsDbSharedImage\r\n                    .Add New clsDbTheme\r\n                    .Add New clsDbProperty\r\n                    .Add New clsDbImexSpec\r\n                    .Add New clsDbTableDef\r\n                    .Add New clsDbQuery\r\n                End If\r\n                ' Additional objects to import after ADP/MDB specific items\r\n                .Add New clsDbForm\r\n                .Add New clsDbMacro\r\n                .Add New clsDbReport\r\n                .Add New clsDbTableData\r\n                If blnMDB Then\r\n                    .Add New clsDbTableDataMacro\r\n                    .Add New clsDbRelation\r\n                    .Add New clsDbDocument\r\n                    .Add New clsDbNavPaneGroup\r\n                    .Add New clsDbHiddenAttribute\r\n                End If\r\n\r\n            ' Process only items that may contain VBA code\r\n            Case ecfVBAItems\r\n\r\n                .Add New clsDbModule\r\n                .Add New clsDbForm\r\n                .Add New clsDbReport\r\n\r\n        End Select\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetClassFromObject\r\n' Author    : Adam Waller\r\n' Date      : 2/22/2023\r\n' Purpose   : Returns a database component class from a database object. (Used when\r\n'           : exporting a single object.)\r\n'           : Note that not all component types are supported.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetClassFromObject(objItem As AccessObject) As IDbComponent\r\n\r\n    Dim cItem As IDbComponent\r\n\r\n    ' Map to correct component class\r\n    Select Case objItem.Type\r\n        Case acForm:    Set cItem = New clsDbForm\r\n        Case acMacro:   Set cItem = New clsDbMacro\r\n        Case acModule:  Set cItem = New clsDbModule\r\n        Case acQuery:   Set cItem = New clsDbQuery\r\n        Case acReport:  Set cItem = New clsDbReport\r\n        Case acTable:   Set cItem = New clsDbTableDef\r\n        Case Else\r\n            ' Not currently supported\r\n    End Select\r\n\r\n    ' Set database item and return class instance\r\n    If Not cItem Is Nothing Then\r\n        Set cItem.DbObject = objItem\r\n        Set GetClassFromObject = cItem\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetComponentClass\r\n' Author    : Adam Waller\r\n' Date      : 6/3/2023\r\n' Purpose   : Return an instance of the component class from the component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetComponentClass(intType As eDatabaseComponentType) As IDbComponent\r\n    Dim cls As IDbComponent\r\n    Select Case intType\r\n        Case edbAdpFunction:        Set cls = New clsAdpFunction\r\n        Case edbAdpServerView:      Set cls = New clsAdpServerView\r\n        Case edbAdpStoredProcedure: Set cls = New clsAdpProcedure\r\n        Case edbAdpTable:           Set cls = New clsAdpTable\r\n        Case edbAdpTrigger:         Set cls = New clsAdpTrigger\r\n        Case edbConnection:         Set cls = New clsDbConnection\r\n        Case edbDbsProperty:        Set cls = New clsDbProperty\r\n        Case edbDocument:           Set cls = New clsDbDocument\r\n        Case edbForm:               Set cls = New clsDbForm\r\n        Case edbHiddenAttribute:    Set cls = New clsDbHiddenAttribute\r\n        Case edbImexSpec:           Set cls = New clsDbImexSpec\r\n        Case edbMacro:              Set cls = New clsDbMacro\r\n        Case edbModule:             Set cls = New clsDbModule\r\n        Case edbNavPaneGroup:       Set cls = New clsDbNavPaneGroup\r\n        Case edbProject:            Set cls = New clsDbProject\r\n        Case edbProjectProperty:    Set cls = New clsDbProjProperty\r\n        Case edbQuery:              Set cls = New clsDbQuery\r\n        Case edbRelation:           Set cls = New clsDbRelation\r\n        Case edbReport:             Set cls = New clsDbReport\r\n        Case edbSavedSpec:          Set cls = New clsDbSavedSpec\r\n        Case edbSharedImage:        Set cls = New clsDbSharedImage\r\n        Case edbTableData:          Set cls = New clsDbTableData\r\n        Case edbTableDataMacro:     Set cls = New clsDbTableDataMacro\r\n        Case edbTableDef:           Set cls = New clsDbTableDef\r\n        Case edbTheme:              Set cls = New clsDbTheme\r\n        Case edbVbeForm:            Set cls = New clsDbVbeForm\r\n        Case edbVbeProject:         Set cls = New clsDbVbeProject\r\n        Case edbVbeReference:       Set cls = New clsDbVbeReference\r\n        Case edbCommandBar:         Set cls = New clsDbCommandBar\r\n        Case Else ' No match, return nothing\r\n    End Select\r\n    Set GetComponentClass = cls\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ContainerHasObject\r\n' Author    : Adam Waller\r\n' Date      : 6/2/2023\r\n' Purpose   : Returns true if the dictionary container has one or more of the specified\r\n'           : object type within it. (This is useful when determining whether we need\r\n'           : to run certain post-build operations.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ContainerHasObject(dContainer As Dictionary, intComponentType As eDatabaseComponentType) As Boolean\r\n\r\n    Dim cCategory As IDbComponent\r\n    Dim dCategory As Dictionary\r\n    Dim varKey As Variant\r\n\r\n    ' Loop through containers\r\n    For Each varKey In dContainer.Keys\r\n        If TypeOf varKey Is IDbComponent Then\r\n            Set cCategory = varKey\r\n            ' Look for matching component type\r\n            If cCategory.ComponentType = intComponentType Then\r\n                Set dCategory = dContainer(varKey)\r\n                If dCategory.Exists(\"Files\") Then ContainerHasObject = (dCategory(\"Files\").Count > 0)\r\n                If dCategory.Exists(\"Objects\") Then ContainerHasObject = (dCategory(\"Objects\").Count > 0)\r\n                Exit For\r\n            End If\r\n        End If\r\n    Next varKey\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ContainerHasAnyObject\r\n' Author    : Adam Waller\r\n' Date      : 6/3/2023\r\n' Purpose   : Return true if ANY of the specified objects types are found.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ContainerHasAnyObject(dContainer As Dictionary, ParamArray intOtherTypes() As Variant) As Boolean\r\n\r\n    Dim intType As Integer\r\n    Dim cCategory As IDbComponent\r\n    Dim dCategory As Dictionary\r\n    Dim varKey As Variant\r\n    Dim blnFound As Boolean\r\n\r\n    ' Loop through types\r\n    For intType = LBound(intOtherTypes) To UBound(intOtherTypes)\r\n        ' Loop through containers\r\n        For Each varKey In dContainer.Keys\r\n            If TypeOf varKey Is IDbComponent Then\r\n                Set cCategory = varKey\r\n                ' Look for matching component type\r\n                If cCategory.ComponentType = intOtherTypes(intType) Then\r\n                    Set dCategory = dContainer(varKey)\r\n                    If dCategory.Exists(\"Files\") Then blnFound = (dCategory(\"Files\").Count > 0)\r\n                    If dCategory.Exists(\"Objects\") Then blnFound = (dCategory(\"Objects\").Count > 0)\r\n                    If blnFound Then Exit For\r\n                End If\r\n            End If\r\n        Next varKey\r\n        If blnFound Then Exit For\r\n    Next intType\r\n\r\n    ' Return true if any matching object was found.\r\n    ContainerHasAnyObject = blnFound\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : MergeIfChanged\r\n' Author    : Adam Waller\r\n' Date      : 6/3/2023\r\n' Purpose   : Merge the component type if the database object seems to be different\r\n'           : than the source file. (For example, after merging a form, you may need\r\n'           : to merge document properties to get the form description.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub MergeIfChanged(intComponentType As eDatabaseComponentType)\r\n\r\n    Dim cComponent As IDbComponent\r\n    Dim dItems As Dictionary\r\n    Dim varKey As Variant\r\n    Dim cItem As IDbComponent\r\n\r\n    ' Convert enum to component class\r\n    Set cComponent = GetComponentClass(intComponentType)\r\n\r\n    ' Check component items for changed database objects.\r\n    With cComponent\r\n        ' Get dictionary of modified items\r\n        Set dItems = .GetAllFromDB(True)\r\n        For Each varKey In dItems.Keys\r\n            Set cItem = dItems(varKey)\r\n            Log.Add \"  \" & FSO.GetFileName(cItem.SourceFile)\r\n            cItem.Merge cItem.SourceFile\r\n            If .SingleFile Then Exit For\r\n        Next varKey\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetQuickObjectCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a quick, non-iterative object count for the collection of\r\n'           : database components. (Used for progress bar)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetQuickObjectCount(colContainers As Collection) As Long\r\n\r\n    Dim lngTotal As Long\r\n    Dim cCont As IDbComponent\r\n\r\n    Perf.OperationStart \"Quick Count Objects\"\r\n    For Each cCont In colContainers\r\n        lngTotal = lngTotal + cCont.QuickCount\r\n    Next cCont\r\n    Perf.OperationEnd\r\n\r\n    GetQuickObjectCount = lngTotal\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetQuickFileCount\r\n' Author    : Adam Waller\r\n' Date      : 6/14/2022\r\n' Purpose   : Return a quick count of the files in each folder so we can increment\r\n'           : the progress of scanning through files in a folder.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetQuickFileCount(colContainers As Collection) As Long\r\n\r\n    Dim lngTotal As Long\r\n    Dim strBase As String\r\n    Dim strFolder As String\r\n    Dim cCont As IDbComponent\r\n\r\n    ' Get base folder path\r\n    Perf.OperationStart \"Quick Count Files\"\r\n    strBase = Options.GetExportFolder\r\n\r\n    For Each cCont In colContainers\r\n        strFolder = cCont.BaseFolder\r\n        If StrComp(strBase, strFolder, vbTextCompare) = 0 Then\r\n            ' Add a single count for the single file\r\n            lngTotal = lngTotal + 1\r\n        Else\r\n            ' Make sure the folder actually exists before getting a file count\r\n            If FSO.FolderExists(strFolder) Then\r\n                ' Add a count of the files in the folder\r\n                lngTotal = lngTotal + FSO.GetFolder(strFolder).Files.Count\r\n            End If\r\n        End If\r\n    Next cCont\r\n    Perf.OperationEnd\r\n\r\n    ' Return total number of files in all source folders\r\n    GetQuickFileCount = lngTotal\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSourceModifiedDate\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : Return the largest modified date for the source files associated with\r\n'           : this component type.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSourceModifiedDate(cmp As IDbComponent, Optional strFile As String) As Date\r\n\r\n    Dim varExt As Variant\r\n    Dim dteLatest As Date\r\n    Dim strBaseFile As String\r\n\r\n    ' Build base file path without extension\r\n    If Len(strFile) Then\r\n        ' Use provided file name first\r\n        strBaseFile = FSO.GetBaseName(strFile)\r\n    Else\r\n        ' Otherwise use default source file name\r\n        strBaseFile = FSO.GetBaseName(cmp.SourceFile)\r\n    End If\r\n\r\n    ' Check each possible file extension to find the most recent date\r\n    For Each varExt In cmp.FileExtensions\r\n        dteLatest = Largest(dteLatest, GetLastModifiedDate(strBaseFile & \".\" & varExt))\r\n    Next varExt\r\n\r\n    ' Return most recent date\r\n    GetSourceModifiedDate = dteLatest\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetLastModifiedSourceFile\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : Return the path of the last modified source file for the specified\r\n'           : component. (This may not be the primary source file used by the index.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetLastModifiedSourceFile(cmp As IDbComponent, Optional strFile As String)\r\n\r\n    Dim varExt As Variant\r\n    Dim dteLatest As Date\r\n    Dim dteTest As Date\r\n    Dim strSourceFile As String\r\n    Dim strLastModifiedFile As String\r\n    Dim strBaseFile As String\r\n\r\n    ' Build base file path without extension\r\n    If Len(strFile) Then\r\n        ' Use provided file name first\r\n        strBaseFile = FSO.GetBaseName(strFile)\r\n    Else\r\n        ' Otherwise use default source file name\r\n        strBaseFile = FSO.GetBaseName(cmp.SourceFile)\r\n    End If\r\n\r\n    ' Check each possible file extension to find the most recent date\r\n    For Each varExt In cmp.FileExtensions\r\n        strSourceFile = strBaseFile & \".\" & varExt\r\n        dteTest = GetLastModifiedDate(strSourceFile)\r\n        If dteTest > dteLatest Then\r\n            dteLatest = dteTest\r\n            strLastModifiedFile = strSourceFile\r\n        End If\r\n    Next varExt\r\n\r\n    ' Return file path for most recently modified file\r\n    GetLastModifiedSourceFile = strLastModifiedFile\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSourceFilesPropertyHash\r\n' Author    : Adam Waller\r\n' Date      : 12/1/2023\r\n' Purpose   : Return a has of the source file dates and sizes for this component type.\r\n'           : (Useful for determining if any of the source files have been modified.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSourceFilesPropertyHash(cmp As IDbComponent, Optional strFile As String) As String\r\n\r\n    Dim varExt As Variant\r\n    Dim strSourceFile As String\r\n    Dim strBaseFile As String\r\n    Dim oFile As Scripting.File\r\n\r\n    Perf.OperationStart \"Get File Property Hash\"\r\n\r\n    ' Build base file path without extension\r\n    If Len(strFile) Then\r\n        ' Use provided file name first\r\n        strBaseFile = FSO.BuildPath(FSO.GetParentFolderName(strFile), FSO.GetBaseName(strFile))\r\n    Else\r\n        ' Otherwise use default source file name\r\n        strBaseFile = FSO.BuildPath(FSO.GetParentFolderName(cmp.SourceFile), FSO.GetBaseName(cmp.SourceFile))\r\n    End If\r\n\r\n    ' Build a combined string with all the properties\r\n    With New clsConcat\r\n\r\n        ' Check each possible file extension to find all the source files\r\n        For Each varExt In cmp.FileExtensions\r\n            strSourceFile = strBaseFile & \".\" & varExt\r\n            If FSO.FileExists(strSourceFile) Then\r\n                Set oFile = FSO.GetFile(strSourceFile)\r\n                .Add oFile.DateLastModified, oFile.Size\r\n            End If\r\n        Next varExt\r\n\r\n        ' Return hash of combined string\r\n        GetSourceFilesPropertyHash = GetStringHash(.GetStr)\r\n        Perf.OperationEnd\r\n    End With\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetVCSVersion\r\n' Author    : Adam Waller\r\n' Date      : 1/28/2019\r\n' Purpose   : Gets the version of the version control system. (Used to turn off fast\r\n'           : save until a full export has been run with the current version of\r\n'           : the MSAccessVCS addin.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetVCSVersion() As String\r\n\r\n    Dim dbs As Database\r\n    Dim prp As DAO.Property\r\n\r\n    Set dbs = CodeDb\r\n\r\n    For Each prp In dbs.Properties\r\n        If prp.Name = \"AppVersion\" Then\r\n            ' Return version\r\n            GetVCSVersion = prp.Value\r\n        End If\r\n    Next prp\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : SaveComponentAsText\r\n' Author    : Adam Waller\r\n' Date      : 4/29/2020\r\n' Purpose   : Wrapper for Application.SaveAsText that verifies that the path exists,\r\n'           : and then removes any existing file before saving the object as text.\r\n'           : Returns a hash of the file content (if applicable) to track changes.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function SaveComponentAsText(intType As AcObjectType _\r\n                                    , strName As String _\r\n                                    , strFile As String _\r\n                                    , Optional cDbObjectClass As IDbComponent = Nothing) As String\r\n\r\n    Const FunctionName As String = ModuleName & \".SaveComponentAsText\"\r\n\r\n    Dim strTempFile As String\r\n    Dim strAltFile As String\r\n    Dim strContent As String\r\n    Dim strPrintSettingsFile As String\r\n    Dim strHash As String\r\n    Dim cParser As clsSourceParser\r\n\r\n    LogUnhandledErrors FunctionName\r\n    On Error GoTo ErrHandler\r\n\r\n    ' Export to temporary file\r\n    strTempFile = GetTempFile\r\n    Perf.OperationStart \"App.SaveAsText()\"\r\n    Application.SaveAsText intType, strName, strTempFile\r\n    Perf.OperationEnd\r\n    VerifyPath strFile\r\n\r\n    ' Delete any existing source file\r\n    If FSO.FileExists(strFile) Then DeleteFile strFile\r\n\r\n    ' Sanitize certain object types\r\n    Set cParser = New clsSourceParser\r\n    Select Case intType\r\n        Case acForm, acReport\r\n\r\n            ' Load content from file\r\n            strContent = ReadSourceFile(strTempFile)\r\n\r\n            ' Process any saved devmode settings\r\n            With New clsDevMode\r\n                ' Build print settings file name.\r\n                strPrintSettingsFile = SwapExtension(strFile, \"json\")\r\n                ' See if we are exporting print vars.\r\n                If Options.SavePrintVars = True Then\r\n                    ' Grab the printer settings before sanitizing the file.\r\n                    .LoadFromExportFile strContent\r\n                    ' Only need to save print settings if they are different\r\n                    ' from the default printer settings.\r\n                    If (.GetHash <> VCSIndex.DefaultDevModeHash) And .HasData Then\r\n                        WriteFile BuildJsonFile(TypeName(cDbObjectClass), .GetDictionary, _\r\n                          strName & \" Print Settings\"), strPrintSettingsFile\r\n                    Else\r\n                        ' No print settings in this object.\r\n                        If FSO.FileExists(strPrintSettingsFile) Then DeleteFile strPrintSettingsFile\r\n                    End If\r\n                Else\r\n                    ' Remove any existing (now orphaned) print settings file.\r\n                    If FSO.FileExists(strPrintSettingsFile) Then DeleteFile strPrintSettingsFile\r\n                End If\r\n            End With\r\n\r\n            ' Sanitizing converts to UTF-8\r\n            With cParser\r\n                .LoadString strContent\r\n                .ObjectName = FSO.GetBaseName(strFile)\r\n                WriteFile .Sanitize(ectObjectDefinition), strFile\r\n                strHash = .Hash\r\n\r\n                ' Process any VBA\r\n                strAltFile = SwapExtension(strFile, \"cls\")\r\n                If Options.SplitLayoutFromVBA And Len(.GetObjectVBA) Then\r\n                    ' Write VBA code as separate .cls file.\r\n                    WriteFile .GetObjectVBA, strAltFile\r\n                Else\r\n                    ' Remove any split VBA file\r\n                    If FSO.FileExists(strAltFile) Then DeleteFile strAltFile\r\n                End If\r\n            End With\r\n\r\n        Case acQuery, acMacro\r\n            ' Sanitizing converts to UTF-8\r\n            With cParser\r\n                .LoadSourceFile strTempFile, intType\r\n                WriteFile .Sanitize(ectObjectDefinition), strFile\r\n                strHash = .Hash\r\n            End With\r\n\r\n        ' Case acModule - Use VBE export instead.\r\n\r\n        Case acTableDataMacro\r\n            ' Table data macros are stored in XML format\r\n            ' The file may not exist if no TD Macro was found\r\n            If FSO.FileExists(strTempFile) Then\r\n                With cParser\r\n                    .LoadSourceFile strTempFile, intType\r\n                    WriteFile .Sanitize(ectXML), strFile\r\n                    strHash = .Hash\r\n                End With\r\n            End If\r\n\r\n        Case Else\r\n            ' Handle UCS conversion if needed\r\n            ConvertUcs2Utf8 strTempFile, strFile\r\n\r\n    End Select\r\n\r\n    ' Remove any leftover temp file.\r\n    If FSO.FileExists(strTempFile) Then DeleteFile strTempFile\r\n\r\n    ' Normal exit\r\n    On Error GoTo 0\r\n\r\n    ' Return content hash\r\n    SaveComponentAsText = strHash\r\n    Exit Function\r\n\r\nErrHandler:\r\n    If Catch(2950) And intType = acTableDataMacro Then\r\n        ' This table apparently didn't have a Table Data Macro.\r\n        Exit Function\r\n    Else\r\n        ' Some other error.\r\n        Log.Error eelError, \"Issue creating output file.\", FunctionName\r\n        'Err.Raise Err.Number\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadComponentFromText\r\n' Author    : Adam Waller\r\n' Date      : 5/5/2020\r\n' Purpose   : Load the object into the database from the saved source file.\r\n'           : Returns True if the loading worked; False if an error occured or other\r\n'           : issue was detected.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function LoadComponentFromText(intType As AcObjectType _\r\n                                    , ByRef strName As String _\r\n                                    , ByRef strFile As String) As Boolean\r\n\r\n    Const FunctionName As String = ModuleName & \".LoadComponentFromText\"\r\n\r\n    Dim blnErrInFunction As Boolean\r\n    Dim strTempFile As String\r\n    Dim strSourceFile As String\r\n    Dim strPrefix As String\r\n    Dim strAltFile As String\r\n    Dim strContent As String\r\n    Dim blnVbaOverlay As Boolean\r\n    Dim blnConvert As Boolean\r\n\r\n    LogUnhandledErrors FunctionName\r\n    On Error GoTo ErrHandler\r\n    Perf.OperationStart FunctionName\r\n\r\nRetryImport:\r\n    ' In most cases we are importing/converting the actual source file.\r\n    strSourceFile = strFile\r\n\r\n    ' Add DevMode structures and VBA code back into forms/reports\r\n    Select Case intType\r\n        Case acForm, acReport\r\n\r\n            ' Read file content. (Should be UTF-8)\r\n            strContent = ReadFile(strFile)\r\n            With New clsSourceParser\r\n                .LoadString strContent\r\n\r\n                ' Check for print settings file\r\n                strAltFile = SwapExtension(strFile, \"json\")\r\n                If FSO.FileExists(strAltFile) Then\r\n                    ' Merge the print settings into the source file content\r\n                    .MergePrintSettings ReadFile(strAltFile)\r\n                End If\r\n\r\n                ' For forms and reports, check for VBA code file that needs to be merged\r\n                strAltFile = SwapExtension(strFile, \"cls\")\r\n                If FSO.FileExists(strAltFile) Then\r\n                    ' Found a companion class file.\r\n                    .MergeVBA ReadFile(strAltFile)\r\n                    blnVbaOverlay = RequiresOverlay(.GetObjectVBA)\r\n                End If\r\n\r\n                ' Write ouput to a new file if anything has changed\r\n                If .OutputModified Then\r\n                    strSourceFile = GetTempFile\r\n                    WriteFile .GetOutput, strSourceFile\r\n                End If\r\n\r\n            End With\r\n\r\n    End Select\r\n\r\n    ' Check UCS-2-LE requirement for the current database.\r\n    ' (Cached after first call)\r\n    Select Case intType\r\n        Case acForm, acReport, acQuery, acMacro, acTableDataMacro\r\n            blnConvert = RequiresUcs2\r\n        Case acModule\r\n            ' Always convert from UTF-8 in case the file contains\r\n            ' UTF-8 encoded characters but does not have a BOM.\r\n            blnConvert = True\r\n    End Select\r\n\r\n    ' Only run conversion if needed.\r\n    If blnConvert Then\r\n        ' Perform file conversion, and import from temp file.\r\n        strTempFile = GetTempFile\r\n        If intType = acModule Then\r\n            ' Convert back to ANSI for VBA modules\r\n            ConvertUtf8Ansi strSourceFile, strTempFile, False\r\n        Else\r\n            ' Other objects converted to UCS2\r\n            ConvertUtf8Ucs2 strSourceFile, strTempFile, False\r\n        End If\r\n        Perf.OperationStart \"App.LoadFromText()\"\r\n        Application.LoadFromText intType, strName, strTempFile\r\n        Perf.OperationEnd\r\n        DeleteFile strTempFile, True\r\n\r\n    Else\r\n        ' Load UTF-8 file\r\n        Perf.OperationStart \"App.LoadFromText()\"\r\n        Application.LoadFromText intType, strName, strSourceFile\r\n        Perf.OperationEnd\r\n    End If\r\n\r\nCleanUp:\r\n    ' Clean up any additional temp file used in the building process\r\n    If strFile <> strSourceFile Then\r\n        If FSO.FileExists(strSourceFile) Then DeleteFile strSourceFile\r\n    End If\r\n\r\n    ' Check for VBA overlay\r\n    If blnVbaOverlay And Not Log.ErrorLevel = eelCritical Then ' don't do this if we're trying to bail out.\r\n        strPrefix = IIf(intType = acForm, \"Form_\", \"Report_\")\r\n        OverlayCodeModule strPrefix & strName, SwapExtension(strFile, \"cls\")\r\n    End If\r\n\r\nExit_Here:\r\n    ' Only set output to true when import and function didn't have any issues.\r\n    LoadComponentFromText = (Not blnErrInFunction) And (Not Log.ErrorLevel = eelCritical)\r\n    Perf.OperationEnd\r\n    Exit Function\r\n\r\nErrHandler:\r\n    ' Issue importing form. We need to prompt user to see if we continue on or not.\r\n    Log.Error eelError, T(\"Import issue with '{0}'\", var0:=strName), FunctionName\r\n\r\n    Select Case MsgBox2(T(\"Could not import '{0}'.\", var0:=strName) _\r\n            , T(\"Abort build, retry importing, or skip?\") _\r\n            , T(\"[Abort] = Abort build process entirely.\" & vbNewLine & _\r\n                \"[Retry] = Retry importing the item.\" & vbNewLine & _\r\n                \"[Ignore] = Skip this item.\") _\r\n            , vbAbortRetryIgnore, \"Error importing!\", vbAbort)\r\n\r\n    Case vbAbort\r\n        Log.Error eelCritical, \"Aborted build.\", FunctionName\r\n        blnErrInFunction = True\r\n        GoTo CleanUp\r\n\r\n    Case vbRetry\r\n        Log.Add T(\"Retrying import for: {0}\", var0:=strName)\r\n        Resume RetryImport\r\n\r\n    Case Else ' this also includes ignore.\r\n        ' Clear out strName because we're going to use it to detect if the import failed.\r\n        Log.Error eelError, T(\"Skipping import of '{0}'. Your application may not run or complile.\", var0:=strName), FunctionName\r\n        blnErrInFunction = True\r\n        Resume Next\r\n\r\n    End Select\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExportVbComponent\r\n' Author    : Adam Waller\r\n' Date      : 5/26/2021\r\n' Purpose   : Export the code module VB component and convert to UTF-8\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExportCodeModule(strName As String, strFile As String)\r\n\r\n    Dim strTempFile As String\r\n    Dim strContent As String\r\n\r\n    Perf.OperationStart \"Export VBE Module\"\r\n\r\n    ' Export to a temp file so we can convert to UTF-8 encoding\r\n    strTempFile = GetTempFile\r\n    CurrentVBProject.VBComponents(strName).Export strTempFile\r\n\r\n    ' Sanitize the VBA code while reading the temp file\r\n    With New clsSourceParser\r\n        .LoadString ReadFile(strTempFile, GetSystemEncoding)\r\n        strContent = .Sanitize(ectVBA)\r\n    End With\r\n\r\n    ' Write the content as UTF-8 to the final destination\r\n    WriteFile strContent, strFile\r\n    DeleteFile strTempFile\r\n\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : OverlayCodeModule\r\n' Author    : Adam Waller\r\n' Date      : 10/24/2023\r\n' Purpose   : Overlay VBA code from an object's *.cls file to the form or report\r\n'           : Note that this opens the object in design view, which may slow the build\r\n'           : process if a large number of items are invovled.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub OverlayCodeModule(strName As String _\r\n                            , strClassFile As String)\r\n\r\n    Const FunctionName As String = ModuleName & \".OverlayCodeModule\"\r\n\r\n    Dim objModule As VBIDE.CodeModule\r\n    Dim strContent As String\r\n    Dim intType As AcObjectType\r\n    Dim strShortName As String\r\n    Dim cParser As clsSourceParser\r\n\r\n    LogUnhandledErrors FunctionName\r\n    'On Error Resume Next\r\n\r\n    Set objModule = CurrentVBProject.VBComponents(strName).CodeModule\r\n    If CatchAny(eelError, T(\"Could not find code module for {0}\", var0:=strName), FunctionName) Then Exit Sub\r\n\r\n    ' Read class file content\r\n    strContent = ReadFile(strClassFile)\r\n    If strContent = vbNullString Then\r\n        Log.Error eelError, T(\"Unable to read {0}\", var0:=strClassFile), FunctionName\r\n        Exit Sub\r\n    End If\r\n\r\n    ' Get object type and short name\r\n    If strName Like \"Form_*\" Then\r\n        intType = acForm\r\n        strShortName = Mid$(strName, 6)\r\n        DoCmd.OpenForm strShortName, acDesign, , , , acHidden\r\n    ElseIf strName Like \"Report_*\" Then\r\n        intType = acReport\r\n        strShortName = Mid$(strName, 8)\r\n        DoCmd.OpenReport strShortName, acViewDesign, , , acHidden\r\n    End If\r\n\r\n    ' Overlay the VBA code, replacing any existing code.\r\n    Set cParser = New clsSourceParser\r\n    objModule.DeleteLines 1, objModule.CountOfLines\r\n    objModule.AddFromString cParser.StripClassHeader(strContent, False)\r\n\r\n    ' Close any form or report object\r\n    Select Case intType\r\n        Case acForm, acReport\r\n            DoCmd.Close intType, strShortName, acSaveYes\r\n    End Select\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RequiresOverlay\r\n' Author    : Adam Waller\r\n' Date      : 11/2/2023\r\n' Purpose   : Returns true if we need to overlay the VBA code through VBE for a form\r\n'           : or report object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function RequiresOverlay(strVbaCode As String) As Boolean\r\n    If modEncoding.GetSystemEncoding(True) = \"utf-8\" Then\r\n        RequiresOverlay = StringHasExtendedASCII(strVbaCode)\r\n    End If\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : RemoveNonBuiltInReferences\r\n' Author    : Adam Waller\r\n' Date      : 10/20/2020\r\n' Purpose   : Remove any references that are not built-in. (Sometimes additional\r\n'           : references are added when creating a new database, but not not really\r\n'           : needed in the completed database when building the project from source.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub RemoveNonBuiltInReferences()\r\n\r\n    Dim intCnt As Integer\r\n    Dim strName As String\r\n    Dim ref As Access.Reference\r\n\r\n    Perf.OperationStart \"Clear References\"\r\n    For intCnt = Application.References.Count To 1 Step -1\r\n        Set ref = Application.References(intCnt)\r\n        If Not ref.BuiltIn Then\r\n            strName = ref.Name\r\n            Application.References.Remove ref\r\n            Log.Add \"  Removed \" & strName, False\r\n        End If\r\n        Set ref = Nothing\r\n    Next intCnt\r\n    Perf.OperationEnd\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetOriginalDbFullPathFromSource\r\n' Author    : Adam Waller\r\n' Date      : 5/5/2020\r\n' Purpose   : Determine the original full path of the database, based on the files\r\n'           : in the source folder. (Assumes that options have been loaded)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetOriginalDbFullPathFromSource(strFolder As String) As String\r\n\r\n    Dim strPath As String\r\n    Dim dContents As Dictionary\r\n    Dim strFile As String\r\n    Dim strExportFolder As String\r\n    Dim lngLevel As Long\r\n\r\n    strPath = FSO.BuildPath(strFolder, \"vbe-project.json\")\r\n    If Not FSO.FileExists(strPath) Then\r\n        Log.Error eelCritical, \"Unable to find source file: \" & strPath, \"GetOriginalDbFullPathFromSource\"\r\n        GetOriginalDbFullPathFromSource = vbNullString\r\n    Else\r\n        ' Look up file name from VBE project file name\r\n        Set dContents = ReadJsonFile(strPath)\r\n        strFile = dNZ(dContents, \"Items\\FileName\")\r\n\r\n        ' Convert legacy relative path\r\n        If Left$(strFile, 4) = \"rel:\" Then strFile = Mid$(strFile, 5)\r\n\r\n        ' Trim off any tailing slash\r\n        strExportFolder = StripSlash(strFolder)\r\n\r\n        ' Check export folder settings\r\n        If Options.ExportFolder = vbNullString Then\r\n            ' Default setting, using parent folder of source directory\r\n            strPath = strExportFolder & PathSep & \"..\" & PathSep & strFile\r\n        Else\r\n            ' Check to see if we are using an absolute export path  (\\\\* or *:*)\r\n            If StartsWith(Options.ExportFolder, PathSep & PathSep) _\r\n                Or (InStr(2, Options.ExportFolder, \":\") > 0) Then\r\n                ' Look for saved build path\r\n                Set dContents = ReadJsonFile(FSO.BuildPath(strFolder, \"proj-properties.json\"))\r\n                strPath = dNZ(dContents, \"Items\\VCS Build Path\")\r\n                If strPath <> vbNullString Then\r\n                    strPath = strPath & PathSep & strFile\r\n                Else\r\n                    ' Unable to determine the original file location.\r\n                    Exit Function\r\n                End If\r\n            Else\r\n                ' Calculate how many levels deep to create original path\r\n                lngLevel = UBound(Split(StripSlash(Options.ExportFolder), PathSep))\r\n                If lngLevel < 0 Then lngLevel = 0   ' Handle \"\\\" to export in current folder.\r\n                strPath = strExportFolder & PathSep & Repeat(\"..\" & PathSep, lngLevel) & strFile\r\n            End If\r\n        End If\r\n\r\n        ' Expand absolute path\r\n        GetOriginalDbFullPathFromSource = FSO.GetAbsolutePathName(strPath)\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : FolderHasVcsOptionsFile\r\n' Author    : Adam Waller\r\n' Date      : 5/5/2020\r\n' Purpose   : Returns true if the folder as a vcs-options.json file, which is required\r\n'           : to build a project from source files.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function FolderHasVcsOptionsFile(strFolder As String) As Boolean\r\n    FolderHasVcsOptionsFile = FSO.FileExists(FSO.BuildPath(strFolder, \"vcs-options.json\"))\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : BuildJsonFile\r\n' Author    : Adam Waller\r\n' Date      : 2/5/2022\r\n' Purpose   : Creates json file content with an info header giving some clues about the\r\n'           : contents of the file. (Helps with upgrades or changes later.)\r\n'           : Set the file format version only when the dictionary structure changes\r\n'           : with potentially breaking changes for prior versions.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function BuildJsonFile(strClassName As String, dItems As Dictionary, strDescription As String, _\r\n    Optional dblExportFormatVersion As Double) As String\r\n\r\n    Dim dContents As Dictionary\r\n    Dim dHeader As Dictionary\r\n\r\n    Set dContents = New Dictionary\r\n    Set dHeader = New Dictionary\r\n\r\n    ' Build dictionary structure\r\n    dHeader.Add \"Class\", strClassName\r\n    dHeader.Add \"Description\", strDescription\r\n    If dblExportFormatVersion <> 0 Then dHeader.Add \"Export File Format\", dblExportFormatVersion\r\n    dContents.Add \"Info\", dHeader\r\n    dContents.Add \"Items\", dItems\r\n\r\n    ' Return assembled content in Json format\r\n    BuildJsonFile = ConvertToJson(dContents, JSON_WHITESPACE)\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CompileAndSaveAllModules\r\n' Author    : Adam Waller\r\n' Date      : 7/10/2021\r\n' Purpose   : Compile and save the modules in the current database\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CompileAndSaveAllModules()\r\n    Perf.OperationStart \"Compile/Save Modules\"\r\n    ' Make sure we are running this in the CurrentDB, not the CodeDB\r\n    Set VBE.ActiveVBProject = CurrentVBProject\r\n    DoCmd.RunCommand acCmdCompileAndSaveAllModules\r\n    DoEvents\r\n    Perf.OperationEnd\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PreloadVBE\r\n' Author    : Adam Waller\r\n' Date      : 5/25/2020\r\n' Purpose   : Force Access to load the VBE project. (This can help prevent crashes\r\n'           : when code is run before the VB Project is fully loaded.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub PreloadVBE()\r\n    Dim strName As String\r\n    DoCmd.Hourglass True\r\n    strName = VBE.ActiveVBProject.Name\r\n    DoCmd.Hourglass False\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetAddInProject\r\n' Author    : Adam Waller\r\n' Date      : 11/10/2020\r\n' Purpose   : Return the VBProject of the MSAccessVCS add-in.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetAddInProject() As VBProject\r\n    Dim oProj As VBProject\r\n    For Each oProj In VBE.VBProjects\r\n        If StrComp(oProj.FileName, GetAddInFileName, vbTextCompare) = 0 Then\r\n            Set GetAddInProject = oProj\r\n            Exit For\r\n        End If\r\n    Next oProj\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : LoadVCSAddIn\r\n' Author    : Adam Waller\r\n' Date      : 11/10/2020\r\n' Purpose   : Load the add-in at the application level so it can stay active\r\n'           : even if the current database is closed.\r\n'           : https://stackoverflow.com/questions/62270088/how-can-i-launch-an-access-add-in-not-com-add-in-from-vba-code\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub LoadVCSAddIn()\r\n    ' The following lines will load the add-in at the application level,\r\n    ' but will not actually call the function. Ignore the error of function not found.\r\n    LogUnhandledErrors\r\n    On Error Resume Next\r\n    Application.Run GetAddInFileName & \"!DummyFunction\"\r\n    If Err Then Err.Clear\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : AfterBuild\r\n' Author    : Adam Waller\r\n' Date      : 12/18/2023\r\n' Purpose   : Run this code after building the add-in from source.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub AfterBuild()\r\n    modResource.VerifyResources\r\n    Translation.LoadTranslations\r\n    ImportCommandBarsTemplate\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckGitFiles\r\n' Author    : Adam Waller\r\n' Date      : 5/23/2022\r\n' Purpose   : If this project appears to be a git repository, this checks to see if\r\n'           : it contains a .gitignore and .gitattributes file. If it doesn't, then\r\n'           : the default files are extracted and added to the project, and the user\r\n'           : notified that these have been added.\r\n'           : Checks both the export folder and the current folder.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CheckGitFiles()\r\n\r\n    Dim strPath As String\r\n    Dim strFile As String\r\n    Dim blnAdded As Boolean\r\n\r\n    ' Check export folder\r\n    strPath = Options.GetExportFolder\r\n    If Not FSO.FolderExists(strPath & \".git\") Then\r\n        ' Check current folder for repository root\r\n        ' (This would be the default usage)\r\n        strPath = CurrentProject.Path & PathSep\r\n        If Not FSO.FolderExists(strPath & \".git\") Then\r\n            ' No git folder found.\r\n            Exit Sub\r\n        End If\r\n    End If\r\n\r\n    ' gitignore file\r\n    strFile = strPath & \".gitignore\"\r\n    If Not FSO.FileExists(strFile) Then\r\n        ExtractResource \"Default .gitignore\", strPath\r\n        Name strFile & \".default\" As strFile\r\n        Log.Add \"Added default .gitignore file\", , , \"blue\"\r\n        blnAdded = True\r\n    End If\r\n\r\n    ' gitattributes file\r\n    strFile = strPath & \".gitattributes\"\r\n    If Not FSO.FileExists(strFile) Then\r\n        ExtractResource \"Default .gitattributes\", strPath\r\n        Name strFile & \".default\" As strFile\r\n        Log.Add \"Added default .gitattributes file\", , , \"blue\"\r\n        blnAdded = True\r\n    End If\r\n\r\n    ' Notify user\r\n    If blnAdded Then MsgBox2 \"Added Default Git File(s)\", _\r\n        \"Added a default .gitignore and/or .gitattributes file to your project.\", _\r\n        \"By default these files exclude the binary database files from version control,\" & vbCrLf & _\r\n        \"allowing you to track changes at the source file level.\" & vbCrLf & vbCrLf & _\r\n        \"You may wish to customize these further for your environment.\", vbInformation\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ShiftOpenDatabase\r\n' Author    : Adam Waller\r\n' Date      : 2/25/2022\r\n' Purpose   : Open a database with the shift key held down so we can (hopefully)\r\n'           : bypass the startup code.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ShiftOpenDatabase(strPath As String, Optional blnExclusive As Boolean = False)\r\n\r\n    Const VK_SHIFT = &H10\r\n\r\n    ' Skip open if we are already on the correct database\r\n    If CurrentProject.FullName = strPath And Not blnExclusive Then Exit Sub\r\n\r\n    ' Close any open database before we try to open another one.\r\n    If DatabaseFileOpen Then\r\n        StageMainForm\r\n        CloseCurrentDatabase2\r\n        DoCmd.OpenForm \"frmVCSMain\", , , , , acHidden\r\n        RestoreMainForm\r\n    End If\r\n\r\n    On Error GoTo Error_Handler\r\n\r\n    Dim abytCodesSrc(0 To 255) As Byte\r\n    Dim abytCodesDest(0 To 255) As Byte\r\n\r\n    If (FSO.FileExists(strPath) = False) Then\r\n        Err.Raise 53\r\n    End If\r\n\r\n    SetForegroundWindow Application.hWndAccessApp\r\n    SetFocus Application.hWndAccessApp\r\n\r\n    ' Set Shift state\r\n    GetKeyboardState abytCodesSrc(0)\r\n    GetKeyboardState abytCodesDest(0)\r\n    abytCodesDest(VK_SHIFT) = 128\r\n    SetKeyboardState abytCodesDest(0)\r\n\r\n    ' Open the database with shift key down\r\n    Application.OpenCurrentDatabase strPath, blnExclusive\r\n\r\n    ' Revert back keyboard state and restore focus\r\n    SetKeyboardState abytCodesSrc(0)\r\n    SetForegroundWindow Application.hWndAccessApp\r\n    SetFocus Application.hWndAccessApp\r\n\r\n    Exit Sub\r\n\r\nError_Handler:\r\n    SetForegroundWindow Application.hWndAccessApp\r\n\r\n    With Err\r\n        .Raise .Number, .Source, .Description, .HelpFile, .HelpContext\r\n    End With\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GetSchemaParams\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Return the schema initialization parameters for dependency injection.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GetSchemaInitParams(strName As String) As Dictionary\r\n\r\n    Dim dParams As Dictionary\r\n    Dim strFile As String\r\n\r\n    ' Load parameters for initializing the connection\r\n    If Options.SchemaExports.Exists(strName) Then\r\n        Set dParams = CloneDictionary(Options.SchemaExports(strName))\r\n    Else\r\n        ' Could be a new schema not yet saved\r\n        Set dParams = New Dictionary\r\n        dParams.CompareMode = TextCompare\r\n    End If\r\n    dParams(\"Name\") = strName\r\n\r\n    ' Check for `Connect` or other parameters in .env file\r\n    strFile = BuildPath2(Options.GetExportFolder & \"databases\", GetSafeFileName(strName), \".env\")\r\n    If FSO.FileExists(strFile) Then\r\n        With New clsDotEnv\r\n            .LoadFromFile strFile\r\n            .MergeIntoDictionary dParams, False\r\n        End With\r\n    End If\r\n\r\n    ' Return initialization parameters\r\n    Set GetSchemaInitParams = dParams\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : PassesSchemaFilter\r\n' Author    : Adam Waller\r\n' Date      : 7/21/2023\r\n' Purpose   : Returns true if this item passed any user-defined filter rules.\r\n'           : The current implementation processes rules sequentially, applying each\r\n'           : rule in order. Last matching rule will apply to the object.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function PassesSchemaFilter(strItem As String, colFilters As Collection) As Boolean\r\n\r\n    Dim blnPass As Boolean\r\n    Dim varRule As Variant\r\n    Dim strRule As String\r\n\r\n    If colFilters Is Nothing Then\r\n        blnPass = True\r\n    ElseIf colFilters.Count = 0 Then\r\n        blnPass = True\r\n    Else\r\n        ' Loop through rules\r\n        For Each varRule In colFilters\r\n            strRule = CStr(varRule)\r\n            Select Case Left$(strRule, 1)\r\n                Case \"#\", vbNullString\r\n                    ' Ignore comments and blank lines\r\n                Case \"!\"\r\n                    ' Negative rule (do not include)\r\n                    If strItem Like Mid$(strRule, 2) Then blnPass = False\r\n                Case Else\r\n                    ' Positive rule\r\n                    If strItem Like strRule Then blnPass = True\r\n            End Select\r\n        Next varRule\r\n    End If\r\n\r\n    ' Return final result\r\n    PassesSchemaFilter = blnPass\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ReadSourceFile\r\n' Author    : Adam Waller\r\n' Date      : 11/8/2023\r\n' Purpose   : Load source file content into a string. (Considers BOM and file type)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ReadSourceFile(strPath As String) As String\r\n\r\n    Dim strTempFile As String\r\n    Dim strExt As String\r\n\r\n    ' Read text from file, and split into lines\r\n    If HasUcs2Bom(strPath) Then\r\n        ReadSourceFile = ReadFile(strPath, \"Unicode\")\r\n    Else\r\n        ' ADP projects may contain mixed Unicode content\r\n        If CurrentProject.ProjectType = acADP Then\r\n            strTempFile = GetTempFile\r\n            ConvertUcs2Utf8 strPath, strTempFile, False\r\n            ReadSourceFile = ReadFile(strTempFile)\r\n            DeleteFile strTempFile\r\n        Else\r\n            strExt = LCase(Right(strPath, 4))\r\n            ' ExportXML defaults to UTF-8 regardless of DB version\r\n            If DbVersion <= 4 And strExt <> \".xml\" Then\r\n                ' Access 2000 format exports using system codepage\r\n                ' See issue #217\r\n                ReadSourceFile = ReadFile(strPath, GetSystemEncoding)\r\n            Else\r\n                ' Newer versions export as UTF-8\r\n                ReadSourceFile = ReadFile(strPath)\r\n            End If\r\n        End If\r\n    End If\r\n\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ImportCommandBarsTemplate\r\n' Author    : bclothier\r\n' Date      : 02/14/2025\r\n' Purpose   : Import the template command bar from the template file for the add-in.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Sub ImportCommandBarsTemplate()\r\n    Dim strTemplatePath As String\r\n\r\n    strTemplatePath = BuildPath2(CurrentProject.Path, \"\\Template\\CommandBars.bin\")\r\n\r\n    If FSO.FileExists(strTemplatePath) Then\r\n        Select Case ImportCommandBars(strTemplatePath, strTemplateCommandBarName)\r\n            Case eicImportedVerified\r\n                ' All good\r\n            Case eicImportedUnableToVerify\r\n                Log.Error eelWarning, \"Template command bar was imported but we cannot verify if it was imported successfully.\"\r\n            Case eicImportedNotVerified\r\n                Log.Error eelError, \"Template command bar was imported  but we didn't find it in the built file. This indicates something went wrong with the import.\"\r\n            Case Else\r\n                Log.Error eelError, \"Template command bar was not imported!\"\r\n        End Select\r\n    Else\r\n        MsgBox2 \"Unable to import the template command bar\", \"The add-in could not locate the '\\Template\\CommandBars.bin' in the repository which is required for the add-in to function correctly. Ensure that you have pulled the latest from the git repository and the file is present before building the add-in.\", , vbCritical, \"Error importing command bar template\"\r\n        Log.Error eelCritical, \"Template command bar could not be imported because the source file is missing.\"\r\n    End If\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ImportCommandBars\r\n' Author    : bclothier\r\n' Date      : 02/14/2025\r\n' Purpose   : Import the command bars from a source database to the specified project.\r\n'           : By default the current Application is used but it can be another instance\r\n'           : (e.g., an automated Access.Appplication instance.)\r\n'           : A zero or a negative return indicates an error with the import.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function ImportCommandBars(strSourceDatabasePath As String, strCommandBarNameToVerify As String, Optional objTargetApplication As Application = Nothing) As eImportCommandBarsResult\r\n    Dim strSql As String\r\n    Dim blnResult As Boolean\r\n\r\n    If objTargetApplication Is Nothing Then\r\n        Set objTargetApplication = Application\r\n    End If\r\n\r\n    With objTargetApplication\r\n        ' If we do not delete the command bar with the same name, it will not import. Deleting it from\r\n        ' application will not actually delete it from its original database so even if we delete some\r\n        ' another database's command bar, it won't actually remove it from that database and it'll be\r\n        ' restored next time it is opened.\r\n        On Error Resume Next\r\n        Do\r\n            .CommandBars(strCommandBarNameToVerify).Delete\r\n        Loop Until Err.Number\r\n        On Error GoTo 0\r\n\r\n        ' Note that we are manipulating the application's WizHook which might not be necessarily\r\n        ' the same one in modWizHook.\r\n        .WizHook.Key = 51488399\r\n        .WizHook.WizCopyCmdbars strSourceDatabasePath\r\n\r\n        ' Verify we have the command bar imported.\r\n        On Error Resume Next\r\n        ' Application.CommandBars is the union of all loaded databases' command bars; just because we can find a\r\n        ' command bar with same name does not mean the database has the command bar loaded into the binary file.\r\n        ' However, this is a good first step in verifying the import since a negative result definitely mean it\r\n        ' wasn't imported at all. We use the target project's Application in case it's an automated instance\r\n        ' independent of the current Application object.\r\n        blnResult = Not (.CommandBars(strCommandBarNameToVerify) Is Nothing)\r\n        If Err.Number Then\r\n            ImportCommandBars = eicFailed\r\n            Exit Function\r\n        End If\r\n        On Error GoTo 0\r\n\r\n        If blnResult Then\r\n            blnResult = False\r\n            ' Not all versions of MDB files or ADP files have MSysAccessStorage table.\r\n            strSql = _\r\n                \"SELECT o.Name \" & _\r\n                \"FROM MSysObjects AS o \" & _\r\n                \"WHERE o.Name = 'MSysAccessStorage' \" & _\r\n                \"  AND o.Type = 1;\"\r\n            With .CurrentProject.Connection.Execute(strSql)\r\n                If Not .EOF Then\r\n                    If .Fields(0).Value = \"MSysAccessStorage\" Then\r\n                        blnResult = True\r\n                    End If\r\n                End If\r\n            End With\r\n        End If\r\n\r\n        If blnResult Then\r\n            ' This project has MSysAccessStorage table so we can determine if it contains the commandbar.\r\n            ' We only need to check the virtual directory for the CmdBars entry. If the virtual directory\r\n            ' has the command bar's name in its listing, we can assume the command bar was succcessfully\r\n            ' imported into this specific database file. The directory entry is delimited as following:\r\n            '   * Chr(4)\r\n            '   * <byte length of the command bar's name> plus 4 more for the ending delimiters\r\n            '   * the command bar's name (Unicode)\r\n            '   * 4 null bytes (or 2 vbNullChars)\r\n            ' Because a string reads in little endian order, we need to swap the Chr(4) and the byte length,\r\n            ' so it becomes ChrW(Hex(((<length> + 4) * 256) + 4).\r\n            strSql = _\r\n                \"SELECT s1.Lv \" & _\r\n                \"FROM MSysAccessStorage AS s1 \" & _\r\n                \"WHERE s1.Name = (Chr(3) & 'DirData') \" & _\r\n                \"  AND s1.Type = 2 \" & _\r\n                \"  AND s1.ParentId = (\" & _\r\n                \"    SELECT s2.Id \" & _\r\n                \"    FROM MSysAccessStorage AS s2 \" & _\r\n                \"    WHERE s2.Name = 'CmdBars' \" & _\r\n                \"      AND s2.Type = 1 \" & _\r\n                \");\"\r\n\r\n            With .CurrentProject.Connection.Execute(strSql)\r\n                If Not .EOF Then\r\n                    If InStr(1, .Fields(0).Value, ChrW(((LenB(strCommandBarNameToVerify) + 4) * 256) + 4) & strCommandBarNameToVerify & vbNullChar & vbNullChar, vbTextCompare) > 0 Then\r\n                        ImportCommandBars = eicImportedVerified\r\n                    Else\r\n                        ImportCommandBars = eicImportedNotVerified\r\n                    End If\r\n                End If\r\n            End With\r\n        Else\r\n            ' We can only tenatively assume success since we don't have the MSysAccessStorage table that can be\r\n            ' easily inspected. Older MDB files use MSysAccessObjects which are even more opaque. No clue how\r\n            ' we'd inspect an ADP file since it won't have any system tables to describe its contents.\r\n            ImportCommandBars = eicImportedUnableToVerify\r\n        End If\r\n    End With\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modVbeForm.bas",
    "content": "﻿Attribute VB_Name = \"modVbeForm\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modVbeForm\r\n' Author    : Adam Waller / Adapted from FormSerializer\r\n' Date      : 1/24/2022\r\n' Purpose   : Serialize a MSForms 2.0 form into human-readable JSON output.\r\n'---------------------------------------------------------------------------------------\r\n\r\n''\r\n' FormSerializer v1.0.0\r\n' (c) Georges Kuenzli - https://github.com/gkuenzli/vbaDeveloper\r\n'\r\n' `FormSerializer` produces a string JSON description of a MSForm.\r\n'\r\n' @module FormSerializer\r\n' @author gkuenzli\r\n' @license MIT (http://www.opensource.org/licenses/mit-license.php)\r\n'' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n''\r\n' Convert a VBComponent of type MSForm to a JSON descriptive data\r\n'\r\n' @method SerializeMSForm\r\n' @param {VBComponent} FormComponent\r\n' @return {Dictionary} MSForm JSON descriptive dictionary\r\n''\r\nPublic Function SerializeMSForm(ByVal FormComponent As VBComponent) As Dictionary\r\n    Set SerializeMSForm = GetMSFormProperties(FormComponent)\r\nEnd Function\r\n\r\nPrivate Function GetMSFormProperties(ByVal FormComponent As VBComponent) As Dictionary\r\n    Dim dict As New Dictionary\r\n    dict.Add \"Name\", FormComponent.Name\r\n    dict.Add \"Designer\", GetDesigner(FormComponent)\r\n    dict.Add \"Properties\", GetProperties(FormComponent, FormComponent.Properties)\r\n    Set GetMSFormProperties = dict\r\nEnd Function\r\n\r\nPrivate Function GetDesigner(ByVal FormComponent As VBComponent) As Dictionary\r\n    Dim dict As New Dictionary\r\n    dict.Add \"Controls\", GetControls(FormComponent.Designer.Controls)\r\n    Set GetDesigner = dict\r\nEnd Function\r\n\r\nPrivate Function GetProperties(ByVal Context As Object, ByVal Properties As VBIDE.Properties) As Dictionary\r\n    Dim dict As New Dictionary\r\n    Dim p As VBIDE.Property\r\n    Dim i As Long\r\n    For i = 1 To Properties.Count\r\n        Set p = Properties(i)\r\n        If IsSerializableProperty(Context, p) Then\r\n            dict.Add p.Name, GetValue(Context, p)\r\n        End If\r\n    Next i\r\n    Set GetProperties = dict\r\nEnd Function\r\n\r\nPrivate Function IsSerializableProperty(ByVal Context As Object, ByVal Property As VBIDE.Property) As Boolean\r\n    Dim tp As VbVarType\r\n    On Error Resume Next\r\n    tp = VarType(Property.Value)\r\n    On Error GoTo 0\r\n    IsSerializableProperty = _\r\n        (tp <> vbEmpty) And (tp <> vbError) And _\r\n        Left(Property.Name, 1) <> \"_\" And _\r\n        InStr(\"ActiveControls,Controls,Handle,MouseIcon,Picture,Selected,DesignMode,ShowToolbox,ShowGridDots,SnapToGrid,GridX,GridY,DrawBuffer,CanPaste\", Property.Name) = 0\r\n\r\n    If TypeName(Context) = \"VBComponent\" Then\r\n        ' We must ignore Top and Height MSForm properties since these seem to be related to the some settings in the Windows user profile.\r\n        IsSerializableProperty = _\r\n            IsSerializableProperty And _\r\n            InStr(\"Top,Height\", Property.Name) = 0\r\n    End If\r\nEnd Function\r\n\r\nPrivate Function GetProperty(ByVal Context As Object, ByVal Property As VBIDE.Property) As Dictionary\r\n    Dim dict As New Dictionary\r\n    dict.Add \"Name\", Property.Name\r\n    If Property.Name = \"Controls\" Then\r\n    Else\r\n        dict.Add \"Value\", GetValue(Context, Property)\r\n    End If\r\n    Set GetProperty = dict\r\nEnd Function\r\n\r\nPrivate Function GetControls(ByVal Controls As MSForms.Controls) As Collection\r\n    Dim coll As New Collection\r\n    Dim ctrl As MSForms.Control\r\n    For Each ctrl In Controls\r\n        If Not ControlExistsInSubElements(Controls, ctrl.Name, 0) Then\r\n            coll.Add GetControl(ctrl)\r\n        End If\r\n    Next ctrl\r\n    Set GetControls = coll\r\nEnd Function\r\n\r\nPrivate Function ControlExistsInSubElements(ByVal Controls As MSForms.Controls, ByVal Name As String, ByVal Depth As Long) As Boolean\r\n    Dim ctrl As MSForms.Control\r\n    Dim o As Object\r\n    For Each ctrl In Controls\r\n        Set o = ctrl\r\n        If Depth > 0 Then\r\n            If Name = ctrl.Name Then\r\n                ControlExistsInSubElements = True\r\n                Exit Function\r\n            End If\r\n        End If\r\n        On Error Resume Next\r\n        ControlExistsInSubElements = ControlExistsInSubElements(o.Controls, Name, Depth + 1)\r\n        On Error GoTo 0\r\n        If ControlExistsInSubElements Then\r\n            Exit Function\r\n        End If\r\n    Next ctrl\r\nEnd Function\r\n\r\nPrivate Function GetControl(ByVal ctl As Object) As Dictionary ' MSForms.Control\r\n    Dim dic As Dictionary\r\n    Dim varName As Variant\r\n    Set dic = New Dictionary\r\n    ' Loop through properties, adding each value to dictionary\r\n    For Each varName In GetPropertyList(TypeName(ctl))\r\n        AddProperty dic, ctl, varName\r\n    Next varName\r\n    ' Return dictionary of control properties\r\n    Set GetControl = dic\r\nEnd Function\r\n\r\nPrivate Sub AddProperty(dic As Dictionary, o As Object, strName As Variant)\r\n    Select Case strName\r\n        Case \"Class\":       dic.Add strName, TypeName(o)\r\n        Case \"Font\":        dic.Add strName, GetFont(o.Font)\r\n        Case \"Picture\":     dic.Add strName, GetPicture(o.Picture)\r\n        Case \"MouseIcon\":   dic.Add strName, GetPicture(o.MouseIcon)\r\n        Case \"Controls\":    dic.Add strName, GetControls(o.Controls)\r\n        Case \"Pages\":       dic.Add strName, GetPages(o.Pages)\r\n        Case \"Tabs\":        dic.Add strName, GetTabs(o.Tabs)\r\n        Case Else\r\n            ' Standard property.\r\n            ' Use CallByName on object to get value if the property exists\r\n            On Error Resume Next\r\n            dic.Add strName, CallByName(o, strName, VbGet)\r\n            If Err Then Err.Clear\r\n    End Select\r\nEnd Sub\r\n\r\nPrivate Function GetPages(ByVal Pages As MSForms.Pages) As Collection\r\n    Dim coll As New Collection\r\n    Dim i As Long\r\n    Dim p As MSForms.Page\r\n    For i = 0 To Pages.Count - 1\r\n        Set p = Pages(i)\r\n        coll.Add GetControl(p)\r\n    Next i\r\n    Set GetPages = coll\r\nEnd Function\r\n\r\nPrivate Function GetTabs(ByVal Tabs As Tabs) As Collection\r\n    Dim coll As New Collection\r\n    Dim i As Long\r\n    Dim T As MSForms.Tab\r\n    For i = 0 To Tabs.Count - 1\r\n        Set T = Tabs(i)\r\n        coll.Add GetControl(T)\r\n    Next i\r\n    Set GetTabs = coll\r\nEnd Function\r\n\r\nPrivate Function GetFont(ByVal fnt As NewFont) As Dictionary\r\n    Set GetFont = New Dictionary\r\n    With GetFont\r\n        .Add \"Bold\", fnt.Bold\r\n        .Add \"Charset\", fnt.Charset\r\n        .Add \"Italic\", fnt.Italic\r\n        .Add \"Name\", fnt.Name\r\n        .Add \"Size\", fnt.Size\r\n        .Add \"Strikethrough\", fnt.Strikethrough\r\n        .Add \"Underline\", fnt.Underline\r\n        .Add \"Weight\", fnt.Weight\r\n    End With\r\nEnd Function\r\n\r\nPrivate Function GetPicture(ByVal Picture As IPictureDisp) As String\r\n\r\n    ' TODO: implement a Base64-encoding of the picture\r\n    'StdFunctions.SavePicture Picture, strFileName\r\n\r\nEnd Function\r\n\r\nPrivate Function GetValue(ByVal Context As Object, ByVal Property As VBIDE.Property) As Variant\r\n    If VarType(Property.Value) = vbObject Then\r\n        Select Case TypeName(Property.Value)\r\n            Case \"Properties\"\r\n                Set GetValue = GetProperties(Context, Property.Value)\r\n            Case Else\r\n                Set GetValue = Nothing\r\n        End Select\r\n    Else\r\n        GetValue = Property.Value\r\n    End If\r\nEnd Function\r\n\r\nPrivate Function GetPropertyList(strType As String) As Collection\r\n\r\n    Set GetPropertyList = New Collection\r\n    With GetPropertyList\r\n\r\n        ' Generic control level properties\r\n        .Add \"Class\"\r\n        .Add \"Name\"\r\n        .Add \"Cancel\"\r\n        .Add \"ControlSource\"\r\n        .Add \"ControlTipText\"\r\n        .Add \"Default\"\r\n        .Add \"Height\"\r\n        .Add \"HelpContextID\"\r\n        .Add \"LayoutEffect\"\r\n        .Add \"Left\"\r\n        .Add \"RowSource\"\r\n        .Add \"RowSourceType\"\r\n        .Add \"TabIndex\"\r\n        .Add \"TabStop\"\r\n        .Add \"Tag\"\r\n        .Add \"Top\"\r\n        .Add \"Visible\"\r\n        .Add \"Width\"\r\n\r\n        ' Specific properties based on control type\r\n        Select Case strType\r\n            Case \"CheckBox\"\r\n                .Add \"Accelerator\"\r\n                .Add \"Alignment\"\r\n                .Add \"AutoSize\"\r\n                .Add \"BackColor\"\r\n                .Add \"BackStyle\"\r\n                .Add \"Caption\"\r\n                .Add \"Enabled\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"GroupName\"\r\n                .Add \"Locked\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"Picture\"\r\n                .Add \"PicturePosition\"\r\n                .Add \"SpecialEffect\"\r\n                .Add \"TextAlign\"\r\n                .Add \"TripleState\"\r\n                .Add \"Value\"\r\n                .Add \"WordWrap\"\r\n\r\n            Case \"ComboBox\", \"RefEdit\"  ' (Also used for Excel Reference control)\r\n                .Add \"AutoSize\"\r\n                .Add \"AutoTab\"\r\n                .Add \"AutoWordSelect\"\r\n                .Add \"BackColor\"\r\n                .Add \"BackStyle\"\r\n                .Add \"BorderColor\"\r\n                .Add \"BorderStyle\"\r\n                .Add \"BoundColumn\"\r\n            '    .Add \"CanPaste\"\r\n                .Add \"ColumnCount\"\r\n                .Add \"ColumnHeads\"\r\n                .Add \"ColumnWidths\"\r\n                .Add \"DragBehavior\"\r\n                .Add \"DropButtonStyle\"\r\n                .Add \"Enabled\"\r\n                .Add \"EnterFieldBehavior\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"HideSelection\"\r\n                .Add \"IMEMode\"\r\n                .Add \"ListRows\"\r\n                .Add \"ListStyle\"\r\n                .Add \"ListWidth\"\r\n                .Add \"Locked\"\r\n                .Add \"MatchEntry\"\r\n                .Add \"MatchRequired\"\r\n                .Add \"MaxLength\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"SelectionMargin\"\r\n                .Add \"ShowDropButtonWhen\"\r\n                .Add \"SpecialEffect\"\r\n                .Add \"Style\"\r\n                .Add \"Text\"\r\n                .Add \"TextAlign\"\r\n                .Add \"TextColumn\"\r\n                .Add \"TopIndex\"\r\n                .Add \"Value\"\r\n\r\n            Case \"CommandButton\"\r\n                .Add \"Accelerator\"\r\n                .Add \"AutoSize\"\r\n                .Add \"BackColor\"\r\n                .Add \"BackStyle\"\r\n                .Add \"Caption\"\r\n                .Add \"Enabled\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"Locked\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"Picture\"\r\n                .Add \"PicturePosition\"\r\n                .Add \"TakeFocusOnClick\"\r\n                .Add \"WordWrap\"\r\n\r\n            Case \"Frame\"\r\n                .Add \"BackColor\"\r\n                .Add \"BorderColor\"\r\n                .Add \"BorderStyle\"\r\n                '.Add \"CanPaste\"\r\n                .Add \"CanRedo\"\r\n                .Add \"CanUndo\"\r\n                .Add \"Caption\"\r\n                .Add \"Controls\"\r\n                .Add \"Cycle\"\r\n                .Add \"Enabled\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"InsideHeight\"\r\n                .Add \"InsideWidth\"\r\n                .Add \"KeepScrollBarsVisible\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"Picture\"\r\n                .Add \"PictureAlignment\"\r\n                .Add \"PictureSizeMode\"\r\n                .Add \"PictureTiling\"\r\n                .Add \"ScrollBars\"\r\n                .Add \"ScrollHeight\"\r\n                .Add \"ScrollLeft\"\r\n                .Add \"ScrollTop\"\r\n                .Add \"ScrollWidth\"\r\n                .Add \"SpecialEffect\"\r\n                .Add \"VerticalScrollBarSide\"\r\n                .Add \"Zoom\"\r\n\r\n            Case \"Image\"\r\n                .Add \"AutoSize\"\r\n                .Add \"BackColor\"\r\n                .Add \"BackStyle\"\r\n                .Add \"BorderColor\"\r\n                .Add \"BorderStyle\"\r\n                .Add \"Enabled\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"Picture\"\r\n                .Add \"PictureAlignment\"\r\n                .Add \"PictureSizeMode\"\r\n                .Add \"PictureTiling\"\r\n                .Add \"SpecialEffect\"\r\n\r\n            Case \"Label\"\r\n                .Add \"Accelerator\"\r\n                .Add \"AutoSize\"\r\n                .Add \"BackColor\"\r\n                .Add \"BackStyle\"\r\n                .Add \"BorderColor\"\r\n                .Add \"BorderStyle\"\r\n                .Add \"Caption\"\r\n                .Add \"Enabled\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"Picture\"\r\n                .Add \"PicturePosition\"\r\n                .Add \"SpecialEffect\"\r\n                .Add \"TextAlign\"\r\n                .Add \"WordWrap\"\r\n\r\n            Case \"ListBox\"\r\n                .Add \"BackColor\"\r\n                .Add \"BorderColor\"\r\n                .Add \"BorderStyle\"\r\n                .Add \"BoundColumn\"\r\n                .Add \"ColumnHeads\"\r\n                .Add \"ColumnWidths\"\r\n                .Add \"Enabled\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"IMEMode\"\r\n                .Add \"IntegralHeight\"\r\n                .Add \"ListIndex\"\r\n                .Add \"ListStyle\"\r\n                .Add \"Locked\"\r\n                .Add \"MatchEntry\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"MultiSelect\"\r\n                .Add \"Selected\"\r\n                .Add \"SpecialEffect\"\r\n                .Add \"Text\"\r\n                .Add \"TextAlign\"\r\n                .Add \"TextColumn\"\r\n                .Add \"TopIndex\"\r\n                .Add \"Value\"\r\n\r\n            Case \"MultiPage\"\r\n                .Add \"BackColor\"\r\n                .Add \"Enabled\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"MultiRow\"\r\n                .Add \"Pages\"\r\n                .Add \"Style\"\r\n                .Add \"TabFixedHeight\"\r\n                .Add \"TabFixedWidth\"\r\n                .Add \"TabOrientation\"\r\n                .Add \"Value\"\r\n\r\n            Case \"OptionButton\"\r\n                .Add \"Accelerator\"\r\n                .Add \"Alignment\"\r\n                .Add \"AutoSize\"\r\n                .Add \"BackColor\"\r\n                .Add \"BackStyle\"\r\n                .Add \"Caption\"\r\n                .Add \"Enabled\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"GroupName\"\r\n                .Add \"Locked\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"Picture\"\r\n                .Add \"PicturePosition\"\r\n                .Add \"SpecialEffect\"\r\n                .Add \"TextAlign\"\r\n                .Add \"TripleState\"\r\n                .Add \"Value\"\r\n                .Add \"WordWrap\"\r\n\r\n            Case \"Page\"\r\n                .Add \"Accelerator\"\r\n                '.Add \"CanPaste\"\r\n                .Add \"CanRedo\"\r\n                .Add \"CanUndo\"\r\n                .Add \"Caption\"\r\n                .Add \"Controls\"\r\n                .Add \"ControlTipText\"\r\n                .Add \"Cycle\"\r\n                .Add \"Enabled\"\r\n                .Add \"Index\"\r\n                .Add \"InsideHeight\"\r\n                .Add \"InsideWidth\"\r\n                .Add \"KeepScrollBarsVisible\"\r\n                .Add \"Name\"\r\n                .Add \"Picture\"\r\n                .Add \"PictureAlignment\"\r\n                .Add \"PictureSizeMode\"\r\n                .Add \"PictureTiling\"\r\n                .Add \"ScrollBars\"\r\n                .Add \"ScrollHeight\"\r\n                .Add \"ScrollLeft\"\r\n                .Add \"ScrollTop\"\r\n                .Add \"ScrollWidth\"\r\n                .Add \"Tag\"\r\n                .Add \"TransitionEffect\"\r\n                .Add \"TransitionPeriod\"\r\n                .Add \"VerticalScrollBarSide\"\r\n                .Add \"Visible\"\r\n                .Add \"Zoom\"\r\n\r\n            Case \"ScrollBar\"\r\n                .Add \"BackColor\"\r\n                .Add \"Delay\"\r\n                .Add \"Enabled\"\r\n                .Add \"ForeColor\"\r\n                .Add \"LargeChange\"\r\n                .Add \"Max\"\r\n                .Add \"Min\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"Orientation\"\r\n                .Add \"ProportionalThumb\"\r\n                .Add \"SmallChange\"\r\n                .Add \"Value\"\r\n\r\n            Case \"SpinButton\"\r\n                .Add \"BackColor\"\r\n                .Add \"Delay\"\r\n                .Add \"Enabled\"\r\n                .Add \"ForeColor\"\r\n                .Add \"Max\"\r\n                .Add \"Min\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"Orientation\"\r\n                .Add \"SmallChange\"\r\n                .Add \"Value\"\r\n\r\n            Case \"Tab\"\r\n                .Add \"Accelerator\"\r\n                .Add \"Caption\"\r\n                .Add \"ControlTipText\"\r\n                .Add \"Enabled\"\r\n                .Add \"Index\"\r\n                .Add \"Name\"\r\n                .Add \"Tag\"\r\n                .Add \"Visible\"\r\n\r\n            Case \"TabStrip\"\r\n                .Add \"BackColor\"\r\n                .Add \"ClientHeight\"\r\n                .Add \"ClientLeft\"\r\n                .Add \"ClientTop\"\r\n                .Add \"ClientWidth\"\r\n                .Add \"Enabled\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"MultiRow\"\r\n                .Add \"Style\"\r\n                .Add \"TabFixedHeight\"\r\n                .Add \"TabFixedWidth\"\r\n                .Add \"TabOrientation\"\r\n                .Add \"Tabs\"\r\n                .Add \"Value\"\r\n\r\n            Case \"TextBox\"\r\n                .Add \"AutoSize\"\r\n                .Add \"AutoTab\"\r\n                .Add \"AutoWordSelect\"\r\n                .Add \"BackColor\"\r\n                .Add \"BackStyle\"\r\n                .Add \"BorderColor\"\r\n                .Add \"BorderStyle\"\r\n                '.Add \"CanPaste\"\r\n                .Add \"CurLine\"\r\n                .Add \"DragBehavior\"\r\n                .Add \"Enabled\"\r\n                .Add \"EnterFieldBehavior\"\r\n                .Add \"EnterKeyBehavior\"\r\n                .Add \"Font\"\r\n                .Add \"ForeColor\"\r\n                .Add \"HideSelection\"\r\n                .Add \"IMEMode\"\r\n                .Add \"IntegralHeight\"\r\n                .Add \"Locked\"\r\n                .Add \"MaxLength\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"MultiLine\"\r\n                .Add \"PasswordChar\"\r\n                .Add \"ScrollBars\"\r\n                .Add \"SelectionMargin\"\r\n                .Add \"SpecialEffect\"\r\n                .Add \"TabKeyBehavior\"\r\n                .Add \"Text\"\r\n                .Add \"TextAlign\"\r\n                .Add \"Value\"\r\n                .Add \"WordWrap\"\r\n\r\n            Case \"ToggleButton\"\r\n                .Add \"Accelerator\"\r\n                .Add \"Alignment\"\r\n                .Add \"AutoSize\"\r\n                .Add \"BackColor\"\r\n                .Add \"BackStyle\"\r\n                .Add \"Caption\"\r\n                .Add \"Enabled\"\r\n                .Add \"ForeColor\"\r\n                .Add \"GroupName\"\r\n                .Add \"Locked\"\r\n                .Add \"MouseIcon\"\r\n                .Add \"MousePointer\"\r\n                .Add \"Picture\"\r\n                .Add \"PicturePosition\"\r\n                .Add \"SpecialEffect\"\r\n                .Add \"TextAlign\"\r\n                .Add \"TripleState\"\r\n                .Add \"Value\"\r\n                .Add \"WordWrap\"\r\n\r\n            Case Else\r\n                Debug.Print \"Warning: Unknown ActiveX Control Type Name : \" & strType\r\n\r\n        End Select\r\n    End With\r\n\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modWizHook.bas",
    "content": "﻿Attribute VB_Name = \"modWizHook\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modWizHook\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2022\r\n' Purpose   : Expose some WizHook functions utilized by this add-in.\r\n'           : Documentation links: http://accessblog.net/2016/06/access-wizhook-library.html\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CloseCurrentDatabase2\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2022\r\n' Purpose   : Unlike the Application method, this WizHook version does not stop all\r\n'           : running code. This allows you to automate the closing of the current\r\n'           : database while still continuing the add-in code.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CloseCurrentDatabase2()\r\n    CheckKey\r\n    WizHook.CloseCurrentDatabase\r\n    DoEvents\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CurrentVBProject\r\n' Author    : Adam Waller\r\n' Date      : 3/10/2023\r\n' Purpose   : Get a reference to the VB Project for the current database\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function CurrentVBProject() As VBProject\r\n    CheckKey\r\n    Set CurrentVBProject = WizHook.DbcVbProject\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : GlobalProcExists\r\n' Author    : Adam Waller\r\n' Date      : 3/10/2023\r\n' Purpose   : Returns true if the procedure exists (Public proc in standard module)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Function GlobalProcExists(strName) As Boolean\r\n    CheckKey\r\n    GlobalProcExists = WizHook.GlobalProcExists(strName)\r\nEnd Function\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CheckKey\r\n' Author    : Adam Waller\r\n' Date      : 5/4/2022\r\n' Purpose   : Make sure we have set the WizHook key before using commands that require\r\n'           : it to be set. (Caches value since it only needs to be set once per\r\n'           : session.)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPrivate Function CheckKey()\r\n    Static lngKey As Long\r\n    If lngKey = 0 Then\r\n        lngKey = 51488399\r\n        WizHook.Key = lngKey\r\n    End If\r\nEnd Function\r\n"
  },
  {
    "path": "Version Control.accda.src/modules/modZip.bas",
    "content": "﻿Attribute VB_Name = \"modZip\"\r\n'---------------------------------------------------------------------------------------\r\n' Module    : modZip\r\n' Author    : Adam Waller\r\n' Date      : 12/4/2020\r\n' Purpose   : Functions for creating and working with Zip files\r\n'---------------------------------------------------------------------------------------\r\nOption Compare Database\r\nOption Private Module\r\nOption Explicit\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CreateZipFile\r\n' Author    : Adam Waller\r\n' Date      : 5/26/2020\r\n' Purpose   : Create an empty zip file to copy files into.\r\n'           : Adapted from: http://www.rondebruin.nl/win/s7/win001.htm\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CreateZipFile(strPath As String)\r\n\r\n    Dim strHeader As String\r\n\r\n    ' Build Zip file header\r\n    strHeader = \"PK\" & Chr$(5) & Chr$(6) & String$(18, 0)\r\n\r\n    ' Write to file\r\n    VerifyPath strPath\r\n    With FSO.CreateTextFile(strPath, True)\r\n        .Write strHeader\r\n        .Close\r\n    End With\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CopyToZip\r\n' Author    : Adam Waller\r\n' Date      : 5/26/2020\r\n' Purpose   : Copy a file into a zip archive.\r\n'           : Adapted from: http://www.rondebruin.nl/win/s7/win001.htm\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CopyFileToZip(strFile As String, strZip As String)\r\n\r\n    Dim oApp As Object\r\n    Dim varZip As Variant\r\n    Dim varFile As Variant\r\n\r\n    ' Must use variants for the CopyHere function to work.\r\n    varZip = strZip\r\n    varFile = strFile\r\n\r\n    Set oApp = CreateObject(\"Shell.Application\")\r\n    oApp.Namespace(varZip).CopyHere varFile\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : CopyFolderToZip\r\n' Author    : Adam Waller\r\n' Date      : 6/3/2020\r\n' Purpose   : Copies a folder of items into a zip file.\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub CopyFolderToZip(strFolder As String, strZip As String, _\r\n    Optional blnPauseTillFinished As Boolean = True, Optional intTimeoutSeconds As Integer = 60)\r\n\r\n    Dim oApp As Object\r\n    Dim varZip As Variant\r\n    Dim varFolder As Variant\r\n    Dim sngTimeout As Single\r\n    Dim lngCount As Long\r\n\r\n    ' Must use variants for the CopyHere function to work.\r\n    varZip = strZip\r\n    varFolder = strFolder\r\n\r\n    ' Count the total items before we start the copy,\r\n    ' since there might already be files in the zip folder.\r\n    Set oApp = CreateObject(\"Shell.Application\")\r\n    lngCount = oApp.Namespace(varFolder).Items.Count + oApp.Namespace(varZip).Items.Count\r\n\r\n    ' Start the copy\r\n    oApp.Namespace(varZip).CopyHere oApp.Namespace(varFolder).Items\r\n\r\n    ' Pause till the copying is complete, or we hit the timeout.\r\n    If blnPauseTillFinished Then\r\n        sngTimeout = Timer + intTimeoutSeconds\r\n        Do While Timer < sngTimeout\r\n            ' Check to see if all the items have been copied.\r\n            If oApp.Namespace(varZip).Items.Count = lngCount Then Exit Do\r\n            Pause 0.5\r\n        Loop\r\n    End If\r\n\r\nEnd Sub\r\n\r\n\r\n'---------------------------------------------------------------------------------------\r\n' Procedure : ExtractFromZip\r\n' Author    : Adam Waller\r\n' Date      : 6/3/2020\r\n' Purpose   : Extracts all the files from a zip archive. (Requires a .zip extension)\r\n'---------------------------------------------------------------------------------------\r\n'\r\nPublic Sub ExtractFromZip(strZip As String, strDestFolder As String, _\r\n    Optional blnPauseTillFinished As Boolean = True, Optional intTimeoutSeconds As Integer = 60)\r\n\r\n    Dim oApp As Object\r\n    Dim varZip As Variant\r\n    Dim varFolder As Variant\r\n    Dim sngTimeout As Single\r\n    Dim lngCount As Long\r\n    Dim strFolder As String\r\n\r\n    ' Build folder path, and make sure it exists\r\n    If Not FSO.FolderExists(strDestFolder) Then FSO.CreateFolder strDestFolder\r\n    strFolder = FSO.GetFolder(strDestFolder).Path\r\n\r\n    ' Must use variants for the CopyHere function to work.\r\n    varZip = strZip\r\n    varFolder = strFolder & PathSep\r\n\r\n    ' Count the total items before we start the copy,\r\n    ' since there might already be files in the zip folder.\r\n    Set oApp = CreateObject(\"Shell.Application\")\r\n    If blnPauseTillFinished Then\r\n        lngCount = oApp.Namespace(varFolder).Items.Count + oApp.Namespace(varZip).Items.Count\r\n    End If\r\n\r\n    ' Begin the extraction\r\n    oApp.Namespace(varFolder).CopyHere oApp.Namespace(varZip).Items\r\n    If blnPauseTillFinished Then\r\n        ' Pause till the copying is complete, or we hit the timeout.\r\n        sngTimeout = Timer + intTimeoutSeconds\r\n        Do While Timer < sngTimeout\r\n            ' Check to see if all the items have been copied.\r\n            If oApp.Namespace(varZip).Items.Count = lngCount Then Exit Do\r\n            Pause 0.5\r\n        Loop\r\n    End If\r\n\r\nEnd Sub\r\n"
  },
  {
    "path": "Version Control.accda.src/nav-pane-groups.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbNavPaneGroup\",\r\n    \"Description\": \"Navigation Pane Custom Groups\"\r\n  },\r\n  \"Items\": {\r\n    \"Categories\": [\r\n      {\r\n        \"Name\": \"Custom\",\r\n        \"Flags\": 0,\r\n        \"Position\": 2,\r\n        \"Groups\": [\r\n          {\r\n            \"Name\": \"Custom Group 1\",\r\n            \"Flags\": 0,\r\n            \"Position\": 2,\r\n            \"Objects\": [\r\n            ]\r\n          }\r\n        ]\r\n      }\r\n    ]\r\n  }\r\n}\r\n"
  },
  {
    "path": "Version Control.accda.src/project.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbProject\",\r\n    \"Description\": \"Project\"\r\n  },\r\n  \"Items\": {\r\n    \"FileFormat\": 12,\r\n    \"RemovePersonalInformation\": false\r\n  }\r\n}\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryMsSqlServerObjects.bas",
    "content": "﻿dbMemo \"SQL\" =\"-- On Microsoft SQL Server, return a listing of all parent level objects\\015\\012\"\r\n    \"SELECT o.[name],\\015\\012    SCHEMA_NAME(o.[schema_id]) AS [schema],\\015\\012\\011C\"\r\n    \"ASE\\015\\012\\011\\011-- Return the most recent modfied date of the object or any d\"\r\n    \"ependent object\\015\\012\\011\\011WHEN isnull(c.max_modified, 0) > o.modify_date TH\"\r\n    \"EN c.max_modified\\015\\012\\011\\011ELSE o.modify_date\\015\\012\\011END AS last_modif\"\r\n    \"ied,\\015\\012\\011o.type_desc,\\015\\012\\011CASE o.[type]\\015\\012\\011\\011WHEN 'V' TH\"\r\n    \"EN 'views'\\015\\012\\011\\011WHEN 'U' THEN 'tables'\\015\\012\\011\\011WHEN 'IT' THEN '\"\r\n    \"tables'\\011\\011-- Internal tables\\015\\012\\011\\011WHEN 'TR' THEN 'tables'\\015\\012\"\r\n    \"\\011\\011WHEN 'P' THEN 'procedures'\\015\\012\\011\\011WHEN 'FN' THEN 'functions'\\011\"\r\n    \"-- Scalar function\\015\\012\\011\\011WHEN 'IF' THEN 'functions'\\011-- Inline table \"\r\n    \"valued function\\015\\012\\011\\011WHEN 'TF' THEN 'functions'\\011-- Table valued fun\"\r\n    \"ction\\015\\012\\011\\011WHEN 'TT' THEN 'types'\\011\\011-- Type table\\015\\012\\011\\011\"\r\n    \"WHEN 'SO' THEN 'sequences'\\011-- Sequence object\\015\\012\\011\\011WHEN 'SN' THEN '\"\r\n    \"synonymns'\\011-- Synonyms\\015\\012\\011\\011ELSE 'unknown'\\015\\012\\011END as folder\"\r\n    \",\\015\\012\\011o.[type] AS object_type\\015\\012    -- ,*\\015\\012FROM sys.objects o\\015\"\r\n    \"\\012LEFT JOIN \\015\\012\\011-- Get most recent modified date of any child object\\015\"\r\n    \"\\012\\011(select \\015\\012\\011\\011parent_object_id,\\015\\012\\011\\011max(modify_date\"\r\n    \") AS max_modified\\015\\012\\011\\011from sys.objects\\015\\012\\011\\011WHERE parent_ob\"\r\n    \"ject_id > 0\\015\\012\\011\\011GROUP BY parent_object_id\\015\\012\\011)AS c \\015\\012\\011\"\r\n    \"ON c.parent_object_id = o.object_id\\015\\012WHERE 1 = 1\\015\\012--AND o.type = 'TT\"\r\n    \"'\\015\\012AND o.parent_object_id = 0\\015\\012AND o.[type] NOT IN (\\015\\012\\011 'S'\"\r\n    \"  -- System Tables\\015\\012\\011,'SQ' -- Service queues\\015\\012\\011,'TR'  -- Trigg\"\r\n    \"ers saved from tables\\015\\012\\011,'IT'  -- Internal tables\\015\\012\\011,'TT'  -- \"\r\n    \"Type tables\\015\\012\\011,'SO'  -- Sequence objects\\015\\012\\011)\\015\\012\"\r\ndbMemo \"Connect\" =\"ODBC;\"\r\ndbBoolean \"ReturnsRecords\" =\"-1\"\r\ndbInteger \"ODBCTimeout\" =\"60\"\r\ndbBoolean \"LogMessages\" =\"0\"\r\ndbByte \"Orientation\" =\"0\"\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryMsSqlServerObjects.sql",
    "content": "﻿-- On Microsoft SQL Server, return a listing of all parent level objects\r\nSELECT o.[name],\r\n    SCHEMA_NAME(o.[schema_id]) AS [schema],\r\n\tCASE\r\n\t\t-- Return the most recent modfied date of the object or any dependent object\r\n\t\tWHEN isnull(c.max_modified, 0) > o.modify_date THEN c.max_modified\r\n\t\tELSE o.modify_date\r\n\tEND AS last_modified,\r\n\to.type_desc,\r\n\tCASE o.[type]\r\n\t\tWHEN 'V' THEN 'views'\r\n\t\tWHEN 'U' THEN 'tables'\r\n\t\tWHEN 'IT' THEN 'tables'\t\t-- Internal tables\r\n\t\tWHEN 'TR' THEN 'tables'\r\n\t\tWHEN 'P' THEN 'procedures'\r\n\t\tWHEN 'FN' THEN 'functions'\t-- Scalar function\r\n\t\tWHEN 'IF' THEN 'functions'\t-- Inline table valued function\r\n\t\tWHEN 'TF' THEN 'functions'\t-- Table valued function\r\n\t\tWHEN 'TT' THEN 'types'\t\t-- Type table\r\n\t\tWHEN 'SO' THEN 'sequences'\t-- Sequence object\r\n\t\tWHEN 'SN' THEN 'synonymns'\t-- Synonyms\r\n\t\tELSE 'unknown'\r\n\tEND as folder,\r\n\to.[type] AS object_type\r\n    -- ,*\r\nFROM sys.objects o\r\nLEFT JOIN \r\n\t-- Get most recent modified date of any child object\r\n\t(select \r\n\t\tparent_object_id,\r\n\t\tmax(modify_date) AS max_modified\r\n\t\tfrom sys.objects\r\n\t\tWHERE parent_object_id > 0\r\n\t\tGROUP BY parent_object_id\r\n\t)AS c \r\n\tON c.parent_object_id = o.object_id\r\nWHERE 1 = 1\r\n--AND o.type = 'TT'\r\nAND o.parent_object_id = 0\r\nAND o.[type] NOT IN (\r\n\t 'S'  -- System Tables\r\n\t,'SQ' -- Service queues\r\n\t,'TR'  -- Triggers saved from tables\r\n\t,'IT'  -- Internal tables\r\n\t,'TT'  -- Type tables\r\n\t,'SO'  -- Sequence objects\r\n\t)\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryMySqlServerObjects.bas",
    "content": "﻿dbMemo \"SQL\" =\"-- On MySQL Server, return a listing of all parent level objects\\015\\012\\015\\012\"\r\n    \"-- Tables\\015\\012SELECT \\015\\012\\011`TABLE_SCHEMA` AS `schema`,\\015\\012    `TABL\"\r\n    \"E_NAME` AS `name`,\\015\\012    coalesce(`UPDATE_TIME`, `CREATE_TIME`) AS `last_mo\"\r\n    \"dified`,\\015\\012    'tables' as `folder`,\\015\\012    null as `definition`\\015\\012\"\r\n    \"FROM information_schema.tables\\015\\012where TABLE_TYPE = 'BASE TABLE'\\015\\012and\"\r\n    \" TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema')\\015\\012\"\r\n    \"\\015\\012-- Views\\015\\012UNION SELECT\\015\\012\\011`TABLE_SCHEMA` AS `schema`,\\015\\012\"\r\n    \"    `TABLE_NAME` AS `name`,\\015\\012    null AS `last_modified`,\\015\\012    'view\"\r\n    \"s' as `folder`,\\015\\012    `VIEW_DEFINITION` as `definition`\\015\\012FROM informa\"\r\n    \"tion_schema.views\\015\\012where 1=1\\015\\012and TABLE_SCHEMA NOT IN ('mysql')\\015\\012\"\r\n    \"\\015\\012-- Functions\\015\\012UNION SELECT\\015\\012\\011`ROUTINE_SCHEMA` AS `schema`\"\r\n    \",\\015\\012    `ROUTINE_NAME` AS `name`,\\015\\012    coalesce(`LAST_ALTERED`, `CREA\"\r\n    \"TED`) AS `last_modified`,\\015\\012    'functions' as `folder`,\\015\\012    null as\"\r\n    \" `definition`\\015\\012FROM information_schema.routines\\015\\012where ROUTINE_TYPE \"\r\n    \"= 'FUNCTION'\\015\\012and ROUTINE_SCHEMA NOT IN ('mysql')\\015\\012\\015\\012-- Stored\"\r\n    \" Procedures\\015\\012UNION SELECT\\015\\012\\011`ROUTINE_SCHEMA` AS `schema`,\\015\\012\"\r\n    \"    `ROUTINE_NAME` AS `name`,\\015\\012    coalesce(`LAST_ALTERED`, `CREATED`) AS \"\r\n    \"`last_modified`,\\015\\012    'procedures' as `folder`,\\015\\012    null as `defini\"\r\n    \"tion`\\015\\012FROM information_schema.routines\\015\\012where ROUTINE_TYPE = 'PROCE\"\r\n    \"DURE'\\015\\012and ROUTINE_SCHEMA NOT IN ('mysql')\\015\\012\\015\\012-- Triggers\\015\\012\"\r\n    \"UNION SELECT\\015\\012\\011`TRIGGER_SCHEMA` AS `schema`,\\015\\012    `TRIGGER_NAME` \"\r\n    \"AS `name`,\\015\\012    `CREATED` AS `last_modified`,\\015\\012    'triggers' as `fo\"\r\n    \"lder`,\\015\\012    `ACTION_STATEMENT` as `definition`\\015\\012FROM information_sch\"\r\n    \"ema.triggers\\015\\012where 1=1\\015\\012\"\r\ndbMemo \"Connect\" =\"ODBC;\"\r\ndbBoolean \"ReturnsRecords\" =\"-1\"\r\ndbInteger \"ODBCTimeout\" =\"60\"\r\ndbBoolean \"LogMessages\" =\"0\"\r\ndbByte \"Orientation\" =\"0\"\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryMySqlServerObjects.sql",
    "content": "﻿-- On MySQL Server, return a listing of all parent level objects\r\n\r\n-- Tables\r\nSELECT \r\n\t`TABLE_SCHEMA` AS `schema`,\r\n    `TABLE_NAME` AS `name`,\r\n    coalesce(`UPDATE_TIME`, `CREATE_TIME`) AS `last_modified`,\r\n    'tables' as `folder`,\r\n    null as `definition`\r\nFROM information_schema.tables\r\nwhere TABLE_TYPE = 'BASE TABLE'\r\nand TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema')\r\n\r\n-- Views\r\nUNION SELECT\r\n\t`TABLE_SCHEMA` AS `schema`,\r\n    `TABLE_NAME` AS `name`,\r\n    null AS `last_modified`,\r\n    'views' as `folder`,\r\n    `VIEW_DEFINITION` as `definition`\r\nFROM information_schema.views\r\nwhere 1=1\r\nand TABLE_SCHEMA NOT IN ('mysql')\r\n\r\n-- Functions\r\nUNION SELECT\r\n\t`ROUTINE_SCHEMA` AS `schema`,\r\n    `ROUTINE_NAME` AS `name`,\r\n    coalesce(`LAST_ALTERED`, `CREATED`) AS `last_modified`,\r\n    'functions' as `folder`,\r\n    null as `definition`\r\nFROM information_schema.routines\r\nwhere ROUTINE_TYPE = 'FUNCTION'\r\nand ROUTINE_SCHEMA NOT IN ('mysql')\r\n\r\n-- Stored Procedures\r\nUNION SELECT\r\n\t`ROUTINE_SCHEMA` AS `schema`,\r\n    `ROUTINE_NAME` AS `name`,\r\n    coalesce(`LAST_ALTERED`, `CREATED`) AS `last_modified`,\r\n    'procedures' as `folder`,\r\n    null as `definition`\r\nFROM information_schema.routines\r\nwhere ROUTINE_TYPE = 'PROCEDURE'\r\nand ROUTINE_SCHEMA NOT IN ('mysql')\r\n\r\n-- Triggers\r\nUNION SELECT\r\n\t`TRIGGER_SCHEMA` AS `schema`,\r\n    `TRIGGER_NAME` AS `name`,\r\n    `CREATED` AS `last_modified`,\r\n    'triggers' as `folder`,\r\n    `ACTION_STATEMENT` as `definition`\r\nFROM information_schema.triggers\r\nwhere 1=1\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryNavPaneGroups.bas",
    "content": "﻿Operation =1\r\nOption =0\r\nWhere =\"(((MSysNavPaneGroups.Name) Is Not Null) AND ((MSysNavPaneGroupCategories.Type)=4\"\r\n    \"))\"\r\nBegin InputTables\r\n    Name =\"MSysNavPaneGroups\"\r\n    Name =\"MSysNavPaneGroupToObjects\"\r\n    Name =\"MSysObjects\"\r\n    Name =\"MSysNavPaneGroupCategories\"\r\nEnd\r\nBegin OutputColumns\r\n    Alias =\"CategoryName\"\r\n    Expression =\"MSysNavPaneGroupCategories.Name\"\r\n    Alias =\"CategoryPosition\"\r\n    Expression =\"MSysNavPaneGroupCategories.Position\"\r\n    Alias =\"CategoryFlags\"\r\n    Expression =\"MSysNavPaneGroupCategories.Flags\"\r\n    Alias =\"GroupName\"\r\n    Expression =\"MSysNavPaneGroups.Name\"\r\n    Alias =\"GroupFlags\"\r\n    Expression =\"MSysNavPaneGroups.Flags\"\r\n    Alias =\"GroupPosition\"\r\n    Expression =\"MSysNavPaneGroups.Position\"\r\n    Alias =\"ObjectType\"\r\n    Expression =\"MSysObjects.Type\"\r\n    Alias =\"ObjectName\"\r\n    Expression =\"MSysObjects.Name\"\r\n    Alias =\"ObjectFlags\"\r\n    Expression =\"MSysNavPaneGroupToObjects.Flags\"\r\n    Alias =\"ObjectIcon\"\r\n    Expression =\"MSysNavPaneGroupToObjects.Icon\"\r\n    Alias =\"ObjectPosition\"\r\n    Expression =\"MSysNavPaneGroupToObjects.Position\"\r\n    Alias =\"NameInGroup\"\r\n    Expression =\"MSysNavPaneGroupToObjects.Name\"\r\n    Alias =\"CategoryID\"\r\n    Expression =\"MSysNavPaneGroupCategories.Id\"\r\n    Alias =\"GroupID\"\r\n    Expression =\"MSysNavPaneGroups.Id\"\r\n    Alias =\"LinkID\"\r\n    Expression =\"MSysNavPaneGroupToObjects.Id\"\r\nEnd\r\nBegin Joins\r\n    LeftTable =\"MSysNavPaneGroupToObjects\"\r\n    RightTable =\"MSysObjects\"\r\n    Expression =\"MSysNavPaneGroupToObjects.ObjectID = MSysObjects.Id\"\r\n    Flag =2\r\n    LeftTable =\"MSysNavPaneGroupCategories\"\r\n    RightTable =\"MSysNavPaneGroups\"\r\n    Expression =\"MSysNavPaneGroupCategories.Id = MSysNavPaneGroups.GroupCategoryID\"\r\n    Flag =1\r\n    LeftTable =\"MSysNavPaneGroups\"\r\n    RightTable =\"MSysNavPaneGroupToObjects\"\r\n    Expression =\"MSysNavPaneGroups.Id = MSysNavPaneGroupToObjects.GroupID\"\r\n    Flag =2\r\nEnd\r\nBegin OrderBy\r\n    Expression =\"MSysNavPaneGroupCategories.Name\"\r\n    Flag =0\r\n    Expression =\"MSysNavPaneGroups.Name\"\r\n    Flag =0\r\n    Expression =\"MSysObjects.Type\"\r\n    Flag =0\r\n    Expression =\"MSysObjects.Name\"\r\n    Flag =0\r\nEnd\r\ndbBoolean \"ReturnsRecords\" =\"-1\"\r\ndbInteger \"ODBCTimeout\" =\"60\"\r\ndbBoolean \"OrderByOn\" =\"0\"\r\ndbByte \"Orientation\" =\"0\"\r\ndbByte \"DefaultView\" =\"2\"\r\ndbBoolean \"FilterOnLoad\" =\"0\"\r\ndbBoolean \"OrderByOnLoad\" =\"-1\"\r\ndbByte \"RecordsetType\" =\"0\"\r\ndbBoolean \"TotalsRow\" =\"0\"\r\nBegin\r\n    Begin\r\n        dbText \"Name\" =\"GroupName\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n        dbInteger \"ColumnWidth\" =\"2085\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectName\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectType\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"GroupFlags\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectFlags\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectIcon\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"ObjectPosition\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"GroupPosition\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"NameInGroup\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"CategoryName\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"CategoryPosition\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"CategoryFlags\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"LinkID\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"GroupID\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"CategoryID\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\nEnd\r\nBegin\r\n    State =0\r\n    Left =0\r\n    Top =0\r\n    Right =1094\r\n    Bottom =544\r\n    Left =-1\r\n    Top =-1\r\n    Right =1078\r\n    Bottom =397\r\n    Left =0\r\n    Top =0\r\n    ColumnsShown =539\r\n    Begin\r\n        Left =251\r\n        Top =42\r\n        Right =440\r\n        Bottom =234\r\n        Top =0\r\n        Name =\"MSysNavPaneGroups\"\r\n        Name =\"\"\r\n    End\r\n    Begin\r\n        Left =513\r\n        Top =40\r\n        Right =752\r\n        Bottom =247\r\n        Top =0\r\n        Name =\"MSysNavPaneGroupToObjects\"\r\n        Name =\"\"\r\n    End\r\n    Begin\r\n        Left =853\r\n        Top =40\r\n        Right =997\r\n        Bottom =376\r\n        Top =0\r\n        Name =\"MSysObjects\"\r\n        Name =\"\"\r\n    End\r\n    Begin\r\n        Left =44\r\n        Top =44\r\n        Right =188\r\n        Bottom =236\r\n        Top =0\r\n        Name =\"MSysNavPaneGroupCategories\"\r\n        Name =\"\"\r\n    End\r\nEnd\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryNavPaneGroups.sql",
    "content": "﻿SELECT\r\n  MSysNavPaneGroupCategories.Name AS CategoryName,\r\n  MSysNavPaneGroupCategories.Position AS CategoryPosition,\r\n  MSysNavPaneGroupCategories.Flags AS CategoryFlags,\r\n  MSysNavPaneGroups.Name AS GroupName,\r\n  MSysNavPaneGroups.Flags AS GroupFlags,\r\n  MSysNavPaneGroups.Position AS GroupPosition,\r\n  MSysObjects.Type AS ObjectType,\r\n  MSysObjects.Name AS ObjectName,\r\n  MSysNavPaneGroupToObjects.Flags AS ObjectFlags,\r\n  MSysNavPaneGroupToObjects.Icon AS ObjectIcon,\r\n  MSysNavPaneGroupToObjects.Position AS ObjectPosition,\r\n  MSysNavPaneGroupToObjects.Name AS NameInGroup,\r\n  MSysNavPaneGroupCategories.Id AS CategoryID,\r\n  MSysNavPaneGroups.Id AS GroupID,\r\n  MSysNavPaneGroupToObjects.Id AS LinkID\r\nFROM\r\n  (\r\n    MSysNavPaneGroupCategories\r\n    INNER JOIN MSysNavPaneGroups ON MSysNavPaneGroupCategories.Id = MSysNavPaneGroups.GroupCategoryID\r\n  )\r\n  LEFT JOIN (\r\n    MSysNavPaneGroupToObjects\r\n    LEFT JOIN MSysObjects ON MSysNavPaneGroupToObjects.ObjectID = MSysObjects.Id\r\n  ) ON MSysNavPaneGroups.Id = MSysNavPaneGroupToObjects.GroupID\r\nWHERE\r\n  (\r\n    (\r\n      (MSysNavPaneGroups.Name) Is Not Null\r\n    )\r\n    AND (\r\n      (\r\n        MSysNavPaneGroupCategories.Type\r\n      )= 4\r\n    )\r\n  )\r\nORDER BY\r\n  MSysNavPaneGroupCategories.Name,\r\n  MSysNavPaneGroups.Name,\r\n  MSysObjects.Type,\r\n  MSysObjects.Name;\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryStrings.bas",
    "content": "﻿Operation =1\r\nOption =0\r\nBegin InputTables\r\n    Name =\"tblLanguages\"\r\n    Name =\"tblStrings\"\r\nEnd\r\nBegin OutputColumns\r\n    Alias =\"LanguageID\"\r\n    Expression =\"tblLanguages.ID\"\r\n    Expression =\"tblStrings.ID\"\r\n    Expression =\"tblStrings.msgid\"\r\n    Expression =\"tblStrings.Context\"\r\n    Expression =\"tblStrings.Reference\"\r\n    Expression =\"tblStrings.Comments\"\r\nEnd\r\ndbBoolean \"ReturnsRecords\" =\"-1\"\r\ndbInteger \"ODBCTimeout\" =\"60\"\r\ndbByte \"RecordsetType\" =\"0\"\r\ndbBoolean \"OrderByOn\" =\"0\"\r\ndbByte \"Orientation\" =\"0\"\r\ndbByte \"DefaultView\" =\"2\"\r\ndbBoolean \"FilterOnLoad\" =\"0\"\r\ndbBoolean \"OrderByOnLoad\" =\"-1\"\r\ndbBoolean \"TotalsRow\" =\"0\"\r\nBegin\r\n    Begin\r\n        dbText \"Name\" =\"tblStrings.Comments\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"LanguageID\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"tblStrings.ID\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"tblStrings.msgid\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n        dbInteger \"ColumnWidth\" =\"3765\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"tblStrings.Context\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"tblStrings.Reference\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\nEnd\r\nBegin\r\n    State =0\r\n    Left =0\r\n    Top =0\r\n    Right =1705\r\n    Bottom =848\r\n    Left =-1\r\n    Top =-1\r\n    Right =1689\r\n    Bottom =535\r\n    Left =0\r\n    Top =0\r\n    ColumnsShown =539\r\n    Begin\r\n        Left =145\r\n        Top =91\r\n        Right =291\r\n        Bottom =265\r\n        Top =0\r\n        Name =\"tblLanguages\"\r\n        Name =\"\"\r\n    End\r\n    Begin\r\n        Left =349\r\n        Top =91\r\n        Right =503\r\n        Bottom =263\r\n        Top =0\r\n        Name =\"tblStrings\"\r\n        Name =\"\"\r\n    End\r\nEnd\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryStrings.sql",
    "content": "﻿SELECT\r\n  tblLanguages.ID AS LanguageID,\r\n  tblStrings.ID,\r\n  tblStrings.msgid,\r\n  tblStrings.Context,\r\n  tblStrings.Reference,\r\n  tblStrings.Comments\r\nFROM\r\n  tblLanguages,\r\n  tblStrings;\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryTranslatedStrings.bas",
    "content": "﻿Operation =1\r\nOption =0\r\nBegin InputTables\r\n    Name =\"tblTranslation\"\r\n    Name =\"qryStrings\"\r\nEnd\r\nBegin OutputColumns\r\n    Expression =\"qryStrings.ID\"\r\n    Expression =\"qryStrings.msgid\"\r\n    Expression =\"qryStrings.Context\"\r\n    Expression =\"qryStrings.Comments\"\r\n    Expression =\"tblTranslation.Translation\"\r\n    Alias =\"Lang\"\r\n    Expression =\"qryStrings.LanguageID\"\r\n    Expression =\"qryStrings.Reference\"\r\n    Alias =\"SortRank\"\r\n    Expression =\"IIf((Len([msgid])=0),1,2)\"\r\n    Alias =\"Key\"\r\n    Expression =\"[Context] & \\\"|\\\" & [msgid]\"\r\nEnd\r\nBegin Joins\r\n    LeftTable =\"qryStrings\"\r\n    RightTable =\"tblTranslation\"\r\n    Expression =\"qryStrings.ID = tblTranslation.StringID\"\r\n    Flag =2\r\n    LeftTable =\"qryStrings\"\r\n    RightTable =\"tblTranslation\"\r\n    Expression =\"qryStrings.LanguageID = tblTranslation.Language\"\r\n    Flag =2\r\nEnd\r\nBegin OrderBy\r\n    Expression =\"IIf((Len([msgid])=0),1,2)\"\r\n    Flag =0\r\n    Expression =\"qryStrings.msgid\"\r\n    Flag =0\r\nEnd\r\ndbBoolean \"ReturnsRecords\" =\"-1\"\r\ndbInteger \"ODBCTimeout\" =\"60\"\r\ndbByte \"RecordsetType\" =\"0\"\r\ndbBoolean \"OrderByOn\" =\"0\"\r\ndbByte \"Orientation\" =\"0\"\r\ndbByte \"DefaultView\" =\"2\"\r\ndbBoolean \"FilterOnLoad\" =\"0\"\r\ndbBoolean \"OrderByOnLoad\" =\"-1\"\r\ndbBoolean \"TotalsRow\" =\"0\"\r\nBegin\r\n    Begin\r\n        dbText \"Name\" =\"tblTranslation.Translation\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n        dbInteger \"ColumnWidth\" =\"7215\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"Key\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n        dbInteger \"ColumnWidth\" =\"3525\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"Lang\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"SortRank\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"qryStrings.ID\"\r\n        dbInteger \"ColumnWidth\" =\"870\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"qryStrings.msgid\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"qryStrings.Context\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"qryStrings.Comments\"\r\n        dbInteger \"ColumnWidth\" =\"3510\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"qryStrings.Reference\"\r\n        dbInteger \"ColumnWidth\" =\"4290\"\r\n        dbBoolean \"ColumnHidden\" =\"0\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\n    Begin\r\n        dbText \"Name\" =\"test\"\r\n        dbLong \"AggregateType\" =\"-1\"\r\n    End\r\nEnd\r\nBegin\r\n    State =0\r\n    Left =0\r\n    Top =0\r\n    Right =1305\r\n    Bottom =848\r\n    Left =-1\r\n    Top =-1\r\n    Right =1289\r\n    Bottom =586\r\n    Left =0\r\n    Top =0\r\n    ColumnsShown =539\r\n    Begin\r\n        Left =453\r\n        Top =146\r\n        Right =597\r\n        Bottom =290\r\n        Top =0\r\n        Name =\"tblTranslation\"\r\n        Name =\"\"\r\n    End\r\n    Begin\r\n        Left =199\r\n        Top =112\r\n        Right =347\r\n        Bottom =295\r\n        Top =0\r\n        Name =\"qryStrings\"\r\n        Name =\"\"\r\n    End\r\nEnd\r\n"
  },
  {
    "path": "Version Control.accda.src/queries/qryTranslatedStrings.sql",
    "content": "﻿SELECT\r\n  qryStrings.ID,\r\n  qryStrings.msgid,\r\n  qryStrings.Context,\r\n  qryStrings.Comments,\r\n  tblTranslation.Translation,\r\n  qryStrings.LanguageID AS Lang,\r\n  qryStrings.Reference,\r\n  IIf(\r\n    (\r\n      Len([msgid])= 0\r\n    ),\r\n    1,\r\n    2\r\n  ) AS SortRank,\r\n  [Context] & \"|\" & [msgid] AS [Key]\r\nFROM\r\n  qryStrings\r\n  LEFT JOIN tblTranslation ON (\r\n    qryStrings.LanguageID = tblTranslation.Language\r\n  )\r\n  AND (\r\n    qryStrings.ID = tblTranslation.StringID\r\n  )\r\nORDER BY\r\n  IIf(\r\n    (\r\n      Len([msgid])= 0\r\n    ),\r\n    1,\r\n    2\r\n  ),\r\n  qryStrings.msgid;\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblConflicts.sql",
    "content": "﻿CREATE TABLE [tblConflicts] (\r\n  [ID] AUTOINCREMENT CONSTRAINT [PrimaryKey] PRIMARY KEY UNIQUE NOT NULL,\r\n  [Component] VARCHAR (255),\r\n  [ItemKey] VARCHAR (255),\r\n  [FileName] VARCHAR (255),\r\n  [ObjectDate] DATETIME,\r\n  [IndexDate] DATETIME,\r\n  [FileDate] DATETIME,\r\n  [Suggestion] LONG,\r\n  [Resolution] LONG,\r\n  [Diff] LONGTEXT\r\n)\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblConflicts.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblConflicts\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblConflicts\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"ID\" index-key=\"ID \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"ItemKey\" index-key=\"ItemKey \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"ID \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOn\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DefaultView\" type=\"2\" value=\"2\"></od:tableProperty>\r\n        <od:tableProperty name=\"DisplayViewsOnSharePointSite\" type=\"2\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"TotalsRow\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"FilterOnLoad\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOnLoad\" type=\"1\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"HideNewField\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ThemeFontIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackThemeColorIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackShade\" type=\"6\" value=\"95\"></od:tableProperty>\r\n        <od:tableProperty name=\"ReadOnlyWhenDisconnected\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetGridlinesThemeColorIndex\" type=\"4\" value=\"3\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetForeThemeColorIndex\" type=\"4\" value=\"0\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"ID\" minOccurs=\"1\" od:jetType=\"autonumber\" od:sqlSType=\"int\" od:autoUnique=\"yes\" od:nonNullable=\"yes\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"Component\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"ItemKey\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"FileName\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"2415\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"ObjectDate\" minOccurs=\"0\" od:jetType=\"datetime\" od:sqlSType=\"datetime\" type=\"xsd:dateTime\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ShowDatePicker\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"IndexDate\" minOccurs=\"0\" od:jetType=\"datetime\" od:sqlSType=\"datetime\" type=\"xsd:dateTime\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ShowDatePicker\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"FileDate\" minOccurs=\"0\" od:jetType=\"datetime\" od:sqlSType=\"datetime\" type=\"xsd:dateTime\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ShowDatePicker\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"Suggestion\" minOccurs=\"0\" od:jetType=\"longinteger\" od:sqlSType=\"int\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DecimalPlaces\" type=\"2\" value=\"255\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"Resolution\" minOccurs=\"0\" od:jetType=\"longinteger\" od:sqlSType=\"int\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DecimalPlaces\" type=\"2\" value=\"255\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"Diff\" minOccurs=\"0\" od:jetType=\"hyperlink\" od:sqlSType=\"ntext\" od:hyperlink=\"yes\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DefaultValue\" type=\"12\" value=\"&quot;Show Diff...&quot;\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AppendOnly\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblLanguages.sql",
    "content": "﻿CREATE TABLE [tblLanguages] (\r\n  [ID] VARCHAR (255) CONSTRAINT [PrimaryKey] PRIMARY KEY UNIQUE NOT NULL,\r\n  [DisplayName] VARCHAR (255)\r\n)\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblLanguages.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblLanguages\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblLanguages\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"ID\" index-key=\"ID \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"ID \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOn\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DefaultView\" type=\"2\" value=\"2\"></od:tableProperty>\r\n        <od:tableProperty name=\"DisplayViewsOnSharePointSite\" type=\"2\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"TotalsRow\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"FilterOnLoad\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOnLoad\" type=\"1\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"HideNewField\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ThemeFontIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackThemeColorIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackShade\" type=\"6\" value=\"95\"></od:tableProperty>\r\n        <od:tableProperty name=\"ReadOnlyWhenDisconnected\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetGridlinesThemeColorIndex\" type=\"4\" value=\"3\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetForeThemeColorIndex\" type=\"4\" value=\"0\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"ID\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"DisplayName\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"1740\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblResources.sql",
    "content": "﻿CREATE TABLE [tblResources] (\r\n  [ResourceName] VARCHAR (255) CONSTRAINT [PrimaryKey] PRIMARY KEY UNIQUE NOT NULL,\r\n  [Content] VARCHAR,\r\n  [Description] VARCHAR (255)\r\n)\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblResources.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblResources\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblResources\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"ID\" index-key=\"ResourceName \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"ResourceName \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOn\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DefaultView\" type=\"2\" value=\"2\"></od:tableProperty>\r\n        <od:tableProperty name=\"DisplayViewsOnSharePointSite\" type=\"2\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"TotalsRow\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"FilterOnLoad\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOnLoad\" type=\"1\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"HideNewField\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ThemeFontIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackThemeColorIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackShade\" type=\"6\" value=\"95\"></od:tableProperty>\r\n        <od:tableProperty name=\"ReadOnlyWhenDisconnected\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetGridlinesThemeColorIndex\" type=\"4\" value=\"3\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetForeThemeColorIndex\" type=\"4\" value=\"0\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"ResourceName\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"2925\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Content\" minOccurs=\"0\" od:jetType=\"complex\" od:jetComplexType=\"MSysComplexType_Attachment\" maxOccurs=\"unbounded\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"126\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:complexType>\r\n            <xsd:sequence>\r\n              <xsd:element name=\"FileData\" minOccurs=\"0\" od:jetType=\"oleobject\" od:sqlSType=\"image\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:base64Binary\">\r\n                    <xsd:maxLength value=\"1476395008\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n              <xsd:element name=\"FileFlags\" minOccurs=\"0\" od:jetType=\"longinteger\" od:sqlSType=\"int\" type=\"xsd:int\"></xsd:element>\r\n              <xsd:element name=\"FileName\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:string\">\r\n                    <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n              <xsd:element name=\"FileTimeStamp\" minOccurs=\"0\" od:jetType=\"datetime\" od:sqlSType=\"datetime\" type=\"xsd:dateTime\"></xsd:element>\r\n              <xsd:element name=\"FileType\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:string\">\r\n                    <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n              <xsd:element name=\"FileURL\" minOccurs=\"0\" od:jetType=\"memo\" od:sqlSType=\"ntext\">\r\n                <xsd:simpleType>\r\n                  <xsd:restriction base=\"xsd:string\">\r\n                    <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n                  </xsd:restriction>\r\n                </xsd:simpleType>\r\n              </xsd:element>\r\n            </xsd:sequence>\r\n          </xsd:complexType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Description\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblStrings.sql",
    "content": "﻿CREATE TABLE [tblStrings] (\r\n  [ID] AUTOINCREMENT CONSTRAINT [PrimaryKey] PRIMARY KEY UNIQUE NOT NULL,\r\n  [msgid] LONGTEXT,\r\n  [Context] LONGTEXT,\r\n  [Reference] LONGTEXT,\r\n  [Comments] LONGTEXT\r\n)\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblStrings.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblStrings\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblStrings\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"ID\" index-key=\"ID \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"ID \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOn\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DefaultView\" type=\"2\" value=\"2\"></od:tableProperty>\r\n        <od:tableProperty name=\"DisplayViewsOnSharePointSite\" type=\"2\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"TotalsRow\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"FilterOnLoad\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOnLoad\" type=\"1\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"HideNewField\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ThemeFontIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackThemeColorIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackShade\" type=\"6\" value=\"95\"></od:tableProperty>\r\n        <od:tableProperty name=\"ReadOnlyWhenDisconnected\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetGridlinesThemeColorIndex\" type=\"4\" value=\"3\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetForeThemeColorIndex\" type=\"4\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"Filter\" type=\"12\" value=\"([tblStrings].[Reference] Like &quot;frmvcssplit*&quot;)\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"ID\" minOccurs=\"1\" od:jetType=\"autonumber\" od:sqlSType=\"int\" od:autoUnique=\"yes\" od:nonNullable=\"yes\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"msgid\" minOccurs=\"0\" od:jetType=\"memo\" od:sqlSType=\"ntext\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"3855\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextFormat\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AppendOnly\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Context\" minOccurs=\"0\" od:jetType=\"memo\" od:sqlSType=\"ntext\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"5895\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextFormat\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AppendOnly\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Reference\" minOccurs=\"0\" od:jetType=\"memo\" od:sqlSType=\"ntext\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"5400\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextFormat\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AppendOnly\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"Comments\" minOccurs=\"0\" od:jetType=\"memo\" od:sqlSType=\"ntext\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"6045\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextFormat\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AppendOnly\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblTableData.sql",
    "content": "﻿CREATE TABLE [tblTableData] (\r\n  [TableIcon] VARCHAR (2),\r\n  [TableName] VARCHAR (255) CONSTRAINT [PrimaryKey] PRIMARY KEY UNIQUE NOT NULL,\r\n  [FormatType] LONG,\r\n  [Flags] LONG,\r\n  [IsSystem] BIT,\r\n  [IsHidden] BIT,\r\n  [IsLocal] BIT,\r\n  [IsOther] BIT\r\n)\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblTableData.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblTableData\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblTableData\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"TableName \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOn\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DefaultView\" type=\"2\" value=\"2\"></od:tableProperty>\r\n        <od:tableProperty name=\"DisplayViewsOnSharePointSite\" type=\"2\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"TotalsRow\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"FilterOnLoad\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOnLoad\" type=\"1\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"HideNewField\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ThemeFontIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackThemeColorIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackShade\" type=\"6\" value=\"95\"></od:tableProperty>\r\n        <od:tableProperty name=\"ReadOnlyWhenDisconnected\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetGridlinesThemeColorIndex\" type=\"4\" value=\"3\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetForeThemeColorIndex\" type=\"4\" value=\"0\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"TableIcon\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"2\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"TableName\" minOccurs=\"1\" od:jetType=\"text\" od:sqlSType=\"nvarchar\" od:nonNullable=\"yes\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"255\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"FormatType\" minOccurs=\"1\" od:jetType=\"longinteger\" od:sqlSType=\"int\" od:nonNullable=\"yes\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DecimalPlaces\" type=\"2\" value=\"255\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DefaultValue\" type=\"12\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"Flags\" minOccurs=\"0\" od:jetType=\"longinteger\" od:sqlSType=\"int\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DecimalPlaces\" type=\"2\" value=\"255\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"IsSystem\" minOccurs=\"1\" od:jetType=\"yesno\" od:sqlSType=\"bit\" od:nonNullable=\"yes\" type=\"xsd:boolean\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Format\" type=\"10\" value=\"Yes/No\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DefaultValue\" type=\"12\" value=\"No\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"106\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"IsHidden\" minOccurs=\"1\" od:jetType=\"yesno\" od:sqlSType=\"bit\" od:nonNullable=\"yes\" type=\"xsd:boolean\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Format\" type=\"10\" value=\"Yes/No\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DefaultValue\" type=\"12\" value=\"No\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"106\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"IsLocal\" minOccurs=\"1\" od:jetType=\"yesno\" od:sqlSType=\"bit\" od:nonNullable=\"yes\" type=\"xsd:boolean\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Format\" type=\"10\" value=\"Yes/No\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DefaultValue\" type=\"12\" value=\"No\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"106\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"IsOther\" minOccurs=\"1\" od:jetType=\"yesno\" od:sqlSType=\"bit\" od:nonNullable=\"yes\" type=\"xsd:boolean\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Format\" type=\"10\" value=\"Yes/No\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DefaultValue\" type=\"12\" value=\"No\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"106\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblTranslation.sql",
    "content": "﻿CREATE TABLE [tblTranslation] (\r\n  [Language] VARCHAR (10),\r\n  [StringID] LONG,\r\n  [Translation] LONGTEXT,\r\n   CONSTRAINT [PrimaryKey] PRIMARY KEY ([Language], [StringID])\r\n)\r\n"
  },
  {
    "path": "Version Control.accda.src/tbldefs/tblTranslation.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:od=\"urn:schemas-microsoft-com:officedata\">\r\n  <xsd:element name=\"dataroot\">\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element ref=\"tblTranslation\" minOccurs=\"0\" maxOccurs=\"unbounded\"></xsd:element>\r\n      </xsd:sequence>\r\n      <xsd:attribute name=\"generated\" type=\"xsd:dateTime\"></xsd:attribute>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n  <xsd:element name=\"tblTranslation\">\r\n    <xsd:annotation>\r\n      <xsd:appinfo>\r\n        <od:index index-name=\"PrimaryKey\" index-key=\"Language StringID \" primary=\"yes\" unique=\"yes\" clustered=\"no\" order=\"asc asc\"></od:index>\r\n        <od:index index-name=\"StringID\" index-key=\"StringID \" primary=\"no\" unique=\"no\" clustered=\"no\" order=\"asc\"></od:index>\r\n        <od:tableProperty name=\"Orientation\" type=\"2\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOn\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DefaultView\" type=\"2\" value=\"2\"></od:tableProperty>\r\n        <od:tableProperty name=\"DisplayViewsOnSharePointSite\" type=\"2\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"TotalsRow\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"FilterOnLoad\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"OrderByOnLoad\" type=\"1\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"HideNewField\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"BackShade\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"ThemeFontIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackThemeColorIndex\" type=\"4\" value=\"1\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackTint\" type=\"6\" value=\"100\"></od:tableProperty>\r\n        <od:tableProperty name=\"AlternateBackShade\" type=\"6\" value=\"95\"></od:tableProperty>\r\n        <od:tableProperty name=\"ReadOnlyWhenDisconnected\" type=\"1\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetGridlinesThemeColorIndex\" type=\"4\" value=\"3\"></od:tableProperty>\r\n        <od:tableProperty name=\"DatasheetForeThemeColorIndex\" type=\"4\" value=\"0\"></od:tableProperty>\r\n        <od:tableProperty name=\"Filter\" type=\"12\" value=\"([tblTranslation].[StringID]=1474)\"></od:tableProperty>\r\n      </xsd:appinfo>\r\n    </xsd:annotation>\r\n    <xsd:complexType>\r\n      <xsd:sequence>\r\n        <xsd:element name=\"Language\" minOccurs=\"0\" od:jetType=\"text\" od:sqlSType=\"nvarchar\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"10\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n        <xsd:element name=\"StringID\" minOccurs=\"0\" od:jetType=\"longinteger\" od:sqlSType=\"int\" type=\"xsd:int\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DecimalPlaces\" type=\"2\" value=\"255\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"DisplayControl\" type=\"3\" value=\"109\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n        </xsd:element>\r\n        <xsd:element name=\"Translation\" minOccurs=\"0\" od:jetType=\"memo\" od:sqlSType=\"ntext\">\r\n          <xsd:annotation>\r\n            <xsd:appinfo>\r\n              <od:fieldProperty name=\"ColumnWidth\" type=\"3\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnOrder\" type=\"3\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ColumnHidden\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"Required\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AllowZeroLength\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMEMode\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"IMESentenceMode\" type=\"2\" value=\"3\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"UnicodeCompression\" type=\"1\" value=\"1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextFormat\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"TextAlign\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AppendOnly\" type=\"1\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"AggregateType\" type=\"4\" value=\"-1\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"ResultType\" type=\"2\" value=\"0\"></od:fieldProperty>\r\n              <od:fieldProperty name=\"CurrencyLCID\" type=\"4\" value=\"0\"></od:fieldProperty>\r\n            </xsd:appinfo>\r\n          </xsd:annotation>\r\n          <xsd:simpleType>\r\n            <xsd:restriction base=\"xsd:string\">\r\n              <xsd:maxLength value=\"536870910\"></xsd:maxLength>\r\n            </xsd:restriction>\r\n          </xsd:simpleType>\r\n        </xsd:element>\r\n      </xsd:sequence>\r\n    </xsd:complexType>\r\n  </xsd:element>\r\n</xsd:schema>\r\n"
  },
  {
    "path": "Version Control.accda.src/vbe-project.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbVbeProject\",\r\n    \"Description\": \"VBE Project\"\r\n  },\r\n  \"Items\": {\r\n    \"Name\": \"MSAccessVCS\",\r\n    \"Description\": \"Version 4.1.2 deployed on 4/12/2025\",\r\n    \"FileName\": \"Version Control.accda\",\r\n    \"HelpFile\": \"\",\r\n    \"HelpContextId\": 0,\r\n    \"ConditionalCompilationArguments\": \"\",\r\n    \"Mode\": 0,\r\n    \"Protection\": 0,\r\n    \"Type\": 100\r\n  }\r\n}\r\n"
  },
  {
    "path": "Version Control.accda.src/vbe-references.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"Class\": \"clsDbVbeReference\",\r\n    \"Description\": \"VBE References\"\r\n  },\r\n  \"Items\": {\r\n    \"stdole\": {\r\n      \"GUID\": \"{00020430-0000-0000-C000-000000000046}\",\r\n      \"Version\": \"2.0\"\r\n    },\r\n    \"DAO\": {\r\n      \"GUID\": \"{4AC9E1DA-5BAD-4AC7-86E3-24F4CDCECA28}\",\r\n      \"Version\": \"12.0\"\r\n    },\r\n    \"ADODB\": {\r\n      \"GUID\": \"{B691E011-1797-432E-907A-4D8C69339129}\",\r\n      \"Version\": \"6.1\"\r\n    },\r\n    \"IWshRuntimeLibrary\": {\r\n      \"GUID\": \"{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}\",\r\n      \"Version\": \"1.0\"\r\n    },\r\n    \"Office\": {\r\n      \"GUID\": \"{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}\",\r\n      \"Version\": \"2.5\"\r\n    },\r\n    \"Scripting\": {\r\n      \"GUID\": \"{420B2830-E718-11CF-893D-00A0C9054228}\",\r\n      \"Version\": \"1.0\"\r\n    },\r\n    \"VBIDE\": {\r\n      \"GUID\": \"{0002E157-0000-0000-C000-000000000046}\",\r\n      \"Version\": \"5.3\"\r\n    },\r\n    \"VBScript_RegExp_55\": {\r\n      \"GUID\": \"{3F4DACA7-160D-11D2-A8E9-00104B365C9F}\",\r\n      \"Version\": \"5.5\"\r\n    },\r\n    \"MSXML2\": {\r\n      \"GUID\": \"{F5078F18-C551-11D3-89B9-0000F81FE221}\",\r\n      \"Version\": \"6.0\"\r\n    },\r\n    \"MSForms\": {\r\n      \"GUID\": \"{0D452EE1-E08F-101A-852E-02608C4D0BB4}\",\r\n      \"Version\": \"2.0\"\r\n    },\r\n    \"UIAutomationClient\": {\r\n      \"GUID\": \"{944DE083-8FB8-45CF-BCB7-C477ACB2F897}\",\r\n      \"Version\": \"1.0\"\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "Version Control.accda.src/vcs-options.json",
    "content": "﻿{\r\n  \"Info\": {\r\n    \"AddinVersion\": \"4.1.2\",\r\n    \"AccessVersion\": \"14.0 32-bit\"\r\n  },\r\n  \"Options\": {\r\n    \"ExportFolder\": \"\",\r\n    \"ShowDebug\": false,\r\n    \"UseFastSave\": true,\r\n    \"UseMergeBuild\": false,\r\n    \"UseGitIntegration\": false,\r\n    \"SavePrintVars\": true,\r\n    \"ExportPrintSettings\": {\r\n      \"Orientation\": true,\r\n      \"PaperSize\": true,\r\n      \"Duplex\": false,\r\n      \"PrintQuality\": false,\r\n      \"DisplayFrequency\": false,\r\n      \"Collate\": false,\r\n      \"Resolution\": false,\r\n      \"DisplayFlags\": false,\r\n      \"Color\": false,\r\n      \"Copies\": false,\r\n      \"ICMMethod\": false,\r\n      \"DefaultSource\": false,\r\n      \"Scale\": false,\r\n      \"ICMIntent\": false,\r\n      \"FormName\": false,\r\n      \"PaperLength\": false,\r\n      \"DitherType\": false,\r\n      \"MediaType\": false,\r\n      \"PaperWidth\": false,\r\n      \"TTOption\": false\r\n    },\r\n    \"SaveQuerySQL\": true,\r\n    \"FormatSQL\": true,\r\n    \"ForceImportOriginalQuerySQL\": false,\r\n    \"SaveTableSQL\": true,\r\n    \"SplitLayoutFromVBA\": true,\r\n    \"StripPublishOption\": true,\r\n    \"SanitizeColors\": 3,\r\n    \"SanitizeLevel\": 2,\r\n    \"ExtractThemeFiles\": false,\r\n    \"TablesToExportData\": {\r\n      \"USysRegInfo\": {\r\n        \"Format\": \"Tab Delimited\"\r\n      },\r\n      \"USysRibbons\": {\r\n        \"Format\": \"Tab Delimited\"\r\n      }\r\n    },\r\n    \"SchemaExports\": {\r\n    },\r\n    \"RunBeforeExport\": \"\",\r\n    \"RunAfterExport\": \"\",\r\n    \"RunBeforeBuild\": \"\",\r\n    \"RunAfterBuild\": \"AfterBuild\",\r\n    \"RunBeforeMerge\": \"\",\r\n    \"RunAfterMerge\": \"\",\r\n    \"ShowVCSLegacy\": true,\r\n    \"HashAlgorithm\": \"SHA256\",\r\n    \"UseShortHash\": true,\r\n    \"BreakOnError\": true,\r\n    \"PreserveRubberDuckID\": false\r\n  }\r\n}\r\n"
  },
  {
    "path": "Wiki/Documentation.md",
    "content": "# Using Version Control in Microsoft Access\n\nIn the process of developing a more complex Microsoft Access database, you may find yourself wondering what changed when, or how you are going to collaborate with other developers. That's what this system was designed for. It basically exports your Access database into individual files representing the various tables, queries, forms, etc... in such a way that you can easily compare changes or revisions in the development process. Let's consider a couple use cases:\n\n## Export and Save\nFor some developers, they simply want to **track the progress** of their work on a database system over time. Using this Version Control add-in, you can simply **export** the project to source files, then commit those changes to GitHub (or other versioning system). If you are looking for something simple, GitHub Desktop provides a nice user interface for working with a GitHub/GitLab repository.\n\n## Collaborative Development\nIn other cases you might have a team of developers that are simultaneously working on the same Microsoft Access Database system. This Version Control add-in allows developers to work independently on their own copies of the database, **exporting** and **committing** changes to a repository. Changes are reviewed and **merged at a source file level**, and then the database is **built** from the source files to combine all the changes together.\n\nA new feature in version 4 is the ability to [merge](Merge-Build) new changes from source into an existing project without having to rebuild the entire project from scratch. (You must perform at least one full build before using the merge feature.)\n\n# Options\n\nThis add-in includes a number of options to customize your experience to your environment and workflow. These options are saved on a per-project basis, as well as allowing you to set defaults for new projects.\n\n[Click here for detailed Options Information](Options).\n"
  },
  {
    "path": "Wiki/Editing-and-Contributing.md",
    "content": "So, you like the Addin. And you want to contribute more. Hurrah!\n\n# BLUF:\n1. Make Pull Requests (PRs) on the `Dev` branch: it's the in use / development branch. Master / Stable is not actively worked on, and is used to provide a \"stable\" base while we work out any kinks in the `dev` branch.\n2. If you can, do not pull directly from your `dev` fork: make a working branch in your repository. This will help ensure reduced conflicts, and to ensure we know what the scope of your PR is more easily. \n3. Keep scope of PRs within a single area of focus: if you fixed two bugs, please keep them to separated PRs. Using specific work branches will help. This will ensure we don't get co-mingled issues, and is a lot cleaner to ensure we don't introduce new bugs from fixing others.\n\n# Details:\nIf you directly edit Access Add-ins (such as this one) within the \"opening\" Access file, changes will not be saved. \nThis is a double edged sword: it allows easy debugging, and trying things out which might otherwise ruin files. Downside is that once you close the session, it will discard any settings or changes you made. \n\nThis is a nice  way to load \"extras\" for users and ensure they don't break things for everyone else. \n\nIf you want to make changes to this Add-In (and contribute them!), do this:\n1. Fork this [MS Access Add-In Repository](https://github.com/joyfullservice/msaccess-vcs-integration) into your GitHub account repos.\n![image](https://user-images.githubusercontent.com/54177882/117137254-6d378280-ad77-11eb-923e-a7a876611fed.png)\n2. Clone your fork to a local repository alongside your other Access dev repos on your machine. \n3. Some put theirs alongside some other Access repositories they utilize.\n![image](https://user-images.githubusercontent.com/54177882/117137620-f353c900-ad77-11eb-9680-047cabd002da.png)\n\n3. Switch to `Dev` Branch: We suggest renaming YOUR `dev` branch to something local to you, especially if you still use some of the legacyVCS tools. I named my local fork of this branch to `dev-addin`.\n4. Connect a second remote to the `joyfullservice/msaccess-vcs-integration` (upstream) repository. This way you can track progress on the upstream ( joyfullservice/msaccess-vcs-integration) repository. There may be changes you don't want to pull into yours, or you may want customization not present on the upstream (in some environments, users have specific changes required to ensure proper integration in their security environment and/or configurations that shouldn't be default for everyone).\n![image](https://user-images.githubusercontent.com/54177882/117138802-84776f80-ad79-11eb-97f0-e55e62f59c38.png)\n\n5. Go into your new local repo, and launch the Addin directly.\n\n6. Make changes, use the add-in to export the add-in code, and commit/push/pull request just like any other repository.\n![image](https://user-images.githubusercontent.com/54177882/117139316-197a6880-ad7a-11eb-95ca-1cb3c12a712f.png)\n\n7. To edit the VCS, click the \"x\" button instead of the \"install\" on the loading form.\n![image](https://user-images.githubusercontent.com/54177882/117144981-990b3600-ad80-11eb-8413-db75258dc9ca.png)\n\n8. To edit forms for the VCS, open in \"Design View\"; their loading code / functions won't run.\n![image](https://user-images.githubusercontent.com/54177882/117144997-9c9ebd00-ad80-11eb-8c31-a56ed881fc18.png)\n\n9. See above for PR guidelines.\n"
  },
  {
    "path": "Wiki/Export-Import-File-Types.md",
    "content": "Note: This page is a work in progress. Feel free to contribute!\n\n# File Structure Overview\nThe following outline illustrates the folder and file structure used by the add-in when exporting source files for a project. (For simplicity, ADP project folders are not shown here.)\n\nNote that in your export folder you will only see the folders and files for the components actually used in your database. See [Supported Objects](Supported-Objects) for a comprehensive list of object types and the related code sections in this add-in.\n\n    .\n    ├── ...\n    ├── .gitattributes                  # (Recommended for git) Configure git attributes\n    ├── .gitignore                      # (Recommended for git) Ignore specific files in git\n    ├── Database.accdb                  # Main database file\n    └── Database.accdb.src              # Folder for source file export (default path)\n        ├── forms                       # Access forms\n        │   ├── MyForm.bas              # Object definition\n        │   └── MyForm.json             # Custom print settings (if used)\n        ├── images                      # Linked images\n        │   ├── MyPicture.png           # Image file\n        │   └── MyPicture.json          # Image metadata\n        ├── imexspecs                   # Legacy table-based import/export specifications\n        │   └── LinkedTableSpec.json    # Json representation of specification\n        ├── macros                      # Macros\n        │   └── AutoExec.bas            # Object definition\n        ├── modules                     # VBA modules (standard and class)\n        │   ├── MyModule.bas            # Standard module\n        │   └── MyClass.cls             # Class module\n        ├── queries                     # Queries\n        │   ├── MyQuery.bas             # Object definition\n        │   └── MyQuery.sql             # Raw SQL definition (easier to see changes)\n        ├── relations                   # Database relationships\n        │   └── Table1Table2.json       # Json representation of a single relationship\n        ├── reports                     # Reports\n        │   ├── MyReport.bas            # Object definition\n        │   └── MyReport.json           # Print settings\n        ├── savedspecs                  # XML-based import/export specifications\n        │   └── MyExport.json           # Metadata with embedded XML\n        ├── tables                      # Table data\n        │   ├── MyTable.txt             # Tab-delimited text format (more readable)\n        │   └── MyTable.xml             # XML table data (better data preservation)\n        ├── tbldefs                     # Table definitions (structure, columns)\n        │   ├── MyTable.sql             # A SQL rendition of the table structure (more readable)\n        │   └── MyTable.xml             # XML object definition (for export/import) \n        ├── tdmacros                    # Table Data Macros\n        │   └── MyTable.xml             # Object definition\n        ├── themes                      # Database themes (visual appearance)\n        │   ├── Angles.thmx             # Self-contained zip file\n        │   └── Executive               # (Alternate) Extracted contents folder\n        │       ├── _rels               # Recommended when customizing the theme\n        │       ├── theme               # files in a database project\n        │       └── ...                 # \n        ├── vbeforms                    # MSForms 2.0 Forms (Traditional VBA Forms)\n        │   ├── MyForm.frm              # Form definition stub\n        │   ├── MyForm.frx              # Binary OLE Blob content and structure\n        │   └── MyForm.json             # Json representation of content and structure\n        ├── Build.log                   # VCS build log\n        ├── dbs-properties.json         # DAO database properties\n        ├── documents.json              # Document container metadata\n        ├── Export.log                  # VCS export log\n        ├── hidden-attributes.json      # Hidden flag attributes\n        ├── nav-pane-groups.json        # Custom navigation pane groups\n        ├── project.json                # File format and Remove Personal Info flag\n        ├── proj-properties.json        # CurrentProject properties\n        ├── vbe-project.json            # VBA project properties\n        ├── vbe-references.json         # VBA project references\n        ├── vcs-index.json              # Index to track changes and versions for VCS\n        └── vcs-options.json            # VCS options for this project\n\n\n# Main Folder\nThese files are present in the MSAccessVCS-Addin project; they may or may not be present in your repository depending on how you have yours set up.\n\n|File |Present When |Details & Description\n|:-|:-:|:-\n|`.gitattributes`|Always|Tells `git` and `github` what each file type does, and how to handle line endings, etc. See [git-scm gitattributes](https://git-scm.com/docs/gitattributes) for more information.\n|`.gitignore`|Always|Tells `git` about any specific files, file types, or folders that should be ignored (ensures you don't commit stuff that shouldn't be there). See [git-scm gitignore](https://git-scm.com/docs/gitignore) for more information\n|Contributing.md|Always (Advised)|Front page information about how to contribute to the MSAccessVCS-Addin project!\n|License.txt|Always (Advised)|Outlines the requirements to share this code. Include in your repository if you share it.\n|README.md|Always (MSAccessVCS-Addin)|Landing page for MSAccessVCS-Addin on github.\n|`Version Control.accda`|MSAccessVCS-Addin |This is the versioned tool; your file will be located in here as well. \n|`Version Control_VCSBackup[n].accda`|MSAccessVCS-Addin|When you build from source, the existing file is backedup. The first one is simply named `[Your Database]_VCSBackup.[extension]`. Subsequent ones are numbered `[Your Database]_VCSBackup[1,2,3,...,n].[extension]` and so forth.\n\n## [YourSource.extension].src\nThe source code for the exported database is (by default) stored in the same folder as the database, with the name `[YourSource.extension].src`. See Options for more information on this if you want to move it elsewhere.\n\nThe below are the default folders for export/import. There may be future work to change this; see [Issue 222 \"Support Rubberduck @Folder annotations in modules\"](https://github.com/joyfullservice/msaccess-vcs-integration/issues/222) if you want to help with this.\n\n\n### [YourSource.extension].src\\forms\nForms are exported into the `forms` folder by default. Each form will be exported as `[FormName].bas`.\n\n|File or Type|Present When |Details & Description\n|:-|:-:|:-\n|`*.bas`|Form Export |Exported for each form present in the database.\n|`*.cls`|Code Behind Form Export |Exported for each form present in the database. **Only exported when [Options/Export/ \"Split Layout from VBA\"](Options#export-tab) is _ON_.**\n|`*.json`|Form Print settings |Only exported if the form has print settings (rare). Contains printer formatting and page settings.\n\n\n### [YourSource.extension].src\\images\nImages stored in `MSysResources` are exported into the `images` folder by default. \n\n|File or Type|Present When |Details & Description\n|:-|:-:|:-\n|`*.[imgext]`|Image Export |Exported for each Image present in the database. Exported with file name and type.\n|`*.json`|Image details and attributes |Exported for each image. Contains meta data such as the image mouseover name, internal name of the file, extension, and handling class (export/import handler).\n\n\n### [YourSource.extension].src\\macros\nMacros are exported into the `macros` folder by default.\n\n|File or Type|Present When |Details & Description\n|:-|:-:|:-\n|`*.bas`|Macro Export |Exported for each macro present in the database. Contains macro source code and other build information.\n\n\n### [YourSource.extension].src\\macros\nMacros are exported into the `macros` folder by default.\n\n|File or Type|Present When |Details & Description\n|:-|:-:|:-\n|`*.bas`|Code Module Export |Exported for each module (non-class) present in the database. Contains source code and other attributes.\n|`*.cls`|Code Class Module Export |Exported for each class module present in the database. Contains source code and other attributes such as default member, internal VBA name, etc.\n\n\n### [YourSource.extension].src\\queries\nQueries are exported into the `queries` folder by default.\n\n|File or Type|Present When |Details & Description\n|:-|:-:|:-\n|`*.bas`|Each Query |Exported for each query present in the database. Contains source code, link data, and other attributes. Note that if you last used the query designer to create the query, it will generate a different source file than if you edit the query through the SQL editor.\n|`SQL` |Each Query|If you were to generate a table via SQL, this would be the SQL you'd use. Because Access SQL (SQL Jet) doesn't care about formatting/display width/display height, if you use this, the query won't \"look\" (visually: it'll still contain the same information) the same when you open it in Access.\n\n\n### [YourSource.extension].src\\reports\nReports are exported into the `reports` folder by default.\n\n|File or Type|Present When |Details & Description\n|:-|:-:|:-\n|`*.bas`|Reports Export |Exported for each Report present in the database.\n|`*.cls`|Code Behind Report Export |Exported for each report present in the database. **Only exported when [Options / Export: \"Split Layout from VBA\"](Options#export-tab) is _ON_.**\n|`*.json`|Report Print settings |Only exported if the report has print settings (nearly always for reports). Contains printer formatting and page settings.\n\n\n### [YourSource.extension].src\\tables\nTable data is exported into `tables` folder by for each table you setup to export data. See [Options](Options#table-data) for what you can choose.\n\n|File or Type|Present When |Details & Description\n|:-|:-:|:-\n|`*.xml`|XML Data Export |There will be one `.XML` file for each table setup to have the data exported as XML.\n|`*.TDF`|TDF Data Export |There will be one `.TDF` file for each table setup to have the data exported as TDF. This is more human readable, but may lose some information. **Not reccomended if \"Special Characters\" are present.**\n\n\n### [YourSource.extension].src\\tbldefs\nTable definitions are exported into the `tbldefs` folder by default. Note table data is not part of the table definition. Table data is stored elsewhere.\n\n|File or Type|Present When |Details & Description\n|:-|:-:|:-\n|`JSON` |Linked Tables Only|Contains additional table attributes (should be all, but I'm sure there's probably an edge case that's not been caught yet), such as connection string, the Access table name, the linked table name (on the server), a list of attributes (it's a `long` type that's used in `bitwise` fashion), and a few other bits to cue MSAccessVCS how to import the table.\n|`XML` |Local Tables Only|Table format and extra information (such as row display height, column display width, required/not, column formatting information (Plain Text/RTF, Date Time formatting, etc.) and so on). It is the most 'reliable' and full export of the local table as it (usually) contains all the meta data about the table. `XML` ensures pretty much all the format and data will be the same when you rebuild. If you export the table data, too (it's stored in a separate folder), then `XML` is the way to go (usually). You can also export Table data via `TDF` (Tab Delimited Format) if you want something more human-readable. `XML` by its nature is not as row or character dependent. Access also isn't careful at exporting the same components in the same way every time, so the export is mildly prone to code noise during export (AKA: `git` will think there's changes, when there aren't).\n|`SQL` |Local Tables Only|If you were to generate a table via SQL, this would be the SQL you'd use. Because Access SQL (SQL Jet) doesn't care about formatting/display width/display height, if you use this, the table won't \"look\" the same when you open it in Access.\n\n\n### [YourSource.extension].src\\themes\nThemes are exported into the `themes` folder by default. See [Options](Options#export-tab) for what you can choose. \n\n|File or Type|Present When |Details & Description\n|:-|:-:|:-\n|`*.thmx`|Extract theme files is *OFF*|If you don't export the Themes, each theme in your database will be extracted separately in its own `.thmx` file. This is simply a `.zip` file with some special handling.\n|`\\ThemeFolder`|Extract theme files is *ON*|If you extract theme files, each theme will be in its own subfolder; there will be `.XML` files inside as well for the vairious theme components.\n"
  },
  {
    "path": "Wiki/FAQs.md",
    "content": "- [Is there a way to use a ribbon with this add-in?](#is-there-a-way-to-use-a-ribbon-with-this-add-in)\n- [Why are some issues/ideas considered out of scope for this project?](#why-are-some-issuesideas-considered-out-of-scope-for-this-project)\n- [Why am I seeing a large number of \"changed\" files after building my project from source?](#why-am-i-seeing-a-large-number-of-changed-files-after-building-my-project-from-source)\n- [How do I also export data from all the tables in my database?](#how-do-i-also-export-data-from-all-the-tables-in-my-database)\n\nOn this page you will find answers and guidance relating to common questions that come up when using this add-in.\n\n## Is there a way to use a ribbon with this add-in?\nYes! Version 4.x includes a ribbon toolbar created through a twinBASIC COM add-in. The source code for the ribbon, as well as the XML definition file is included in this project. The ribbon and related files are currently on the `dev` branch until the official release of version 4.\n\n## Why are some issues/ideas considered out of scope for this project?\nThis is described in more detail on [Project-Scope](Project-Scope).\n\n## Why am I seeing a large number of \"changed\" files after building my project from source?\nBefore going into some technical details, let me clarify that in normal operation, this add-in is designed to be able to build a project with minimal, if any, changes showing between builds.\n\nThere are several possible reasons for files showing as changed even when you didn't intentionally change the source objects. Click the heading to view additional information that may be relevant in your case.\n\n<details>\n<summary><b>Form source files are showing changes in color values</b></summary>\n\nThis issue usually comes up in relation to a project being built on different computers, due to how Access internally stores the color values.\n\nThe number you see in the exported source file is affected by the current color profile and settings used by your monitor to represent the colors you see on your screen.\n  \n  Example:\n  \n  ```diff\n  -     BackColor =11830108\n  +     BackColor =12874308\n  ```\n  \n</details>\n\n<details>\n<summary><b>Changes in form dimension values</b></summary>\n\nThis often happens when exporting/building on computers with different screen resolutions or monitor arrangements. These changes can often be ignored, since those values are dynamically generated. \n\nIn most cases it would be a bit too complex to try to build the logic to determine this from the source file content, to the extend that we could discard unneeded values. One place that we have successfully done this is on the `Right` and `Bottom` dimensions of reports. (See the `SanitizeFile` function for details.)\n</details>\n\n<details>\n<summary><b>Query source is significantly different</b></summary>\n\nYou may observe that the source file for a query seems to be updated to an entirely different file structure. This has to do with whether the query was saved in a compiled state in the database. If you have issues with this frequently causing changes in source files, you may want to review your workflow for editing queries. (Saving via the designer will save one way, while using the SQL view will save another way.)\n</details>\n\n<details>\n<summary><b>Case changes in VBA code</b></summary>\n\nIf you see a lot of changes happening with the capitalization of variables, keyworks, properties and methods, this may be caused by the VBA editor trying to enforce consistency in the naming. This is an internal feature to VBA that some people hate and some people love. There isn't much that you can do about this behavior in the VBA IDE, but the following tips have been helpful to me in minimizing the negative effects:\n* Use Pascal casing for procedures, methods and properties\n* Use Hungarian notation (or similar) for variable names (i.e `lngTotal`, `strCaption`)\n\nWhile many modern languages and IDE editors tend towards `camelCase` names, this just doesn't work as nicely in VBA. I personally find better success sticking with the original naming conventions the IDE was designed to work with.\n  \nExample (\"**c**\" > \"**C**\"):\n  \n  ```diff\n  -    cancel = True\n  +    Cancel = True\n  ```\n \n</details>\n\n## How do I also export data from all the tables in my database?\nPerhaps a more important question is to ask _**why**_ you want to do this... It's not that you can't, it just usually indicates a misunderstanding on the purpose of this tool. This add-in is designed to work in connection with a version control system like git to save snapshots of the _design structure_ (not the data) of a database application project. This allows you to track changes and build a copy from any point in the project's development history.\n\nSometimes there are pieces of data that should be included in a build, such as a table that stores configuration settings. But it can actually be very risky to commit a project's data records to version control, as this could inadvertently expose customer information (PII) or other sensitive data. It is because of this risk, and the huge problems this could cause for a well-meaning developer that we intentionally did not include a \"select-all\" option to easily export data from all tables.\n\nIf your underlying need is for a data backup, you might be better off reaching for another tool. (Or you could create a simple VBA script or Macro to automate the process.) If you are one of those rare but legitimate cases where you actually do need to include all the table data in version control, you will just need to go through the list of tables in the Options dialog and select each one and specify the export format. (This selection is saved with the options, and will be used on each subsequent export.) If you have a huge number of tables, you can manually edit the `options.json` file. Just be sure to follow the same format for the new entries."
  },
  {
    "path": "Wiki/Home.md",
    "content": "# Version Control System\nWelcome! On this project wiki you will find some more detailed information about the installation and usage of this Version Control Add-in for Microsoft Access.\n\n## [Quick Start](wiki/Quick-Start)\nWant to get started quickly? Here is a brief overview to get you started.\n\n## [Documentation](wiki/Documentation)\nDescribes how to install and use this tool, as well as detailed descriptions of the options provided.\n\n### [FAQs](wiki/FAQs)\nCommon questions and answers\n\n## [Supported Objects](wiki/Supported-Objects)\nHere you can find an extensive list of what is and isn't supported by this system.\n\n## [Editing and Contributing](wiki/Editing-and-Contributing)\nIf you enjoy using this add-in, you are welcome to contribute to the ongoing development of this project. Here are some guidelines for doing so, and suggested practices.\n\n## [Project Scope](wiki/Project-Scope)\nDescribes what this add-in is designed to do, and what is outside the scope of this project."
  },
  {
    "path": "Wiki/Installation.md",
    "content": "# Install\n 1. Download the [latest **Version_Control_vX.X.X.zip**](https://github.com/joyfullservice/msaccess-vcs-integration/releases/latest).\n 2. Extract `Version Control.accda` from the zip archive.\n 3. Remove \"Mark of the Web (MOTW)\", if needed. See [this blog post](https://nolongerset.com/install-vcs-addin/) for additional information. <p>*NOTE:* MOTW does not always get set on files, and won't if you extract with 7Zip.\n 4. *(Optional)* Adjust install options (see below).\n 5. Click **Install Add-In**.\n\n![Install Form Image](img/install.jpg)\n\n## Install Options\n*NOTE:* None of these are required to use the add-in, but they are designed to allow for easier installation and use.\n\n|Setting <img width = 175> |**Default** <p> *(Setting)*|Description\n|-|:-:|:-\n|**Trust Add-In Folder**|**Default: On**|During installation, the VCS Addin install folder will be added as a trusted location in Microsoft Access/Office. This allows Access to load the Add-In correctly in some protected setups, and will enable faster loading in others. The default install location is `C:\\Users\\%username%\\AppData\\Roaming\\MSAccessVCS\\`\n|**Advanced Options**|**Default: Off**|In some protected computing environments you may need to select additional options. See below for more details.\n\n![Install Form Image](img/install-AdvancedOptions.jpg)\n\n|Setting <img width = 175> |**Default** <p> *(Setting)*|Description\n|-|:-:|:-\n|**Use Ribbon Addin**|**Default: On**|With Version 4 and up the addin and most features can be directly launched with a custom ribbon. \n||_On_|Install the ribbon and load to Access's ribbon. The ribbon will automatically load when Access loads, making launching version control much simpler.\n||_Off_|Do not install the Ribbon. You can still load the Addin after installation by going to [Database Tools]>[Add-Ins]>[VCS Open] or [VCS Export All Source].\n|**Open add-in after installing to trust the add-in file**|**Default: Off**|In some protected computing environments (I.e. Government, Banking), the Add-In must be opened from the install location to be properly trusted. This option will open the add-in file immediately after install, and give you the opportunity to trust the file.\n|**Install Folder**||By default, the addin will be installed to `C:\\Users\\%username%\\AppData\\Roaming\\MSAccessVCS\\`. If you need to select an alternate location for installation due to your development environment, security settings, etc., you can change the installation Folder. <p> **WARNING: You MUST _completely uninstall_ the Addin prior to changing the installation folder. Failure to do this may lead to unexpected issues and difficulty removing the addin.**\n\n# Uninstall\n[Click here for Uninstall Instructions](<Options#Remove Add-In>)"
  },
  {
    "path": "Wiki/Merge-Build.md",
    "content": "What exactly is a ***merge build***? Essentially, it is a fast-running partial import that ensures that the current working database has been updated to match the set of source files. \n\nIf nothing has changed in the source files, this only involves a quick scan. If some of the items have been updated on the source file side, only those items will be updated in the database.\n\nKeep in mind that your database exports should be fairly current to effectively use the merge build functionality. If you have database objects that have been modified in the database, but not exported to source, you may encounter conflicts if you attempt to update these objects from source.\n\n## Example Workflow:\n1. Make changes to database objects (application development)\n1. Export source files\n1. Update source files (i.e. switch branches, pull commits, etc...)\n1. Run Merge Build to bring the source file changes into the project\n\n# Intended Use Cases\nThis feature is primarily intended for larger/complex databases where a full build might be much slower, or where some external resources may not be available in the development environment.\n\n**Multi-user development** - If multiple users are working on different parts of the same database, this is a great way to pull in changes from other developers without having to do a full build.\n\n**Testing/Switching Branches** - This is also a faster way to test out slight variations in sets of source files. For example, you might build from the main branch, then switch to a feature branch, and use a merge build to get your database updated to the feature branch. When your review/testing is finished, you can switch back to the main branch and again run a merge build to update your database to match the main branch source files.\n\n# Expected Behavior\n|Source File|Database Object|Resulting Action |\n|-----------|---------------|-----------------|\n|Unchanged  |Unchanged      |None             |\n|Unchanged  |Modified       |None             |\n|New        |Missing        |Import           |\n|Modified   |Unchanged      |Import           |\n|Missing    |Unchanged      |Delete           |\n|Modified   |Modified       |Conflict (Import)|\n|Missing    |Modified       |Conflict (Delete)|\n\n# Additional Notes\nFor the merge build functionality to work correctly, it is very important that the `vcs-index.json` file be paired with the (binary) database file and *not* committed to version control. The recommended (and default) setup is that the binary database file and index file are excluded from version control.\n\nWhile a merge build is a very helpful development tool, a *full build* is the most robust way to ensure that a database project fully reflects the source files. A full build is recommended for final testing and deployment.\n\nLike full builds, each merge build makes a backup copy of the current database before applying changes to the database. If something goes wrong in the merge, you can always rename this backup to the original file name to get back to where you started.\n\nSee issue [#81](https://github.com/joyfullservice/msaccess-vcs-addin/issues/81) for additional discussion that ultimately led to the implementation of this feature."
  },
  {
    "path": "Wiki/Options.md",
    "content": "\nClick **Options** to open the **Options dialog**. Internally options are stored in a `vcs-options.json` file in the path of the exported source code. Options are loaded and used when exporting to source files, or when building a project from source.\n\n## General Tab\n![General Options Tab](img/options-general.jpg)\n\n|Setting <img width = 175> |**Default** <p> *(Setting)*|Description\n|-|:-:|:-\n|**Show Detailed Output**|**Default: Off**|Enable verbose output of each step; useful when you're real curious. This may slow down Export and Build operations.\n|**Debug VBA Errors**|**Default: Off**|*(Advanced Users/Add-in Developers)* If an unexpected error occurs in the add-in code, stop and open the VBA IDE to do further debugging. This may be helpful when debugging a problem with the add-in, or reviewing an existing issue. *You should generally keep Off unless trying to find the source of a bug, or doing development work on the add-in.*\n|**Show Legacy Prompts**|**Default: On**|If upgrading from the integrated version of this project, there may be legacy VCS modules left in your database project that are no longer needed. This will notify you if these legacy modules are found. If you would like to keep them in your project, you can clear this option to disable the notification.\n|**Use short hashes in index**|**Default: On**|If on, `git` style hashes (first 7 characters) will be used in the file index. <p>*NOTE: This was done to improve readability and reduce file size, but you can always uncheck this box if you want to store the full hash.*\n|**Hash Algorithm**|**Default: SHA256**|Choose the hashing algorithm here. This may affect build time if you choose a more complex option. Hashes are used to help determine whether source files changed between import/export operations.\n||~~*SHA1*~~|Use SHA-1 hashing algorithm. ([Not recommended](https://en.wikipedia.org/wiki/SHA-1). While hashing is not used for security purposes in this add-in, any usage of SHA1 is not allowed in some network environments.)\n||*SHA256*|Use SHA-256 hashing algorithm.\n||*SHA512*|Use SHA-512 hashing algorithm.\n|**Developer Settings**||_Note:_ Developer Settings are local-only and are not saved in the settings config. They are set as a registry preference (same as install preferences).\n|File Diff Tool|**Default: WinMerge**|Select the diff tool used by git.\n||_WinMerge_\n||_VSCode_\n||_TortoiseGitDiff_\n|**Open Repository**|**Default: <p> [Blank]**|Select the tool you want to use to open the repository for git integration.\n||_[Blank]_\n||_Github Desktop_\n||_VSCode_\n||_SourceTree_\n||_Tortoise Git_\n|**Language**|**Default: English**|Select the language used by the AddIn. <p>NOTE: Not all screens or prompts have been updated to use translation. When not updated, the text will display in **English**.<p> See [Translations Page](wiki/Translations) for more details.\n||English (Localization Testing)|Used when performing localization tests.\n||English\n||Brazilian Portuguese\n|[Translations...]()||Click the Translations... link to open the Translation Tab. See [Translation](wiki/Translation#Options-Translation-Tab) for more detail and how to use this feature.\n\n\n## Export Tab\n\nNote that these options only determine what is *Exported* and saved to the JSON file. Any settings defined in the JSON source file will be applied when the Form or Report object is imported, regardless of the currently specified options.\n\n![Export Options Tab](img/options-export.jpg) \n\n|Setting <img width = 175> |**Default** <p> *(Setting)*|Description\n|-|:-:|:-\n|**Export Folder** |**Default: [Blank]**| Keeping this blank ensures the source code stays local to the development file, and works best with git and other version control systems. Your environment may need other options.\n||*[Blank]*|Use default name of `\\[database.accdb].src`, i.e. `\\Test.accdb.src`\n||*Relative Path*|Prefix folder name with a backslash. For example, to export source into a subfolder called `Source`, you would enter `\\Source` in this box. \n||*Absolute Path* |You may also use a full path name to a folder. I.e. `W:\\Git\\Projects\\Vehicles Database` \n||*Placeholder* |In combination with the above options, you may also use a `%dbName%` [placeholder](https://github.com/joyfullservice/msaccess-vcs-integration/issues/139) to use the database filename in a custom path. I.e. `\\src\\%dbName%.src\\`\n|**Use Fast Save**|**Default: On**|Major performance gain with small changes to large projects. This attempts to only export the objects that have changed since the last export. This especially helps to not have to export forms and reports if they have not changed.\n|**Extract Theme Files**|**Default: Off**|Extract the contents of the `*.thmx` files. Microsoft Office Theme files `*.thmx` are actually zip files containing XML and other files that define the specifics of a theme. You can use Theme Files for form style and color consistency. If you are customizing a theme, you may wish to extract these files so your changes can be tracked in Version Control. <p> **_Note:_** _Extracting Theme files may create noise in your database due to slight variations in rendering machine to machine._\n|**Sanitize Level**|**Default: Standard**|Set level for sanitize routines to remove noise. Sanitizing allows you to remove noise from your exported files. Turn off to export raw file outputs. Sanitization routines are checked to ensure most do not affect building of exported files.\n||*None (Off)* | Turn off sanitization, export raw files. These may not import properly, but they may be useful when trying to troubleshoot. <p>_**Note:** Files will still be converted to UTF-8 or System Codepage encoding depending on Access Version in this mode._ <p>**_NOTE:_ If you set Sanitize level to \"*None (Off)*\", none of the Sanitize Options will be used.**\n||*Minimal*| Only basic sanitization to ensure reliable rebuilding of files.\n|| *Standard*| Remove most exported noise (GUIDs, the like). Removes object GUIDs, name maps, and other data that changes from build to build. (These values are recreated automatically when importing source files.) From a development perspective, these are more like binary artifacts that just add noise to the version control commits, reducing clarity on actual code changes.\n||*Extended*|Remove as much as possible. This may lead to unexpected changes upon rebuilding. Features that are still in testing or confirmed to be temperamental may be introduced here prior to being implemented. **_User beware!_**<p>\n|**Sanitize Colors**|**Default: Minimal**|Removes color exports on forms where themes are used, or other situations where the color can be correctly set upon rebuild. These colors export differently in different machines, or different settings and are largely noise. <P> ***NOTE:* The most aggressive options may lead to unexpected color changes on forms!**\n|**Save Printer Settings**|**Default: On**|Saves a copy of the print configuration for reports and forms. This is especially useful when you are using specific printer settings. The output is stored in human-readable json. By default, page orientation and paper size are saved with each report, but additional options are also available. <details><summary>Show Advanced Printer Options...</summary> ![Printer Settings Options Screen Image](img/options-printer-settings.jpg)<p></details>\n |**Save Query SQL**|**Default: On**|In addition to the Access object, this option exports a copy of just the SQL code from queries. I find this much more readable than the source of the Access Object when reviewing what I actually changed on the SQL side. (The Access object includes other information relating to the layout of the query designer.)\n|**Save Table SQL**|**Default: On**|In addition to the Access object, this creates a SQL statement like what you would use to create the table. Here again I find this easier to see at a glance what changed in the actual structure of the table between versions.\n|**Format SQL**|**Default: On**|While exporting SQL for Tables and Queries attempt to format the SQL using common indentation and conventions. See [Issue #426](https://github.com/joyfullservice/msaccess-vcs-addin/issues/426) for some additional discussion and information.\n|**Split Layout from VBA**|**Default: On**|Split Forms and Reports into a layout and code files. This can improve readability and reduce code noise when developing code only changes. <p> **_Note:_** If you also select `Save Printer Settings` an additional printer setting `.json` file will be exported as well; this option is separate from printer exports. <p> **Note:** See [Split Files Wiki page](https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Split-Files) for additional documentation on the feature and how to migrate to it if your source code did not start with this option set.\n||_On_|The layout and colors of Forms and Reports will be exported to a `.bas` file and the code components will be exported to `.cls` file.\n||_Off_|Forms and reports will be exported as a single `.bas` file.\n|**Run Sub Before Export**|**Default: [Blank]**|Run a VBA subroutine before exporting the source code. This can be used to clean up temporary data, mask sensitive information, or anything else you want to do. This will be called using  `Application.Run`.\n|**Run Sub After Export**|**Default: [Blank]**|Similar to the option above, this allows you to specify a VBA subroutine to run *after* exporting the source code.\n\n## Databases (External)\n![Databases Options Tab Image](img/options-Databases.jpg)\n\nThe Databases Tab allows you to use this tool to export externally backend connected databases from MySQL or Microsoft SQL Server (MSSQL). This can allow new developers without other version control tools to track changes on the backend and \"sync\" them to front end tools.\n\nThis tool is limited, and does not have the full featured tools of other Database tools such as SSMS.\n\n**This is an _EXPORT ONLY_ feature: this will not send data to your database.**\n\n*This tool only provides a 'quick export' fuction due to the significant time required to perform the SQL export. To perform a full export, delete the `\\Databases\\` folder in your VCS folder. Changes are tracked in the index, so you shouldn't miss anything.\n\n|Setting <img width = 175> |**Default** <p> *(Setting)*|Description\n|-|:-:|:-\n|**Database List**||Lists database connections which will be exported by this tool. Double click on database connection to edit.\n|**Delete**||Delete selected database connection.\n|**Edit**||Edit selected database connection information.\n|**Add**||Add a new database connection.\n\n\n### Database External Connection Editing\n![Databases Options Tab Image](img/options-Databases-Connection.jpg)\n\n|Setting <img width = 175> |**Default** <p> *(Setting)*|Description\n|-|:-:|:-\n|**Connection Name:**||Human readible name for the database connection. The Database Name is typical for this.\n|**Enabled**|**Default: On**|Export this connection. If disabled, this connection is skipped (not exported) when running an export operation.\n|**Use UTC Dates**|**Default: Off**|Use UTC Timestamps and dates when exporting SQL details. This can be very helpful when your developers are in disparate time zones.\n|**Database Type:**|**Default: Microsoft SQL Server**|The backend server type for the external database.\n||_Microsoft SQL Server_|Select this if your external database is hosted on a Microsoft SQL Server instance.\n||_MySQL Server_|Select this if your external database is hosted on a MySQL Server instance.\n|**Description:**||If you wish to provide additional context to the Connection Name, type it here. Examples might be \"Production\", or \"Test\", or \"Staging1\"\n|**ADO (OLE) Connection String:**||The ADO (OLE) Connection string. This needs to be an OLE connection string as this tool is built on the ADODB functions. The examples which populate automatically are based on linked tables in your database, and are generally ODBC connection strings. If you have an MSSQL type database backend, this tool can attempt to create a compliant OLE connection string.\n|**Save in .env file**|**Default: On**|Save the connection string (and passwords) in a `.env` file. <p>**NOTE: ensure the `.env` files are excluded from version control if you have a password string.**\n|**Attempt Conversion to ADO (OLE) String**|**Default: On**|Attempt to convert linked table string from ODBC to OLE.\n|**Filter for database objects (source file paths):**||Use this to exclude objects from export. An example might be that you do not want the code for a login check to be exported, or you do not want some views exported.\n\n\n## Table Data\n![Table Data Options Tab Image](img/options-table-data.jpg)\n\nThe Table Data tab allows you to selectively include certain tables from which to include table ***data*** in version control. The *structure* of the tables is already being saved, but this gives you the additional option of saving the *data* itself.\n\nThe dialog box shows tables in the database. If the column does not show an option in *Export As* the data is not exported for that table. The example screenshot shows the tables in the Addin.\n\nAn example of where you might use this would be a table that defines options or settings in your database application. You might want to track when these settings change. Another example would be a `USysRibbons` table that defines the layout of a custom application ribbon. Note `USysRibbons` is stored by default.\n\nThe concept here is that you are selecting the table from which you want to save data, choosing the format to use, and clicking Update to save the changes.\n\n**Note:** See [This FAQ](https://github.com/joyfullservice/msaccess-vcs-addin/wiki/FAQs#how-do-i-also-export-data-from-all-the-tables-in-my-database) for discussion on exporting all tables. Putting production data into version control may have significant PII/Cybersecurity/other consequences and is generally frowned upon.\n\n|Setting <img width = 175> |**Default** <p> *(Setting)*|Description\n|-|:-:|:-\n|**Show Hidden**|**Default: Off**|List hidden tables in the current database.\n|**Show System**|**Default: Off**|List system tables in the current database.\n|**Show Other**|**Default: Off**|List table names that are saved in the options, but do not exist in the current database. You can also manually add table names to your `vcs-options.json` file. (Add a table through the interface first, and use the same syntax.)\n|**Selected Table**||This highlights which table you have selected to set the export format. To add a table that is not listed, click the [*Other...*](#table-data) link.\n|Selected Table: **<u>Other...</u>**||Click [*Other...*](#table-data) to enter a table name that doesn't exist in the database but you want to export if found.\n|**Export As**|**Default: Tab Delimited**|Select the format to use for the exported data.\n||*Tab Delimited*|Separate values with tab character. This is a good format to use when importing to Microsoft Excel, or reading the values in Version Control files.\n||*XML Format* (Suggested)|Select this option for the most complete and robust representation of the data. It is harder to read in source files, but should import back in to accurate recreate the original data.\n||*No Data* (BLANK) |Don't save data for this table.\n|**Other Table Name**||Enter a table name to directly save output specification changes for the table. You should see the Save Data column update in the list of tables when you click the Update button. See [Issue 250](https://github.com/joyfullservice/msaccess-vcs-addin/issues/250) for additional discussion on this feature.\n|**Export As**||Select the export format to export table.\n\n### Default Tables\nThe following tables are added to the default list, but can be removed (if you desire...we strongly suggest you keep them).\n\n   | Table Name   | Type | Explanation  \n   |-|-|-\n   | `USysRegInfo` |System | Stores registry information about Access (such as installed tooling). Exports as _Tab Delimited_.\n   | `USysRibbons` |System | Stores custom ribbon information. Exports as _Tab Delimited_.\n\n**NOTE:** The following tables should not be added to the export list, as they are already handled by this tool elsewhere.\n\n   | Table Name   | Type | Explanation  \n   |-|-|-\n   | `MSysResources` |System | Images and Themes are exported in the `.\\images\\` and `.\\themes\\` folders, respectively.\n   <P>\n   <p>\n\n## Build\n![Build Options Tab Image](img/options-build.jpg)\n\nA unique feature of this add-in is the ability to build a fully functioning database completely from source files. This allows multi-user development in a more typical development workflow where source files are exported and combined to build the end product.\n\n|Setting <img width = 175> |**Default** <p> *(Setting)*|Description\n|-|:-:|:-\n|**Force import of original SQL for queries**|**Default: Off**|In some cases, such as when a query contains a subquery, _AND_ has been modified in the visual query designer, it may be imported incorrectly and unable to run. For these cases we have added an option to overwrite the .SQL property with the SQL that we saved separately during the export. See [issue #76](https://github.com/joyfullservice/msaccess-vcs-integration/issues/76) for further details.\n|**Run Sub Before Build**|**Default: [Blank]**|Same as below, except before the build.\n|**Run Sub After Build**|**Default: [Blank]**|Run the specified subroutine after building the project from source files. This is a great way to extend the add-in to add any custom post-build functionality that you need after the build finishes. For example, you might use this to trigger an automated testing routine that verifies that the application is fully functional.\n\n## Settings\nThese affect your system at large; not just the currently open Access Project.\n\n![Settings Options Tab](img/options-settings.jpg)\n\n|Setting <img width = 175> |Description\n|-|:-\n|***System Defaults***\n|**Save as Default**|Save the current options as default for new projects. Anytime you export source and a `vcs-options.json` file does not already exist, it will use the default options you have specified.\n|**Restore Defaults**|In the current project (open database), restore options to default values based on saved system defaults.\n|**Clear Defaults**|Reset options to default the settings specified in the add-in source code. If you click this button, then the **Save as Default** button, it will reset all user customizations to the default options.\n|**Open Install Folder**|Opens the directory the Addin is installed in. This can be handy to verify installation location, especially if your environment requires you to install in a non-standard location. See [Installation](https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Installation) for more information.\n|***Remove Add-In***\n|**Uninstall**|Uninstalls the add-in from your user profile, including all saved defaults and encryption keys. <p> [Click here for Install / Uninstall Instructions](Installation)"
  },
  {
    "path": "Wiki/Project-Scope.md",
    "content": "I personally believe that one of the keys to the long-term success of this project is establishing a clearly defined scope of what it is intended to do, and sticking to that scope.\n\n## Guiding Principles\nSince much of this could be considered subjective archetecutual design decisions, let me outline some of the guiding principles that I am trying to keep in the forefront of this project.\n* The **fundemental purpose** of this add-in is two-fold:\n  * **Export** database objects as source files\n  * **Import**/**Build**/**Merge** source files to database objects\n* The goal is to replicate the original database as **closely as possible** when building from source.\n* The add-in is **not intended** to fix/repair/enhance the target database, other than what is necessary to perform the basic functions of exporting and importing source files.\n* The **user interface** should be as intuitive and user-friendly as possible. It should be both efficient for the expert, and easy for the beginner. Flexibility without clutter.\n* The tool should be **extensible**, where internal code can be added to carry out additional tasks outside the scope of this add-in.\n\n\n## How Features are Evaluated\nFeatures add complexity, and complexity increases [cost of carry](https://martinfowler.com/bliki/Yagni.html). Great features are welcomed and make this tool better every year. Unecessary or overly complex features bog down the project and slow progress on more important issues.\n\n**Feature considerations:**\n* How many users does this affect? Will this benefit everyone, or just a single user with a really unique setup?\n* How complex is the feature? Is it limited to changes in a few areas of code, or are we talking about a significant refactoring?\n* Do functionality changes cause any risks for those currently using the add-in in production environments?\n\nIf your idea didn't get implemented, don't take it personally.  :-)  Remember, this is an ongoing work in progress, and someone has to make the hard decisions about what gets added and what doesn't.\n"
  },
  {
    "path": "Wiki/Quick-Start.md",
    "content": "# Quick Start\n\nThis add-in performs two primary functions to support rapid application development in Microsoft Access.\n- **Export** - Generate text-based source files representing the database components of your project. (table structure, queries, forms, [etc...](Supported-Objects))\n- **Build** - Build a fully functional version of your database project directly from source files. You can also [merge](Merge-Build) updated source files into an existing project.\n\n## Install Add-in\n 1. Download the [latest release](https://github.com/joyfullservice/msaccess-vcs-integration/releases/latest) from GitHub.\n 1. Extract `Version Control.accda` from the downloaded zip archive to a trusted[^1] location.\n 1. Open `Version Control.accda` to launch the installer.\n 1. Click **Install Add-In**.\n\n![Install Form Image](img/install.jpg)\n\n## Export Source Files\nAfter installing the add-in, you should see a new ribbon toolbar in Microsoft Access. Open your database project, then click **Export** on the ribbon.\n\n![Ribbon Export](img/ribbon-export.jpg)\n\nThis will launch the add-in and export your project to source files. (By default, in a subfolder under your database project.)\n\n![Full Export](img/full-export-finished.png)\n\nSubsequent exports will be much faster because they will only export changed items.\n\n![Fast Save](img/quick-export-finished.png)\n\nAt this point you can switch over to your version control system (I like GitHub Desktop) and commit the source changes to version control.\n\n![GitHub Desktop Changes](img/github-desktop-changes.png)\n\n## Build From Source\n\nAt some point you will probably want to build your project from source, perhaps pulling in source changes from other developers. With your project open, simply click **Build From Source** on the ribbon.\n\n![Build From Source Ribbon](img/build-from-source-ribbon.png)\n\nAfter clicking to confirm the build from source, the add-in will save your current database as a backup, and build a fresh copy from source files.\n\n![Build From Source](img/build-finished.png)\n\nAll set! Your freshly built database is ready to use and continue development. (Note that the first export after a build will be a full export.)\n\nFeel free to check out other wiki pages to learn about [additional options](Options), [merge builds](Merge-Build), [FAQs](FAQs), and more.\n\nEnjoy!!\n\n[^1]: This location must be trusted by Microsoft Access, or the add-in file must be trusted to allow the VBA installercode to run correctly. See `Microsoft Access` -> `Options` -> `Trust Center` -> `Trusted Locations`.\n"
  },
  {
    "path": "Wiki/Security-Considerations.md",
    "content": "We have worked hard to make this add-in as accessible to as many environments as possible, including those that have higher security requirements. It does not require administrative privileges to install or run, and utilizes a trusted location to allow global settings to remain more restrictive.\n\nThe entire project with all source code is available on GitHub at: [joyfullservice/msaccess-vcs-integration](https://github.com/joyfullservice/msaccess-vcs-integration) See [LICENSE.txt](https://github.com/joyfullservice/msaccess-vcs-integration/blob/master/LICENSE.txt) for specific terms and disclaimers. (BSD-style license)\n\n\n# Minium Security Requirements\nThe following settings need to be allowed in the Security Center for the add-in to be able to interact with databases and function as designed.\n- **Allow Trusted Locations** (Including network locations, if the database is not on the development computer.)\n- **Allow Trusted Documents** - In some environments the add-in file must be trusted as well.\n- **ActiveX Settings - Prompt before enabling** - This is required for importing XML content, such as table definitions.\n- **COM Add-ins** - Needed to use the Ribbon toolbar. (Primary launcher for Version 4 and above)\n\n# Normal Behavior\nThis add-in was created in Microsoft Access VBA, which gives a powerful platform for developing a highly performant add-in with easily accessible source code and live debugging capabilities.\n\nAs with any Microsoft Access add-in, this powerful tool comes with inherent security considerations. This page is intended to guide you in understanding some of the fundamental operations of the add-in, and how this might affect the security of your systems and data.\n\n## Export to Source Code\nAn existing database can be exported to (primarily) text-based source files that can be committed to a file-based version control system such as Git. While primarily focused on the database *component* objects like forms, queries, table definitions and VBA source code, you can optionally choose to export *data* from tables as well.\n\nHere are a few considerations to think about when exporting to source:\n- Some connection information, such as ODBC connection strings may be present in your exported source files.\n- Paths to network locations and the database location may be present in some source files.\n- Any table data that is exported could contain PII or other sensitive content, which could then be easily committed to version control.\n- Access to the exported source files should be protected, just like you would protect the database itself.\n\n## Build from Source Files\nThis add-in provides the ability to build a functioning database application entirely from the source files saved during an export. This is a very powerful collaboration tool when combined with a version control system. Changes at the source file level can be merged and used to build a specific version of a database application.\n\nHere are some potential security considerations when building from source:\n\n- Probably the most important aspect here is to protect your *source files* from any unauthorized modifications.\n- The build operation can include \"hooks\" to run specific VBA code during or after the build process. This is a very powerful feature, but could be used maliciously by someone with access to the source files.\n- Any source files from external locations should be carefully reviewed before including in a project for build.\n\n## Add-in Behavior\nDuring the export and/or build process, the add-in uses the following:\n- **`CreateObject()`** - Create ActiveX objects like `Shell.Application`. (Most objects are created using early binding.)\n- **Windows API Calls** - Used in many areas including performance timing, string hashing, and time zone determination.\n- **Files** - Creates, modifies and deletes source files for the project.\n- **Registry** - Reads and writes values to the Windows registry. (Current User Hive)\n\n# File System Access\nMost of the processing happens in the source folder for the database, but the user's Windows temporary folder is also used for processing and comparing source files.\n\nThe add-in is installed in the user's `AppData` folder, and includes the COM add-in ActiveX DLL that provides the application Ribbon interface.\n\n# Registry Access\nThe registry is used to store certain program settings, as well as transient values used during some processes like building from source. Other registry settings are used to determine security settings in the current environment.\n\nThe following sections are both read and written by the add-in.\n\n**Trusted Locations** - Used when making the add-in installation path a trusted location.\n\n`HKCU\\Software\\Microsoft\\Office\\[version]\\Access\\Security\\Trusted Locations\\`\n\n**Add-in Menu** - Adds shortcuts to the Microsoft Access add-ins menu. (The primary way to launch the add-in before the ribbon was added in version 4.)\n\n`HKCU\\Software\\Microsoft\\Office\\[version]\\Access\\Menu Add-Ins\\`\n\n**Ribbon UI** - The ribbon was created in twinBASIC as a COM add-in (ActiveX DLL) which is self-registering using a call to `regsvr32` during the installation process. (COM registration entries are also created in the current user hive.)\n\n`HKCU\\SOFTWARE\\Microsoft\\Office\\Access\\Addins\\MSAccessVCSLib.AddInRibbon`\n\n**Local Settings** - Program settings and transient values. (VBA's native `GetSetting`/`SaveSetting` functions.)\n\n`HKCU\\SOFTWARE\\VB and VBA Program Settings\\MSAccessVCS\\`\n\n# Telemetry\nThis add-in does not communicate in any way with remote servers or systems. It does not check for updates or send any usage statistics, and does not require an Internet connection to use the tool.\n\nThat being said, it *can* interact with linked tables and potentially remote servers and systems in connection with databases being exported or built from source. This is entirely within the configuration and intended usage of the add-in.\n\nFor example, if your database has a linked table on a SQL server, the add-in will attempt to connect to this remote table to retrieve a list of columns that are written to a source file. This all happens locally within the context of your network environment and development computer.\n\n# Encryption / Cryptography\nSome countries and corporate environments have specific requirements when it comes to cryptography. Here are a few ways this add-in uses cryptography:\n- **Zip** - Database theme files (`*.thmx`) natively use Zip compression.\n- **File Hashes** - Used to detect and index changes to files. These hashes are `SHA256` by default, but can be changed to `SHA1` or `SHA512` in the add-in options.\n- No reversible encryption is used in this project at this time.\n\n# VBA References\nIn addition to the built-in Microsoft Access references, this add-in also uses the following references:\n- **Microsoft ActiveX Data Objects 6.1 Library** - For ADO interactions with some database objects.\n- **Windows Script Host Object Model** - For registry access.\n- **Microsoft Office [version] Object Library** - For open dialogs and other general Office integration.\n- **Microsoft Scripting Runtime** - For `FileSystemObject` and other related components for interacting with the file system in a Unicode compliant way.\n- **Microsoft Visual Basic For Applications Extensibility** - For interaction with VBA source code components.\n- **Microsoft VBScript Regular Expressions 5.5** - For parsing source file content.\n- **Microsoft XML, v6.0** - For managing XML source file content.\n- **Microsoft Forms 2.0 Object Library** - For interacting with native VBA forms (different from Microsoft Access forms).\n- **UIAutomationClient** - For interactions with navigation pane objects, such as selecting a single object for export.\n\n# Additions / Updates\nIf you have further items that would be helpful to add to this page, please feel free to open an issue or create a pull request!\n\nReviewed 4/19/2023 by @joyfullservice"
  },
  {
    "path": "Wiki/Split-Files.md",
    "content": "One of the challenges with the .git system is that when a single file is split into two different files the line history *might* follow one of the files, but not both. You loose the line history in one or both files.\n\nThat might be a serious problem if you have years of history that you are wanting to preserve. Thankfully, there is a [technical workaround](https://devblogs.microsoft.com/oldnewthing/20190919-00/?p=102904) that does allow you to preserve the history in both files.\n\n# Please Read Before Splitting Files in Existing .git Projects\n\nYou can turn on the option and simply perform an export to split the files, **but if you want to preserve the .git history in both files**, please read this section carefully. This add-in includes a utility to help you split the files while retaining the line history in both files. Because this involves **committing** to the repository as part of the process, I want to clarify exactly how this works.\n\n# How This Works\n\nBecause this isn't a built-in feature in .git, we need to implement a bit of a clever workaround as documented by Raymond Chen in [this article](https://devblogs.microsoft.com/oldnewthing/20190919-00/?p=102904). In a nutshell, we will create a (temporary) new branch in git, rename the file in the new branch, restore the original file, then merge the new branch back into the original. This will result in two files that both carry the history of the original file.\n\n# Before You Start\n\nYou will need to run this process from a **clean branch**. If you have any outstanding changes, please commit or discard them before splitting files. Be aware that this will create **two additional commits** in your repository, so it can be helpful to do this for batches of files, rather than individually for each file.\n\n# Select and Split Layout from VBA\n\nOn the VCS ribbon, click Open the **Advanced Tools > Split Files** to open the following dialog.\n\n![split-files-dialog](img/split-files-dialog.jpg)\n\nClick the *Add Forms and Reports...* link to automatically load in the source files for the forms and reports in the current project and create the corresponding `*.cls` files.\n\nClick the button to Split Files. This will begin the automated process of splitting the files while preserving change history. Details and the full output of the git commands can be found in `git.log` in the source files folder.\n\nWith the files now split, turn on the VCS option to **Split Layout from VBA** and run an export. At this point you should see both source files modified for each object as the layout is removed from the class file, and the VBA is removed from the layout file.\n\nThat's it! Now the VBA code changes can be tracked in the `*.cls` file, and the layout in the `*.bas` file."
  },
  {
    "path": "Wiki/Supported-Objects.md",
    "content": "# Compatibility\nThis page outlines the types of objects and information that can be exported from a Microsoft Access Database and which items are supported when building a database from source files. If you find that you are missing something in the export or import, please open an issue and link it to the appropriate row in the table.\n\nLegend: ✔️ Fully supported ⚠️ Partially supported ❌ Not supported\n\n## Summary\nMost types of objects can be exported and imported using this tool.\n\n|Type     |Export|Import|\n|---------|:----:|:----:|\n|Tables   |✔️|✔️|\n|Queries  |✔️|✔️|\n|Forms    |✔️|✔️|\n|Reports  |✔️|✔️|\n|Macros   |✔️|✔️|\n|Modules  |✔️|✔️|\n|Database Settings|✔️|✔️|\n\n## Detailed List\n\nIf you are looking for a specific type of object or property that you want to export or import, you can refer to the following more comprehensive list. (Updates and additions welcome.)\n\nThe *Testing* column indicates whether a test item and testing code has been created in the *Testing.accdb* database to verify that particular item after import from source code. *Test location* indicates where to find the object in the database.\n\n|Type                             |Export|Import|VBA Class|Testing|Test Location|\n|---------------------------------|:----:|:----:|---------|:-----:|-------------|\n|💼 **TABLES**\n|Access Table                     |✔️|✔️|clsDbTableDef|✔️|tblInternal|\n|Extended Properties              |✔️|✔️|clsDbTableDef|✔️|tblInternal|\n|Table SQL                        |✔️|✔️|clsDbTableDef|✔️|tblInternal|\n|Linked Table                     |✔️|✔️|clsDbTableDef|✔️|tblLinkedAccess|\n|Linked PrimaryKey                |✔️|✔️|clsDbTableDef|✔️|tblLinkedAccess|\n|ODBC Table                       |✔️|✔️|clsDbTableDef||\n|Linked Structure                 |✔️|✔️|clsDbTableDef|✔️|tblLinkedAccess|\n|Table Data (TDF)                 |✔️|✔️|clsDbTableData|✔️|tblInternal.txt|\n|Table Data (XML)                 |✔️|✔️|clsDbTableData|✔️|tblSaveXML.xml|\n|Table Data Macros                |✔️|✔️|clsDbTableDataMacro|✔️|tblSaveXML|\n|💼 **QUERIES**\n|Designer Layout                  |✔️|✔️|clsDbQuery||\n|SQL Output                       |✔️|✔️|clsDbQuery||\n|Pass Through Queries             |✔️|✔️|clsDbQuery||\n|💼 **FORMS**\n|Form objects                     |✔️|✔️|clsDbForm||\n|Saved print settings             |✔️|✔️|clsDbReport|Optional\n|💼 **REPORTS**\n|Report objects                   |✔️|✔️|clsDbReport\n|Saved print settings             |✔️|✔️|clsDbReport|Optional\n|💼 **MACROS**\n|Macro objects                    |✔️|✔️|clsDbMacro\n|💼 **MODULES**\n|Standard Modules                 |✔️|✔️|clsDbModule\n|Class Modules                    |✔️|✔️|clsDbModule\n|Object Modules                   |✔️|✔️|clsDbModule\n|Hidden VBE Attributes            |✔️|✔️|clsDbModule\n|💼 **DATABASE**\n|DAO Properties                   |✔️|✔️|clsDbProperty\n|Project Properties               |✔️|✔️|clsDbProjProperty\n|Object Descriptions              |✔️|✔️|clsDbDocument\n|Hidden Attribute                 |✔️|✔️|clsDbHiddenAttribute\n|Remove Personal Info             |✔️|✔️|clsDbProject\n|Application Icon                 |✔️|✔️|clsDbProperty\n|Embedded Images                  |✔️|✔️|clsDbSharedImage\n|Saved Imp/Exp Specs              |✔️|✔️|clsDbSavedSpec\n|System Imp/Exp Specs             |✔️|✔️|clsDbImexSpec\n|Summary Properties               |✔️|✔️|clsDbDocument\n|Relationships                    |✔️|✔️|clsDbRelation\n|Nav. Pane Groups                 |✔️|✔️|clsDbNavPaneGroup\n|Embedded Office Theme(s)         |✔️|✔️|clsDbTheme\n|CommandBars (Menus and Toolbars) |✔️|◒|clsDbCommandBars\n|💼 **VBE PROJECT**\n|Project Properties               |✔️|✔️|clsDbVbeProject\n|Compilation Arguments            |✔️|✔️|clsDbVbeProject\n|GUID References                  |✔️|✔️|clsDbVbeReference\n|File/Lib References              |✔️|✔️|clsDbVbeReference\n|Forms 2.0 documents              |✔️|✔️|clsDbVbeForm\n|💼 **ADP PROJECTS**\n|Connection Settings              |||clsDbProjProperty\n|SQL Functions                    |✔️|n/a|clsAdpFunction\n|SQL Views                        |✔️|n/a|clsAdpServerView\n|SQL Stored Procedures            |✔️|n/a|clsAdpProcedure\n|SQL Tables                       |✔️|n/a|clsAdpTable\n|SQL Triggers                     |✔️|n/a|clsAdpTrigger\n|💼 **EXTERNAL DATABASE**\n|Microsoft SQL                    |✔️|n/a|clsSchemaMsSql\n|MySQL/MariaDB                    |✔️|n/a|clsSchemaMySql\n|💼 **OTHER**\n|Saved VCS Options                |✔️|✔️|clsOptions\n\n\n## ADP Projects\n\nWorking with *.adp files is very similar to working with regular (MDB) Microsoft Access databases. All of the main database objects can be exported and imported just like MDB files. When it comes to SQL server objects, the object definitions are exported for tracking in source control, but this system does not attempt to modify any SQL server objects. (Hence the n/a note on importing SQL objects.)\n\n## CommandBars (Menus and Toolbars)\n\nAll commandbars can be exported. However, there are known instances of legacy commandbars that may fail to import. In those cases, those commandbars are \"built-in\" but are in fact customized version. If the commandbar's Id no longer exists in the Access, the build will fail. We do handle a subset of the custom built-in Access commandbars, particularly those designed to open a specific Access object. Here's a table of supported custom built-in commandbars:\n\n| Id |Command                       |\n|----|------------------------------|\n|1835|Open Table\n|1836|Open Query\n|1837|Open Form\n|1838|Open Report\n|1839|Run Marco\n|3885|Open Access Data Page\n|3886|Open Server View (ADP)\n|~3887~|~Open Database Diagram (ADP)~ \n|3888|Open Stored Procedure (ADP)\n\nNOTE: The database diagram command is not included because we were unable to create a custom commandbar with this Id. If anyone has this and wish to support this, submit a PR including the commandbar. \n\nIf your application project contains legacy ocmmandbars that cannot be built becuase they are custom and are not in the list, and you do not wish to lose the functionality, you can make use of the [After Build procedure](https://github.com/joyfullservice/msaccess-vcs-addin/wiki/Options#build) and perform the import. You can call ImportCommandBars routine to perform the import from a template file. For an example of this, refer to the code [here](https://github.com/joyfullservice/msaccess-vcs-addin/blob/5744030b02b5d4e9c7677a05e5016a4c347dea76/Version%20Control.accda.src/modules/modVCSUtility.bas#L1096-L1107) and [here](https://github.com/joyfullservice/msaccess-vcs-addin/blob/5744030b02b5d4e9c7677a05e5016a4c347dea76/Version%20Control.accda.src/modules/modVCSUtility.bas#L1375-L1479). \n\n\n## Supported Versions\nThis add-in is designed to work in Microsoft Access 2010 and newer. Access 2007 users, please see [this issue](https://github.com/joyfullservice/msaccess-vcs-addin/issues/464).\n\nMissing something? Create an issue or a pull request.\n"
  },
  {
    "path": "Wiki/Terminology-and-Style-Guide.md",
    "content": "This page contains style and terminology for how to describe elements within the Wiki, Code, and Interface for a consistent approach.\n\nThis is a constant work in progress, and is not complete. Items added here were added for clarity where a universal consensus may not exist.\n\nUse active voice for documentation.\n\nWhere not found here, use of [Google's Style Guide](https://developers.google.com/style/) is generally accepted; though some deviations from their guide have been made in favor of other methods where appropriate.\n<p>\n<p>\n\n# Action / Descriptor Terms\nThe following are either excerpted or differ from the above style guide. Use these methods first. If you don't find what you need here, go to the above guide(s).\n\n|Item|Term|When used\n|-|:-:|-\n|Checkbox **State**|**On** (☑) <p>Within `Code`: `True` |Checkbox has a check in it. <p>**For user-facing content:** Use **On** (TagTips, Form Text, Wiki Documentation, etc.). <p>**For `code` comments:** You may use `True`, as you will be working with the `boolean` value directly.<p>**NOTE:** This differs from the *Action* performed on the **Checkbox**.\n||**Off** (☐) <p>Within `Code`: `False`|Checkbox is cleared. <p>**For user-facing content:** Use **Off** (TagTips, Form Text, Wiki Documentation, etc.). <p>**For `code` comments:** You may use `False`, as you will be working with the `boolean` value directly.<p>**NOTE:** This differs from the *Action* performed on the **Checkbox**.\n||**`Null`**|In some cases, [checkboxes](https://docs.microsoft.com/en-us/office/vba/api/access.checkbox.triplestate) may allow a `TripleState` of `Null`; this may also be referred to as `Indeterminate`. See also [Tri-State](https://en.wikipedia.org/wiki/Checkbox#Tri-state%20checkbox) `Null`. <p>*Example:* In permission lists, if a parent permission includes children, and some of the children are set to allow, and some are off/cleared, the parent may appear in an indeterminate state.<p>All cases use: `Null`.<p> Appears as a square or gray interior inside the checkbox instead of a checkmark or cleared state.\n|UI **Action**|Select|Chose the **(Option name)**, set **(Option name)** checkbox state to `True`.<p>*Example:* Select **Use Fast Save** to enable a significant performance boost.\n||Clear|Clear **(Option name)** checkbox (set state to `False`), remove text.<p>*Example:* Clear **Use Fast Save** to export all elements in your file every time.\n|Keyboard Keystrokes|\\<kbd>|Enclose keystrokes with the \\<kbd></kbd> tag. To group simultaneous keystrokes, use the + charachter.<p>*Example:*<p> `Press <kbd>[Shift]+[Q]</kbd> to do a fancy thing.` <p>will render like this:<p> Press <kbd>[Shift]+[Q]</kbd> to do a fancy thing.\n||<kbd>[]</kbd>|Use of Square Brackets ([ ]) is desirable, especially within code comments as the \\<kbd> tag is not rendered in the VBA IDE, and to disambiguate between combined keystrokes and the <kbd>[+]</kbd> key.<p> *Example in Code:* ` ' To break the execution of this function, Press [Ctrl]+[Break] or [Esc]` <p> *Example Wiki:* To close Access immediately, press <kbd>[Alt]+[F4]</kbd>\n|Text entry in fields||Enclose text entered by users with \\` charachter (code formatting). <p> *Example:* Type `Potato` into the **Desired Food** box.\n|Referencing Field|**Bold**|To refer to a field, use **Bold** (Markdown= \\**[stuff]**)\n|Action||Generally, the action performed does not need to be highlighted. Instead, highlight the **Field** or the **Menu Item**. <P>*Example:* Goto the **Options > Export** tab. Select desired **Sanitize Level**.\n"
  },
  {
    "path": "Wiki/Translation.md",
    "content": "\nThe most recent version of this tool can provide translation support in the AddIn interface (not for your application).\n\n\nSee [Translations Issue](https://github.com/joyfullservice/msaccess-vcs-integration/issues/87) for more details.\n\n\n# Options Translation Tab \n\n**Note:** This tab is normally hidden.\n\n![General Options Tab](img/options-Translation.jpg)\n\n|Setting <img width = 175> |**Default** <p> *(Setting)*|Description\n|-|:-:|:-\n|**Contribute to Translations**|**Default: _On_**|Select this button if you wish to help manage translations.\n|**Translations Path**|**Default: _[Blank]_**|Define the FOLDER path to the `*.pot` translation files.\n|**Button _\"Sync Files\"_**||Click button to sync the database with the updated Translation files.\n\n\n# Defining Translations in the AddIn\n\nTranslations for text are facilitated by calling `T()`. `clsTranslation` is the translation class facilitating language translation functions. `modObjects.T` is where `clsTranslation` is loaded.\n\n|Input (Output) <img width = 175>|Variable Type|**Required** <p> *(Optional Default Value)*|Description\n|-|:-:|:-:|:-\n|strText|`String`|**Required**|The text to be translated. Use variable tags to facilitate variable substitutions (such as file paths).<p>Example: `T(\"Some String Translation Path: {0}\",var0:=SomePath)`\n|strReference|`String`|Optional <p>_`vbNullString`_|\n|strContext|`String`|Optional <p>_`vbNullString`_|\n|strComments|`String`|Optional <p>_`vbNullString`_|\n|var0|`Variant`|Optional|`{0}` substitution; converts to `String` for translation.\n|var1|`Variant`|Optional|`{1}` substitution; converts to `String` for translation.\n|var2|`Variant`|Optional|`{2}` substitution; converts to `String` for translation.\n|var3|`Variant`|Optional|`{3}` substitution; converts to `String` for translation.\n|var4|`Variant`|Optional|`{4}` substitution; converts to `String` for translation.\n|var5|`Variant`|Optional|`{5}` substitution; converts to `String` for translation.\n|var6|`Variant`|Optional|`{6}` substitution; converts to `String` for translation.\n|var7|`Variant`|Optional|`{7}` substitution; converts to `String` for translation.\n|var8|`Variant`|Optional|`{8}` substitution; converts to `String` for translation.\n|var9|`Variant`|Optional|`{9}` substitution; converts to `String` for translation.\n\n\n# Translation Definition\n\nSee `modObjects` for most up to date definition.\n\n```VBA\n'---------------------------------------------------------------------------------------\n' Procedure : T\n' Author    : Adam Waller\n' Date      : 3/19/2024\n' Purpose   : Wrapper function to translate to current language\n'---------------------------------------------------------------------------------------\n'\nPublic Function T(strText As String, Optional strReference As String, _\n    Optional strContext As String, Optional strComments As String, _\n    Optional var0, Optional var1, Optional var2, Optional var3, Optional var4, _\n    Optional var5, Optional var6, Optional var7, Optional var8, Optional var9)\n    T = Translation.T(strText, strReference, strContext, strComments, _\n        var0, var1, var2, var3, var4, var5, var6, var7, var8, var9)\nEnd Function\n```\n\n# Example Use of T()\nIn this example, the result of `T()` to a (fictitional) translation lanuage `ENG ALL CAPS` of `\"Error with module: {0}\"` would be `ERROR WITH MODULE: Some Cool Name\"`.\n\n```VBA\n' Example Use of T()\nPublic Sub RunStuffOrNot()\n    Const FunctionName As String = ModuleName & \".RunStuffOrNot\"\n    Dim strName As String\n\n    ' All Kinds of code\n    strName = \"Some Cool Name\"\n\n    CatchAny eelError, T(\"Error with module: {0}\", var0:=strName), FunctionName\n\nEnd Sub\n```"
  }
]