Наша сборка Qt VS Tools
giy
2022-09-02 ca47896204482bf4a6979e3838bf7f09f61cebeb
Обновление до версии 2.9.0
201 files added
197 files modified
28238 ■■■■ changed files
.editorconfig 2 ●●● patch | view | raw | blame | history
.github/ISSUE_TEMPLATE/bug.md 31 ●●●●● patch | view | raw | blame | history
.github/ISSUE_TEMPLATE/feature.md 20 ●●●●● patch | view | raw | blame | history
.gitignore 6 ●●●●● patch | view | raw | blame | history
Changelog 31 ●●●●● patch | view | raw | blame | history
GUIDELINES.using-directive.md 54 ●●●●● patch | view | raw | blame | history
QMakeFileReader/evaluator/proitems.cpp 2 ●●● patch | view | raw | blame | history
QtMSBuild/QtMSBuild.csproj 47 ●●●● patch | view | raw | blame | history
QtMSBuild/QtMsBuild/qt5.natvis.xml 835 ●●●●● patch | view | raw | blame | history
QtMSBuild/QtMsBuild/qt6.natvis.xml 406 ●●●●● patch | view | raw | blame | history
QtMSBuild/QtMsBuild/qt_defaults.props 7 ●●●●● patch | view | raw | blame | history
QtMSBuild/QtMsBuild/qt_globals.targets 259 ●●●● patch | view | raw | blame | history
QtMSBuild/QtMsBuild/qt_inner.targets 138 ●●●●● patch | view | raw | blame | history
QtMSBuild/QtMsBuild/qt_private.props 116 ●●●● patch | view | raw | blame | history
QtMSBuild/QtMsBuild/qt_settings.xml 52 ●●●●● patch | view | raw | blame | history
QtMSBuild/QtMsBuild/qt_vars.targets 74 ●●●●● patch | view | raw | blame | history
QtMSBuild/QtMsBuild/translation/qttranslation.targets 16 ●●●● patch | view | raw | blame | history
QtMSBuild/Tasks/CriticalSection.cs 2 ●●●●● patch | view | raw | blame | history
QtMSBuild/Tasks/QtRunTask.cs 246 ●●●●● patch | view | raw | blame | history
QtVsTest/Macro.cs 127 ●●●●● patch | view | raw | blame | history
QtVsTest/MacroParser.cs 9 ●●●●● patch | view | raw | blame | history
QtVsTest/MacroServer.cs 10 ●●●●● patch | view | raw | blame | history
QtVsTest/QtVsTest.cs 4 ●●●● patch | view | raw | blame | history
QtVsTest/QtVsTest.csproj 94 ●●●● patch | view | raw | blame | history
QtVsTools.Core/BuildConfig.cs 1 ●●●● patch | view | raw | blame | history
QtVsTools.Core/CommandLineParser.cs 35 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/Common/EnumExt.cs 12 ●●●● patch | view | raw | blame | history
QtVsTools.Core/Common/LazyFactory.cs 57 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/Common/QtVSIPSettingsShared.cs 261 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/CompilerToolWrapper.cs 81 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/CxxStreamReader.cs 4 ●●●● patch | view | raw | blame | history
QtVsTools.Core/ExportProjectDialog.cs 6 ●●●● patch | view | raw | blame | history
QtVsTools.Core/Extensions.cs 9 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/FakeFilter.cs 7 ●●●● patch | view | raw | blame | history
QtVsTools.Core/Filters.cs 21 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/HelperFunctions.cs 629 ●●●● patch | view | raw | blame | history
QtVsTools.Core/Legacy/QtProject.cs 290 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/Legacy/QtVSIPSettings.cs 226 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/Legacy/QtVersionManager.cs 63 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/LinkerToolWrapper.cs 4 ●●●● patch | view | raw | blame | history
QtVsTools.Core/MainWinWrapper.cs 20 ●●●● patch | view | raw | blame | history
QtVsTools.Core/Messages.cs 134 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/MocCmdChecker.cs 4 ●●●● patch | view | raw | blame | history
QtVsTools.Core/MsBuildProject.cs 104 ●●●● patch | view | raw | blame | history
QtVsTools.Core/OutputWindowPane.cs 180 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/ProFileContent.cs 40 ●●●● patch | view | raw | blame | history
QtVsTools.Core/ProFileOption.cs 100 ●●●● patch | view | raw | blame | history
QtVsTools.Core/ProSolution.cs 23 ●●●● patch | view | raw | blame | history
QtVsTools.Core/ProjectExporter.cs 85 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/ProjectImporter.cs 112 ●●●● patch | view | raw | blame | history
QtVsTools.Core/QMake.cs 30 ●●●● patch | view | raw | blame | history
QtVsTools.Core/QMakeConf.cs 16 ●●●● patch | view | raw | blame | history
QtVsTools.Core/QMakeQuery.cs 22 ●●●● patch | view | raw | blame | history
QtVsTools.Core/QrcPrefix.cs 2 ●●● patch | view | raw | blame | history
QtVsTools.Core/QtConfig.cs 17 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/QtModule.cs 12 ●●●● patch | view | raw | blame | history
QtVsTools.Core/QtModules.cs 66 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/QtMsBuild.cs 80 ●●●● patch | view | raw | blame | history
QtVsTools.Core/QtProject.cs 944 ●●●● patch | view | raw | blame | history
QtVsTools.Core/QtVSIPSettings.cs 477 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/QtVersionManager.cs 246 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/QtVsTools.Core.csproj 57 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/RccOptions.cs 15 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/Resources.cs 7 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/Resources.resx 40 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/SR.cs 29 ●●●● patch | view | raw | blame | history
QtVsTools.Core/VersionInformation.cs 25 ●●●● patch | view | raw | blame | history
QtVsTools.Core/VisualStudio/InfoBarMessage.cs 166 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/VisualStudio/VsSearch.cs 146 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/VisualStudio/VsServiceProvider.cs 9 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/VisualStudio/VsShell.cs 107 ●●●●● patch | view | raw | blame | history
QtVsTools.Core/WaitDialog.cs 41 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Common/Concurrent.cs 7 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Common/ConcurrentStopwatch.cs 2 ●●● patch | view | raw | blame | history
QtVsTools.Package/Common/Json/DeferredObject.cs 2 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Common/Json/Serializable.cs 8 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Common/Json/SerializableEnum.cs 3 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Common/Json/Serializer.cs 28 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Common/NativeAPI.cs 8 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Common/PriorityQueue.cs 19 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Common/Prototyped.cs 9 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Common/Timestamp.cs 9 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Editors/Editor.QtDesigner.cs 21 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Editors/Editor.QtResourceEditor.cs 13 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Editors/Editor.cs 100 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Icons/Monikers.imagemanifest 54 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Icons/prf32.png patch | view | raw | blame | history
QtVsTools.Package/Icons/pri32.png patch | view | raw | blame | history
QtVsTools.Package/Icons/pro32.png patch | view | raw | blame | history
QtVsTools.Package/Icons/qml32.png patch | view | raw | blame | history
QtVsTools.Package/Icons/qrc32.png patch | view | raw | blame | history
QtVsTools.Package/Icons/ts32.png patch | view | raw | blame | history
QtVsTools.Package/Icons/ui32.png patch | view | raw | blame | history
QtVsTools.Package/Legacy/ChangeFor.cs 32 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/FormChangeQtVersion.Designer.cs 87 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/FormChangeQtVersion.cs 107 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/FormChangeQtVersion.resx 120 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/FormProjectQtSettings.Designer.cs 159 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/FormProjectQtSettings.cs 166 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/FormProjectQtSettings.resx 120 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/ProjectQtSettings.cs 336 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/QtMenu.cs 109 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/QtOptionsPage.cs 161 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Legacy/Translation.cs 134 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Marketplace/Overview.html_TT 39 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Options/QtOptionsPage.cs 67 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Options/QtVersionsPage.cs 92 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Options/QtVersionsTable.cs 348 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Options/QtVersionsTable.xaml 24 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/DteEventsHandler.cs 143 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/ExtLoader.cs 19 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/Notifications.cs 111 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/QMakeWrapper.cs 2 ●●● patch | view | raw | blame | history
QtVsTools.Package/Package/QtHelp.cs 113 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/QtHelpLinkChooser.xaml 60 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/QtHelpLinkChooser.xaml.cs 28 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/QtItemContextMenu.cs 41 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/QtMainMenu.cs 115 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/QtMsBuildConverter.cs 33 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/QtProjectContextMenu.cs 128 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/QtSolutionContextMenu.cs 107 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/SR.cs 53 ●●●● patch | view | raw | blame | history
QtVsTools.Package/Package/Translation.cs 171 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs 42 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs 4 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs 13 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs 4 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Classification/QmlTag.cs 18 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Breakpoint.cs 19 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Engine.cs 46 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Events.cs 28 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Expression.cs 11 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7InfoHelpers.cs 10 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Program.cs 80 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Property.cs 46 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7StackFrame.cs 24 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/QmlDebugLauncher.cs 63 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/QmlDebugger.cs 47 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/QmlFileSystem.cs 15 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Continue.cs 3 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsObject.cs 1 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Message.cs 6 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Client.cs 32 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Protocol.cs 16 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Parser/QmlParserDiagnostics.cs 4 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Parser/QmlParserInterop.cs 16 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Syntax/QmlAst.cs 2 ●●● patch | view | raw | blame | history
QtVsTools.Package/QML/Syntax/QmlSyntax.cs 2 ●●● patch | view | raw | blame | history
QtVsTools.Package/QtMenus.vsct_TT 93 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QtMsBuild/QtModulesEditor.cs 38 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml 8 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml.cs 8 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QtMsBuild/QtProjectBuild.cs 377 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QtMsBuild/QtProjectIntelliSense.cs 32 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QtMsBuild/QtProjectTracker.cs 170 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QtMsBuild/QtVersionProvider.cs 1 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QtVsTools.Icons.pkgdef 20 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/QtVsTools.Package.csproj 166 ●●●● patch | view | raw | blame | history
QtVsTools.Package/QtVsToolsPackage.cs 82 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/Resources.resx 39 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/qt5.natvis.xml 51 ●●●● patch | view | raw | blame | history
QtVsTools.Package/qt6.natvis.xml 33 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/qt6modules.xml 510 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/qtmodules.xml 56 ●●●●● patch | view | raw | blame | history
QtVsTools.Package/source.extension.vsixmanifest_TT 18 ●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/Properties/AssemblyInfo.cs 1 ●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/QtVsTools.RegExpr.csproj 11 ●●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/expression/CharClassSet.cs 13 ●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/expression/RegExprAssert.cs 4 ●●● patch | view | raw | blame | history
QtVsTools.RegExpr/expression/RegExprRepeat.cs 1 ●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/expression/RegExprToken.cs 18 ●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/expression/Renderer.cs 1 ●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/parser/ParseTree.cs 11 ●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/parser/Parser.cs 10 ●●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/production/Production.cs 14 ●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/production/ProductionRule.cs 5 ●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/production/ProductionRuleAction.cs 6 ●●●●● patch | view | raw | blame | history
QtVsTools.RegExpr/utils/Consts.cs 21 ●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/GuiPage.xaml 307 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/GuiPage.xaml.cs 74 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/UiClassInclusion.cs 37 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/WizardData.cs 68 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/WizardIntroPage.xaml 102 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/WizardIntroPage.xaml.cs 39 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/WizardPage.cs 115 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/WizardResult.cs 37 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/WizardWindow.xaml 44 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Common/WizardWindow.xaml.cs 133 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ItemWizard/QtClass/QtClassPage.xaml 298 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ItemWizard/QtClass/QtClassPage.xaml.cs 68 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ItemWizard/QtClass/QtClassWizard.cs 193 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ItemWizard/Translation/TranslationPage.xaml 149 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ItemWizard/Translation/TranslationPage.xaml.cs 91 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ItemWizard/Translation/TranslationWizard.cs 119 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ItemWizard/WidgetsClass/WidgetsClassWizard.cs 245 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/ConfigPage.xaml 298 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/ConfigPage.xaml.cs 489 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Console/ConsoleWizard.cs 89 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Designer/DesignerPage.xaml 310 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Designer/DesignerPage.xaml.cs 70 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Designer/DesignerWizard.cs 179 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Empty/EmptyWizard.cs 75 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Gui/GuiWizard.cs 365 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Library/LibraryClassPage.xaml 238 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Library/LibraryClassPage.xaml.cs 64 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Library/LibraryWizard.cs 142 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/ProjectTemplateWizard.cs 722 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Quick/QuickWizard.cs 75 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Server/ServerPage.xaml 259 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Server/ServerPage.xaml.cs 64 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/ProjectWizard/Server/ServerWizard.cs 170 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/QtVsTools.Wizards.csproj 157 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Util/ClassNameValidationRule.cs 67 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Util/FileExistsInFilterValidationRule.cs 68 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Util/FileNameValidationRule.cs 55 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Util/NativeMethods.cs 41 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Util/UiClassInclusionConverter.cs 43 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Util/UnsafeNativeMethods.cs 41 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Util/VCLanguageManagerValidationRule.cs 53 ●●●●● patch | view | raw | blame | history
QtVsTools.Wizards/Util/VCRulePropertyStorageHelper.cs 69 ●●●●● patch | view | raw | blame | history
README.md 2 ●●● patch | view | raw | blame | history
Templates/console/QtTemplate.Project.Console.csproj 47 ●●●● patch | view | raw | blame | history
Templates/console/console.vcxproj.filters 6 ●●●● patch | view | raw | blame | history
Templates/designer/QtTemplate.Project.Designer.csproj 49 ●●●● patch | view | raw | blame | history
Templates/designer/designer.vcxproj.filters 6 ●●●● patch | view | raw | blame | history
Templates/designer/plugin.h 2 ●●● patch | view | raw | blame | history
Templates/designer/widget.h 2 ●●● patch | view | raw | blame | history
Templates/dialogbuttonbottom/QtTemplate.Item.DialogButtonBottom.csproj 49 ●●●● patch | view | raw | blame | history
Templates/dialogbuttonright/QtTemplate.Item.DialogButtonRight.csproj 49 ●●●● patch | view | raw | blame | history
Templates/empty/QtTemplate.Project.Empty.csproj 49 ●●●● patch | view | raw | blame | history
Templates/gui/QtTemplate.Project.Gui.csproj 55 ●●●● patch | view | raw | blame | history
Templates/gui/gui.vcxproj.filters 2 ●●● patch | view | raw | blame | history
Templates/gui/gui.vstemplate_TT 2 ●●●●● patch | view | raw | blame | history
Templates/gui/main.cpp 2 ●●● patch | view | raw | blame | history
Templates/gui/widget.cpp 10 ●●●● patch | view | raw | blame | history
Templates/gui/widget.h 10 ●●●●● patch | view | raw | blame | history
Templates/gui/widget.qrc 4 ●●●● patch | view | raw | blame | history
Templates/lib/QtTemplate.Project.Lib.csproj 49 ●●●● patch | view | raw | blame | history
Templates/lib/lib.vcxproj.filters 6 ●●●● patch | view | raw | blame | history
Templates/mainwindow/QtTemplate.Item.MainWindow.csproj 49 ●●●● patch | view | raw | blame | history
Templates/qml/QtTemplate.Item.QMLFile.csproj 49 ●●●● patch | view | raw | blame | history
Templates/qmldir/QtTemplate.Item.QMLDir.csproj 49 ●●●● patch | view | raw | blame | history
Templates/qtclass/Properties/AssemblyInfo.cs 67 ●●●●● patch | view | raw | blame | history
Templates/qtclass/QtTemplate.Item.QtClass.csproj 157 ●●●●● patch | view | raw | blame | history
Templates/qtclass/header.h 9 ●●●●● patch | view | raw | blame | history
Templates/qtclass/qtclass.ico patch | view | raw | blame | history
Templates/qtclass/qtclass.vstemplate_TT 71 ●●●●● patch | view | raw | blame | history
Templates/qtclass/source.cpp 8 ●●●●● patch | view | raw | blame | history
Templates/quick/QtTemplate.Project.Quick.csproj 42 ●●●●● patch | view | raw | blame | history
Templates/quick/quick.vcxproj.filters 4 ●●●● patch | view | raw | blame | history
Templates/resource/QtTemplate.Item.Resource.csproj 49 ●●●● patch | view | raw | blame | history
Templates/server/QtTemplate.Project.Server.csproj 49 ●●●● patch | view | raw | blame | history
Templates/server/header.h 2 ●●● patch | view | raw | blame | history
Templates/server/server.vcxproj.filters 2 ●●● patch | view | raw | blame | history
Templates/translation/Properties/AssemblyInfo.cs 67 ●●●●● patch | view | raw | blame | history
Templates/translation/QtTemplate.Item.Translation.csproj 155 ●●●●● patch | view | raw | blame | history
Templates/translation/translation.ico patch | view | raw | blame | history
Templates/translation/translation.ts 4 ●●●● patch | view | raw | blame | history
Templates/translation/translation.vstemplate_TT 69 ●●●●● patch | view | raw | blame | history
Templates/widget/QtTemplate.Item.Widget.csproj 49 ●●●● patch | view | raw | blame | history
Templates/widgetsclass/Properties/AssemblyInfo.cs 67 ●●●●● patch | view | raw | blame | history
Templates/widgetsclass/QtTemplate.Item.WidgetsClass.csproj 157 ●●●●● patch | view | raw | blame | history
Templates/widgetsclass/widget.cpp 11 ●●●●● patch | view | raw | blame | history
Templates/widgetsclass/widget.h 15 ●●●●● patch | view | raw | blame | history
Templates/widgetsclass/widget.ui 22 ●●●●● patch | view | raw | blame | history
Templates/widgetsclass/widgetsclass.ico patch | view | raw | blame | history
Templates/widgetsclass/widgetsclass.vstemplate_TT 70 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/generator/Program.cs 115 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj 8 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.h 2 ●●● patch | view | raw | blame | history
Tests/BigSolution/template/BigSolution.sln 12 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/QtClassLibrary/QtClass.cpp 38 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/QtClassLibrary/QtClass.h 41 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/QtClassLibrary/QtClassLibrary.cpp 33 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/QtClassLibrary/QtClassLibrary.h 37 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/QtClassLibrary/QtClassLibrary.vcxproj 114 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/QtClassLibrary/QtClassLibrary.vcxproj.filters 42 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/QtClassLibrary/qtclasslibrary_global.h 41 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/StaticLib/Header.h 4 ●●●● patch | view | raw | blame | history
Tests/BigSolution/template/StaticLib/StaticLib.cpp 31 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/StaticLib/StaticLib.vcxproj 93 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/StaticLib/StaticLib.vcxproj.filters 27 ●●●●● patch | view | raw | blame | history
Tests/BigSolution/template/loop_msbuild.bat 36 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/100/QtProjectV100.cpp 10 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/100/QtProjectV100.h 16 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/100/QtProjectV100.pro 6 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/100/QtProjectV100.qrc 4 ●●●● patch | view | raw | blame | history
Tests/ProjectFormats/100/QtProjectV100.ui 28 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/100/QtProjectV100.vcxproj 215 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/100/QtProjectV100.vcxproj.filters 95 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/100/main.cpp 10 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/200/QtProjectV200.cpp 7 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/200/QtProjectV200.h 15 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/200/QtProjectV200.qrc 4 ●●●● patch | view | raw | blame | history
Tests/ProjectFormats/200/QtProjectV200.sln 22 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/200/QtProjectV200.ui 29 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/200/QtProjectV200.vcxproj 133 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/200/QtProjectV200.vcxproj.filters 75 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/200/main.cpp 10 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/300/QtProjectV300.cpp 7 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/300/QtProjectV300.h 15 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/300/QtProjectV300.qrc 4 ●●●● patch | view | raw | blame | history
Tests/ProjectFormats/300/QtProjectV300.sln 25 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/300/QtProjectV300.ui 29 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/300/QtProjectV300.vcxproj 106 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/300/QtProjectV300.vcxproj.filters 50 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/300/main.cpp 10 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/301/QtProjectV301.cpp 7 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/301/QtProjectV301.h 15 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/301/QtProjectV301.qrc 4 ●●●● patch | view | raw | blame | history
Tests/ProjectFormats/301/QtProjectV301.sln 25 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/301/QtProjectV301.ui 29 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/301/QtProjectV301.vcxproj 109 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/301/QtProjectV301.vcxproj.filters 50 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/301/main.cpp 10 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/302/QtProjectV302.cpp 7 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/302/QtProjectV302.h 15 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/302/QtProjectV302.qrc 4 ●●●● patch | view | raw | blame | history
Tests/ProjectFormats/302/QtProjectV302.sln 25 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/302/QtProjectV302.ui 28 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/302/QtProjectV302.vcxproj 102 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/302/QtProjectV302.vcxproj.filters 46 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/302/main.cpp 10 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/303/QtProjectV303.cpp 7 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/303/QtProjectV303.h 15 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/303/QtProjectV303.qrc 4 ●●●● patch | view | raw | blame | history
Tests/ProjectFormats/303/QtProjectV303.sln 25 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/303/QtProjectV303.ui 28 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/303/QtProjectV303.vcxproj 106 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/303/QtProjectV303.vcxproj.filters 49 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/303/main.cpp 10 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/304/QtProjectV304.cpp 10 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/304/QtProjectV304.h 16 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/304/QtProjectV304.qrc 4 ●●●● patch | view | raw | blame | history
Tests/ProjectFormats/304/QtProjectV304.sln 25 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/304/QtProjectV304.ui 28 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/304/QtProjectV304.vcxproj 105 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/304/QtProjectV304.vcxproj.filters 48 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/304/main.cpp 10 ●●●●● patch | view | raw | blame | history
Tests/ProjectFormats/ProjectFormats.md 1324 ●●●●● patch | view | raw | blame | history
Tests/Test_QtMsBuild.Tasks/Properties/AssemblyInfo.cs 1 ●●●● patch | view | raw | blame | history
Tests/Test_QtMsBuild.Tasks/TestTaskLoggingHelper.cs 4 ●●●● patch | view | raw | blame | history
Tests/Test_QtMsBuild.Tasks/Test_Join.cs 25 ●●●●● patch | view | raw | blame | history
Tests/Test_QtMsBuild.Tasks/Test_QtMsBuild.Tasks.csproj 33 ●●●● patch | view | raw | blame | history
Tests/Test_QtMsBuild.Tasks/Test_QtRunTask.cs 90 ●●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.Core/Properties/AssemblyInfo.cs 20 ●●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.Core/Test_LazyFactory.cs 77 ●●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.Core/Test_QtVsTools.Core.csproj 129 ●●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.Package/Properties/AssemblyInfo.cs 20 ●●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.Package/QtVsTestClient.cs 144 ●●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.Package/Test_QtVersionsPage.cs 247 ●●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.Package/Test_QtVsTools.Package.csproj 120 ●●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.PriorityQueue/Properties/AssemblyInfo.cs 1 ●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.PriorityQueue/Test_PriorityQueue.cs 15 ●●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.PriorityQueue/Test_QtVsTools.PriorityQueue.csproj 6 ●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.RegExpr/Properties/AssemblyInfo.cs 1 ●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.RegExpr/Test_MacroParser.cs 3 ●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj 6 ●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.RegExpr/Test_SubTokens.cs 1 ●●●● patch | view | raw | blame | history
Tests/Test_QtVsTools.RegExpr/Test_XmlIntParser.cs 3 ●●●● patch | view | raw | blame | history
doc/config/qtvstools-project.qdocconf 8 ●●●● patch | view | raw | blame | history
doc/config/style/qt5-sidebar.html 51 ●●●● patch | view | raw | blame | history
doc/images/front-advanced.png patch | view | raw | blame | history
doc/images/front-coding.png patch | view | raw | blame | history
doc/images/front-gs.png patch | view | raw | blame | history
doc/images/front-help.png patch | view | raw | blame | history
doc/images/front-preview.png patch | view | raw | blame | history
doc/images/front-projects.png patch | view | raw | blame | history
doc/images/qtvstools-msbuild-diagram.png patch | view | raw | blame | history
doc/images/qtvstools-options-qt-general.png patch | view | raw | blame | history
doc/images/qtvstools-qt-project-settings.png patch | view | raw | blame | history
doc/images/qtvstools-qt-translation-file-wizard.png patch | view | raw | blame | history
doc/images/qtvstools-qt-widget-class-wizard.png patch | view | raw | blame | history
doc/images/qtvstools-qtquick-app-modules.png patch | view | raw | blame | history
doc/images/qtvstools-quick-addressbook-entries.png patch | view | raw | blame | history
doc/images/qtvstools-quick-addressbook-mainwindow.png patch | view | raw | blame | history
doc/images/qtvstools-quick-addressbook-popup.png patch | view | raw | blame | history
doc/images/qtvstools-remote-debugging.png patch | view | raw | blame | history
doc/qtvstools-online.qdocconf 2 ●●● patch | view | raw | blame | history
doc/src/qtvstools.qdoc 868 ●●●●● patch | view | raw | blame | history
doc/tutorial/AddressBook/adddialog.h 2 ●●● patch | view | raw | blame | history
doc/tutorial/QuickAddressBook/QuickAddressBook.sln 25 ●●●●● patch | view | raw | blame | history
doc/tutorial/QuickAddressBook/QuickAddressBook.vcxproj 135 ●●●●● patch | view | raw | blame | history
doc/tutorial/QuickAddressBook/QuickAddressBook.vcxproj.filters 45 ●●●●● patch | view | raw | blame | history
doc/tutorial/QuickAddressBook/QuickAddressBookTypes/AddressBookItem.qml 59 ●●●●● patch | view | raw | blame | history
doc/tutorial/QuickAddressBook/QuickAddressBookTypes/NewAddressPopup.qml 88 ●●●●● patch | view | raw | blame | history
doc/tutorial/QuickAddressBook/main.cpp 45 ●●●●● patch | view | raw | blame | history
doc/tutorial/QuickAddressBook/main.qml 74 ●●●●● patch | view | raw | blame | history
doc/tutorial/QuickAddressBook/qml.qrc 8 ●●●●● patch | view | raw | blame | history
doc/tutorial/QuickAddressBook/qmldir 2 ●●●●● patch | view | raw | blame | history
references.props 163 ●●●●● patch | view | raw | blame | history
version.targets 2 ●●● patch | view | raw | blame | history
vsconfig/2017.vsconfig 48 ●●●●● patch | view | raw | blame | history
vsconfig/2019.vsconfig 44 ●●●●● patch | view | raw | blame | history
vsconfig/2022.vsconfig 42 ●●●●● patch | view | raw | blame | history
vstools.bat 37 ●●●● patch | view | raw | blame | history
vstools.sln 99 ●●●●● patch | view | raw | blame | history
Сборка.md 2 ●●● patch | view | raw | blame | history
.editorconfig
@@ -19,7 +19,7 @@
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
dotnet_sort_system_directives_first = true
file_header_template = unset
# this. and Me. preferences
.github/ISSUE_TEMPLATE/bug.md
New file
@@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
 - Qt VS Tool Version [e.g. 2.7.1, 2.8.1, 2.8.1 (Rev.06)]
 - Visual Studio version [e.g. VS2017, VS2019, VS2022]
**Additional context**
Add any other context about the problem here.
.github/ISSUE_TEMPLATE/feature.md
New file
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
.gitignore
@@ -315,3 +315,9 @@
QtNatvisPoC/*.txt
Tests/BigSolution/generated/
vstools.pri
templates/widgetsclass/Properties/AssemblyInfo.tt.cs
templates/widgetsclass/widgetsclass.vstemplate
templates/qtclass/Properties/AssemblyInfo.tt.cs
templates/qtclass/qtclass.vstemplate
templates/translation/Properties/AssemblyInfo.tt.cs
templates/translation/translation.vstemplate
Changelog
@@ -1,3 +1,34 @@
Qt Visual Studio Tools version 2.9.0:
Changes
-------
 - Added QStringRef and QStringView debug visualizers
 - Check if Qt translation tools are available
 - New widget class wizards accessible from "Add New Item"
 - New Qt class wizards accessible from "Add New Item"
 - InfoBar notification of missing Qt installation
 - InfoBar notification of recently installed new Qt VS Tools version
 - Additional compiler options from Qt included in the build
 - Enabled reporting issues and feature requests via GitHub
 - Debug visualizer for Qt types added to PDB
 - Improved intellisense performance for Qt projects
- Fixed QTVSADDINBUG-626: Qt VS Tools does not generate the same code as Qt Creator
- Fixed QTVSADDINBUG-707: Integrating the main window widget into the Qt Gui application wizard.
- Fixed QTVSADDINBUG-884: QStringRef and QStringView have no natvis definitions
- Fixed QTVSADDINBUG-925: Can't add an "add-on" module to project using GUI
- Fixed QTVSADDINBUG-937: OpenGL Widgets module missing
- Fixed QTVSADDINBUG-951: Can't build QT6 WebEngine example
- Fixed QTVSADDINBUG-965: Pragma warning not working with "external header warning level" option
- Fixed QTVSADDINBUG-966: Debug version of Qt Dlls are linked even if we asked for release mode
- Fixed QTVSADDINBUG-970: Qt6 in Visual Studio plugin doesn't load qmake projects
- Fixed QTVSADDINBUG-972: VS Plugin is ignoring module core5compat when loading project with QT6
- Fixed QTVSADDINBUG-973: unable to add a new qt version with visual studio 2022
- Fixed QTVSADDINBUG-979: Project Properties are overwritten by Qt Vs Tools
- Fixed QTVSADDINBUG-980: The "Release Notes" link does not work
- Fixed QTVSADDINBUG-982: Can't add new Qt Version
Qt Visual Studio Tools version 2.8.1:
Changes
GUIDELINES.using-directive.md
New file
@@ -0,0 +1,54 @@
# Coding guidelines -- `using` directives
Three kinds of `using` directives are available:
  * **Reference**: `using <namespace>` -- _namespace_ can be omitted when referencing types.
  * **Alias**: `using <alias> = <namespace|type>` -- _alias_ can be used in place of _namespace_
    or _type_.
  * **Static**: `using static <type>` -- all static members of _type_ are accessible without
    having to specify the type name.
The following conventions apply to `using` directives in the Qt VS Tools code:
  * `using` directives are grouped by root namespace, and ordered alphabetically within each group.
  * Directives that reference external namespaces are located at the start of the source file,
    before the local namespace declaration.
  * The order of external namespace groups is as follows:
      1. `System*` namespaces.
      2. `Microsoft*` namespaces.
      3. `EnvDTE*` namespaces.
      4. All other external namespaces.
  * Directives referencing in-solution namespaces (i.e. `QtVsTools*` or `QtVsTest*` namespaces) are
    nested within the local namespace block.
  * In-solution reference directives will use an abbreviated form of the  namespace, whenever
    possible.
  * Alias and static directives can be specified either at top-level or nested in the local
    namespace.
  * Alias directives are specified after all reference directives.
  * Static directives are specified after all reference directives and all alias directives.
  * Optionally, directives of different kinds can be separated with an empty line.
## Example
```csharp
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
using EnvDTE;
using EnvDTE80;
using Task = System.Threading.Tasks.Task;
namespace QtVsTools
{
    using Core;
    using QtMsBuild;
    using RegExprParser = SyntaxAnalysis.RegExpr.Parser;
    using static SyntaxAnalysis.RegExpr;
    class ...
```
QMakeFileReader/evaluator/proitems.cpp
@@ -341,7 +341,7 @@
        totalLength += this_.at(i).size();
    if (sz)
        totalLength += sepSize * (sz - 1);
        totalLength += int(sepSize) * (sz - 1);
    QString res(totalLength, Qt::Uninitialized);
    QChar *ptr = (QChar *)res.constData();
QtMSBuild/QtMSBuild.csproj
@@ -67,24 +67,42 @@
  <ItemGroup>
    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
    <Reference Include="System" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Runtime" />
    <Reference Include="$(VCTargetsPath)\Application Type\Linux\1.0\Microsoft.Build.Linux.Tasks.dll" />
    <Reference Include="$(VCTargetsPath)\Application Type\Linux\1.0\liblinux.dll" />
  </ItemGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Version specific references
  // General package references
  // -->
  <Import Project="$(SolutionDir)\references.props" />
  <ItemGroup>
    <PackageReference Include="Microsoft.Build"
      Version="$(Version_Microsoft_Build)" />
    <PackageReference Include="Microsoft.Build.Framework"
      Version="$(Version_Microsoft_Build_Framework)" />
    <PackageReference Include="Microsoft.Build.Tasks.Core"
      Version="$(Version_Microsoft_Build_Tasks_Core)" />
    <PackageReference Include="Microsoft.Bcl.AsyncInterfaces"
      Version="$(Version_Microsoft_Bcl_AsyncInterfaces)" />
    <PackageReference Include="$(Name_Microsoft_VSSDK_BuildTools)" Version="$(Version_Microsoft_VSSDK_BuildTools)" />
    <PackageReference Include="$(Name_Microsoft_VisualStudio_SDK)" Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" />
    <PackageReference Include="$(Name_Microsoft_Build)" Version="$(Version_Microsoft_Build)" />
    <PackageReference Include="$(Name_Microsoft_Build_Tasks_Core)" Version="$(Version_Microsoft_Build_Tasks_Core)" />
  </ItemGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Version specific package references
  // -->
  <Choose>
    <When Condition="'$(VisualStudioVersion)'=='17.0'">
      <ItemGroup>
      </ItemGroup>
    </When>
    <When Condition="'$(VisualStudioVersion)'=='16.0'">
      <ItemGroup>
        <PackageReference Include="$(Name_Microsoft_VisualStudio_Validation)" Version="$(Version_Microsoft_VisualStudio_Validation)" />
        <PackageReference Include="$(Name_Microsoft_VisualStudio_RpcContracts)" Version="$(Version_Microsoft_VisualStudio_RpcContracts)" />
      </ItemGroup>
    </When>
    <When Condition="'$(VisualStudioVersion)'=='15.0'">
      <ItemGroup>
      </ItemGroup>
    </When>
  </Choose>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Solution project references
@@ -112,6 +130,12 @@
      <DesignTime>True</DesignTime>
      <DependentUpon>AssemblyInfo.cs</DependentUpon>
    </Compile>
    <Content Include="QtMSBuild\qt5.natvis.xml">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Content Include="QtMSBuild\qt6.natvis.xml">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <!--
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Qt/MSBuild common property pages and targets
@@ -145,6 +169,10 @@
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Content Include="QtMSBuild\qt_vars.targets">
      <SubType>Designer</SubType>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Content Include="QtMSBuild\qt_inner.targets">
      <SubType>Designer</SubType>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
@@ -380,6 +408,7 @@
    // Inline tasks
    // -->
    <Compile Include="Tasks\CriticalSection.cs" />
    <Compile Include="Tasks\QtRunTask.cs" />
    <Compile Include="Tasks\GetVarsFromMSBuild.cs" />
    <Compile Include="Tasks\HostExec_LinuxWSL_Error.cs" />
    <Compile Include="Tasks\HostTranslatePaths_LinuxWSL_Error.cs" />
QtMSBuild/QtMsBuild/qt5.natvis.xml
New file
@@ -0,0 +1,835 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
    ****************************************************************************
    **
    ** Copyright (C) 2022 The Qt Company Ltd.
    ** Contact: https://www.qt.io/licensing/
    **
    ** This file is part of the Qt VS Tools.
    **
    ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
    ** Commercial License Usage
    ** Licensees holding valid commercial Qt licenses may use this file in
    ** accordance with the commercial license agreement provided with the
    ** Software or, alternatively, in accordance with the terms contained in
    ** a written agreement between you and The Qt Company. For licensing terms
    ** and conditions see https://www.qt.io/terms-conditions. For further
    ** information use the contact form at https://www.qt.io/contact-us.
    **
    ** GNU General Public License Usage
    ** Alternatively, this file may be used under the terms of the GNU
    ** General Public License version 3 as published by the Free Software
    ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
    ** included in the packaging of this file. Please review the following
    ** information to ensure the GNU General Public License requirements will
    ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
    **
    ** $QT_END_LICENSE$
    **
    ****************************************************************************
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
    <Type Name="##NAMESPACE##::QPoint">
        <AlternativeType Name="##NAMESPACE##::QPointF"/>
        <DisplayString>{{ x = {xp}, y = {yp} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QRect">
        <DisplayString>{{ x = {x1}, y = {y1}, width = {x2 - x1 + 1}, height = {y2 - y1 + 1} }}</DisplayString>
        <Expand>
            <Item Name="[x]">x1</Item>
            <Item Name="[y]">y1</Item>
            <Item Name="[width]">x2 - x1 + 1</Item>
            <Item Name="[height]">y2 - y1 + 1</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QRectF">
        <DisplayString>{{ x = {xp}, y = {yp}, width = {w}, height = {h} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
            <Item Name="[width]">w</Item>
            <Item Name="[height]">h</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QSize">
        <AlternativeType Name="##NAMESPACE##::QSizeF"/>
        <DisplayString>{{ width = {wd}, height = {ht} }}</DisplayString>
        <Expand>
            <Item Name="[width]">wd</Item>
            <Item Name="[height]">ht</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QLine">
        <AlternativeType Name="##NAMESPACE##::QLineF"/>
        <DisplayString>{{ start point = {pt1}, end point = {pt2} }}</DisplayString>
        <Expand>
            <Synthetic Name="[start point]">
                <DisplayString>{pt1}</DisplayString>
                <Expand>
                    <ExpandedItem>pt1</ExpandedItem>
                </Expand>
            </Synthetic>
            <Synthetic Name="[end point]">
                <DisplayString>{pt2}</DisplayString>
                <Expand>
                    <ExpandedItem>pt2</ExpandedItem>
                </Expand>
            </Synthetic>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QPolygon">
        <DisplayString>{{ size = {d-&gt;size} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <ArrayItems>
                <Size>d-&gt;size</Size>
                <ValuePointer>(##NAMESPACE##::QPoint*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QPolygonF">
        <DisplayString>{{ size = {d-&gt;size} }}</DisplayString>
        <Expand>
            <Item Name="[closed]">
                d-&gt;size &gt; 0
                    &amp;&amp; ((((##NAMESPACE##::QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)[0]).xp
                == (((##NAMESPACE##::QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)[d-&gt;size - 1]).xp)
                    &amp;&amp; ((((##NAMESPACE##::QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)[0]).yp
                == (((##NAMESPACE##::QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)[d-&gt;size - 1]).yp)
            </Item>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <ArrayItems>
                <Size>d-&gt;size</Size>
                <ValuePointer>(##NAMESPACE##::QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name ="##NAMESPACE##::QVector2D">
        <DisplayString>{{ x = {xp}, y = {yp} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
        </Expand>
    </Type>
    <Type Name ="##NAMESPACE##::QVector3D">
        <DisplayString>{{ x = {xp}, y = {yp}, z = {zp} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
            <Item Name="[z]">zp</Item>
        </Expand>
    </Type>
    <Type Name ="##NAMESPACE##::QVector4D">
        <DisplayString>{{ x = {xp}, y = {yp}, z = {zp}, w = {wp} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
            <Item Name="[z]">zp</Item>
            <Item Name="[w]">wp</Item>
        </Expand>
    </Type>
    <Type Name ="##NAMESPACE##::QMatrix">
        <DisplayString>
            {{ m11 = {_m11}, m12 = {_m12}, m21 = {_m21}, m22 = {_m22}, ... }}
        </DisplayString>
        <Expand>
            <Item Name="[m11]">_m11</Item>
            <Item Name="[m12]">_m12</Item>
            <Item Name="[m21]">_m21</Item>
            <Item Name="[m22]">_m22</Item>
            <Item Name="[dx]">_dx</Item>
            <Item Name="[dy]">_dy</Item>
        </Expand>
    </Type>
    <Type Name ="##NAMESPACE##::QMatrix4x4">
        <DisplayString>
            {{ m11 = {m[0][0]}, m12 = {m[1][0]}, m13 = {m[2][0]}, m14 = {m[3][0]}, ... }}
        </DisplayString>
        <Expand>
            <Item Name="[m11]">m[0][0]</Item>
            <Item Name="[m12]">m[1][0]</Item>
            <Item Name="[m13]">m[2][0]</Item>
            <Item Name="[m14]">m[3][0]</Item>
            <Item Name="[m21]">m[0][1]</Item>
            <Item Name="[m22]">m[1][1]</Item>
            <Item Name="[m23]">m[2][1]</Item>
            <Item Name="[m24]">m[3][1]</Item>
            <Item Name="[m31]">m[0][2]</Item>
            <Item Name="[m32]">m[1][2]</Item>
            <Item Name="[m33]">m[2][2]</Item>
            <Item Name="[m34]">m[3][2]</Item>
            <Item Name="[m41]">m[0][3]</Item>
            <Item Name="[m42]">m[1][3]</Item>
            <Item Name="[m43]">m[2][3]</Item>
            <Item Name="[m44]">m[3][3]</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QSizePolicy">
        <DisplayString>
            {{ horizontal = {static_cast&lt;Policy&gt;(bits.horPolicy)}, vertical = {static_cast&lt;Policy&gt;(bits.verPolicy)}, type = {ControlType(1 &lt;&lt; bits.ctype)} }}
        </DisplayString>
        <Expand>
            <Synthetic Name="[vertical policy]">
                <DisplayString>##NAMESPACE##::QSizePolicy::Policy::{static_cast&lt;Policy&gt;(bits.verPolicy)}</DisplayString>
            </Synthetic>
            <Synthetic Name="[horizontal policy]">
                <DisplayString>##NAMESPACE##::QSizePolicy::Policy::{static_cast&lt;Policy&gt;(bits.horPolicy)}</DisplayString>
            </Synthetic>
            <Synthetic Name="[control type]">
                <DisplayString>##NAMESPACE##::QSizePolicy::ControlType::{ControlType(1 &lt;&lt; bits.ctype)}</DisplayString>
            </Synthetic>
            <Synthetic Name="[expanding directions]">
                <DisplayString
                    Condition="(static_cast&lt;Policy&gt;(bits.verPolicy) &amp; ExpandFlag)">
                        ##NAMESPACE##::Qt::Vertical (2)
                    </DisplayString>
                <DisplayString
                    Condition="(static_cast&lt;Policy&gt;(bits.horPolicy) &amp; ExpandFlag)">
                        ##NAMESPACE##::Qt::Horizontal (1)
                </DisplayString>
            </Synthetic>
            <Item Name="[vertical stretch]">static_cast&lt;int&gt;(bits.verStretch)</Item>
            <Item Name="[horizontal stretch]">static_cast&lt;int&gt;(bits.horStretch)</Item>
            <Item Name="[has height for width]">bits.hfw == 1</Item>
            <Item Name="[has width for height]">bits.wfh == 1</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QChar">
        <DisplayString>{ucs,c}</DisplayString>
        <StringView>ucs,c</StringView>
        <Expand>
            <Item Name="[latin 1]">ucs > 0xff ? '\0' : char(ucs),c</Item>
            <Item Name="[unicode]">ucs,c</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QString">
        <DisplayString>{((reinterpret_cast&lt;unsigned short*&gt;(d)) + d-&gt;offset / 2),sub}</DisplayString>
        <StringView>((reinterpret_cast&lt;unsigned short*&gt;(d)) + d-&gt;offset / 2),sub</StringView>
        <Expand>
            <Item Name="[size]">d-&gt;size</Item>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <ArrayItems>
                <Size>d-&gt;size</Size>
                <ValuePointer>((reinterpret_cast&lt;unsigned short*&gt;(d)) + d-&gt;offset / 2),c</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QStringRef">
        <Intrinsic Name="offset" Expression="(reinterpret_cast&lt;char16_t*&gt;(m_string-&gt;d))
            + m_string-&gt;d->offset / 2" />
        <DisplayString Condition="m_string == nullptr">{m_string,[m_size]} u""</DisplayString>
        <DisplayString Condition="m_string != nullptr">{offset() + m_position,[m_size]}</DisplayString>
        <Expand>
            <Item Name="[position]" ExcludeView="simple">m_position</Item>
            <Item Name="[size]" ExcludeView="simple">m_size</Item>
            <ArrayItems Condition="m_string != nullptr">
                <Size>m_size</Size>
                <ValuePointer>offset()+m_position</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QStringView">
        <DisplayString>{m_data,[m_size]}</DisplayString>
        <StringView>m_data,[m_size]</StringView>
        <Expand>
            <Item Name="[size]" ExcludeView="simple">m_size</Item>
            <ArrayItems>
                <Size>m_size</Size>
                <ValuePointer>m_data</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QByteArray">
        <DisplayString>{((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset),sb}</DisplayString>
        <StringView>((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset),sb</StringView>
        <Expand>
            <Item Name="[size]">d-&gt;size</Item>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <ArrayItems>
                <Size>d-&gt;size</Size>
                <ValuePointer>((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset),c</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QUrl">
        <Intrinsic Name="isEmpty" Expression="size==0">
            <Parameter Name="size" Type="int"/>
        </Intrinsic>
        <Intrinsic Name="memberOffset" Expression="sizeof(QAtomicInt) + sizeof(int) + (sizeof(QString) * count)">
            <Parameter Name="count" Type="int"/>
        </Intrinsic>
        <Intrinsic Name="scheme" Expression="*((QString*)(((char*)(d) + memberOffset(0))))" />
        <Intrinsic Name="username" Expression="*((QString*)(((char*)(d) + memberOffset(1))))" />
        <Intrinsic Name="password" Expression="*((QString*)(((char*)(d) + memberOffset(2))))" />
        <Intrinsic Name="host" Expression="*((QString*)(((char*)(d) + memberOffset(3))))" />
        <Intrinsic Name="path" Expression="*((QString*)(((char*)(d) + memberOffset(4))))" />
        <Intrinsic Name="query" Expression="*((QString*)(((char*)(d) + memberOffset(5))))" />
        <Intrinsic Name="fragment" Expression="*((QString*)(((char*)(d) + memberOffset(6))))" />
        <DisplayString Condition="!isEmpty(scheme().d-&gt;size)">{scheme()}://{host()}{path()}</DisplayString>
        <DisplayString Condition="isEmpty(scheme().d-&gt;size)">{path()}</DisplayString>
        <Expand>
            <Item Name="[scheme]">scheme()</Item>
            <Item Name="[username]">username()</Item>
            <Item Name="[password]">password()</Item>
            <Item Name="[host]">host()</Item>
            <Item Name="[path]">path()</Item>
            <Item Name="[query]">query()</Item>
            <Item Name="[fragment]">fragment()</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QBitArray">
        <DisplayString>{{ size = {(d.d-&gt;size &lt;&lt; 3) - *((reinterpret_cast&lt;char*&gt;(d.d)) + d.d-&gt;offset)} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d.d-&gt;ref.atomic._q_value</Item>
            <IndexListItems>
                <Size>(d.d-&gt;size &lt;&lt; 3) - *((reinterpret_cast&lt;char*&gt;(d.d)) + d.d-&gt;offset)</Size>
                <ValueNode>
                    (*(reinterpret_cast&lt;const unsigned char*&gt;((reinterpret_cast&lt;char*&gt;(d.d)) + d.d-&gt;offset) + 1
                        + ($i &gt;&gt; 3)) &amp; (1 &lt;&lt; ($i &amp; 7))) != 0
                </ValueNode>
            </IndexListItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QVarLengthArray&lt;*&gt;">
        <AlternativeType Name="##NAMESPACE##::QVarLengthArray&lt;*, int&gt;"/>
        <DisplayString>{{ size = {s} }}</DisplayString>
        <Expand>
            <Item Name="[capacity]">a</Item>
            <ArrayItems>
                <Size>s</Size>
                <ValuePointer>ptr</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QDate">
        <DisplayString>{{ julian day = {jd} }}</DisplayString>
        <Expand></Expand>
    </Type>
    <Type Name="##NAMESPACE##::QTime">
        <DisplayString
            Condition="mds == 1">{{ millisecond = {mds} }}</DisplayString>
        <DisplayString
            Condition="mds != 1">{{ milliseconds = {mds} }}</DisplayString>
        <Expand>
            <Item Name="[hour]"
                  Condition="(mds / 3600000) == 1">mds / 3600000, d</Item>
            <Item Name="[hours]"
                  Condition="(mds / 3600000) != 1">mds / 3600000, d</Item>
            <Item Name="[minute]"
                  Condition="((mds % 3600000) / 60000) == 1">(mds % 3600000) / 60000, d</Item>
            <Item Name="[minutes]"
                  Condition="((mds % 3600000) / 60000) != 1">(mds % 3600000) / 60000, d</Item>
            <Item Name="[second]"
                  Condition="((mds / 1000) % 60) == 1">(mds / 1000) % 60, d</Item>
            <Item Name="[seconds]"
                  Condition="((mds / 1000) % 60) != 1">(mds / 1000) % 60, d</Item>
            <Item Name="[millisecond]"
                  Condition="(mds % 1000) == 1">mds % 1000, d</Item>
            <Item Name="[milliseconds]"
                  Condition="(mds % 1000) != 1">mds % 1000, d</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QRegularExpression">
        <DisplayString>{d.pattern}</DisplayString>
    </Type>
    <Type Name="##NAMESPACE##::QSharedData">
        <Expand>
            <Item Name="[referenced]">ref._q_value</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QSharedPointer&lt;*&gt;">
        <DisplayString>strong reference to shared pointer of type {"$T1"}</DisplayString>
        <Expand>
            <Item Name="[is null]">value == 0</Item>
            <Item Name="[weak referenced]">d-&gt;weakref._q_value</Item>
            <Item Name="[strong referenced]">d-&gt;strongref._q_value</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QSharedDataPointer&lt;*&gt;">
        <DisplayString>pointer to implicit shared object of type {"$T1"}</DisplayString>
        <Expand>
            <ExpandedItem>d</ExpandedItem>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QExplicitlySharedDataPointer&lt;*&gt;">
        <DisplayString>pointer to explicit shared object of type {"$T1"}</DisplayString>
        <Expand>
            <ExpandedItem>d</ExpandedItem>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QPointer&lt;*&gt;">
        <DisplayString>guarded pointer to subclass of QObject of type {"$T1"}</DisplayString>
        <Expand>
            <Item Name="[is null]">wp.d == 0 || wp.d-&gt;strongref._q_value == 0 || wp.value == 0</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QWeakPointer&lt;*&gt;">
        <DisplayString>weak reference to shared pointer of type {"$T1"}</DisplayString>
        <Expand>
            <Item Name="[is null]">d == 0 || d-&gt;strongref._q_value == 0 || value == 0</Item>
            <Item Name="[weak referenced]">d-&gt;weakref._q_value</Item>
            <Item Name="[strong referenced]">d-&gt;strongref._q_value</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QScopedPointer&lt;*&gt;">
        <DisplayString>scoped pointer to a dynamically allocated object of type {"$T1"}</DisplayString>
        <Expand>
            <Item Name="[is null]">!d</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QScopedArrayPointer&lt;*&gt;">
        <DisplayString>scoped pointer to dynamically allocated array of objects of type {"$T1"}</DisplayString>
        <Expand>
            <Item Name="[is null]">!d</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QPair&lt;*,*&gt;">
        <DisplayString>({first}, {second})</DisplayString>
        <Expand>
            <Item Name="[first]">first</Item>
            <Item Name="[second]">second</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QVector&lt;*&gt;">
        <AlternativeType Name="##NAMESPACE##::QStack&lt;*&gt;"></AlternativeType>
        <DisplayString>{{ size = {d-&gt;size} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <ArrayItems>
                <Size>d-&gt;size</Size>
                <ValuePointer>($T1*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QList&lt;*&gt;">
        <AlternativeType Name="##NAMESPACE##::QQueue&lt;*&gt;"></AlternativeType>
        <DisplayString>{{ size = {d-&gt;end - d-&gt;begin} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <IndexListItems>
                <Size>d-&gt;end - d-&gt;begin</Size>
                <ValueNode>*reinterpret_cast&lt;$T1*&gt;((sizeof($T1) &gt; sizeof(void*))
                    ? reinterpret_cast&lt;Node*&gt;(d-&gt;array + d-&gt;begin + $i)-&gt;v
                    : reinterpret_cast&lt;$T1*&gt;(d-&gt;array + d-&gt;begin + $i))
                </ValueNode>
            </IndexListItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QStringList">
        <DisplayString>{{ size = {d-&gt;end - d-&gt;begin} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <IndexListItems>
                <Size>d-&gt;end - d-&gt;begin</Size>
                <ValueNode>
                    *reinterpret_cast&lt;QString*&gt;((sizeof(QString) &gt; sizeof(void*))
                    ? reinterpret_cast&lt;Node*&gt;(d-&gt;array + d-&gt;begin + $i)-&gt;v
                    : reinterpret_cast&lt;QString*&gt;(d-&gt;array + d-&gt;begin + $i))
                </ValueNode>
            </IndexListItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QList&lt;QVariant&gt;">
        <DisplayString>{{ size = {d-&gt;end - d-&gt;begin} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <IndexListItems>
                <Size>d-&gt;end - d-&gt;begin</Size>
                <ValueNode>
                    *reinterpret_cast&lt;QVariant*&gt;((sizeof(QVariant) &gt; sizeof(void*))
                    ? reinterpret_cast&lt;Node*&gt;(d-&gt;array + d-&gt;begin + $i)-&gt;v
                    : reinterpret_cast&lt;QVariant*&gt;(d-&gt;array + d-&gt;begin + $i))
                </ValueNode>
            </IndexListItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QLinkedList&lt;*&gt;">
        <DisplayString>{{ size = {d-&gt;size} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <LinkedListItems>
                <Size>d-&gt;size</Size>
                <HeadPointer>d-&gt;n</HeadPointer>
                <NextPointer>n</NextPointer>
                <ValueNode>(*(##NAMESPACE##::QLinkedListNode&lt;$T1&gt;*)this).t</ValueNode>
            </LinkedListItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QMapNode&lt;*,*&gt;">
        <DisplayString>({key}, {value})</DisplayString>
        <Expand>
            <Item Name="[key]">key</Item>
            <Item Name="[value]">value</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QMap&lt;*,*&gt;">
        <AlternativeType Name="##NAMESPACE##::QMultiMap&lt;*,*&gt;"/>
        <DisplayString>{{ size = {d-&gt;size} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <TreeItems>
                <Size>d-&gt;size</Size>
                <HeadPointer>d-&gt;header.left</HeadPointer>
                <LeftPointer>left</LeftPointer>
                <RightPointer>right</RightPointer>
                <ValueNode>*((##NAMESPACE##::QMapNode&lt;$T1,$T2&gt;*)this)</ValueNode>
            </TreeItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QHashNode&lt;*,*&gt;">
        <DisplayString Condition="next == 0">(empty)</DisplayString>
        <DisplayString Condition="next != 0">({key}, {value})</DisplayString>
        <Expand>
            <Item Name="[key]" Condition="next != 0">key</Item>
            <Item Name="[value]" Condition="next != 0">value</Item>
            <Item Name="[next]" Condition="next != 0">next</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QHash&lt;*,*&gt;">
        <AlternativeType Name="##NAMESPACE##::QMultiHash&lt;*,*&gt;"/>
        <DisplayString>{{ size = {d-&gt;size} }}</DisplayString>
        <Expand>
            <ArrayItems IncludeView="buckets">
                <Size>d-&gt;numBuckets</Size>
                <ValuePointer>reinterpret_cast&lt;Node **&gt;(d-&gt;buckets)</ValuePointer>
            </ArrayItems>
            <CustomListItems ExcludeView="buckets">
                <Variable Name="n" InitialValue="d-&gt;numBuckets"/>
                <Variable Name="bucket" InitialValue="d-&gt;buckets"/>
                <Variable Name="node" InitialValue="d-&gt;buckets[0]"/>
                <Variable Name="keyValuePair" InitialValue="reinterpret_cast&lt;Node *&gt;(0)"/>
                <Size>d-&gt;size</Size>
                <Loop>
                    <Break Condition="n == 0"/>
                    <Exec>node = *(bucket++)</Exec>
                    <Exec>--n</Exec>
                    <Loop>
                        <Break Condition="!node || !node-&gt;next"/>
                        <Exec>keyValuePair = reinterpret_cast&lt;Node *&gt;(node)</Exec>
                        <Item Name="[{keyValuePair-&gt;key}]">keyValuePair-&gt;value</Item>
                        <Exec>node = node-&gt;next</Exec>
                    </Loop>
                </Loop>
            </CustomListItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QHashNode&lt;*,##NAMESPACE##::QHashDummyValue&gt;">
        <DisplayString Condition="next == 0">(empty)</DisplayString>
        <DisplayString Condition="next != 0">({key})</DisplayString>
        <Expand>
            <Item Name="[key]" Condition="next != 0">key</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QSet&lt;*&gt;">
        <DisplayString>{{ size = {q_hash.d-&gt;size} }}</DisplayString>
        <Expand>
            <ExpandedItem>q_hash</ExpandedItem>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QCache&lt;*,*&gt;::Node">
        <DisplayString>({*keyPtr}, {*t})</DisplayString>
        <Expand>
            <Item Name="[key]">*keyPtr</Item>
            <Item Name="[value]">*t</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QCache&lt;*,*&gt;">
        <DisplayString>{{ size = {hash.d-&gt;size} }}</DisplayString>
        <Expand>
            <Item Name="[max coast]">mx</Item>
            <Item Name="[total coast]">total</Item>
            <Item Name="[referenced]">hash.d-&gt;ref.atomic._q_value</Item>
            <LinkedListItems>
                <Size>hash.d-&gt;size</Size>
                <HeadPointer>f</HeadPointer>
                <NextPointer>n</NextPointer>
                <ValueNode>*((Node*)this)</ValueNode>
            </LinkedListItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QStandardItemPrivate">
        <Intrinsic Name="memberOffset" Expression="sizeof(QStandardItemModel *)
                                                 + sizeof(QStandardItem *)
                                                 + sizeof(int *)
                                                 + sizeof(int *)
                                                 + (sizeof(int) * count)">
            <Parameter Name="count" Type="int"/>
        </Intrinsic>
        <Intrinsic Name="rows" Expression="*((int*)(((char*)(this)) + memberOffset(0)))" />
        <Intrinsic Name="columns" Expression="*((int*)(((char*)(this)) + memberOffset(1)))" />
    </Type>
    <Type Name="##NAMESPACE##::QStandardItem">
        <DisplayString>{{ row count = {(*d_ptr.d).rows()}, column count = {(*d_ptr.d).columns()} }}</DisplayString>
        <Expand>
            <Item Name="[d]">d_ptr.d,!</Item>
            <Item Name="[row count]">(*d_ptr.d).rows()</Item>
            <Item Name="[column count]">(*d_ptr.d).columns()</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QVariant">
        <!--Region DisplayString QVariant-->
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::UnknownType">Invalid</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::Bool">{d.data.b}</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::Int">{d.data.i}</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::UInt">{d.data.u}</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::LongLong">{d.data.ll}</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::ULongLong">{d.data.ull}</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::Double">{d.data.d}</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QChar">{d.data.c}</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVariantMap">
            {*((##NAMESPACE##::QMap&lt;##NAMESPACE##::QString,##NAMESPACE##::QVariant&gt;*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVariantList">
            {*((##NAMESPACE##::QList&lt;##NAMESPACE##::QVariant&gt;*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QString">
            {*((##NAMESPACE##::QString*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QStringList">
            {*((##NAMESPACE##::QStringList*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QByteArray">
            {*((##NAMESPACE##::QByteArray*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QBitArray">
            {*((##NAMESPACE##::QBitArray*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QDate">
            {*((##NAMESPACE##::QDate*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QTime">
            {*((##NAMESPACE##::QTime*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QDateTime">DateTime</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QUrl">Url</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QLocale">Locale</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRect">
            {*((##NAMESPACE##::QRect*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRectF">
            {*((##NAMESPACE##::QRectF*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QSize">
            {*((##NAMESPACE##::QSize*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QSizeF">
            {*((##NAMESPACE##::QSizeF*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QLine">
            {*((##NAMESPACE##::QLine*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QLineF">
            {*((##NAMESPACE##::QLineF*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPoint">
            {*((##NAMESPACE##::QPoint*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPointF">
            {*((##NAMESPACE##::QPointF*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRegExp">RegExp</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRegularExpression">RegularExpression</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVariantHash">
            {*((##NAMESPACE##::QHash&lt;##NAMESPACE##::QString,##NAMESPACE##::QVariant&gt;*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))}
        </DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QEasingCurve">EasingCurve</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QUuid">Uuid</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QModelIndex">ModelIndex</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::LastCoreType">LastCoreType</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QFont">Font</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPixmap">Pixmap</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QBrush">Brush</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QColor">Color</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPalette">Palette</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QImage">Image</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPolygon">Polygon</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRegion">Region</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QBitmap">Bitmap</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QCursor">Cursor</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QKeySequence">KeySequence</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPen">Pen</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QTextLength">TextLength</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QTextFormat">TextFormat</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QMatrix">Matrix</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QTransform">Transform</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QMatrix4x4">Matrix4x4</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVector2D">Vector2D</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVector3D">Vector3D</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVector4D">Vector4D</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QQuaternion">Quaternion</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPolygonF">PolygonF</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QIcon">Icon</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::LastGuiType">LastGuiType</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QSizePolicy">SizePolicy</DisplayString>
        <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::User">UserType</DisplayString>
        <DisplayString Condition="d.type == 0xffffffff">LastType</DisplayString>
        <!--End region DisplayString QVariant-->
        <!--Region DisplayView QVariant-->
        <StringView Condition="d.type == ##NAMESPACE##::QMetaType::QChar">d.data.c</StringView>
        <StringView Condition="d.type == ##NAMESPACE##::QMetaType::QString">
            *((##NAMESPACE##::QString*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
        </StringView>
        <StringView Condition="d.type == ##NAMESPACE##::QMetaType::QByteArray">
            *((##NAMESPACE##::QByteArray*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
        </StringView>
        <!--End region DisplayView QVariant-->
        <!--Region Expand QVariant-->
        <Expand>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QVariantMap">
                *((##NAMESPACE##::QMap&lt;##NAMESPACE##::QString,##NAMESPACE##::QVariant&gt;*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QVariantList">
                *((##NAMESPACE##::QList&lt;##NAMESPACE##::QVariant&gt;*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QString">
                *((##NAMESPACE##::QString*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QStringList">
                *((##NAMESPACE##::QStringList*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QByteArray">
                *((##NAMESPACE##::QByteArray*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QBitArray">
                *((##NAMESPACE##::QBitArray*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QDate">
                *((##NAMESPACE##::QDate*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QTime">
                *((##NAMESPACE##::QTime*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QRect">
                *((##NAMESPACE##::QRect*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QRectF">
                *((##NAMESPACE##::QRectF*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QSize">
                *((##NAMESPACE##::QSize*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QSizeF">
                *((##NAMESPACE##::QSizeF*)(d.is_shared ? d.data.shared-&gt;ptr
                : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QLine">
                *((##NAMESPACE##::QLine*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QLineF">
                *((##NAMESPACE##::QLineF*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QPoint">
                *((##NAMESPACE##::QPoint*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QPointF">
                *((##NAMESPACE##::QPointF*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
            <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QVariantHash">
                *((##NAMESPACE##::QHash&lt;##NAMESPACE##::QString,##NAMESPACE##::QVariant&gt;*)(d.is_shared ? d.data.shared-&gt;ptr
                    : reinterpret_cast&lt;const void *&gt;(&amp;d.data.ptr)))
            </ExpandedItem>
        </Expand>
        <!--End region Expand QVariant-->
    </Type>
</AutoVisualizer>
QtMSBuild/QtMsBuild/qt6.natvis.xml
New file
@@ -0,0 +1,406 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
    ****************************************************************************
    **
    ** Copyright (C) 2022 The Qt Company Ltd.
    ** Contact: https://www.qt.io/licensing/
    **
    ** This file is part of the Qt VS Tools.
    **
    ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
    ** Commercial License Usage
    ** Licensees holding valid commercial Qt licenses may use this file in
    ** accordance with the commercial license agreement provided with the
    ** Software or, alternatively, in accordance with the terms contained in
    ** a written agreement between you and The Qt Company. For licensing terms
    ** and conditions see https://www.qt.io/terms-conditions. For further
    ** information use the contact form at https://www.qt.io/contact-us.
    **
    ** GNU General Public License Usage
    ** Alternatively, this file may be used under the terms of the GNU
    ** General Public License version 3 as published by the Free Software
    ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
    ** included in the packaging of this file. Please review the following
    ** information to ensure the GNU General Public License requirements will
    ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
    **
    ** $QT_END_LICENSE$
    **
    ****************************************************************************
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
   <Type Name="##NAMESPACE##::QSpecialInteger&lt;*&gt;">
        <DisplayString>{val}</DisplayString>
        <Expand>
            <Item Name="[value]">val</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QBasicAtomicInteger&lt;*&gt;">
        <DisplayString>{_q_value}</DisplayString>
        <Expand>
            <Item Name="[value]">_q_value</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QBasicAtomicPointer&lt;*&gt;">
        <Intrinsic Name="isNull" Expression="value()==0" />
        <Intrinsic Name="value" Expression="_q_value.value()" />
        <DisplayString Condition="isNull()">empty</DisplayString>
        <DisplayString Condition="!isNull()">{_q_value}</DisplayString>
        <Expand>
            <Item Name=" " Condition="!isNull()">*value()</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QPoint">
        <AlternativeType Name="##NAMESPACE##::QPointF"/>
        <DisplayString>{{ x = {xp}, y = {yp} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QRect">
        <DisplayString>{{ x = {x1}, y = {y1}, width = {x2 - x1 + 1}, height = {y2 - y1 + 1} }}</DisplayString>
        <Expand>
            <Item Name="[x]">x1</Item>
            <Item Name="[y]">y1</Item>
            <Item Name="[width]">x2 - x1 + 1</Item>
            <Item Name="[height]">y2 - y1 + 1</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QRectF">
        <DisplayString>{{ x = {xp}, y = {yp}, width = {w}, height = {h} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
            <Item Name="[width]">w</Item>
            <Item Name="[height]">h</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QSize">
        <AlternativeType Name="##NAMESPACE##::QSizeF"/>
        <DisplayString>{{ width = {wd}, height = {ht} }}</DisplayString>
        <Expand>
            <Item Name="[width]">wd</Item>
            <Item Name="[height]">ht</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QLine">
        <AlternativeType Name="##NAMESPACE##::QLineF"/>
        <DisplayString>{{ start point = {pt1}, end point = {pt2} }}</DisplayString>
        <Expand>
            <Synthetic Name="[start point]">
                <DisplayString>{pt1}</DisplayString>
                <Expand>
                    <ExpandedItem>pt1</ExpandedItem>
                </Expand>
            </Synthetic>
            <Synthetic Name="[end point]">
                <DisplayString>{pt2}</DisplayString>
                <Expand>
                    <ExpandedItem>pt2</ExpandedItem>
                </Expand>
            </Synthetic>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QPolygon">
        <DisplayString>{{ size={d-&gt;size} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <ArrayItems>
                <Size>d-&gt;size</Size>
                <ValuePointer>(QPoint*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QPolygonF">
        <DisplayString>{{ size={d-&gt;size} }}</DisplayString>
        <Expand>
            <Item Name="[closed]">
                d-&gt;size &gt; 0
                &amp;&amp; ((((QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)[0]).xp
                == (((QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)[d-&gt;size - 1]).xp)
                &amp;&amp; ((((QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)[0]).yp
                == (((QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)[d-&gt;size - 1]).yp)
            </Item>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <ArrayItems>
                <Size>d-&gt;size</Size>
                <ValuePointer>(QPointF*)((reinterpret_cast&lt;char*&gt;(d)) + d-&gt;offset)</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QVector2D">
        <DisplayString>{{ x = {xp}, y = {yp} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QVector3D">
        <DisplayString>{{ x = {xp}, y = {yp}, z = {zp} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
            <Item Name="[z]">zp</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QVector4D">
        <DisplayString>{{ x = {xp}, y = {yp}, z = {zp}, w = {wp} }}</DisplayString>
        <Expand>
            <Item Name="[x]">xp</Item>
            <Item Name="[y]">yp</Item>
            <Item Name="[z]">zp</Item>
            <Item Name="[w]">wp</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QMatrix">
        <DisplayString>
            {{ m11 = {_m11}, m12 = {_m12}, m21 = {_m21}, m22 = {_m22}, ... }}
        </DisplayString>
        <Expand>
            <Item Name="[m11]">_m11</Item>
            <Item Name="[m12]">_m12</Item>
            <Item Name="[m21]">_m21</Item>
            <Item Name="[m22]">_m22</Item>
            <Item Name="[dx]">_dx</Item>
            <Item Name="[dy]">_dy</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QMatrix4x4">
        <DisplayString>
            {{ m11 = {m[0][0]}, m12 = {m[1][0]}, m13 = {m[2][0]}, m14 = {m[3][0]}, ... }}
        </DisplayString>
        <Expand>
            <Item Name="[m11]">m[0][0]</Item>
            <Item Name="[m12]">m[1][0]</Item>
            <Item Name="[m13]">m[2][0]</Item>
            <Item Name="[m14]">m[3][0]</Item>
            <Item Name="[m21]">m[0][1]</Item>
            <Item Name="[m22]">m[1][1]</Item>
            <Item Name="[m23]">m[2][1]</Item>
            <Item Name="[m24]">m[3][1]</Item>
            <Item Name="[m31]">m[0][2]</Item>
            <Item Name="[m32]">m[1][2]</Item>
            <Item Name="[m33]">m[2][2]</Item>
            <Item Name="[m34]">m[3][2]</Item>
            <Item Name="[m41]">m[0][3]</Item>
            <Item Name="[m42]">m[1][3]</Item>
            <Item Name="[m43]">m[2][3]</Item>
            <Item Name="[m44]">m[3][3]</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QSizePolicy">
        <DisplayString>
            {{ horizontal = {static_cast&lt;Policy&gt;(bits.horPolicy)}, vertical = {static_cast&lt;Policy&gt;(bits.verPolicy)}, type = {ControlType(1 &lt;&lt; bits.ctype)} }}
        </DisplayString>
        <Expand>
            <Synthetic Name="[vertical policy]">
                <DisplayString>QSizePolicy::Policy::{static_cast&lt;Policy&gt;(bits.verPolicy)}</DisplayString>
            </Synthetic>
            <Synthetic Name="[horizontal policy]">
                <DisplayString>QSizePolicy::Policy::{static_cast&lt;Policy&gt;(bits.horPolicy)}</DisplayString>
            </Synthetic>
            <Synthetic Name="[control type]">
                <DisplayString>QSizePolicy::ControlType::{ControlType(1 &lt;&lt; bits.ctype)}</DisplayString>
            </Synthetic>
            <Synthetic Name="[expanding directions]">
                <DisplayString
                    Condition="(static_cast&lt;Policy&gt;(bits.verPolicy) &amp; ExpandFlag)">
                    Qt::Vertical (2)
                </DisplayString>
                <DisplayString
                    Condition="(static_cast&lt;Policy&gt;(bits.horPolicy) &amp; ExpandFlag)">
                    Qt::Horizontal (1)
                </DisplayString>
            </Synthetic>
            <Item Name="[vertical stretch]">static_cast&lt;int&gt;(bits.verStretch)</Item>
            <Item Name="[horizontal stretch]">static_cast&lt;int&gt;(bits.horStretch)</Item>
            <Item Name="[has height for width]">bits.hfw == 1</Item>
            <Item Name="[has width for height]">bits.wfh == 1</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QChar">
        <DisplayString>{ucs,c}</DisplayString>
        <StringView>ucs,c</StringView>
        <Expand>
            <Item Name="[latin 1]">ucs > 0xff ? '\0' : char(ucs),c</Item>
            <Item Name="[unicode]">ucs,c</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QString">
        <DisplayString>&quot;{(reinterpret_cast&lt;unsigned short*&gt;(d.ptr)),sub}&quot;</DisplayString>
        <StringView>(reinterpret_cast&lt;unsigned short*&gt;(d.ptr)),sub</StringView>
        <Expand>
            <Item Name="[size]">d.size</Item>
            <ArrayItems>
                <Size>d.size</Size>
                <ValuePointer>d.ptr</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QStringRef">
        <DisplayString Condition="m_string == nullptr">{m_string,[m_size]} u""</DisplayString>
        <DisplayString Condition="m_string != nullptr">{m_string-&gt;d.ptr+m_position,[m_size]}</DisplayString>
        <StringView Condition="m_string == nullptr">""</StringView>
        <StringView Condition="m_string != nullptr">m_string,[m_position+m_size]</StringView>
        <Expand>
            <Item Name="[position]" ExcludeView="simple">m_position</Item>
            <Item Name="[size]" ExcludeView="simple">m_size</Item>
            <ArrayItems Condition="m_string != nullptr">
                <Size>m_size</Size>
                <ValuePointer>m_string-&gt;d.ptr+m_position</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QStringView">
        <DisplayString>{m_data,[m_size]}</DisplayString>
        <StringView>m_data,[m_size]</StringView>
        <Expand>
            <Item Name="[size]" ExcludeView="simple">m_size</Item>
            <ArrayItems>
                <Size>m_size</Size>
                <ValuePointer>m_data</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QByteArray">
        <DisplayString>&quot;{((reinterpret_cast&lt;char*&gt;(d.ptr))),sb}&quot;</DisplayString>
        <StringView>((reinterpret_cast&lt;char*&gt;(d.ptr))),sb</StringView>
        <Expand>
            <Item Name="[size]">d.size</Item>
            <ArrayItems>
                <Size>d.size</Size>
                <ValuePointer>d.ptr</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QUrl">
        <Intrinsic Name="isEmpty" Expression="size==0">
            <Parameter Name="size" Type="int"/>
        </Intrinsic>
        <Intrinsic Name="memberOffset" Expression="sizeof(QAtomicInt) + sizeof(int) + (sizeof(QString) * count)">
            <Parameter Name="count" Type="int"/>
        </Intrinsic>
        <Intrinsic Name="scheme" Expression="*((QString*)(((char*)(d) + memberOffset(0))))" />
        <Intrinsic Name="username" Expression="*((QString*)(((char*)(d) + memberOffset(1))))" />
        <Intrinsic Name="password" Expression="*((QString*)(((char*)(d) + memberOffset(2))))" />
        <Intrinsic Name="host" Expression="*((QString*)(((char*)(d) + memberOffset(3))))" />
        <Intrinsic Name="path" Expression="*((QString*)(((char*)(d) + memberOffset(4))))" />
        <Intrinsic Name="query" Expression="*((QString*)(((char*)(d) + memberOffset(5))))" />
        <Intrinsic Name="fragment" Expression="*((QString*)(((char*)(d) + memberOffset(6))))" />
        <DisplayString Condition="!isEmpty(scheme().d-&gt;size)">{scheme()}://{host()}{path()}</DisplayString>
        <DisplayString Condition="isEmpty(scheme().d-&gt;size)">{path()}</DisplayString>
        <Expand>
            <Item Name="[scheme]">scheme()</Item>
            <Item Name="[username]">username()</Item>
            <Item Name="[password]">password()</Item>
            <Item Name="[host]">host()</Item>
            <Item Name="[path]">path()</Item>
            <Item Name="[query]">query()</Item>
            <Item Name="[fragment]">fragment()</Item>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QDate">
        <DisplayString>{{ julian day = {jd} }}</DisplayString>
    </Type>
   <Type Name="##NAMESPACE##::QTime">
        <Intrinsic Name="hour" Expression="mds / 3600000" />
        <Intrinsic Name="minute" Expression="(mds % 3600000) / 60000" />
        <Intrinsic Name="second" Expression="(mds / 1000) % 60" />
        <Intrinsic Name="millisecond" Expression="mds % 1000" />
        <DisplayString Condition="mds == 1">{{ millisecond = {mds} }}</DisplayString>
        <DisplayString Condition="mds != 1">{{ milliseconds = {mds} }}</DisplayString>
        <Expand>
            <Item Name="[hour]"
                  Condition="(mds / 3600000) == 1">hour(), d</Item>
            <Item Name="[hours]"
                  Condition="(mds / 3600000) != 1">hour(), d</Item>
            <Item Name="[minute]"
                  Condition="((mds % 3600000) / 60000) == 1">minute(), d</Item>
            <Item Name="[minutes]"
                  Condition="((mds % 3600000) / 60000) != 1">minute(), d</Item>
            <Item Name="[second]"
                  Condition="((mds / 1000) % 60) == 1">second(), d</Item>
            <Item Name="[seconds]"
                  Condition="((mds / 1000) % 60) != 1">second(), d</Item>
            <Item Name="[millisecond]"
                  Condition="(mds % 1000) == 1">millisecond(), d</Item>
            <Item Name="[milliseconds]"
                  Condition="(mds % 1000) != 1">millisecond(), d</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QPair&lt;*,*&gt;">
        <DisplayString>({first}, {second})</DisplayString>
        <Expand>
            <Item Name="[first]">first</Item>
            <Item Name="[second]">second</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QList&lt;*&gt;">
        <AlternativeType Name="##NAMESPACE##::QVector&lt;*&gt;"/>
        <DisplayString>{{ size={d.size} }}</DisplayString>
        <Expand>
            <ArrayItems>
                <Size>d.size</Size>
                <ValuePointer>reinterpret_cast&lt;$T1*&gt;(d.ptr)</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QVarLengthArray&lt;*&gt;">
        <DisplayString>{{ size={s} }}</DisplayString>
        <Expand>
            <Item Name="[capacity]">a</Item>
            <ArrayItems>
                <Size>s</Size>
                <ValuePointer>ptr</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QMap&lt;*,*&gt;">
        <AlternativeType Name="##NAMESPACE##::QMultiMap&lt;*,*&gt;"/>
        <DisplayString>{{ size={d.d-&gt;m._Mypair._Myval2._Myval2._Mysize} }}</DisplayString>
        <Expand>
            <Item Name="[std::map]">d.d-&gt;m</Item>
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QHash&lt;*,*&gt;">
        <AlternativeType Name="##NAMESPACE##::QMultiHash&lt;*,*&gt;"/>
        <DisplayString>{{ size = {d-&gt;size} }}</DisplayString>
        <Expand>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
        </Expand>
    </Type>
</AutoVisualizer>
QtMSBuild/QtMsBuild/qt_defaults.props
@@ -71,6 +71,9 @@
    <QtPathBinaries>bin</QtPathBinaries>
    <QtPathLibraryExecutables>bin</QtPathLibraryExecutables>
    <!--// Run Qt tools during design-time build -->
    <QtToolsDesignTime>true</QtToolsDesignTime>
    <!--// qmake template -->
    <QtQMakeTemplate>vcapp</QtQMakeTemplate>
@@ -89,6 +92,7 @@
      DEFINES=/Project/ItemDefinitionGroup/ClCompile/PreprocessorDefinitions;
      INCLUDEPATH=/Project/ItemDefinitionGroup/ClCompile/AdditionalIncludeDirectories;
      STDCPP=/Project/ItemDefinitionGroup/ClCompile/LanguageStandard;
      RUNTIME=/Project/ItemDefinitionGroup/ClCompile/RuntimeLibrary;
      CL_OPTIONS=/Project/ItemDefinitionGroup/ClCompile/AdditionalOptions;
      LIBS=/Project/ItemDefinitionGroup/Link/AdditionalDependencies;
      LINK_OPTIONS=/Project/ItemDefinitionGroup/Link/AdditionalOptions;
@@ -103,6 +107,9 @@
    <!--// Qt build config -->
    <QtBuildConfig Condition="'$(Configuration)' == 'Debug'">debug</QtBuildConfig>
    <QtBuildConfig Condition="'$(Configuration)' != 'Debug'">release</QtBuildConfig>
    <!--// Qt Plugin default-->
    <QtPlugin>false</QtPlugin>
  </PropertyGroup>
  <!--
QtMSBuild/QtMsBuild/qt_globals.targets
@@ -84,6 +84,29 @@
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Set up inner build if Qt vars property file is outdated
  // -->
  <PropertyGroup Condition="'$(QtInnerBuild)' == ''">
    <QtVarsOutdated>false</QtVarsOutdated>
    <QtVarsOutdated
      Condition="!Exists('$(QtVarsFilePath)')
        OR '$(QtBkup_QtInstall)'                != '$(QtInstall)'
        OR '$(QtBkup_QtModules)'                != '$(QtModules)'
        OR '$(QtBkup_QtPathBinaries)'           != '$(QtPathBinaries)'
        OR '$(QtBkup_QtPathLibraryExecutables)' != '$(QtPathLibraryExecutables)'
        OR '$(QtBkup_QtHeaderSearchPath)'       != '$(QtHeaderSearchPath)'
        OR '$(QtBkup_QtLibrarySearchPath)'      != '$(QtLibrarySearchPath)'
        OR '$(QtBkup_QtVars)'                   != '$(QtVars)'
        OR '$(QtBkup_QMakeCodeLines)'           != '$(QMakeCodeLines)'
        OR '$(QtBkup_QtBuildConfig)'            != '$(QtBuildConfig)'"
    >true</QtVarsOutdated>
  </PropertyGroup>
  <!-- // Enable inner build targets -->
  <Import Condition="'$(QtVarsOutdated)' == 'true'" Project="qt_inner.targets"/>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// TARGET QtGetDefaultClCompile
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Get default C++ properties
@@ -201,7 +224,8 @@
               OR ('$(project_changed)' == 'true' AND '$(log_hash)' != '$(work_hash)')"
        >true</do_work>
      <skip_work
        Condition="'$(do_work)' != 'true' OR '$(DesignTimeBuild)' == 'true'"
        Condition="'$(do_work)' != 'true'
          OR ('$(QtDesignTimeBuild)' == 'true' AND '$(QtToolsDesignTime)' != 'true')"
        >true</skip_work>
    </PropertyGroup>
@@ -268,8 +292,7 @@
    // -->
    <QtRunWork
      Condition="'$(ApplicationType)' != 'Linux' AND '@(QtWork)' != ''
        AND '%(QtWork.ParallelBuild)' == 'true'
        AND '$(DesignTimeBuild)' != 'true'"
        AND '%(QtWork.ParallelBuild)' == 'true'"
      QtWork="@(QtWork)" QtMaxProcs="$(QtMaxProcs)" QtDebug="$(QtDebug)">
      <Output TaskParameter="Result" ItemName="QtWorkResult" />
    </QtRunWork>
@@ -280,8 +303,7 @@
    // -->
    <QtRunWork
      Condition="'$(ApplicationType)' != 'Linux' AND '@(QtWork)' != ''
        AND '%(QtWork.ParallelBuild)' != 'true'
        AND '$(DesignTimeBuild)' != 'true'"
        AND '%(QtWork.ParallelBuild)' != 'true'"
      QtWork="@(QtWork)" QtMaxProcs="1" QtDebug="$(QtDebug)">
      <Output TaskParameter="Result" ItemName="QtWorkResult" />
    </QtRunWork>
@@ -293,13 +315,13 @@
    <!-- // Translate local paths to host paths -->
    <Flatten
      Condition="'$(ApplicationType)' == 'Linux'
        AND '@(QtWork)' != '' AND '$(DesignTimeBuild)' != 'true'"
        AND '@(QtWork)' != '' AND '$(QtDesignTimeBuild)' != 'true'"
      Items="@(QtWork)" Metadata="ResourceFiles">
      <Output TaskParameter="Result" ItemName="ResourceFiles"/>
    </Flatten>
    <ItemGroup
      Condition="'$(ApplicationType)' == 'Linux'
        AND '@(QtWork)' != '' AND '$(DesignTimeBuild)' != 'true'">
        AND '@(QtWork)' != '' AND '$(QtDesignTimeBuild)' != 'true'">
      <LocalPath Include="%(QtWork.Identity)">
        <Name>InputPath</Name>
        <Item>%(QtWork.Identity)</Item>
@@ -320,7 +342,7 @@
    </ItemGroup>
    <HostTranslatePaths
      Condition="'$(ApplicationType)' == 'Linux'
        AND '@(QtWork)' != '' AND '$(DesignTimeBuild)' != 'true'"
        AND '@(QtWork)' != '' AND '$(QtDesignTimeBuild)' != 'true'"
      Items="@(LocalPath)" Names="InputPath;OutputPath">
      <Output TaskParameter="Result" ItemName="HostPath"/>
    </HostTranslatePaths>
@@ -332,7 +354,7 @@
    <!-- // Run command -->
    <HostExec
      Condition="'$(ApplicationType)' == 'Linux'
        AND '%(Identity)' != '' AND '$(DesignTimeBuild)' != 'true'"
        AND '%(Identity)' != '' AND '$(QtDesignTimeBuild)' != 'true'"
      Message="@(QtWork->'%(WorkType) %(Identity)')"
      Command="@(QtWork->'%(ToolPath) %(Options)')"
      Inputs="@(InputPath)"
@@ -344,7 +366,7 @@
    <!-- // Generate result item -->
    <ItemGroup
      Condition="'$(ApplicationType)' == 'Linux'
        AND '@(QtWork)' != '' AND '$(DesignTimeBuild)' != 'true'">
        AND '@(QtWork)' != '' AND '$(QtDesignTimeBuild)' != 'true'">
      <QtWorkResult Include="@(QtWork)">
        <ExitCode>0</ExitCode>
      </QtWorkResult>
@@ -354,9 +376,11 @@
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Save tracking log of files read during build; used by VS to check the up-to-date status
    // -->
    <ItemGroup Condition="'$(DesignTimeBuild)' != 'true' AND '$(QtVSToolsBuild)' != 'true'">
    <ItemGroup>
      <read_log Include="^%(QtWorkResult.FullPath);%(QtWorkResult.AdditionalDependencies)"
        Condition="'%(QtWorkResult.ExitCode)' == '0' AND '%(QtWorkResult.DisableLog)' != 'true'">
        Condition="'%(QtWorkResult.ExitCode)' == '0'
               AND '%(QtWorkResult.DisableLog)' != 'true'
               AND '%(QtWorkResult.Skipped)' != 'true'">
        <WorkType>%(QtWorkResult.WorkType)</WorkType>
      </read_log>
      <read_log>
@@ -377,9 +401,11 @@
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Save tracking log of files written during build; used by VS to check the up-to-date status
    // -->
    <ItemGroup Condition="'$(DesignTimeBuild)' != 'true' AND '$(QtVSToolsBuild)' != 'true'">
    <ItemGroup>
      <write_log Include="^%(QtWorkResult.FullPath);%(QtWorkResult.OutputFile)"
        Condition="'%(QtWorkResult.ExitCode)' == '0' AND '%(QtWorkResult.DisableLog)' != 'true'">
        Condition="'%(QtWorkResult.ExitCode)' == '0'
               AND '%(QtWorkResult.DisableLog)' != 'true'
               AND '%(QtWorkResult.Skipped)' != 'true'">
        <WorkType>%(QtWorkResult.WorkType)</WorkType>
      </write_log>
      <write_log>
@@ -398,7 +424,7 @@
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Log output files; this is used by VS to determine what files to delete on "Clean"
    // -->
    <ItemGroup Condition="'$(DesignTimeBuild)' != 'true' AND '$(QtVSToolsBuild)' != 'true'">
    <ItemGroup>
      <clean_log Include="%(QtWorkResult.OutputFile)"
        Condition="'%(QtWorkResult.ExitCode)' == '0'">
        <Source>@(QtWorkResult, '|')</Source>
@@ -414,7 +440,7 @@
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Log calls to Qt tools; used in QtWorkPrepare to detect changes to the options of Qt tools
    // -->
    <WriteLinesToFile Condition="'@(QtWorkLog)' != '' AND '$(DesignTimeBuild)' != 'true'"
    <WriteLinesToFile Condition="'@(QtWorkLog)' != ''"
      File="$(QtLogFilePath)"
      Lines="@(QtWorkLog->'%(Identity)|%(Hash)')"
      Overwrite="true" Encoding="Unicode"/>
@@ -425,8 +451,7 @@
    // -->
    <Error
      Condition="'%(QtWorkResult.ExitCode)' != ''
        AND '%(QtWorkResult.ExitCode)' != '0'
        AND '$(DesignTimeBuild)' != 'true'"
        AND '%(QtWorkResult.ExitCode)' != '0'"
      File="%(QtWorkResult.Identity)" Code="%(QtWorkResult.ExitCode)"
      Text="%(QtWorkResult.WorkType) (%(QtWorkResult.ToolPath))"/>
@@ -513,6 +538,7 @@
    // Clean-up
    // -->
    <ItemGroup>
      <ClCompile    Remove="DefaultClCompile" />
      <QtWork       Remove="@(QtWork)"/>
      <QtWorkResult Remove="@(QtWorkResult)"/>
      <QtWorkLog    Remove="@(QtWorkLog)"/>
@@ -524,51 +550,158 @@
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// TARGET Qt
  /// TARGET QtSetAdditionalOptions
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Root Qt target
  // Adds additional compiler options flowing from Qt.
  // Skips options that are already specified in the user project.
  // -->
  <Target Name="Qt" DependsOnTargets="QtPrepare;QtWork" BeforeTargets="FixupCLCompileOptions">
  <Target Name="QtSetAdditionalOptions" Condition="'@(ClCompile)' != ''">
    <!-- Command line parser regex -->
    <PropertyGroup>
      <CmdLineParser>"[^"]*"|[^\s]+</CmdLineParser>
    </PropertyGroup>
    <!-- Parse compiler options from Qt -->
    <PropertyGroup>
      <QtCmdLine
          >$([System.Text.RegularExpressions.Regex]::Matches(
            '$(Qt_CL_OPTIONS_)', '$(CmdLineParser)'))</QtCmdLine>
    </PropertyGroup>
    <!-- Calculate command line for each source file in the project -->
    <QtRunTask
      Items="@(ClCompile)"
      AssemblyPath="$(VCTargetsPath)\Microsoft.Build.CPPTasks.Common.dll"
      TaskName="Microsoft.Build.CPPTasks.CLCommandLine"
      TaskInput="Sources"
      TaskOutput="CommandLines"
      NewMetadata="ProjectOptions">
      <Output TaskParameter="Result" ItemName="ClCompile_CmdLine" />
    </QtRunTask>
    <!-- Append excluded Qt options to calculated command line -->
    <ItemGroup>
      <ClCompile Remove="DefaultClCompile" />
      <ClCompile_CmdLine Condition="'$(QtExcludedOptions)' != ''">
        <ProjectOptions>%(ProjectOptions) $(QtExcludedOptions)</ProjectOptions>
      </ClCompile_CmdLine>
    </ItemGroup>
    <CriticalSection Lock="false" Name="$(ProjectGuid)" />
    <OnError ExecuteTargets="QtLeaveCriticalSection_OnError"/>
    <!-- Parse compiler command line of each source file -->
    <ItemGroup>
      <ClCompile_CmdLine>
        <ProjectOptions
          >$([System.Text.RegularExpressions.Regex]::Matches(
            '%(ProjectOptions)', '$(CmdLineParser)'))</ProjectOptions>
      </ClCompile_CmdLine>
    </ItemGroup>
    <!-- Add (previously parsed) Qt options to each source file -->
    <ItemGroup>
      <ClCompile_CmdLine>
        <QtOptions>$(QtCmdLine)</QtOptions>
      </ClCompile_CmdLine>
    </ItemGroup>
    <!-- Result of parsing command lines, per source file:
          * ClCompile_CmdLine ::= (Item x ProjectOptions x QtOptions), where:
             * Item ::= source file
             * ProjectOptions ::= list of tokens from the compiler command line for Item
             * QtOptions ::= list of tokens from the Qt additional options -->
    <!-- Flatten results into a list of tokens per source file:
          * ClOptions ::= (Item x Name x Value), where:
             * Item  ::= source file
             * Name  ::= token origin: from compiler command line or from Qt
             * Value ::= token value -->
    <Flatten Items="@(ClCompile_CmdLine)" Metadata="ProjectOptions;QtOptions">
      <Output TaskParameter="Result" ItemName="ClOptions" />
    </Flatten>
    <!-- Remove non-switch tokens, i.e. tokens that do not start with '/' or '-' -->
    <ItemGroup>
      <ClOptions
        Remove="@(ClOptions)"
        Condition="!$([System.String]::Copy('%(Value)').StartsWith('-'))
               AND !$([System.String]::Copy('%(Value)').StartsWith('/'))"/>
    </ItemGroup>
    <!-- Calculate option id: token without leading '/' or '-', and without trailing '-' -->
    <ItemGroup>
      <ClOptions>
        <OptionId>$([System.String]::Copy('%(Value)').Substring(1).TrimEnd('-'))</OptionId>
      </ClOptions>
    </ItemGroup>
    <!-- Split into list of Qt options and list of project options -->
    <ItemGroup>
      <QtOptions
        Include="@(ClOptions->'%(Item)')"
        Condition="'%(ClOptions.Name)' == 'QtOptions'"/>
      <ProjectOptions
        Include="@(ClOptions->'%(Item)')"
        Condition="'%(ClOptions.Name)' == 'ProjectOptions'"/>
    </ItemGroup>
    <!-- Find conflicting options, i.e. defined both in Qt options and project options -->
    <ItemGroup>
      <QtOptions
        Condition="'@(QtOptions)' != ''
               AND '@(ProjectOptions)' != ''
               AND '%(Item)' != ''
               AND '%(OptionId)' != ''">
        <Conflict>true</Conflict>
      </QtOptions>
    </ItemGroup>
    <!-- Set additional compiler options for all source files -->
    <ItemGroup>
      <ClCompile Condition="'%(Identity)' != '' AND '%(QtOptions.Conflict)' != 'true'">
        <AdditionalOptions
          >@(QtOptions->'%(Value)', ' ') @(ClCompile->'%(AdditionalOptions)')</AdditionalOptions>
      </ClCompile>
      <ClCompile Condition="'%(AdditionalOptions)' != ''">
        <AdditionalOptions
          >$([System.String]::Copy('%(AdditionalOptions)').Trim())</AdditionalOptions>
      </ClCompile>
    </ItemGroup>
    <!-- Print result to build log, if requested -->
    <Message
      Condition="'$(QtOptionsBuildLog)' == 'true'"
      Importance="High"
      Text=" Qt - Additional Compiler Options"/>
    <Message
      Condition="'$(QtOptionsBuildLog)' == 'true'
             AND '%(Identity)' != '' AND '%(QtOptions.Conflict)' != 'true'"
      Importance="High"
      Text="    [%(Identity)]: @(QtOptions->'%(Value)', ' ')"/>
    <!-- Clean-up -->
    <PropertyGroup>
      <CmdLineParser/>
      <QtCmdLine/>
    </PropertyGroup>
    <ItemGroup>
      <ClCompile_CmdLine Remove="@(ClCompile_CmdLine)"/>
      <ClOptions Remove="@(ClOptions)"/>
      <ProjectOptions Remove="@(ProjectOptions)"/>
      <QtOptions Remove="@(QtOptions)"/>
    </ItemGroup>
  </Target>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// TARGET QtOuterBuild
  /// TARGET Qt
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Run targets in $(QtOuterBuildDependsOn) and then recursively invoke build
  // Root Qt target
  // -->
  <Target Name="QtOuterBuild" DependsOnTargets="$(QtOuterBuildDependsOn)">
    <!--// Invoke inner build: recursive build in second MSBuild instance -->
    <MSBuild
      Projects="$(MSBuildProjectFullPath)"
      Targets="Build"
      Properties="QtInnerBuild=$(MSBuildProjectFullPath);RandomFileName=$(RandomFileName);BuildProjectReferences=false">
    </MSBuild>
  <Target
    Name="Qt"
    DependsOnTargets="QtPrepare;QtWork;QtSetAdditionalOptions">
    <CriticalSection Lock="false" Name="$(ProjectGuid)" />
    <OnError ExecuteTargets="QtLeaveCriticalSection_OnError"/>
  </Target>
  <PropertyGroup
    Condition="'$(QtInnerBuild)' == '' AND '$(DesignTimeBuild)' != 'true'">
    <!--// Outer build: invoke inner build -->
    <BuildDependsOn>
      $(QtOuterBuildPrepare);
      QtOuterBuild;
      $(QtOuterBuildFinalize)
    </BuildDependsOn>
    <QtInnerBuild>$(MSBuildProjectFullPath)</QtInnerBuild>
    <RandomFileName>$([System.IO.Path]::GetRandomFileName())</RandomFileName>
  </PropertyGroup>
  <PropertyGroup
    Condition="'$(QtInnerBuild)' != '$(MSBuildProjectFullPath)' AND '$(DesignTimeBuild)' != 'true'">
    <!--// Dependent project inner build: skip build -->
    <BuildDependsOn>$(QtOuterBuildPrepare);$(QtOuterBuildFinalize)</BuildDependsOn>
  </PropertyGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
@@ -633,4 +766,28 @@
  <Target Name="QtLeaveCriticalSection_OnError">
    <CriticalSection Lock="false" Name="$(ProjectGuid)" />
  </Target>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// TARGET QtNatvis
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Copies the .natvis file matching the Qt version and replaces the namespace placeholder
  // -->
  <Target Name="QtNatvis" BeforeTargets="Link"
          Condition="'$(Configuration)' == 'Debug' AND '$(LinkNatvisFile)' == 'true'"
          Inputs="$(MSBuildProjectFile);$(QtMsBuild)\qt$(QtVersionMajor).natvis.xml"
          Outputs="$(IntDir)\qt.natvis">
    <PropertyGroup>
      <InputFile>$(QtMsBuild)\qt$(QtVersionMajor).natvis.xml</InputFile>
    </PropertyGroup>
    <WriteLinesToFile Condition="'$(QtNamespace)' == ''"
      Overwrite="true"
      File="$(IntDir)\qt.natvis"
      Lines="$([System.IO.File]::ReadAllText($(InputFile)).Replace('##NAMESPACE##::',''))" />
    <WriteLinesToFile Condition="'$(QtNamespace)' != ''"
      Overwrite="true"
      File="$(IntDir)\qt.natvis"
      Lines="$([System.IO.File]::ReadAllText($(InputFile)).Replace('##NAMESPACE##','$(QtNamespace)'))" />
  </Target>
</Project>
QtMSBuild/QtMsBuild/qt_inner.targets
New file
@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup
    Condition="'$(QtInnerBuild)' == ''">
    <!--// Outer build: invoke inner build -->
    <BuildDependsOn>
      $(QtOuterBuildPrepare);
      QtOuterBuild;
      $(QtOuterBuildFinalize)
    </BuildDependsOn>
    <QtInnerBuild>$(MSBuildProjectFullPath)</QtInnerBuild>
    <RandomFileName>$([System.IO.Path]::GetRandomFileName())</RandomFileName>
  </PropertyGroup>
  <PropertyGroup
    Condition="'$(QtInnerBuild)' != '$(MSBuildProjectFullPath)'">
    <!--// Dependent project inner build: skip build -->
    <BuildDependsOn>$(QtOuterBuildPrepare);$(QtOuterBuildFinalize)</BuildDependsOn>
  </PropertyGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// TARGET QtOuterBuild
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Run targets in $(QtOuterBuildDependsOn) and then recursively invoke build
  // -->
  <Target Name="QtOuterBuild" DependsOnTargets="$(QtOuterBuildDependsOn)">
    <!--// Invoke inner build: recursive build in second MSBuild instance -->
    <MSBuild
      Projects="$(MSBuildProjectFullPath)"
      Targets="Build"
      Properties="QtInnerBuild=$(MSBuildProjectFullPath);RandomFileName=$(RandomFileName);BuildProjectReferences=false">
    </MSBuild>
    <OnError ExecuteTargets="QtLeaveCriticalSection_OnError"/>
  </Target>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// TARGET GetClCommandLineForReference
  /////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // -->
  <Target
    Name="GetClCommandLineForReference"
    DependsOnTargets="$(QtOuterBuildDependsOn)"
    Returns="@(ClCommandLineForReference)">
    <MSBuild
      Projects="$(MSBuildProjectFullPath)"
      Targets="GetClCommandLineForReference"
      Properties="QtInnerBuild=$(MSBuildProjectFullPath);RandomFileName=$(RandomFileName);BuildProjectReferences=false">
      <Output TaskParameter="TargetOutputs" ItemName="ClCommandLineForReference"/>
    </MSBuild>
  </Target>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// TARGET GetGeneratedFiles
  /////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // -->
  <Target
    Name="GetGeneratedFiles"
    DependsOnTargets="$(QtOuterBuildDependsOn)"
    Returns="@(_GeneratedFiles)">
    <MSBuild
      Projects="$(MSBuildProjectFullPath)"
      Targets="GetGeneratedFiles"
      Properties="QtInnerBuild=$(MSBuildProjectFullPath);RandomFileName=$(RandomFileName);BuildProjectReferences=false">
      <Output TaskParameter="TargetOutputs" ItemName="_GeneratedFiles"/>
    </MSBuild>
  </Target>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// TARGET GetProjectReferencesInfo
  /////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // -->
  <Target
    Name="GetProjectReferencesInfo"
    DependsOnTargets="$(QtOuterBuildDependsOn)"
    Returns="@(_ProjectReferencesInfo)">
    <MSBuild
      Projects="$(MSBuildProjectFullPath)"
      Targets="GetProjectReferencesInfo"
      Properties="QtInnerBuild=$(MSBuildProjectFullPath);RandomFileName=$(RandomFileName);BuildProjectReferences=false">
      <Output TaskParameter="TargetOutputs" ItemName="_ProjectReferencesInfo"/>
    </MSBuild>
  </Target>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// TARGET GetClCommandLines
  /////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // -->
  <Target
    Name="GetClCommandLines"
    DependsOnTargets="$(QtOuterBuildDependsOn)"
    Returns="@(ClCommandLines)">
    <MSBuild
      Projects="$(MSBuildProjectFullPath)"
      Targets="GetClCommandLines"
      Properties="QtInnerBuild=$(MSBuildProjectFullPath);RandomFileName=$(RandomFileName);BuildProjectReferences=false">
      <Output TaskParameter="TargetOutputs" ItemName="ClCommandLines"/>
    </MSBuild>
  </Target>
</Project>
QtMSBuild/QtMsBuild/qt_private.props
@@ -57,6 +57,18 @@
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Check if design-time build
  // -->
  <PropertyGroup>
    <QtDesignTimeBuild>false</QtDesignTimeBuild>
    <QtDesignTimeBuild
      Condition="'$(DesignTimeBuild)' == 'true'
              OR '$(QtVSToolsBuild)'  == 'true'"
      >true</QtDesignTimeBuild>
  </PropertyGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Setup Qt installation path
  // -->
  <PropertyGroup Condition="'$(QtVsProjectSettings)' == 'true'">
@@ -112,21 +124,29 @@
      >$([System.String]::Copy($([System.IO.File]::ReadAllText('$(QtVarsIndexPathDesignTime)'))).Replace('&#xD;&#xA;',''))</QtVarsDesignTime>
  </PropertyGroup>
  <!--// Import Qt variables (full build) -->
  <Import
    Condition="'$(DesignTimeBuild)' != 'true' AND Exists('$(QtVarsFilePath)')"
    Project="$(QtVarsFilePath)"/>
  <PropertyGroup>
    <!--// Path to Qt variables .props: full build -->
    <QtVarsImportPath
      Condition="'$(QtDesignTimeBuild)' != 'true'
        AND Exists('$(QtVarsFilePath)')"
      >$(QtVarsFilePath)</QtVarsImportPath>
  <!--// Import Qt variables (design-time build) -->
  <Import
    Condition="'$(DesignTimeBuild)' == 'true' AND Exists('$(QtVarsDesignTime)')"
    Project="$(QtVarsDesignTime)"/>
    <!--// Path to Qt variables .props: design-time build -->
    <QtVarsImportPath
      Condition="'$(QtDesignTimeBuild)' == 'true'
        AND Exists('$(QtVarsDesignTime)')"
      >$(QtVarsDesignTime)</QtVarsImportPath>
  <!--// Import Qt variables (fall-back) -->
  <Import
    Condition=
"'$(DesignTimeBuild)' == 'true' AND !Exists('$(QtVarsDesignTime)') AND Exists('$(QtVarsFilePath)')"
    Project="$(QtVarsFilePath)"/>
    <!--// Path to Qt variables .props: fall-back -->
    <QtVarsImportPath
      Condition="'$(QtDesignTimeBuild)' == 'true'
        AND !Exists('$(QtVarsDesignTime)')
        AND  Exists('$(QtVarsFilePath)')"
      >$(QtVarsFilePath)</QtVarsImportPath>
  </PropertyGroup>
  <!--// Import Qt vars property file -->
  <Import Condition="Exists('$(QtVarsImportPath)')" Project="$(QtVarsImportPath)"/>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
@@ -175,6 +195,61 @@
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Normalize QtVars (incl. backup)
  // -->
  <PropertyGroup>
    <QtVars
      Condition="'$(QtVars)' !=''"
      >$(QtVars
        .Replace(' ', '')
        .Replace('%0a', '')
        .Replace('%0d', '')
        .Trim(';'))</QtVars>
    <QtBkup_QtVars
      Condition="'$(QtBkup_QtVars)' !=''"
      >$(QtBkup_QtVars
        .Replace(' ', '')
        .Replace('%0a', '')
        .Replace('%0d', '')
        .Trim(';'))</QtBkup_QtVars>
  </PropertyGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Figure out depending on the user settings if we need to link the .natvis file into the PDB
  // file. Reads the content of qconfig.pri to find Qt's namespace if set. Predefines the path to
  // the .natvis file used during link which corresponds to the final output generated by the
  // QtNatvis target.
  // Evaluation order: first look at the project settings, if empty take from the global settings.
  // -->
  <PropertyGroup>
    <LinkNatvisFile>$(QtLinkNatvisFile)</LinkNatvisFile>
  </PropertyGroup>
  <PropertyGroup Condition="'$(QtLinkNatvisFile)' == ''">
    <LinkNatvisRegValue>$([MSBuild]::GetRegistryValue(
      'HKEY_CURRENT_USER\SOFTWARE\Digia\Qt5VS2017','LinkNatvis'
      ))</LinkNatvisRegValue>
    <LinkNatvisFile>true</LinkNatvisFile>
    <LinkNatvisFile Condition="'$(LinkNatvisRegValue)' == '0'">false</LinkNatvisFile>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)' == 'Debug'
                            AND '$(LinkNatvisFile)' == 'true' AND '$(QtInstallDir)' != ''">
    <QConfigPriPath>$([System.IO.Path]::Combine('$(QtInstallDir)',
        'mkspecs', 'qconfig.pri'
      ))</QConfigPriPath>
    <QConfigPriContent>$([MSBuild]::Unescape(
      $([System.IO.File]::ReadAllText('$(QConfigPriPath)')
        .Replace('&#xD;&#xA;', ';')
        .Replace(' =', '=')
        .Replace('= ', '='))
      ))</QConfigPriContent>
    <QtNamespace>$([System.Text.RegularExpressions.Regex]::Match($(QConfigPriContent),
        'QT_NAMESPACE=([a-zA-Z0-9]+)').get_Groups().get_Item(1)
      )</QtNamespace>
  </PropertyGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Default item metadata
  // -->
  <ItemDefinitionGroup>
@@ -186,24 +261,27 @@
      <PreprocessorDefinitions Condition="'$(QtQMLDebugEnable)' == 'true'"
        >QT_QML_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories Condition="'$(Qt_INCLUDEPATH_)' != ''"
        >$(Qt_INCLUDEPATH_);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
        >%(AdditionalIncludeDirectories);$(Qt_INCLUDEPATH_)</AdditionalIncludeDirectories>
      <LanguageStandard Condition="'$(Qt_STDCPP_)' != ''"
        >$(Qt_STDCPP_)</LanguageStandard>
      <AdditionalOptions Condition="'$(Qt_CL_OPTIONS_)' != ''"
        >$(Qt_CL_OPTIONS_) %(AdditionalOptions)</AdditionalOptions>
      <RuntimeLibrary Condition="'$(Qt_RUNTIME_)' != ''"
        >$(Qt_RUNTIME_)</RuntimeLibrary>
    </ClCompile>
    <!--// Linker (.obj files) -->
    <Link>
      <AdditionalDependencies Condition="'$(Qt_LIBS_)' != ''"
        >$(Qt_LIBS_);%(AdditionalDependencies)</AdditionalDependencies>
        >%(AdditionalDependencies);$(Qt_LIBS_)</AdditionalDependencies>
      <AdditionalLibraryDirectories Condition="'$(Qt_LIBPATH_)' != ''"
        >$(Qt_LIBPATH_);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
        >%(AdditionalLibraryDirectories);$(Qt_LIBPATH_)</AdditionalLibraryDirectories>
      <SharedLibrarySearchPath Condition="'$(Qt_LIBPATH_)' != ''"
        >$(Qt_LIBPATH_);%(SharedLibrarySearchPath)</SharedLibrarySearchPath>
        >%(SharedLibrarySearchPath);$(Qt_LIBPATH_)</SharedLibrarySearchPath>
      <AdditionalOptions Condition="'$(Qt_LINK_OPTIONS_)' != ''"
        >$(Qt_LINK_OPTIONS_) %(AdditionalOptions)</AdditionalOptions>
      <AdditionalOptions Condition="'$(Configuration)' == 'Debug' AND '$(LinkNatvisFile)' == 'true'"
        >/NATVIS:&quot;$(IntDir)\qt.natvis&quot; %(AdditionalOptions)</AdditionalOptions>
    </Link>
  </ItemDefinitionGroup>
  <!--
QtMSBuild/QtMsBuild/qt_settings.xml
@@ -43,6 +43,8 @@
    <Category Name="QtSettings_02_Paths" DisplayName="Paths"/>
    <Category Name="QtSettings_03_QMake" DisplayName="qmake"/>
    <Category Name="QtSettings_04_QML" DisplayName="QML"/>
    <Category Name="QtSettings_05_AdditionalOptions" DisplayName="Qt Additional Compiler Options"/>
    <Category Name="QtSettings_06_AdditionalLinkOptions" DisplayName="Qt Additional Linker Options"/>
  </Rule.Categories>
  <EnumProperty
    Name="Keyword"
@@ -85,6 +87,12 @@
      <ValueEditor EditorType="QtModulesEditor" DisplayName="&lt;Select Modules...&gt;" />
    </StringListProperty.ValueEditors>
  </StringListProperty>
  <BoolProperty
    Name="QtPlugin"
    Category="QtSettings_01_General"
    DisplayName="Qt Plugin"
    Description="Select whether this project is used to generate a Qt plugin.">
  </BoolProperty>
  <EnumProperty
    Name="QtBuildConfig"
    Category="QtSettings_01_General"
@@ -98,6 +106,14 @@
    Category="QtSettings_01_General"
    DisplayName="Run Deployment Tool"
    Description="Select whether Qt for Windows Deployment Tool (windeployqt) should be called after build."/>
  <EnumProperty
    Name="QtToolsDesignTime"
    Category="QtSettings_01_General"
    DisplayName="Design Time Build"
    Description="Run Qt tools during IntelliSense builds.">
    <EnumValue Name="true" DisplayName="Run Qt Tools"/>
    <EnumValue Name="false" DisplayName="Skip Qt Tools"/>
  </EnumProperty>
  <StringProperty
    Name="QtPathBinaries"
    Category="QtSettings_02_Paths"
@@ -145,4 +161,38 @@
    DisplayName="Enable QML Debugging"
    Description="Select whether to launch a QML session when debugging.">
  </BoolProperty>
</Rule>
  <StringProperty
    Name="Qt_CL_OPTIONS_"
    ReadOnly="true"
    Category="QtSettings_05_AdditionalOptions"
    DisplayName="Additional Options"
    Description="
Additional compiler options required by Qt. These options will be passed
to the compiler, unless specifically excluded in the next field." />
  <StringProperty
    Name="QtExcludedOptions"
    Category="QtSettings_05_AdditionalOptions"
    DisplayName="Excluded Options"
    Description="
Options to exclude from the above compiler options required by Qt.
These options will NOT be passed to the compiler. Prefix options
with '/' or '-', and separate them with spaces." />
  <BoolProperty
    Name="QtOptionsBuildLog"
    Category="QtSettings_05_AdditionalOptions"
    DisplayName="Show in Build Log"
    Description="
Print to the build log the list of additional options passed to the compiler." />
  <EnumProperty
    Name="QtLinkNatvisFile"
    Category="QtSettings_06_AdditionalLinkOptions"
    DisplayName="Embed .natvis file into PDB"
    Description=
"Embeds the debugger visualizations (.natvis file) into the PDB file
generated by LINK. While setting this option, the embedded Natvis file
will take precedence over user-specific Natvis files (for example the
files located in %USERPROFILE%\\Documents\\Visual Studio 2022\\Visualizers).">
    <EnumValue Name="false" DisplayName="No" />
    <EnumValue Name="true" DisplayName="Yes" />
  </EnumProperty>
  </Rule>
QtMSBuild/QtMsBuild/qt_vars.targets
@@ -199,7 +199,8 @@
    <ItemGroup>
      <QMakeArgsList Condition="'$(QMakeOptionEarly)' == 'true'" Include="-early"/>
      <QMakeArgsList Include="CONFIG -= debug release debug_and_release"/>
      <QMakeArgsList Include="CONFIG += $(QtBuildConfig)"/>
      <QMakeArgsList Include="CONFIG += $(QtBuildConfig) warn_off"/>
      <QMakeArgsList Condition="'$(QtPlugin)' == 'true'" Include="CONFIG += plugin"/>
      <QMakeArgsList Include="$(QMakeExtraArgs)"/>
    </ItemGroup>
    <ItemGroup>
@@ -281,7 +282,32 @@
        $(QtVarsProFileInput)
        DEFINES -= UNICODE _UNICODE
      </QtVarsProFileInput>
      <!--
# Add dummy QML object if needed -->
      <QtVarsProFileInput Condition="$(QtModules.Contains('quick'))">
        $(QtVarsProFileInput)
        RESOURCES += qml.qrc
      </QtVarsProFileInput>
    </PropertyGroup>
    <!--// Write dummy QML and QRC files -->
    <WriteLinesToFile
      Condition="$(QtModules.Contains('quick'))"
      File="$(QtVarsWorkDir)\main.qml"
      Lines="
import QtQuick
DummyQmlObject { }
"/>
    <WriteLinesToFile
      Condition="$(QtModules.Contains('quick'))"
      File="$(QtVarsWorkDir)\qml.qrc"
      Lines="
&lt;RCC&gt;
    &lt;qresource prefix=&quot;/&quot;&gt;
        &lt;file&gt;main.qml&lt;/file&gt;
    &lt;/qresource&gt;
&lt;/RCC&gt;
"/>
    <!--// Write .pro file to temp path -->
    <WriteLinesToFile
@@ -313,6 +339,7 @@
      <Cmd><![CDATA["$(QtToolsPath)/qmake" $(QMakeArgs) qtvars.pro]]></Cmd>
    </PropertyGroup>
    <HostExec
      Condition="'$(ApplicationType)' == 'Linux'"
      Command="$(Cmd)" RedirectStdOut="qtvars.log" RedirectStdErr="STDOUT"
      WorkingDirectory="@(WorkDir->'%(HostPath)')"
      Inputs="@(QMakeProj)"
@@ -322,6 +349,45 @@
      IgnoreExitCode="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorLevel"/>
    </HostExec>
    <!--// Run qmake in Windows: set %CD% to subfolder of %TEMP% -->
    <PropertyGroup
      Condition="'$(ApplicationType)' != 'Linux'">
      <QMakeTempDir>$(Temp)\$([System.IO.Path]::GetRandomFileName())</QMakeTempDir>
    </PropertyGroup>
    <MakeDir
      Condition="'$(ApplicationType)' != 'Linux'"
      Directories="$(QMakeTempDir)" />
    <Copy
      Condition="'$(ApplicationType)' != 'Linux'"
      SourceFiles="$(QtVarsWorkDir)\qtvars.pro"
      DestinationFolder="$(QMakeTempDir)" />
    <Copy
      Condition="'$(ApplicationType)' != 'Linux' AND Exists('$(QtVarsWorkDir)\main.qml')"
      SourceFiles="$(QtVarsWorkDir)\main.qml"
      DestinationFolder="$(QMakeTempDir)" />
    <Copy
      Condition="'$(ApplicationType)' != 'Linux' AND Exists('$(QtVarsWorkDir)\qml.qrc')"
      SourceFiles="$(QtVarsWorkDir)\qml.qrc"
      DestinationFolder="$(QMakeTempDir)" />
    <HostExec
      Condition="'$(ApplicationType)' != 'Linux'"
      Command="$(Cmd)" RedirectStdOut="qtvars.log" RedirectStdErr="STDOUT"
      WorkingDirectory="$(QMakeTempDir)"
      IgnoreExitCode="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorLevel"/>
    </HostExec>
    <ItemGroup>
      <QMakeGeneratedFiles Include="$(QMakeTempDir)\*" />
    </ItemGroup>
    <Copy
      Condition="'$(ApplicationType)' != 'Linux'"
      SkipUnchangedFiles="true"
      SourceFiles="@(QMakeGeneratedFiles)"
      DestinationFolder="$(QtVarsWorkDir)" />
    <RemoveDir
      Condition="'$(ApplicationType)' != 'Linux'"
      Directories="$(QMakeTempDir)" />
    <!--// Check qmake result -->
    <PropertyGroup
@@ -501,15 +567,15 @@
    <!--// In design-time, copy generated .props to randomly named file -->
    <PropertyGroup>
      <QtVarsDesignTimeNew
        Condition="'$(ErrorLevel)' == '0' AND '$(QtVSToolsBuild)' == 'true'"
        Condition="'$(ErrorLevel)' == '0' AND '$(QtDesignTimeBuild)' == 'true'"
        >$([System.IO.Path]::Combine('$(TEMP)','$([System.IO.Path]::GetRandomFileName()).designtime.props'))
      </QtVarsDesignTimeNew>
    </PropertyGroup>
    <Copy
      Condition="'$(ErrorLevel)' == '0' AND '$(QtVSToolsBuild)' == 'true'"
      Condition="'$(ErrorLevel)' == '0' AND '$(QtDesignTimeBuild)' == 'true'"
      SourceFiles="$(QtVarsFilePath)" DestinationFiles="$(QtVarsDesignTimeNew)"/>
    <WriteLinesToFile
      Condition="'$(ErrorLevel)' == '0' AND '$(QtVSToolsBuild)' == 'true'"
      Condition="'$(ErrorLevel)' == '0' AND '$(QtDesignTimeBuild)' == 'true'"
      File="$(QtVarsIndexPathDesignTime)" Overwrite="true" Lines="$(QtVarsDesignTimeNew)"/>
    <!--// Clean-up -->
QtMSBuild/QtMsBuild/translation/qttranslation.targets
@@ -90,10 +90,14 @@
      <QtTranslation>
        <InputFiles
          >$(QtTranslationInput)</InputFiles>
        <LUpdate
        <LUpdate Condition="'$(ApplicationType)' == 'Linux'"
          >$(QtToolsPath)/lupdate</LUpdate>
        <LRelease
        <LRelease Condition="'$(ApplicationType)' == 'Linux'"
          >$(QtToolsPath)/lrelease</LRelease>
        <LUpdate Condition="'$(ApplicationType)' != 'Linux'"
          >$(QtToolsPath)/lupdate.exe</LUpdate>
        <LRelease Condition="'$(ApplicationType)' != 'Linux'"
          >$(QtToolsPath)/lrelease.exe</LRelease>
        <TsFile
          >%(Identity)</TsFile>
        <QmFile
@@ -395,6 +399,7 @@
      <Cmd>$(Cmd.Trim())</Cmd>
    </PropertyGroup>
    <HostExec
      Condition="Exists(@(Options->'%(CmdExec)', ''))"
      Message="%(QtTranslationUpdate.UpdateDescription)"
      Command="$(Cmd)"
      Inputs="@(Options->'%(InputListFile)');@(Options->'%(InputFiles)')"
@@ -402,6 +407,9 @@
      RemoteTarget="$(ResolvedRemoteTarget)"
      RemoteProjectDir="$(_ResolvedRemoteProjectDir)">
    </HostExec>
    <Warning
      Condition="!Exists(@(Options->'%(CmdExec)', ''))"
      File="%(QtTranslationUpdate.Identity)" Text="'lupdate' not found; skipping" />
    <!--
    ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -556,6 +564,7 @@
      <Cmd>$(Cmd.Trim())</Cmd>
    </PropertyGroup>
    <HostExec
      Condition="Exists(@(Options->'%(CmdExec)', ''))"
      Message="%(QtTranslationRelease.ReleaseDescription)"
      Command="$(Cmd)"
      Inputs="@(Options->'%(InputFile)')"
@@ -563,6 +572,9 @@
      RemoteTarget="$(ResolvedRemoteTarget)"
      RemoteProjectDir="$(_ResolvedRemoteProjectDir)">
    </HostExec>
    <Warning
      Condition="!Exists(@(Options->'%(CmdExec)', ''))"
      File="%(QtTranslationRelease.Identity)" Text="'lrelease' not found; skipping" />
    <!--
    ///////////////////////////////////////////////////////////////////////////////////////////////
QtMSBuild/Tasks/CriticalSection.cs
@@ -84,8 +84,6 @@
            if (Lock) {
                // Wait until locked
                if (!buildLock.WaitOne(1000)) {
                    // Issue waiting warning
                    Log.LogWarning("Qt::BuildLock[{0}]: Waiting...", Name);
                    var t = Stopwatch.StartNew();
                    do {
                        // Check for build errors
QtMSBuild/Tasks/QtRunTask.cs
New file
@@ -0,0 +1,246 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#region Task TaskName="QtRunTask"
#region Reference
//System.Runtime
#endregion
#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
#endregion
#region Comment
/////////////////////////////////////////////////////////////////////////////////////////////////
/// TASK QtRunTask
/////////////////////////////////////////////////////////////////////////////////////////////////
// Execute a task from the specified assembly, taking as input a list of items. Each item
// metadata is copied to a task property with the same name. After the task is executed, the
// result for each item is then stored in a new metadata for that item. The final output is the
// list of modified items.
// Parameters /////////////////////////////////////////////////////////////////////////////////
//  in ITaskItem[]   Items........................List of source items.
//  in String        AssemblyPath.................Path to assembly containing the task.
//  in String        TaskName.....................Full name of task, including namespace.
//  in String        TaskInput....................Name of task input property.
//  in String        TaskOutput (optional)........Name of task output property.
//    If unspecified, task output is ignored.
//  in String        NewMetadata (optional).......Name of new item metadata to store result.
//    If unspecified, defaults to TaskOutput.
// out ITaskItem[]   Result.......................Output list of modified items.
//    If TaskOutput is unspecified, Result will be empty.
// Example //////////////////////////////////////////////////////////////////////////////////////
//  Items = {
//      ClCompile {
//          Identity = "main.cpp",
//          ...
//          EnforceTypeConversionRules = False,
//          ...
//      }
//  }
//  AssemblyPath = "$(VCTargetsPath)\Microsoft.Build.CPPTasks.Common.dll"
//  TaskName = "Microsoft.Build.CPPTasks.CLCommandLine"
//  TaskInput = "Sources"
//  TaskOutput = "CommandLines"
//  NewMetadata = "CommandLine"
//  Result = {
//      ClCompile_Modified {
//          Identity = "main.cpp",
//          ...
//          EnforceTypeConversionRules = False,
//          ...
//          CommandLine = "... /Zc:rvalueCast- ..."
//      }
//  }
#endregion
namespace QtVsTools.QtMsBuild.Tasks
{
    public static class QtRunTask
    {
        public static QtMSBuild.ITaskLoggingHelper Log { get; set; }
        public static IBuildEngine BuildEngine { get; set; }
        public static ITaskHost HostObject { get; set; }
        public static bool Execute(
        #region Parameters
            Microsoft.Build.Framework.ITaskItem[] Items,
            System.String AssemblyPath,
            System.String TaskName,
            System.String TaskInput,
            out Microsoft.Build.Framework.ITaskItem[] Result,
            System.String TaskOutput = null,
            System.String NewMetadata = null)
        #endregion
        {
            #region Code
            var reserved = new HashSet<string>
            {
                "AccessedTime", "CreatedTime", "DefiningProjectDirectory",
                "DefiningProjectExtension", "DefiningProjectFullPath", "DefiningProjectName",
                "Directory", "Extension", "Filename", "FullPath", "Identity", "ModifiedTime",
                "RecursiveDir", "RelativeDir", "RootDir",
            };
            // Output default values
            Result = null;
            // Load specified assembly
            var taskAssembly = Assembly.LoadFile(AssemblyPath);
            if (taskAssembly == null)
                throw new ArgumentException("AssemblyPath");
            // Access task type
            var taskType = taskAssembly.GetType(TaskName);
            if (taskType == null)
                throw new ArgumentException("TaskName");
            // Task type has the following requirements:
            //  * Must be a descendant of the ToolTask type
            //  * Cannot be an abstract class
            //  * Cannot have generic type arguments
            //  * Must have a public default constructor
            if (!typeof(ToolTask).IsAssignableFrom(taskType))
                throw new NotSupportedException("Not a ToolTask derived type");
            if (taskType.IsAbstract)
                throw new NotSupportedException("Abstract class");
            if (taskType.ContainsGenericParameters)
                throw new NotSupportedException("Generic class");
            var ctorInfo = ((TypeInfo)taskType).DeclaredConstructors
                .Where(x => x.GetParameters().Length == 0)
                .FirstOrDefault();
            if (ctorInfo == null)
                throw new NotSupportedException("No default constructor");
            // Access input property of task type
            var inputProperty = taskType.GetProperty(TaskInput);
            if (inputProperty == null)
                throw new ArgumentException("TaskInput");
            if (inputProperty.PropertyType != typeof(ITaskItem)
                && inputProperty.PropertyType != typeof(ITaskItem[])) {
                throw new NotSupportedException("Input property type is not supported");
            }
            // If output was specified, access corresponding property of task type
            PropertyInfo outputProperty = null;
            if (TaskOutput != null) {
                outputProperty = taskType.GetProperty(TaskOutput);
                if (outputProperty == null)
                    throw new ArgumentException("TaskOutput");
                if (outputProperty.PropertyType != typeof(string)
                    && outputProperty.PropertyType != typeof(string[])
                    && outputProperty.PropertyType != typeof(ITaskItem[])) {
                    throw new NotSupportedException("Output property type is not supported");
                }
                if (NewMetadata == null)
                    NewMetadata = TaskOutput;
            }
            var resultItems = new List<ITaskItem>();
            foreach (var item in Items) {
                // For each source item ...
                // Instantiate task
                var task = ctorInfo.Invoke(new object[0]) as ToolTask;
                task.BuildEngine = BuildEngine;
                task.HostObject = HostObject;
                // Set task input property to the source item
                var inputPropertyType = inputProperty.PropertyType;
                if (inputPropertyType == typeof(ITaskItem)) {
                    inputProperty.SetValue(task, item);
                } else if (inputPropertyType == typeof(ITaskItem[])) {
                    inputProperty.SetValue(task, new ITaskItem[] { item });
                }
                var names = item.MetadataNames.Cast<string>()
                    .Where(x => !reserved.Contains(x));
                foreach (var name in names) {
                    // For each metadata in the source item ...
                    // Try to obtain a task property with the same name
                    var taskProperty = taskType.GetProperty(name);
                    if (taskProperty != null) {
                        // If the property exists, set it to the metadata value
                        string metadataValue = item.GetMetadata(name);
                        var propertyType = taskProperty.PropertyType;
                        if (propertyType == typeof(bool)) {
                            taskProperty.SetValue(task, metadataValue.Equals("true",
                                StringComparison.InvariantCultureIgnoreCase));
                        } else if (propertyType == typeof(string)) {
                            taskProperty.SetValue(task, metadataValue);
                        } else if (propertyType == typeof(string[])) {
                            taskProperty.SetValue(task, metadataValue.Split(';'));
                        }
                    }
                }
                // Run task
                if (!task.Execute())
                    throw new Exception(string.Format("Task failed ({0})", item.ItemSpec));
                // Record task output
                if (TaskOutput != null) {
                    // Create output item as copy of source item
                    var resultItem = new TaskItem(item);
                    // Set new metadata and add output item to the result list
                    string outputValue;
                    object propertyValue = outputProperty.GetValue(task);
                    if (propertyValue == null)
                        outputValue = string.Empty;
                    else if (outputProperty.PropertyType == typeof(string))
                        outputValue = propertyValue as string;
                    else if (outputProperty.PropertyType == typeof(string[]))
                        outputValue = (propertyValue as string[]).FirstOrDefault();
                    else if (outputProperty.PropertyType == typeof(ITaskItem[]))
                        outputValue = (propertyValue as ITaskItem[]).FirstOrDefault().ItemSpec;
                    else
                        outputValue = string.Empty;
                    if (NewMetadata != null)
                        resultItem.SetMetadata(NewMetadata, outputValue);
                    resultItems.Add(resultItem);
                }
            }
            // Return the list of output items
            Result = resultItems.ToArray();
            #endregion
            return true;
        }
    }
}
#endregion
QtVsTest/Macro.cs
@@ -82,7 +82,7 @@
        /// <summary>
        /// Name of reusable macro
        /// </summary>
        public string Name { get; private set; }
        private string Name { get; set; }
        /// <summary>
        /// True if macro compilation was successful
@@ -104,8 +104,9 @@
        /// </summary>
        public bool QuitWhenDone { get; private set; }
        AsyncPackage Package { get; set; }
        EnvDTE80.DTE2 Dte { get; set; }
        AsyncPackage Package { get; }
        EnvDTE80.DTE2 Dte { get; }
        IntPtr MainWindowHWnd { get; }
        AutomationElement UiRoot => AutomationElement.RootElement;
@@ -115,65 +116,55 @@
            get
            {
                if (_UiVsRoot == null)
#if VS2022
                    _UiVsRoot = AutomationElement.FromHandle(Dte.MainWindow.HWnd);
#else
                    _UiVsRoot = AutomationElement.FromHandle(new IntPtr(Dte.MainWindow.HWnd));
#endif
                    _UiVsRoot = AutomationElement.FromHandle(MainWindowHWnd);
                return _UiVsRoot;
            }
        }
        JoinableTaskFactory JoinableTaskFactory { get; set; }
        CancellationToken ServerLoop { get; set; }
        JoinableTaskFactory JoinableTaskFactory { get; }
        CancellationToken ServerLoop { get; }
        string Message { get; set; }
        static MacroParser Parser { get; set; }
        MacroLines MacroLines { get; set; }
        List<string> SelectedAssemblies { get { return _SelectedAssemblies; } }
        List<string> _SelectedAssemblies =
            new List<string>
            {
                "QtVsTest",
                "System.Core",
            };
        private List<string> SelectedAssemblies { get; } = new List<string>
        {
            typeof(Macro).Assembly.FullName,
            typeof(EnvDTE.DTE).Assembly.FullName,
            typeof(AutomationElement).Assembly.FullName,
            "System.Core",
        };
        IEnumerable<string> RefAssemblies { get; set; }
        List<string> Namespaces { get { return _Namespaces; } }
        List<string> _Namespaces =
            new List<string>
            {
                "System",
                "System.Linq",
                "System.Reflection",
                "Task = System.Threading.Tasks.Task",
                "System.Windows.Automation",
                "EnvDTE",
                "EnvDTE80",
            };
        private List<string> Namespaces { get; } = new List<string>
        {
            "System",
            "System.Linq",
            "System.Reflection",
            "Task = System.Threading.Tasks.Task",
            "System.Windows.Automation",
            "EnvDTE",
            "EnvDTE80",
        };
        Dictionary<string, VSServiceRef> ServiceRefs { get { return _ServiceRefs; } }
        Dictionary<string, VSServiceRef> _ServiceRefs =
            new Dictionary<string, VSServiceRef>
        private Dictionary<string, VSServiceRef> ServiceRefs { get; } = new Dictionary<string, VSServiceRef>
        {
            {
                {
                    "Dte", new VSServiceRef
                "Dte", new VSServiceRef
                    { Name = "Dte", Interface = "DTE2", Type = "DTE" }
                },
            };
            },
        };
        Dictionary<string, GlobalVar> GlobalVars { get { return _GlobalVars; } }
        Dictionary<string, GlobalVar> _GlobalVars =
            new Dictionary<string, GlobalVar>
        private Dictionary<string, GlobalVar> GlobalVars { get; } = new Dictionary<string, GlobalVar>
        {
            {
                {
                    "Result", new GlobalVar
                "Result", new GlobalVar
                    { Type = "string", Name = "Result", InitialValueExpr = "string.Empty" }
                },
            };
            },
        };
        string CSharpMethodCode { get; set; }
        string CSharpClassCode { get; set; }
@@ -187,10 +178,10 @@
        const BindingFlags PUBLIC_STATIC = BindingFlags.Public | BindingFlags.Static;
        const StringComparison IGNORE_CASE = StringComparison.InvariantCultureIgnoreCase;
        static ConcurrentDictionary<string, Macro> Macros
        static readonly ConcurrentDictionary<string, Macro> Macros
            = new ConcurrentDictionary<string, Macro>();
        static ConcurrentDictionary<string, object> Globals
        static readonly ConcurrentDictionary<string, object> Globals
            = new ConcurrentDictionary<string, object>();
        /// <summary>
@@ -202,6 +193,7 @@
        public Macro(
            AsyncPackage package,
            EnvDTE80.DTE2 dte,
            IntPtr mainWindowHWnd,
            JoinableTaskFactory joinableTaskFactory,
            CancellationToken serverLoop)
        {
@@ -209,6 +201,7 @@
            JoinableTaskFactory = joinableTaskFactory;
            ServerLoop = serverLoop;
            Dte = dte;
            MainWindowHWnd = mainWindowHWnd;
            ErrorMsg("Uninitialized");
        }
@@ -510,6 +503,8 @@
                    return false;
                break;
            }
            csharp.AppendLine();
            return true;
        }
@@ -615,6 +610,38 @@
                csharp.AppendFormat(@"
                    await WaitExpr({0}, () => UiContext = {1});", timeout, context);
            } else if (s.Args[0].Equals("find", IGNORE_CASE)) {
                //# ui find [all] [_var_name_] [_timeout_] => <_scope_>, <_condition_>
                var args = new Queue<string>(s.Args.Skip(1));
                bool findAll = false;
                if (args.Any() && args.Peek().Equals("all", IGNORE_CASE)) {
                    findAll = true;
                    args.Dequeue();
                }
                string funcName = findAll ? "FindAll" : "FindFirst";
                string varType = findAll ? "AutomationElementCollection" : "AutomationElement";
                string varName = null;
                if (args.Any() && !char.IsDigit(args.Peek()[0]))
                    varName = args.Dequeue();
                if (findAll && string.IsNullOrEmpty(varName))
                    return ErrorMsg("Invalid #ui statement");
                int timeout = 3000;
                if (args.Any() && char.IsDigit(args.Peek()[0]))
                    timeout = int.Parse(args.Dequeue());
                if (varName == null) {
                    varName = "UiContext";
                } else {
                    csharp.Append($@"
                        {varType} {varName} = null;");
                }
                csharp.Append($@"
                    await WaitExpr({timeout}, () => {varName} = UiContext.{funcName}({s.Code}));");
            } else if (s.Args[0].Equals("pattern", IGNORE_CASE)) {
                //# ui pattern <_TypeName_> <_VarName_> [ => _string_ [, _string_, ... ] ]
@@ -790,7 +817,9 @@
                    File.Delete(macroDllPath);
                return ErrorMsg(string.Join("\r\n",
                    CompilerResults.Errors.Cast<CompilerError>()
                        .Select(x => x.ErrorText)));
                        .Select(x => $"{x.Line}: {x.ErrorText}")
                        .Append(CSharpClassCode)
                        .Union(RefAssemblies)));
            }
            MacroAssembly = AppDomain.CurrentDomain.Load(File.ReadAllBytes(macroDllPath));
@@ -927,8 +956,7 @@
            foreach (var globalVar in GlobalVars.Values) {
                string varName = globalVar.Name;
                Type varType = globalVar.FieldInfo.FieldType;
                object value;
                if (Globals.TryGetValue(varName, out value)) {
                if (Globals.TryGetValue(varName, out object value)) {
                    Type valueType = value.GetType();
                    if (!varType.IsAssignableFrom(valueType)) {
                        throw new InvalidCastException(string.Format(
@@ -968,8 +996,7 @@
        static Macro GetMacro(string name)
        {
            Macro macro;
            if (!Macros.TryGetValue(name, out macro))
            if (!Macros.TryGetValue(name, out Macro macro))
                return null;
            return macro;
        }
QtVsTest/MacroParser.cs
@@ -27,17 +27,18 @@
****************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using QtVsTools.SyntaxAnalysis;
namespace QtVsTest.Macros
{
    using System.Collections;
    using static QtVsTools.SyntaxAnalysis.RegExpr;
    using static RegExpr;
    class MacroLines : IEnumerable<MacroLine>
    {
        List<MacroLine> Lines = new List<MacroLine>();
        readonly List<MacroLine> Lines = new List<MacroLine>();
        public void Add(MacroLine line) { Lines.Add(line); }
@@ -120,7 +121,7 @@
    public class CodeLine : MacroLine
    {
        public string Code;
        public readonly string Code;
        public CodeLine(string code)
        {
            Code = code;
QtVsTest/MacroServer.cs
@@ -46,10 +46,10 @@
    /// </summary>
    class MacroServer
    {
        public CancellationTokenSource Loop { get; private set; }
        public CancellationTokenSource Loop { get; }
        AsyncPackage Package { get; set; }
        JoinableTaskFactory JoinableTaskFactory { get; set; }
        AsyncPackage Package { get; }
        JoinableTaskFactory JoinableTaskFactory { get; }
        /// <summary>
        /// Macro server constructor
@@ -70,6 +70,7 @@
        {
            await JoinableTaskFactory.SwitchToMainThreadAsync(Loop.Token);
            var DTE = await Package.GetServiceAsync(typeof(DTE)) as DTE2;
            var mainWindowHWnd = new IntPtr((long)DTE.MainWindow.HWnd);
            await TaskScheduler.Default;
            var pipeName = string.Format("QtVSTest_{0}", Process.GetCurrentProcess().Id);
@@ -97,7 +98,8 @@
                            if (Loop.Token.IsCancellationRequested)
                                break;
                            var macro = new Macro(Package, DTE, JoinableTaskFactory, Loop.Token);
                            var macro = new Macro(
                                Package, DTE, mainWindowHWnd, JoinableTaskFactory, Loop.Token);
                            await macro.CompileAsync(Encoding.UTF8.GetString(data));
                            if (macro.AutoRun)
                                await macro.RunAsync();
QtVsTest/QtVsTest.cs
@@ -27,6 +27,7 @@
****************************************************************************/
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.VisualStudio.Shell;
@@ -38,7 +39,6 @@
namespace QtVsTest
{
    using Macros;
    using System.IO;
    [Guid(PackageGuidString)]
    [InstalledProductRegistration(
@@ -53,7 +53,7 @@
    public sealed class QtVsTest : AsyncPackage
    {
        public const string PackageGuidString = "0e258dce-fc8a-49a2-81c5-c9e138bfe500";
        MacroServer MacroServer { get; set; }
        MacroServer MacroServer { get; }
        public QtVsTest()
        {
QtVsTest/QtVsTest.csproj
@@ -2,7 +2,7 @@
<!--
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -69,6 +69,8 @@
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <PlatformTarget Condition="'$(VisualStudioVersion)' == '17.0'">x64</PlatformTarget>
    <PlatformTarget Condition="'$(VisualStudioVersion)' != '17.0'">x86</PlatformTarget>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
@@ -87,55 +89,36 @@
    <Reference Include="System" />
    <Reference Include="System.Design" />
    <Reference Include="System.Drawing" />
    <Reference Include="Microsoft.VisualStudio.ProjectSystem">
      <HintPath>$(VsInstallRoot)\Common7\IDE\CommonExtensions\Microsoft\Project\Microsoft.VisualStudio.ProjectSystem.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.VisualStudio.Composition">
      <HintPath>$(VsInstallRoot)\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.Composition.dll</HintPath>
    </Reference>
  </ItemGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Version specific references
  // General package references
  // -->
  <Import Project="$(SolutionDir)\references.props" />
  <ItemGroup>
    <PackageReference Include="System.ComponentModel.Composition"
      Version="$(Version_System_ComponentModel_Composition)" />
    <PackageReference Include="Stub.System.Data.SQLite.Core.NetFramework"
      Version="$(Version_Stub_System_Data_SQLite_Core_NetFramework)" />
    <PackageReference Include="Newtonsoft.Json"
      Version="$(Version_Newtonsoft_Json)" />
    <PackageReference Include="Microsoft.Build"
      Version="$(Version_Microsoft_Build)" />
    <PackageReference Include="Microsoft.Build.Framework"
      Version="$(Version_Microsoft_Build_Framework)" />
    <PackageReference Include="Microsoft.Build.Tasks.Core"
      Version="$(Version_Microsoft_Build_Tasks_Core)" />
    <PackageReference Include="Microsoft.VisualStudio.SDK"
      Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" />
    <PackageReference Include="Microsoft.VSSDK.BuildTools"
      Version="$(Version_Microsoft_VSSDK_BuildTools)" />
    <PackageReference Include="Microsoft.VisualStudio.Shell.Framework"
      Version="$(Version_Microsoft_VisualStudio_Shell_Framework)" />
    <PackageReference Include="Microsoft.VisualStudio.Validation"
      Version="$(Version_Microsoft_VisualStudio_Validation)" />
    <PackageReference Include="$(Name_Microsoft_VisualStudio_Threading)"
      Version="$(Version_Microsoft_VisualStudio_Threading)" />
    <PackageReference Include="System.Collections.Immutable"
      Version="$(Version_System_Collections_Immutable)" />
    <PackageReference Include="$(Name_Microsoft_VSSDK_BuildTools)" Version="$(Version_Microsoft_VSSDK_BuildTools)" />
    <PackageReference Include="$(Name_Microsoft_VisualStudio_SDK)" Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" />
    <PackageReference Include="$(Name_Microsoft_VisualStudio_ProjectSystem)" Version="$(Version_Microsoft_VisualStudio_ProjectSystem)" />
  </ItemGroup>
  <ItemGroup Condition="'$(VisualStudioVersion)'=='17.0'">
    <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)"
      Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" />
  </ItemGroup>
  <ItemGroup Condition="'$(VisualStudioVersion)'=='16.0'">
    <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)"
      Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" />
  </ItemGroup>
  <ItemGroup Condition="'$(VisualStudioVersion)'=='15.0'">
    <Reference Include="Microsoft.VisualStudio.VCProjectEngine" />
  </ItemGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Version specific package references
  // -->
  <Choose>
    <When Condition="'$(VisualStudioVersion)'=='17.0'">
      <ItemGroup>
      </ItemGroup>
    </When>
    <When Condition="'$(VisualStudioVersion)'=='16.0'">
      <ItemGroup>
        <PackageReference Include="$(Name_Microsoft_VisualStudio_Validation)" Version="$(Version_Microsoft_VisualStudio_Validation)" />
      </ItemGroup>
    </When>
    <When Condition="'$(VisualStudioVersion)'=='15.0'">
      <ItemGroup>
      </ItemGroup>
    </When>
  </Choose>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Solution project references
@@ -210,6 +193,26 @@
    </Content>
  </ItemGroup>
  <Choose>
    <When Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.22000.0')">
      <PropertyGroup>
        <Win10SDKPath>$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.22000.0</Win10SDKPath>
      </PropertyGroup>
    </When>
    <When Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.20348.0')">
      <PropertyGroup>
        <Win10SDKPath>$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.20348.0</Win10SDKPath>
      </PropertyGroup>
    </When>
    <When Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.19041.0')">
      <PropertyGroup>
        <Win10SDKPath>$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.19041.0</Win10SDKPath>
      </PropertyGroup>
    </When>
    <When Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.18362.0')">
      <PropertyGroup>
        <Win10SDKPath>$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.18362.0</Win10SDKPath>
      </PropertyGroup>
    </When>
    <When Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.17763.0')">
      <PropertyGroup>
        <Win10SDKPath>$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.17763.0</Win10SDKPath>
@@ -232,7 +235,7 @@
    </When>
  </Choose>
  <PropertyGroup Condition="'$(Win10SDKPath)' != ''">
    <UIAVerifyPath>$(Win10SDKPath)\x86\UIAVerify</UIAVerifyPath>
    <UIAVerifyPath>$(Win10SDKPath)\$(PlatformTarget)\UIAVerify</UIAVerifyPath>
  </PropertyGroup>
  <ItemGroup Condition="'$(UIAVerifyPath)' != ''">
    <Reference Include="Interop.UIAutomationClient">
@@ -246,9 +249,6 @@
      <Aliases>global</Aliases>
      <EmbedInteropTypes>False</EmbedInteropTypes>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="$(SolutionDir)\transform.targets" />
QtVsTools.Core/BuildConfig.cs
@@ -30,7 +30,6 @@
{
    public struct BuildConfig
    {
        public const uint Both = 0x03;
        public const uint Release = 0x01;
        public const uint Debug = 0x02;
QtVsTools.Core/CommandLineParser.cs
@@ -29,10 +29,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace QtVsTools.Core.CommandLine
{
@@ -40,13 +40,12 @@
    public class Parser
    {
        List<Option> commandLineOptionList = new List<Option>();
        Dictionary<string, int> nameHash = new Dictionary<string, int>();
        Dictionary<int, List<string>> optionValuesHash = new Dictionary<int, List<string>>();
        List<string> optionNames = new List<string>();
        List<string> positionalArgumentList = new List<string>();
        List<string> unknownOptionNames = new List<string>();
        readonly List<Option> commandLineOptionList = new List<Option>();
        readonly Dictionary<string, int> nameHash = new Dictionary<string, int>();
        readonly Dictionary<int, List<string>> optionValuesHash = new Dictionary<int, List<string>>();
        readonly List<string> optionNames = new List<string>();
        readonly List<string> positionalArgumentList = new List<string>();
        readonly List<string> unknownOptionNames = new List<string>();
        bool needsParsing = true;
        public enum SingleDashWordOptionMode
@@ -118,8 +117,7 @@
        IEnumerable<string> Aliases(string optionName)
        {
            int optionIndex;
            if (!nameHash.TryGetValue(optionName, out optionIndex)) {
            if (!nameHash.TryGetValue(optionName, out int optionIndex)) {
                return new List<string>();
            }
            return commandLineOptionList[optionIndex].Names;
@@ -204,8 +202,7 @@
             IEnumerator<string> argumentEnumerator, ref bool atEnd)
        {
            const char assignChar = '=';
            int optionOffset;
            if (nameHash.TryGetValue(optionName, out optionOffset)) {
            if (nameHash.TryGetValue(optionName, out int optionOffset)) {
                int assignPos = argument.IndexOf(assignChar);
                bool withValue = !string.IsNullOrEmpty(
                    commandLineOptionList[optionOffset].ValueName);
@@ -267,7 +264,7 @@
                            var optFilePath = macros.ExpandString(argData.Substring(1));
                            string[] additionalArgs = File.ReadAllLines(
                                Path.Combine(workingDir, optFilePath));
                            if (additionalArgs != null) {
                            if (additionalArgs.Length != 0) {
                                var additionalArgsString = string.Join(" ", additionalArgs
                                    .Select(x => "\"" + x.Replace("\"", "\\\"") + "\""));
                                arguments.AddRange(TokenizeArgs(additionalArgsString, macros));
@@ -356,10 +353,9 @@
                            if (!RegisterFoundOption(optionName)) {
                                error = true;
                            } else {
                                int optionOffset;
                                Trace.Assert(nameHash.TryGetValue(
                                    optionName,
                                    out optionOffset));
                                    out int optionOffset));
                                bool withValue = !string.IsNullOrEmpty(
                                    commandLineOptionList[optionOffset].ValueName);
                                if (withValue) {
@@ -397,10 +393,9 @@
                        if (argument.Length > 2) {
                            string possibleShortOptionStyleName = argument.Substring(1, 1);
                            int shortOptionIdx;
                            if (nameHash.TryGetValue(
                                possibleShortOptionStyleName,
                                out shortOptionIdx)) {
                                out int shortOptionIdx)) {
                                var arg = commandLineOptionList[shortOptionIdx];
                                if ((arg.Flags & Option.Flag.ShortOptionStyle) != 0) {
                                    RegisterFoundOption(possibleShortOptionStyleName);
@@ -466,8 +461,7 @@
        public IEnumerable<string> Values(string optionName)
        {
            CheckParsed("Values");
            int optionOffset;
            if (nameHash.TryGetValue(optionName, out optionOffset)) {
            if (nameHash.TryGetValue(optionName, out int optionOffset)) {
                var values = optionValuesHash[optionOffset];
                return values;
            }
@@ -525,7 +519,6 @@
        public IEnumerable<string> Names
        {
            get;
            private set;
        }
        public string ValueName
@@ -558,7 +551,7 @@
    static class Lexer
    {
        static Regex lexer = new Regex(
        static readonly Regex lexer = new Regex(
            /* Newline    */ @"(\n)" +
            /* Unquoted   */ @"|((?:(?:[^\s\""])|(?:(?<=\\)\""))+)" +
            /* Quoted     */ @"|(?:\""((?:(?:[^\""])|(?:(?<=\\)\""))+)\"")" +
QtVsTools.Core/Common/EnumExt.cs
@@ -40,6 +40,8 @@
    /// </summary>
    public static class EnumExt
    {
        static LazyFactory StaticLazy { get; } = new LazyFactory();
        /// <summary>
        /// Wrapper for enum cast values.
        /// </summary>
@@ -64,7 +66,7 @@
        [AttributeUsage(AttributeTargets.All)]
        public sealed class StringAttribute : Attribute, ICast<string>
        {
            public string Value { get; private set; }
            public string Value { get; }
            public StringAttribute(string str) { Value = str; }
        }
@@ -128,8 +130,7 @@
        /// </summary>
        public static TEnum Cast<T, TEnum>(this T valueT, TEnum defaultValue) where TEnum : struct
        {
            TEnum value;
            return TryCast(valueT, out value) ? value : defaultValue;
            return TryCast(valueT, out TEnum value) ? value : defaultValue;
        }
        /// <summary>
@@ -201,15 +202,14 @@
                .FirstOrDefault();
        }
        static IEnumerable<Type> _CastAttribTypes;
        /// <summary>
        /// List of cast attribute types.
        /// </summary>
        /// <remarks>
        /// Future cast attribute types need to be added to this list.
        /// </remarks>
        static IEnumerable<Type> CastAttribTypes => _CastAttribTypes
            ?? (_CastAttribTypes = new[]
        static IEnumerable<Type> CastAttribTypes => StaticLazy.Get(() =>
            CastAttribTypes, () => new[]
            {
                typeof(StringAttribute)
            });
QtVsTools.Core/Common/LazyFactory.cs
New file
@@ -0,0 +1,57 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
namespace QtVsTools.Common
{
    public class LazyFactory
    {
        private Lazy<ConcurrentDictionary<PropertyInfo, Lazy<object>>> LazyObjs { get; }
        private ConcurrentDictionary<PropertyInfo, Lazy<object>> Objs => LazyObjs.Value;
        public LazyFactory()
        {
            LazyObjs = new Lazy<ConcurrentDictionary<PropertyInfo, Lazy<object>>>();
        }
        public T Get<T>(Expression<Func<T>> propertyRef, Func<T> initFunc) where T : class
        {
            var lazyPropertyExpr = propertyRef?.Body as MemberExpression;
            var lazyProperty = lazyPropertyExpr?.Member as PropertyInfo;
            if (lazyProperty == null)
                throw new ArgumentException("Invalid property reference", "propertyRef");
            var lazyObj = Objs.GetOrAdd(lazyProperty, (_) => new Lazy<object>(() => initFunc()));
            return lazyObj.Value as T;
        }
    }
}
QtVsTools.Core/Common/QtVSIPSettingsShared.cs
New file
@@ -0,0 +1,261 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
using Microsoft.Win32;
using QtVsTools.Core;
namespace QtVsTools.Common
{
    public static class QtVSIPSettingsShared
    {
        private static readonly Dictionary<string, string> mocDirCache
            = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        private static readonly Dictionary<string, string> uicDirCache
            = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        private static readonly Dictionary<string, string> rccDirCache
            = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        public static string GetDirectory(EnvDTE.Project project, string type)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            // check for directory in following order:
            // - stored in project
            // - stored in cache
            // - retrieve from moc/uic steps
            // - fall-back on hard-coded directory
            var fullName = project?.FullName;
            if (string.IsNullOrEmpty(fullName))
                return GetDirectory(type); // - fall-back on hard-coded directory
            if (project.Globals.get_VariablePersists(type)) // - stored in project
                return HelperFunctions.NormalizeRelativeFilePath(project.Globals[type] as string);
            switch (type) { // - stored in cache
            case Resources.mocDirKeyword:
                if (mocDirCache.ContainsKey(fullName))
                    return mocDirCache[fullName];
                break;
            case Resources.uicDirKeyword:
                if (uicDirCache.ContainsKey(fullName))
                    return uicDirCache[fullName];
                break;
            case Resources.rccDirKeyword:
                if (rccDirCache.ContainsKey(fullName))
                    return rccDirCache[fullName];
                break;
            default:
                return GetDirectory(type); // - fall-back on hard-coded directory
            }
            try {
                string configName = null;
                string platformName = null;
                QtCustomBuildTool tool = null;
                foreach (VCFile vcfile in (project.Object as VCProject).Files as IVCCollection) {
                    var name = vcfile?.Name;
                    if (string.IsNullOrEmpty(name))
                        continue;
                    if (!(HelperFunctions.IsHeaderFile(name) || HelperFunctions.IsMocFile(name)
                        || HelperFunctions.IsUicFile(name) || HelperFunctions.IsQrcFile(name)))
                        continue;
                    foreach (VCFileConfiguration config in vcfile?.FileConfigurations as IVCCollection) {
                        tool = new QtCustomBuildTool(config);
                        if (tool == null)
                            continue;
                        configName = config.Name.Remove(config.Name.IndexOf('|'));
                        var vcConfig = config.ProjectConfiguration as VCConfiguration;
                        platformName = (vcConfig.Platform as VCPlatform).Name;
                        var cmd = tool.CommandLine;
                        if (cmd.Contains("moc.exe") || cmd.Contains("uic.exe") || cmd.Contains("rcc.exe"))
                            break;
                        tool = null;
                    }
                    if (tool != null)
                        break;
                }
                if (tool == null)
                    return GetDirectory(type); // - fall-back on hard-coded directory
                var dir = ".";
                var lastindex = tool.Outputs.LastIndexOf('\\');
                if (tool.Outputs.LastIndexOf('/') > lastindex)
                    lastindex = tool.Outputs.LastIndexOf('/');
                if (lastindex != -1)
                    dir = tool.Outputs.Substring(0, lastindex);
                dir = dir.Replace("\"", "");
                if (type == Resources.mocDirKeyword) {
                    int index = dir.IndexOf(configName, StringComparison.OrdinalIgnoreCase);
                    if (index != -1)
                        dir = dir.Replace(dir.Substring(index, configName.Length), "$(ConfigurationName)");
                    index = dir.IndexOf(platformName, StringComparison.OrdinalIgnoreCase);
                    if (index != -1)
                        dir = dir.Replace(dir.Substring(index, platformName.Length), "$(PlatformName)");
                    dir = HelperFunctions.NormalizeRelativeFilePath(dir);
                    mocDirCache.Add(fullName, dir);
                } else if (type == Resources.uicDirKeyword) {
                    dir = HelperFunctions.NormalizeRelativeFilePath(dir);
                    uicDirCache.Add(fullName, dir);
                } else if (type == Resources.rccDirKeyword) {
                    dir = HelperFunctions.NormalizeRelativeFilePath(dir);
                    rccDirCache.Add(fullName, dir);
                } else {
                    dir = HelperFunctions.NormalizeRelativeFilePath(dir);
                }
                CleanUpCache(project);
                return dir; // - retrieved from moc/uic/rcc steps
            } catch { }
            return GetDirectory(type); // - fall-back on hard-coded directory
        }
        private const string registryPath = "SOFTWARE\\" + Resources.registryPackagePath;
        public static string GetDirectory(string type)
        {
            try {
                var key = Registry.CurrentUser.OpenSubKey(registryPath);
                if (key != null) {
                    if (key.GetValue(type, null) is string path)
                        return HelperFunctions.NormalizeRelativeFilePath(path);
                }
            } catch { }
            if (type == Resources.mocDirKeyword)
                return Resources.generatedFilesDir + "\\$(ConfigurationName)";
            return Resources.generatedFilesDir;
        }
        public static string GetOption(EnvDTE.Project project, string type)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            // check for directory in following order:
            // - stored in project
            // - globally defined default option
            // - empty options
            if (project != null && project.Globals.get_VariablePersists(type))
                return project.Globals[type] as string;
            return GetOption(type);
        }
        public static string GetOption(string type)
        {
            try {
                var key = Registry.CurrentUser.OpenSubKey(registryPath);
                if (key != null) {
                    if (key.GetValue(type, null) is string opt)
                        return opt;
                }
            } catch { }
            return null;
        }
        public static bool GetBoolValue(EnvDTE.Project project, string type)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            // check for directory in following order:
            // - stored in project
            // - globally defined default option
            // - empty options
            if (project != null && project.Globals.get_VariablePersists(type))
                return Convert.ToInt32(project.Globals[type] as string) > 0;
            return GetBoolValue(type, false);
        }
        public static bool GetBoolValue(string key, bool defaultValue)
        {
            var regKey = Registry.CurrentUser.OpenSubKey(registryPath);
            if (regKey == null)
                return defaultValue;
            return ((int)regKey.GetValue(key, defaultValue ? 1 : 0)) > 0;
        }
        public static bool ValueExists(string key)
        {
            var regKey = Registry.CurrentUser.OpenSubKey(registryPath);
            if (regKey != null) {
                foreach (var s in regKey.GetValueNames()) {
                    if (s == key)
                        return true;
                }
            }
            return false;
        }
        public static string GetProjectQtSetting(EnvDTE.Project project, string propertyName)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var activeConfig = project?.ConfigurationManager?.ActiveConfiguration;
            if (activeConfig == null)
                return null;
            var activeConfigId = $"{activeConfig.ConfigurationName}|{activeConfig.PlatformName}";
            try {
                var props = project.Object as VCProject as IVCBuildPropertyStorage;
                return props?.GetPropertyValue(propertyName, activeConfigId, "ProjectFile");
            } catch {
                return null;
            }
        }
        public static void CleanUpCache(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var projects = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            foreach (var p in HelperFunctions.ProjectsInSolution(project.DTE))
                projects.Add(p.FullName);
            mocDirCache.RemoveValues(projects);
            uicDirCache.RemoveValues(projects);
            rccDirCache.RemoveValues(projects);
        }
        static void RemoveValues(this Dictionary<string, string> cache, HashSet<string> projects)
        {
            foreach (var key in cache.Keys) {
                if (projects.Contains(key))
                    cache.Remove(key);
            }
        }
    }
}
QtVsTools.Core/CompilerToolWrapper.cs
@@ -26,11 +26,11 @@
**
****************************************************************************/
using Microsoft.VisualStudio.VCProjectEngine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.VCProjectEngine;
namespace QtVsTools.Core
{
@@ -44,7 +44,7 @@
    /// Using VCCLCompilerTool directly will break the VS integration for Win CE.
    class CompilerToolWrapper
    {
        private VCCLCompilerTool compilerTool;
        private readonly VCCLCompilerTool compilerTool;
        private readonly Object compilerObj;
        private readonly Type compilerType;
@@ -302,13 +302,6 @@
            return GetStringProperty("AdditionalIncludeDirectories");
        }
        public string GetPrecompiledHeaderFile()
        {
            if (compilerTool != null)
                return compilerTool.PrecompiledHeaderFile;
            return GetStringProperty("PrecompiledHeaderFile");
        }
        public string GetPrecompiledHeaderThrough()
        {
            if (compilerTool != null)
@@ -326,20 +319,6 @@
            if (obj == null)
                return pchOption.pchNone;
            return (pchOption)obj;
        }
        public void SetDebugInformationFormat(debugOption value)
        {
            if (compilerTool != null) {
                compilerTool.DebugInformationFormat = value;
            } else {
                compilerType.InvokeMember(
                    "DebugInformationFormat",
                    BindingFlags.SetProperty,
                    null,
                    compilerObj,
                    new object[] { value });
            }
        }
        public runtimeLibraryOption RuntimeLibrary
@@ -364,62 +343,6 @@
                } else {
                    compilerTool.RuntimeLibrary = value;
                }
            }
        }
        public void SetOptimization(optimizeOption value)
        {
            if (compilerTool != null) {
                compilerTool.Optimization = value;
            } else {
                compilerType.InvokeMember(
                    "Optimization",
                    BindingFlags.SetProperty,
                    null,
                    compilerObj,
                    new object[] { value });
            }
        }
        public void SetTreatWChar_tAsBuiltInType(bool value)
        {
            if (compilerTool != null) {
                compilerTool.TreatWChar_tAsBuiltInType = value;
            } else {
                compilerType.InvokeMember(
                    "TreatWChar_tAsBuiltInType",
                    BindingFlags.SetProperty,
                    null,
                    compilerObj,
                    new object[] { value });
            }
        }
        public void SetPrecompiledHeaderFile(string file)
        {
            if (compilerTool != null) {
                compilerTool.PrecompiledHeaderFile = file;
            } else {
                compilerType.InvokeMember(
                    "PrecompiledHeaderFile",
                    BindingFlags.SetProperty,
                    null,
                    compilerObj,
                    new object[] { file });
            }
        }
        public void SetPrecompiledHeaderThrough(string value)
        {
            if (compilerTool != null) {
                compilerTool.PrecompiledHeaderThrough = value;
            } else {
                compilerType.InvokeMember(
                    "PrecompiledHeaderThrough",
                    BindingFlags.SetProperty,
                    null,
                    compilerObj,
                    new object[] { value });
            }
        }
QtVsTools.Core/CxxStreamReader.cs
@@ -42,12 +42,12 @@
            Normal, Comment, String
        }
        private State state = State.Normal;
        private StreamReader sr;
        private readonly StreamReader sr;
        private string partialLine;
        bool disposed;
        int _lineNum;
        string[] _lines;
        readonly string[] _lines;
        public CxxStreamReader(string[] lines)
        {
QtVsTools.Core/ExportProjectDialog.cs
@@ -68,11 +68,7 @@
            optionTextBox.Text = "";
            openCheckBox.Text = SR.GetString("ExportProjectDialog_Open");
            createPriFileCheckBox.Text = SR.GetString("ExportProjectDialog_CreatePri");
            if (SR.LanguageName == "de")
                Size = new Size(470, 300);
            else
                Size = new Size(400, 300);
            Size = new Size(400, 300);
            ShowInTaskbar = false;
            Shown += ExportProjectDialog_Shown;
QtVsTools.Core/Extensions.cs
@@ -32,15 +32,6 @@
{
    public static class Extensions
    {
        public static string Quoute(this string input)
        {
            if (!input.StartsWith("\"", StringComparison.Ordinal))
                input = "\"" + input;
            if (!input.EndsWith("\"", StringComparison.Ordinal))
                input += "\"";
            return input;
        }
        public static string Replace(this string original, string oldValue, string newValue,
            StringComparison comparison)
        {
QtVsTools.Core/FakeFilter.cs
@@ -34,11 +34,6 @@
        public string Filter { get; set; }
        public string UniqueIdentifier { get; set; }
        private bool parseFiles = true;
        public bool ParseFiles
        {
            get { return parseFiles; }
            set { parseFiles = value; }
        }
        public bool ParseFiles { get; set; } = true;
    }
}
QtVsTools.Core/Filters.cs
@@ -71,17 +71,6 @@
            };
        }
        public static FakeFilter TranslationFiles()
        {
            return new FakeFilter
            {
                UniqueIdentifier = "{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}",
                Name = SR.GetString("Resources_TranslationFiles"),
                Filter = "ts",
                ParseFiles = false
            };
        }
        public static FakeFilter GeneratedFiles()
        {
            return new FakeFilter
@@ -89,16 +78,6 @@
                UniqueIdentifier = "{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}",
                Name = SR.GetString("Resources_GeneratedFiles"),
                Filter = "moc;h;cpp",
            };
        }
        public static FakeFilter OtherFiles()
        {
            return new FakeFilter
            {
                UniqueIdentifier = "{B67473BF-9FA1-4674-831E-CB28F72D4791}",
                Name = SR.GetString("Resources_OtherFiles"),
                Filter = "*",
            };
        }
    }
QtVsTools.Core/HelperFunctions.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,10 +26,6 @@
**
****************************************************************************/
using EnvDTE;
using Microsoft.VisualStudio.VCProjectEngine;
using Microsoft.Win32;
using QtVsTools.Core.QtMsBuild;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -40,105 +36,31 @@
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
#if VS2017
using Microsoft.Win32;
#endif
using EnvDTE;
using Process = System.Diagnostics.Process;
namespace QtVsTools.Core
{
    using QtMsBuild;
    public static class HelperFunctions
    {
        public static string FindQtDirWithTools(Project project)
        {
            var versionManager = QtVersionManager.The();
            string projectQtVersion = null;
            if (IsQtProject(project))
                projectQtVersion = versionManager.GetProjectQtVersion(project);
            return FindQtDirWithTools(projectQtVersion);
        }
        public static string FindQtDirWithTools(string projectQtVersion)
        {
            string tool = null;
            return FindQtDirWithTools(tool, projectQtVersion);
        }
        public static string FindQtDirWithTools(string tool, string projectQtVersion)
        {
            if (!string.IsNullOrEmpty(tool)) {
                if (!tool.StartsWith("\\bin\\", StringComparison.OrdinalIgnoreCase))
                    tool = "\\bin\\" + tool;
                if (!tool.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
                    tool += ".exe";
            }
            var versionManager = QtVersionManager.The();
            string qtDir = null;
            if (projectQtVersion != null)
                qtDir = versionManager.GetInstallPath(projectQtVersion);
            if (qtDir == null)
                qtDir = Environment.GetEnvironmentVariable("QTDIR");
            var found = false;
            if (tool == null) {
                found = File.Exists(qtDir + "\\bin\\designer.exe")
                    && File.Exists(qtDir + "\\bin\\linguist.exe");
            } else {
                found = File.Exists(qtDir + tool);
            }
            if (!found) {
                VersionInformation exactlyMatchingVersion = null;
                VersionInformation matchingVersion = null;
                VersionInformation somehowMatchingVersion = null;
                var viProjectQtVersion = versionManager.GetVersionInfo(projectQtVersion);
                foreach (var qtVersion in versionManager.GetVersions()) {
                    var vi = versionManager.GetVersionInfo(qtVersion);
                    if (tool == null) {
                        found = File.Exists(vi.qtDir + "\\bin\\designer.exe")
                            && File.Exists(vi.qtDir + "\\bin\\linguist.exe");
                    } else {
                        found = File.Exists(vi.qtDir + tool);
                    }
                    if (!found)
                        continue;
                    if (viProjectQtVersion != null
                        && vi.qtMajor == viProjectQtVersion.qtMajor
                        && vi.qtMinor == viProjectQtVersion.qtMinor) {
                        exactlyMatchingVersion = vi;
                        break;
                    }
                    if (matchingVersion == null
                        && viProjectQtVersion != null
                        && vi.qtMajor == viProjectQtVersion.qtMajor) {
                        matchingVersion = vi;
                    }
                    if (somehowMatchingVersion == null)
                        somehowMatchingVersion = vi;
                }
                if (exactlyMatchingVersion != null)
                    qtDir = exactlyMatchingVersion.qtDir;
                else if (matchingVersion != null)
                    qtDir = matchingVersion.qtDir;
                else if (somehowMatchingVersion != null)
                    qtDir = somehowMatchingVersion.qtDir;
                else
                    qtDir = null;
            }
            return qtDir;
        }
        static readonly HashSet<string> _sources = new HashSet<string>(new[] { ".c", ".cpp", ".cxx" },
            StringComparer.OrdinalIgnoreCase);
        static public bool IsSourceFile(string fileName)
        public static bool IsSourceFile(string fileName)
        {
            return _sources.Contains(Path.GetExtension(fileName));
        }
        static readonly HashSet<string> _headers = new HashSet<string>(new[] { ".h", ".hpp", ".hxx" },
            StringComparer.OrdinalIgnoreCase);
        static public bool IsHeaderFile(string fileName)
        public static bool IsHeaderFile(string fileName)
        {
            return _headers.Contains(Path.GetExtension(fileName));
        }
@@ -173,23 +95,28 @@
            return ".qml".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase);
        }
        static public void SetDebuggingEnvironment(Project prj)
        public static void SetDebuggingEnvironment(Project prj)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            SetDebuggingEnvironment(prj, string.Empty);
        }
        static public void SetDebuggingEnvironment(Project prj, string solutionConfig)
        public static void SetDebuggingEnvironment(Project prj, string solutionConfig)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            SetDebuggingEnvironment(prj, "PATH=$(QTDIR)\\bin;$(PATH)", false, solutionConfig);
        }
        static public void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite)
        public static void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            SetDebuggingEnvironment(prj, envpath, overwrite, string.Empty);
        }
        static public void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite, string solutionConfig)
        public static void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite, string solutionConfig)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (QtProject.GetFormatVersion(prj) >= Resources.qtMinFormatVersion_Settings)
                return;
@@ -252,28 +179,29 @@
            }
        }
        public static bool IsProjectInSolution(DTE dteObject, string fullName)
        public static Project ProjectFromSolution(DTE dteObject, string fullName)
        {
            var fi = new FileInfo(fullName);
            ThreadHelper.ThrowIfNotOnUIThread();
            fullName = new FileInfo(fullName).FullName;
            foreach (var p in ProjectsInSolution(dteObject)) {
                if (p.FullName.ToLower() == fi.FullName.ToLower())
                    return true;
                if (p.FullName.Equals(fullName, StringComparison.OrdinalIgnoreCase))
                    return p;
            }
            return false;
            return null;
        }
        /// <summary>
        /// Returns the normalized file path of a given file.
        /// </summary>
        /// <param name="name">file name</param>
        static public string NormalizeFilePath(string name)
        public static string NormalizeFilePath(string name)
        {
            var fi = new FileInfo(name);
            return fi.FullName;
        }
        static public string NormalizeRelativeFilePath(string path)
        public static string NormalizeRelativeFilePath(string path)
        {
            if (path == null)
                return ".\\";
@@ -299,7 +227,7 @@
            return path;
        }
        static public bool IsAbsoluteFilePath(string path)
        public static bool IsAbsoluteFilePath(string path)
        {
            path = path.Trim();
            if (path.Length >= 2 && path[1] == ':')
@@ -316,7 +244,7 @@
        /// </summary>
        /// <param name="streamReader"></param>
        /// <returns>the composite string</returns>
        static private string ReadProFileLine(StreamReader streamReader)
        private static string ReadProFileLine(StreamReader streamReader)
        {
            var line = streamReader.ReadLine();
            if (line == null)
@@ -337,7 +265,7 @@
        /// </summary>
        /// <param name="profile">full name of .pro file to read</param>
        /// <returns>true if this is a subdirs file</returns>
        static public bool IsSubDirsFile(string profile)
        public static bool IsSubDirsFile(string profile)
        {
            StreamReader sr = null;
            try {
@@ -407,55 +335,6 @@
        }
        /// <summary>
        /// Replaces a string in the commandLine, description, outputs and additional dependencies
        /// in all Custom build tools of the project
        /// </summary>
        /// <param name="project">Project</param>
        /// <param name="oldString">String, which is going to be replaced</param>
        /// <param name="oldString">String, which is going to replace the other one</param>
        /// <returns></returns>
        public static void ReplaceInCustomBuildTools(Project project, string oldString, string replaceString)
        {
            var vcPro = (VCProject)project.Object;
            if (vcPro == null)
                return;
            var qtMsBuild = new QtMsBuildContainer(new VCPropertyStorageProvider());
            qtMsBuild.BeginSetItemProperties();
            foreach (VCFile vcfile in (IVCCollection)vcPro.Files) {
                foreach (VCFileConfiguration config in (IVCCollection)vcfile.FileConfigurations) {
                    try {
                        if (vcfile.ItemType == "CustomBuild") {
                            var tool = GetCustomBuildTool(config);
                            if (tool == null)
                                continue;
                            tool.CommandLine = tool.CommandLine
                                .Replace(oldString, replaceString,
                                StringComparison.OrdinalIgnoreCase);
                            tool.Description = tool.Description
                                .Replace(oldString, replaceString,
                                StringComparison.OrdinalIgnoreCase);
                            tool.Outputs = tool.Outputs
                                .Replace(oldString, replaceString,
                                StringComparison.OrdinalIgnoreCase);
                            tool.AdditionalDependencies = tool.AdditionalDependencies
                                .Replace(oldString, replaceString,
                                StringComparison.OrdinalIgnoreCase);
                        } else {
                            var tool = new QtCustomBuildTool(config, qtMsBuild);
                            tool.CommandLine = tool.CommandLine
                                .Replace(oldString, replaceString,
                                StringComparison.OrdinalIgnoreCase);
                        }
                    } catch (Exception) {
                    }
                }
            }
            qtMsBuild.EndSetItemProperties();
        }
        /// <summary>
        /// Since VS2010 it is possible to have VCCustomBuildTools without commandlines
        /// for certain filetypes. We are not interested in them and thus try to read the
        /// tool's commandline. If this causes an exception, we ignore it.
@@ -463,23 +342,19 @@
        /// </summary>
        /// <param name="config">File configuration</param>
        /// <returns></returns>
        static public VCCustomBuildTool GetCustomBuildTool(VCFileConfiguration config)
        public static VCCustomBuildTool GetCustomBuildTool(VCFileConfiguration config)
        {
            var file = config.File as VCFile;
            if (file == null || file.ItemType != "CustomBuild")
                return null;
            var tool = config.Tool as VCCustomBuildTool;
            if (tool == null)
                return null;
            try {
                // TODO: The return value is not used at all?
                var cmdLine = tool.CommandLine;
            } catch {
                return null;
            if (config.File is VCFile file
                && file.ItemType == "CustomBuild"
                && config.Tool is VCCustomBuildTool tool) {
                    try {
                        _ = tool.CommandLine;
                    } catch {
                        return null;
                    }
                    return tool;
            }
            return tool;
            return null;
        }
        /// <summary>
@@ -488,8 +363,10 @@
        /// has to be "CustomBuild"
        /// </summary>
        /// <param name="projectItem">Project Item which needs to have custom build tool</param>
        static public void EnsureCustomBuildToolAvailable(ProjectItem projectItem)
        public static void EnsureCustomBuildToolAvailable(ProjectItem projectItem)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            foreach (Property prop in projectItem.Properties) {
                if (prop.Name == "ItemType") {
                    if ((string)prop.Value != "CustomBuild")
@@ -499,169 +376,10 @@
            }
        }
        /// <summary>
        /// As Qmake -tp vc Adds the full path to the additional dependencies
        /// we need to do the same when toggling project kind to qmake generated.
        /// </summary>
        /// <returns></returns>
        private static string AddFullPathToAdditionalDependencies(string qtDir, string additionalDependencies)
        {
            var returnString = additionalDependencies;
            returnString =
                Regex.Replace(returnString, "Qt(\\S+5?)\\.lib", qtDir + "\\lib\\Qt${1}.lib");
            returnString =
                Regex.Replace(returnString, "(qtmaind?5?)\\.lib", qtDir + "\\lib\\${1}.lib");
            returnString =
                Regex.Replace(returnString, "(enginiod?5?)\\.lib", qtDir + "\\lib\\${1}.lib");
            return returnString;
        }
        /// <summary>
        /// Toggles the kind of a project. If the project is a QMake generated project (qmake -tp vc)
        /// it is transformed to an Qt VS Tools project and vice versa.
        /// </summary>
        /// <param name="project">Project</param>
        /// <returns></returns>
        public static void ToggleProjectKind(Project project)
        {
            if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings)
                return;
            string qtDir = null;
            var vcPro = (VCProject)project.Object;
            if (!IsQMakeProject(project))
                return;
            if (IsQtProject(project)) {
                // TODO: qtPro is never used.
                var qtPro = QtProject.Create(project);
                var vm = QtVersionManager.The();
                qtDir = vm.GetInstallPath(project);
                foreach (var global in (string[])project.Globals.VariableNames) {
                    if (global.StartsWith("Qt5Version", StringComparison.Ordinal))
                        project.Globals.set_VariablePersists(global, false);
                }
                foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                    var compiler = CompilerToolWrapper.Create(config);
                    var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                    var librarian = (VCLibrarianTool)((IVCCollection)config.Tools).Item("VCLibrarianTool");
                    if (compiler != null) {
                        var additionalIncludes = compiler.GetAdditionalIncludeDirectories();
                        additionalIncludes = additionalIncludes.Replace("$(QTDIR)", qtDir,
                            StringComparison.OrdinalIgnoreCase);
                        compiler.SetAdditionalIncludeDirectories(additionalIncludes);
                    }
                    if (linker != null) {
                        linker.AdditionalLibraryDirectories = linker.AdditionalLibraryDirectories.
                            Replace("$(QTDIR)", qtDir, StringComparison.OrdinalIgnoreCase);
                        linker.AdditionalDependencies = AddFullPathToAdditionalDependencies(qtDir, linker.AdditionalDependencies);
                    } else {
                        librarian.AdditionalLibraryDirectories = librarian.AdditionalLibraryDirectories
                            .Replace("$(QTDIR)", qtDir, StringComparison.OrdinalIgnoreCase);
                        librarian.AdditionalDependencies = AddFullPathToAdditionalDependencies(qtDir, librarian.AdditionalDependencies);
                    }
                }
                ReplaceInCustomBuildTools(project, "$(QTDIR)", qtDir);
            } else {
                qtDir = GetQtDirFromQMakeProject(project);
                var vm = QtVersionManager.The();
                var qtVersion = vm.GetQtVersionFromInstallDir(qtDir);
                if (qtVersion == null)
                    qtVersion = vm.GetDefaultVersion();
                if (qtDir == null)
                    qtDir = vm.GetInstallPath(qtVersion);
                var vi = vm.GetVersionInfo(qtVersion);
                var platformName = vi.GetVSPlatformName();
                vm.SaveProjectQtVersion(project, qtVersion, platformName);
                var qtPro = QtProject.Create(project);
                if (!qtPro.SelectSolutionPlatform(platformName) || !qtPro.HasPlatform(platformName)) {
                    var newProject = false;
                    qtPro.CreatePlatform("Win32", platformName, null, vi, ref newProject);
                    if (!qtPro.SelectSolutionPlatform(platformName))
                        Messages.Print("Can't select the platform " + platformName + ".");
                }
                var activeConfig = project.ConfigurationManager.ActiveConfiguration.ConfigurationName;
                var activeVCConfig = (VCConfiguration)((IVCCollection)qtPro.VCProject.Configurations).Item(activeConfig);
                if (activeVCConfig.ConfigurationType == ConfigurationTypes.typeDynamicLibrary) {
                    var compiler = CompilerToolWrapper.Create(activeVCConfig);
                    var linker = (VCLinkerTool)((IVCCollection)activeVCConfig.Tools).Item("VCLinkerTool");
                    var ppdefs = compiler.GetPreprocessorDefinitions();
                    if (ppdefs != null
                        && ppdefs.IndexOf("QT_PLUGIN", StringComparison.Ordinal) > -1
                        && ppdefs.IndexOf("QDESIGNER_EXPORT_WIDGETS", StringComparison.Ordinal) > -1
                        && ppdefs.IndexOf("QtDesigner", StringComparison.Ordinal) > -1
                        && linker.AdditionalDependencies != null
                        && linker.AdditionalDependencies.IndexOf("QtDesigner", StringComparison.Ordinal) > -1) {
                        qtPro.MarkAsDesignerPluginProject();
                    }
                }
                CleanupQMakeDependencies(project);
                foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                    var compiler = CompilerToolWrapper.Create(config);
                    var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                    if (compiler != null) {
                        var additionalIncludes = compiler.AdditionalIncludeDirectories;
                        if (additionalIncludes != null) {
                            ReplaceDirectory(ref additionalIncludes, qtDir, "$(QTDIR)", project);
                            compiler.AdditionalIncludeDirectories = additionalIncludes;
                        }
                    }
                    if (linker != null) {
                        var linkerToolWrapper = new LinkerToolWrapper(linker);
                        var paths = linkerToolWrapper.AdditionalLibraryDirectories;
                        if (paths != null) {
                            ReplaceDirectory(ref paths, qtDir, "$(QTDIR)", project);
                            linkerToolWrapper.AdditionalLibraryDirectories = paths;
                        }
                    }
                }
                ReplaceInCustomBuildTools(project, qtDir, "$(QTDIR)");
                qtPro.TranslateFilterNames();
            }
            project.Save(project.FullName);
        }
        /// <summary>
        /// Replaces every occurrence of oldDirectory with replacement in the array of strings.
        /// Parameter oldDirectory must be an absolute path.
        /// This function converts relative directories to absolute paths internally
        /// and replaces them, if necessary. If no replacement is done, the path isn't altered.
        /// </summary>
        /// <param name="project">The project is needed to convert relative paths to absolute paths.</param>
        private static void ReplaceDirectory(ref List<string> paths, string oldDirectory, string replacement, Project project)
        {
            for (var i = 0; i < paths.Count; ++i) {
                var dirName = paths[i];
                if (dirName.StartsWith("\"", StringComparison.Ordinal) && dirName.EndsWith("\"", StringComparison.Ordinal)) {
                    dirName = dirName.Substring(1, dirName.Length - 2);
                }
                if (!Path.IsPathRooted(dirName)) {
                    // convert to absolute path
                    dirName = Path.Combine(Path.GetDirectoryName(project.FullName), dirName);
                    dirName = Path.GetFullPath(dirName);
                    var alteredDirName = dirName.Replace(oldDirectory, replacement, StringComparison
                        .OrdinalIgnoreCase);
                    if (alteredDirName == dirName)
                        continue;
                    dirName = alteredDirName;
                } else {
                    dirName = dirName.Replace(oldDirectory, replacement, StringComparison
                        .OrdinalIgnoreCase);
                }
                paths[i] = dirName;
            }
        }
        public static string GetQtDirFromQMakeProject(Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var vcProject = project.Object as VCProject;
            if (vcProject == null)
                return null;
@@ -742,13 +460,25 @@
        }
        /// <summary>
        /// Return true if the project is a Qt project, otherwise false.
        /// Return true if the project is a VS tools project; false otherwise.
        /// </summary>
        /// <param name="proj">project</param>
        /// <returns></returns>
        public static bool IsQtProject(VCProject proj)
        public static bool IsVsToolsProject(Project proj)
        {
            if (!IsQMakeProject(proj))
            ThreadHelper.ThrowIfNotOnUIThread(); // C++ Project Type GUID
            if (proj == null || proj.Kind != "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")
                return false;
            return IsVsToolsProject(proj.Object as VCProject);
        }
        /// <summary>
        /// Return true if the project is a VS tools project; false otherwise.
        /// </summary>
        /// <param name="proj">project</param>
        public static bool IsVsToolsProject(VCProject proj)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!IsQtProject(proj))
                return false;
            if (QtProject.GetFormatVersion(proj) >= Resources.qtMinFormatVersion_Settings)
@@ -759,95 +489,39 @@
                return false;
            foreach (var global in envPro.Globals.VariableNames as string[]) {
                if (global.StartsWith("Qt5Version", StringComparison.Ordinal) && envPro.Globals.get_VariablePersists(global))
                if (global.StartsWith("Qt5Version", StringComparison.Ordinal)
                    && envPro.Globals.get_VariablePersists(global)) {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Returns true if the specified project is a Qt Project.
        /// Return true if the project is a Qt project; false otherwise.
        /// </summary>
        /// <param name="proj">project</param>
        public static bool IsQtProject(Project proj)
        {
            try {
                if (proj != null && proj.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")
                    return IsQtProject(proj.Object as VCProject);
            } catch { }
            return false;
            ThreadHelper.ThrowIfNotOnUIThread(); //C++ Project Type GUID
            if (proj == null || proj.Kind != "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")
                return false;
            return IsQtProject(proj.Object as VCProject);
        }
        /// <summary>
        /// Return true if the project is a QMake -tp vc project, otherwise false.
        /// Return true if the project is a Qt project; false otherwise.
        /// </summary>
        /// <param name="proj">project</param>
        /// <returns></returns>
        public static bool IsQMakeProject(VCProject proj)
        public static bool IsQtProject(VCProject proj)
        {
            if (proj == null)
                return false;
            var keyword = proj.keyword;
            if (keyword == null ||
                (!keyword.StartsWith(Resources.qtProjectV2Keyword, StringComparison.Ordinal)
                && !keyword.StartsWith(Resources.qtProjectKeyword, StringComparison.Ordinal))) {
            if (string.IsNullOrEmpty(keyword))
                return false;
            }
            return true;
        }
        /// <summary>
        /// Returns true if the specified project is a QMake -tp vc Project.
        /// </summary>
        /// <param name="proj">project</param>
        public static bool IsQMakeProject(Project proj)
        {
            try {
                if (proj != null && proj.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")
                    return IsQMakeProject(proj.Object as VCProject);
            } catch { }
            return false;
        }
        public static void CleanupQMakeDependencies(Project project)
        {
            var vcPro = (VCProject)project.Object;
            // clean up qmake mess
            var rxp1 = new Regex("\\bQt\\w+d?5?\\.lib\\b");
            var rxp2 = new Regex("\\bQAx\\w+\\.lib\\b");
            var rxp3 = new Regex("\\bqtmaind?.lib\\b");
            var rxp4 = new Regex("\\benginiod?.lib\\b");
            foreach (VCConfiguration cfg in (IVCCollection)vcPro.Configurations) {
                var linker = (VCLinkerTool)((IVCCollection)cfg.Tools).Item("VCLinkerTool");
                if (linker == null || linker.AdditionalDependencies == null)
                    continue;
                var linkerWrapper = new LinkerToolWrapper(linker);
                var deps = linkerWrapper.AdditionalDependencies;
                var newDeps = new List<string>();
                foreach (var lib in deps) {
                    var m1 = rxp1.Match(lib);
                    var m2 = rxp2.Match(lib);
                    var m3 = rxp3.Match(lib);
                    var m4 = rxp4.Match(lib);
                    if (m1.Success)
                        newDeps.Add(m1.ToString());
                    else if (m2.Success)
                        newDeps.Add(m2.ToString());
                    else if (m3.Success)
                        newDeps.Add(m3.ToString());
                    else if (m4.Success)
                        newDeps.Add(m4.ToString());
                    else
                        newDeps.Add(lib);
                }
                // Remove Duplicates
                var uniques = new Dictionary<string, int>();
                foreach (var dep in newDeps)
                    uniques[dep] = 1;
                var uniqueList = new List<string>(uniques.Keys);
                linkerWrapper.AdditionalDependencies = uniqueList;
            }
            return keyword.StartsWith(Resources.qtProjectKeyword, StringComparison.Ordinal)
                || keyword.StartsWith(Resources.qtProjectV2Keyword, StringComparison.Ordinal);
        }
        /// <summary>
@@ -951,6 +625,30 @@
            }
        }
        /// <summary>
        /// Converts all directory separators of the path to the alternate character
        /// directory separator. For instance, FromNativeSeparators("c:\\winnt\\system32")
        /// returns "c:/winnt/system32".
        /// </summary>
        /// <param name="path">The path to convert.</param>
        /// <returns>Returns path using '/' as file separator.</returns>
        public static string FromNativeSeparators(string path)
        {
            return path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
        }
        /// <summary>
        /// Converts all alternate directory separators characters of the path to the native
        /// directory separator. For instance, ToNativeSeparators("c:/winnt/system32")
        /// returns "c:\\winnt\\system32".
        /// </summary>
        /// <param name="path">The path to convert.</param>
        /// <returns>Returns path using '\' as file separator.</returns>
        public static string ToNativeSeparator(string path)
        {
            return path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
        }
        public static string ChangePathFormat(string path)
        {
            return path.Replace('\\', '/');
@@ -981,6 +679,8 @@
        public static void CollapseFilter(UIHierarchyItem item, UIHierarchy hierarchy, string nodeToCollapseFilter)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (string.IsNullOrEmpty(nodeToCollapseFilter))
                return;
@@ -994,6 +694,8 @@
        public static void CollapseFilter(UIHierarchyItem item, UIHierarchy hierarchy)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var subItems = item.UIHierarchyItems;
            if (subItems != null) {
                foreach (UIHierarchyItem innerItem in subItems) {
@@ -1042,7 +744,7 @@
        public static List<string> GetProjectFiles(Project pro, FilesToList filter)
        {
            var fileList = new List<string>();
            ThreadHelper.ThrowIfNotOnUIThread();
            VCProject vcpro;
            try {
@@ -1052,6 +754,7 @@
                return null;
            }
            var fileList = new List<string>();
            var configurationName = pro.ConfigurationManager.ActiveConfiguration.ConfigurationName;
            foreach (VCFile vcfile in (IVCCollection)vcpro.Files) {
@@ -1120,21 +823,24 @@
        /// <param name="fileName"></param>
        public static void RemoveFileInProject(VCProject vcpro, string fileName)
        {
            var qtProj = QtProject.Create(vcpro);
            var fi = new FileInfo(fileName);
            ThreadHelper.ThrowIfNotOnUIThread();
            fileName = new FileInfo(fileName).FullName;
            foreach (VCFile vcfile in (IVCCollection)vcpro.Files) {
                if (vcfile.FullPath.ToLower() == fi.FullName.ToLower()) {
                if (vcfile.FullPath.Equals(fileName, StringComparison.OrdinalIgnoreCase)) {
                    vcpro.RemoveFile(vcfile);
                    qtProj.MoveFileToDeletedFolder(vcfile);
                    QtProject.Create(vcpro)?.MoveFileToDeletedFolder(vcfile);
                }
            }
        }
        public static Project GetSelectedProject(DTE dteObject)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (dteObject == null)
                return null;
            Array prjs = null;
            try {
                prjs = (Array)dteObject.ActiveSolutionProjects;
@@ -1146,30 +852,23 @@
                return null;
            // don't handle multiple selection... use the first one
            if (prjs.GetValue(0) is Project)
                return (Project)prjs.GetValue(0);
            if (prjs.GetValue(0) is Project project)
                return project;
            return null;
        }
        public static Project GetActiveDocumentProject(DTE dteObject)
        {
            if (dteObject == null)
                return null;
            var doc = dteObject.ActiveDocument;
            if (doc == null)
                return null;
            if (doc.ProjectItem == null)
                return null;
            return doc.ProjectItem.ContainingProject;
            ThreadHelper.ThrowIfNotOnUIThread();
            return dteObject?.ActiveDocument?.ProjectItem?.ContainingProject;
        }
        public static Project GetSingleProjectInSolution(DTE dteObject)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var projectList = ProjectsInSolution(dteObject);
            if (dteObject == null || dteObject.Solution == null ||
                    projectList.Count != 1)
            if (projectList.Count != 1)
                return null; // no way to know which one to select
            return projectList[0];
@@ -1182,22 +881,24 @@
        /// </summary>
        public static Project GetSelectedQtProject(DTE dteObject)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            // can happen sometimes shortly after starting VS
            if (dteObject == null || dteObject.Solution == null
                || ProjectsInSolution(dteObject).Count == 0)
            if (ProjectsInSolution(dteObject).Count == 0)
                return null;
            Project pro;
            if ((pro = GetSelectedProject(dteObject)) == null) {
            var pro = GetSelectedProject(dteObject);
            if (pro == null) {
                if ((pro = GetSingleProjectInSolution(dteObject)) == null)
                    pro = GetActiveDocumentProject(dteObject);
            }
            return IsQtProject(pro) ? pro : null;
            return IsVsToolsProject(pro) ? pro : null;
        }
        public static VCFile[] GetSelectedFiles(DTE dteObject)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (GetSelectedQtProject(dteObject) == null)
                return null;
@@ -1239,6 +940,8 @@
        public static RccOptions ParseRccOptions(string cmdLine, VCFile qrcFile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var pro = VCProjectToProject((VCProject)qrcFile.project);
            var rccOpts = new RccOptions(pro, qrcFile);
@@ -1261,11 +964,17 @@
        public static Project VCProjectToProject(VCProject vcproj)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return (Project)vcproj.Object;
        }
        public static List<Project> ProjectsInSolution(DTE dteObject)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (dteObject == null)
                return new List<Project>();
            var projects = new List<Project>();
            var solution = dteObject.Solution;
            if (solution != null) {
@@ -1287,6 +996,8 @@
        private static void addSubProjects(Project prj, ref List<Project> projects)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            // If the actual object of the project is null then the project was probably unloaded.
            if (prj.Object == null)
                return;
@@ -1303,6 +1014,8 @@
        private static void addSubProjects(ProjectItems subItems, ref List<Project> projects)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (subItems == null)
                return;
@@ -1393,21 +1106,6 @@
            return true;
        }
        public static string FindFileInPATH(string fileName)
        {
            var envPATH = Environment.ExpandEnvironmentVariables("%PATH%");
            var directories = envPATH.Split(';');
            foreach (var directory in directories) {
                var fullFilePath = directory;
                if (!fullFilePath.EndsWith("\\", StringComparison.Ordinal))
                    fullFilePath += '\\';
                fullFilePath += fileName;
                if (File.Exists(fullFilePath))
                    return fullFilePath;
            }
            return null;
        }
        /// <summary>
        /// This method copies the specified directory and all its child directories and files to
        /// the specified destination. The destination directory is created if it does not exist.
@@ -1453,13 +1151,14 @@
            string platformName,
            string filePath = null)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (project == null
                || string.IsNullOrEmpty(configName)
                || string.IsNullOrEmpty(platformName))
                return false;
            var vcProject = project.Object as VCProject;
            if (filePath == null) {
                var vcConfig = (from VCConfiguration _config
                                in (IVCCollection)vcProject.Configurations
@@ -1503,12 +1202,10 @@
            VCProject vcProj = null;
            VCFile vcFile = null;
            string configName = "", platformName = "";
            var vcConfig = config as VCConfiguration;
            if (vcConfig != null) {
            if (config is VCConfiguration vcConfig) {
                vcProj = vcConfig.project as VCProject;
                configName = vcConfig.ConfigurationName;
                var vcPlatform = vcConfig.Platform as VCPlatform;
                if (vcPlatform != null)
                if (vcConfig.Platform is VCPlatform vcPlatform)
                    platformName = vcPlatform.Name;
                try {
                    expanded = vcConfig.Evaluate(expanded);
@@ -1520,11 +1217,9 @@
                vcFile = vcFileConfig.File as VCFile;
                if (vcFile != null)
                    vcProj = vcFile.project as VCProject;
                var vcProjConfig = vcFileConfig.ProjectConfiguration as VCConfiguration;
                if (vcProjConfig != null) {
                if (vcFileConfig.ProjectConfiguration is VCConfiguration vcProjConfig) {
                    configName = vcProjConfig.ConfigurationName;
                    var vcPlatform = vcProjConfig.Platform as VCPlatform;
                    if (vcPlatform != null)
                    if (vcProjConfig.Platform is VCPlatform vcPlatform)
                        platformName = vcPlatform.Name;
                }
                try {
@@ -1637,6 +1332,7 @@
            return true;
        }
#if VS2017
        private static string GetRegistrySoftwareString(string subKeyName, string valueName)
        {
            var keyName = new StringBuilder();
@@ -1644,28 +1340,25 @@
            if (System.Environment.Is64BitOperatingSystem && IntPtr.Size == 4)
                keyName.Append(@"WOW6432Node\");
            keyName.Append(subKeyName);
            try {
                using (var key = Registry.LocalMachine.OpenSubKey(keyName.ToString(), false)) {
                    if (key == null)
                        return ""; //key not found
                    RegistryValueKind valueKind = key.GetValueKind(valueName);
                    if (valueKind != RegistryValueKind.String
                        && valueKind != RegistryValueKind.ExpandString) {
                        return ""; //wrong value kind
                    }
                    Object objValue = key.GetValue(valueName);
                    if (objValue == null)
                        return ""; //error getting value
                    return objValue.ToString();
                }
            } catch {
                return "";
            }
        }
#endif
        public static string GetWindows10SDKVersion()
        {
@@ -1713,11 +1406,6 @@
            string vcPath = Path.Combine(vsPath, "VC");
#endif
            return vcPath;
        }
        public static bool SetVCVars(ProcessStartInfo startInfo)
        {
            return SetVCVars(null, startInfo);
        }
        public static bool SetVCVars(VersionInformation VersionInfo, ProcessStartInfo startInfo)
@@ -1820,8 +1508,21 @@
                    }
                }
            }
            // Get PATH
            string envPath = startInfo.EnvironmentVariables["PATH"];
            string clPath = envPath.Split(';')
            // Remove invalid chars
            envPath = string.Join("", envPath.Split(Path.GetInvalidPathChars()));
            // Split into list of paths
            var paths = envPath
                .Split(';')
                .Where(x => !string.IsNullOrEmpty(x))
                .Select(x => x.Trim());
            // Check if cl.exe is in PATH
            string clPath = paths
                .Select(path => Path.Combine(path, "cl.exe"))
                .Where(pathToCl => File.Exists(pathToCl))
                .FirstOrDefault();
@@ -1872,12 +1573,6 @@
            } else {
                return canonicalPath;
            }
        }
        public static bool PathEquals(string path1, string path2)
        {
            return (CanonicalPath(path1).Equals(CanonicalPath(path2),
                StringComparison.InvariantCultureIgnoreCase));
        }
        public static bool PathIsRelativeTo(string path, string subPath)
QtVsTools.Core/Legacy/QtProject.cs
New file
@@ -0,0 +1,290 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
namespace QtVsTools.Core.Legacy
{
    using Core;
    public static class QtProject
    {
        public static void MarkAsDesignerPluginProject(Core.QtProject qtPro)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            qtPro.Project.Globals["IsDesignerPlugin"] = true.ToString();
            if (!qtPro.Project.Globals.get_VariablePersists("IsDesignerPlugin"))
                qtPro.Project.Globals.set_VariablePersists("IsDesignerPlugin", true);
        }
        public static bool PromptChangeQtVersion(Project project, string oldVersion, string newVersion)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var versionManager = Core.QtVersionManager.The();
            var viOld = versionManager.GetVersionInfo(oldVersion);
            var viNew = versionManager.GetVersionInfo(newVersion);
            if (viOld == null || viNew == null)
                return true;
            var oldIsWinRt = viOld.isWinRT();
            var newIsWinRt = viNew.isWinRT();
            if (newIsWinRt == oldIsWinRt || newIsWinRt == IsWinRT(project))
                return true;
            var caption = string.Format("Change Qt Version ({0})", project.Name);
            var text = string.Format(
                "Changing Qt version from {0} to {1}.\r\n" +
                "Project might not build. Are you sure?",
                newIsWinRt ? "Win32" : "WinRT",
                newIsWinRt ? "WinRT" : "Win32"
            );
            return MessageBox.Show(text, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
                == DialogResult.Yes;
        }
        public static bool HasModule(Project project, int id, string qtVersion = null)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var foundInIncludes = false;
            var foundInLibs = false;
            var vm = Core.QtVersionManager.The();
            var versionInfo = qtVersion != null ? vm.GetVersionInfo(qtVersion)
                : vm.GetVersionInfo(project);
            if (versionInfo == null)
                versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion());
            if (versionInfo == null)
                return false; // neither a default or project Qt version
            var info = QtModules.Instance.Module(id, versionInfo.qtMajor);
            if (info == null)
                return false;
            var vcPro = project.Object as VCProject;
            foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                var compiler = CompilerToolWrapper.Create(config);
                var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                if (compiler != null) {
                    if (compiler.GetAdditionalIncludeDirectories() == null)
                        continue;
                    var incPathList = info.GetIncludePath();
                    var includeDirs = compiler.GetAdditionalIncludeDirectoriesList();
                    foundInIncludes = (incPathList.Count > 0);
                    foreach (var incPath in incPathList) {
                        var fixedIncludeDir = FixFilePathForComparison(incPath);
                        if (!includeDirs.Any(dir =>
                            FixFilePathForComparison(dir) == fixedIncludeDir)) {
                            foundInIncludes = false;
                            break;
                        }
                    }
                }
                if (foundInIncludes)
                    break;
                List<string> libs = null;
                if (linker != null) {
                    var linkerWrapper = new LinkerToolWrapper(linker);
                    libs = linkerWrapper.AdditionalDependencies;
                }
                if (libs != null) {
                    var moduleLibs = info.GetLibs(IsDebugConfiguration(config), versionInfo);
                    foundInLibs = moduleLibs.All(moduleLib => libs.Contains(moduleLib));
                }
            }
            return foundInIncludes || foundInLibs;
        }
        public static void AddModule(Project project, int id)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (HasModule(project, id))
                return;
            var vm = Core.QtVersionManager.The();
            var versionInfo = vm.GetVersionInfo(project);
            if (versionInfo == null)
                versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion());
            var vcPro = project.Object as VCProject;
            foreach (VCConfiguration config in vcPro.Configurations as IVCCollection) {
                var info = QtModules.Instance.Module(id, versionInfo.qtMajor);
                if (Core.QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) {
                    var config3 = config as VCConfiguration3;
                    if (config3 == null)
                        continue;
                    if (!string.IsNullOrEmpty(info.proVarQT)) {
                        var qtModulesValue = config.GetUnevaluatedPropertyValue("QtModules");
                        var qtModules = new HashSet<string>(
                            !string.IsNullOrEmpty(qtModulesValue)
                                ? qtModulesValue.Split(';')
                                : new string[] { });
                        qtModules.UnionWith(info.proVarQT.Split(' '));
                        config3.SetPropertyValue(Resources.projLabelQtSettings, true,
                            "QtModules", string.Join(";", qtModules));
                    }
                    // In V3 project format, compiler and linker options
                    // required by modules are set by Qt/MSBuild.
                    continue;
                }
                var compiler = CompilerToolWrapper.Create(config);
                var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                if (compiler != null) {
                    foreach (var define in info.Defines)
                        compiler.AddPreprocessorDefinition(define);
                    var incPathList = info.GetIncludePath();
                    foreach (var incPath in incPathList)
                        compiler.AddAdditionalIncludeDirectories(incPath);
                }
                if (linker != null) {
                    var moduleLibs = info.GetLibs(IsDebugConfiguration(config), versionInfo);
                    var linkerWrapper = new LinkerToolWrapper(linker);
                    var additionalDeps = linkerWrapper.AdditionalDependencies;
                    var dependenciesChanged = false;
                    if (additionalDeps == null || additionalDeps.Count == 0) {
                        additionalDeps = moduleLibs;
                        dependenciesChanged = true;
                    } else {
                        foreach (var moduleLib in moduleLibs) {
                            if (!additionalDeps.Contains(moduleLib)) {
                                additionalDeps.Add(moduleLib);
                                dependenciesChanged = true;
                            }
                        }
                    }
                    if (dependenciesChanged)
                        linkerWrapper.AdditionalDependencies = additionalDeps;
                }
            }
        }
        public static void RemoveModule(Project project, int id)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var vm = Core.QtVersionManager.The();
            var versionInfo = vm.GetVersionInfo(project);
            if (versionInfo == null)
                versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion());
            var vcPro = project.Object as VCProject;
            foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                var compiler = CompilerToolWrapper.Create(config);
                var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                var info = QtModules.Instance.Module(id, versionInfo.qtMajor);
                if (compiler != null) {
                    foreach (var define in info.Defines)
                        compiler.RemovePreprocessorDefinition(define);
                    var additionalIncludeDirs = compiler.AdditionalIncludeDirectories;
                    if (additionalIncludeDirs != null) {
                        var lst = new List<string>(additionalIncludeDirs);
                        foreach (var includePath in info.IncludePath) {
                            lst.Remove(includePath);
                            lst.Remove('\"' + includePath + '\"');
                        }
                        compiler.AdditionalIncludeDirectories = lst;
                    }
                }
                if (linker != null && linker.AdditionalDependencies != null) {
                    var linkerWrapper = new LinkerToolWrapper(linker);
                    var moduleLibs = info.GetLibs(IsDebugConfiguration(config), versionInfo);
                    var additionalDependencies = linkerWrapper.AdditionalDependencies;
                    var dependenciesChanged = false;
                    foreach (var moduleLib in moduleLibs)
                        dependenciesChanged |= additionalDependencies.Remove(moduleLib);
                    if (dependenciesChanged)
                        linkerWrapper.AdditionalDependencies = additionalDependencies;
                }
            }
        }
        internal static bool IsDesignerPluginProject(Core.QtProject qtPro)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var b = false;
            if (qtPro.Project.Globals.get_VariablePersists("IsDesignerPlugin")) {
                var s = qtPro.Project.Globals["IsDesignerPlugin"] as string;
                try {
                    b = bool.Parse(s);
                } catch { }
            }
            return b;
        }
        private static bool IsWinRT(Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            try {
                var vcProject = project.Object as VCProject;
                var vcConfigs = vcProject.Configurations as IVCCollection;
                var vcConfig = vcConfigs.Item(1) as VCConfiguration;
                var appType = vcConfig.GetEvaluatedPropertyValue("ApplicationType");
                if (appType == "Windows Store")
                    return true;
            } catch { }
            return false;
        }
        private static string FixFilePathForComparison(string path)
        {
            return HelperFunctions.NormalizeRelativeFilePath(path).ToLower();
        }
        private static bool IsDebugConfiguration(VCConfiguration conf)
        {
            var tool = CompilerToolWrapper.Create(conf);
            if (tool != null) {
                return tool.RuntimeLibrary == runtimeLibraryOption.rtMultiThreadedDebug
                    || tool.RuntimeLibrary == runtimeLibraryOption.rtMultiThreadedDebugDLL;
            }
            return false;
        }
    }
}
QtVsTools.Core/Legacy/QtVSIPSettings.cs
New file
@@ -0,0 +1,226 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using Microsoft.VisualStudio.Shell;
using QtVsTools.Common;
namespace QtVsTools.Core.Legacy
{
    public static class QtVSIPSettings
    {
        #region UIC
        public static string GetUicDirectory(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return QtVSIPSettingsShared.GetDirectory(project, Resources.uicDirKeyword);
        }
        public static void SaveUicDirectory(EnvDTE.Project project, string directory)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (directory == null)
                directory = QtVSIPSettingsShared.GetDirectory(project, Resources.uicDirKeyword);
            SaveDirectory(project, Resources.uicDirKeyword, directory);
        }
        #endregion
        #region MOC
        public static string GetMocDirectory(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return QtVSIPSettingsShared.GetDirectory(project, Resources.mocDirKeyword);
        }
        public static void SaveMocDirectory(EnvDTE.Project project, string directory)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (directory == null)
                directory = QtVSIPSettingsShared.GetDirectory(project, Resources.mocDirKeyword);
            SaveDirectory(project, Resources.mocDirKeyword, directory);
        }
        public static string GetMocOptions()
        {
            return QtVSIPSettingsShared.GetOption(Resources.mocOptionsKeyword);
        }
        public static string GetMocOptions(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return QtVSIPSettingsShared.GetOption(project, Resources.mocOptionsKeyword);
        }
        public static void SaveMocOptions(EnvDTE.Project project, string options)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (options == null)
                options = GetMocOptions();
            SaveOption(project, Resources.mocOptionsKeyword, options);
        }
        #endregion
        #region LUpdate
        public static bool GetLUpdateOnBuild(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (QtVSIPSettingsShared.GetProjectQtSetting(project, "QtRunLUpdateOnBuild") == "true")
                return true;
            return QtVSIPSettingsShared.GetBoolValue(project, Resources.lupdateKeyword);
        }
        public static void SaveLUpdateOnBuild(EnvDTE.Project project, bool value)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            SetBoolValue(project, Resources.lupdateKeyword, value);
        }
        public static string GetLUpdateOptions()
        {
            return QtVSIPSettingsShared.GetOption(Resources.lupdateOptionsKeyword);
        }
        public static string GetLUpdateOptions(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            string qtLUpdateOptions = QtVSIPSettingsShared.GetProjectQtSetting(project, "QtLUpdateOptions");
            if (!string.IsNullOrEmpty(qtLUpdateOptions))
                return qtLUpdateOptions;
            return QtVSIPSettingsShared.GetOption(project, Resources.lupdateOptionsKeyword);
        }
        public static void SaveLUpdateOptions(EnvDTE.Project project, string options)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (options == null)
                options = GetLUpdateOptions();
            SaveOption(project, Resources.lupdateOptionsKeyword, options);
        }
        #endregion
        #region LRelease
        public static string GetLReleaseOptions()
        {
            return QtVSIPSettingsShared.GetOption(Resources.lreleaseOptionsKeyword);
        }
        public static string GetLReleaseOptions(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            string qtLReleaseOptions = QtVSIPSettingsShared.GetProjectQtSetting(project, "QtLReleaseOptions");
            if (!string.IsNullOrEmpty(qtLReleaseOptions))
                return qtLReleaseOptions;
            return QtVSIPSettingsShared.GetOption(project, Resources.lreleaseOptionsKeyword);
        }
        public static void SaveLReleaseOptions(EnvDTE.Project project, string options)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (options == null)
                options = GetLReleaseOptions();
            SaveOption(project, Resources.lreleaseOptionsKeyword, options);
        }
        #endregion
        #region RCC
        public static string GetRccDirectory(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return QtVSIPSettingsShared.GetDirectory(project, Resources.rccDirKeyword);
        }
        public static void SaveRccDirectory(EnvDTE.Project project, string directory)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (directory == null)
                directory = QtVSIPSettingsShared.GetDirectory(project, Resources.rccDirKeyword);
            SaveDirectory(project, Resources.rccDirKeyword, directory);
        }
        #endregion
        #region QML
        public static bool GetQmlDebug(EnvDTE.Project project)
        {
            return Core.QtProject.Create(project).QmlDebug;
        }
        public static void SaveQmlDebug(EnvDTE.Project project, bool enabled)
        {
            Core.QtProject.Create(project).QmlDebug = enabled;
        }
        #endregion
        private static void SaveDirectory(EnvDTE.Project project, string type, string dir)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            dir = HelperFunctions.NormalizeRelativeFilePath(dir);
            project.Globals[type] = dir;
            if (!project.Globals.get_VariablePersists(type))
                project.Globals.set_VariablePersists(type, true);
            QtVSIPSettingsShared.CleanUpCache(project);
        }
        private static void SaveOption(EnvDTE.Project project, string type, string option)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            project.Globals[type] = option;
            if (!project.Globals.get_VariablePersists(type))
                project.Globals.set_VariablePersists(type, true);
        }
        private static void SetBoolValue(EnvDTE.Project project, string type, bool value)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            project.Globals[type] = Convert.ToInt32(value).ToString();
            if (!project.Globals.get_VariablePersists(type))
                project.Globals.set_VariablePersists(type, true);
        }
    }
}
QtVsTools.Core/Legacy/QtVersionManager.cs
New file
@@ -0,0 +1,63 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Core.Legacy
{
    public static class QtVersionManager
    {
        public static string GetSolutionQtVersion(EnvDTE.Solution solution)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (solution == null)
                return null;
            if (solution.Globals.get_VariableExists("Qt5Version")) {
                var version = (string)solution.Globals["Qt5Version"];
                return Core.QtVersionManager.The().VerifyIfQtVersionExists(version) ? version : null;
            }
            return null;
        }
        public static bool SaveSolutionQtVersion(EnvDTE.Solution solution, string version)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!Core.QtVersionManager.The().IsVersionAvailable(version) && version != "$(DefaultQtVersion)")
                return false;
            solution.Globals["Qt5Version"] = version;
            if (!solution.Globals.get_VariablePersists("Qt5Version"))
                solution.Globals.set_VariablePersists("Qt5Version", true);
            return true;
        }
    }
}
QtVsTools.Core/LinkerToolWrapper.cs
@@ -26,11 +26,11 @@
**
****************************************************************************/
using Microsoft.VisualStudio.VCProjectEngine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.VCProjectEngine;
namespace QtVsTools.Core
{
@@ -39,7 +39,7 @@
    /// </summary>
    public class LinkerToolWrapper
    {
        private VCLinkerTool linker;
        private readonly VCLinkerTool linker;
        public LinkerToolWrapper(VCLinkerTool linkerTool)
        {
QtVsTools.Core/MainWinWrapper.cs
@@ -28,30 +28,18 @@
using System;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Core
{
    public class MainWinWrapper : IWin32Window
    {
        private readonly EnvDTE.DTE dteObject;
        public IntPtr Handle { get; }
        public MainWinWrapper(EnvDTE.DTE dte)
        {
            dteObject = dte;
        }
        public IntPtr Handle
        {
            get
            {
                if (dteObject != null)
#if VS2022
                    return dteObject.MainWindow.HWnd;
#else
                    return new IntPtr(dteObject.MainWindow.HWnd);
#endif
                return new IntPtr(0);
            }
            ThreadHelper.ThrowIfNotOnUIThread();
            Handle = new IntPtr((long)dte.MainWindow.HWnd);
        }
    }
}
QtVsTools.Core/Messages.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,34 +26,25 @@
**
****************************************************************************/
using EnvDTE;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Thread = System.Threading.Thread;
using System.Windows.Forms;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Threading;
using QtVsTools.VisualStudio;
using Task = System.Threading.Tasks.Task;
namespace QtVsTools.Core
{
    using VisualStudio;
    public static class Messages
    {
        private static OutputWindow Window { get; set; }
        private static OutputWindowPane Pane { get; set; }
        private static OutputWindowPane _BuildPane;
        private static OutputWindowPane BuildPane
        {
            get
            {
                return _BuildPane ?? (_BuildPane = Window.OutputWindowPanes.Cast<OutputWindowPane>()
                    .Where(pane => pane.Guid == "{1BD8A850-02D1-11D1-BEE7-00A0C913D1F8}")
                    .FirstOrDefault());
            }
        }
        private static readonly string PaneName = "Qt VS Tools";
        private static readonly Guid PaneGuid = new Guid("8f6a1e44-fa0b-49e5-9934-1c050555350e");
        /// <summary>
        /// Show a message on the output pane.
@@ -69,13 +60,15 @@
            FlushMessages();
        }
        static void OutputWindowPane_Print(string text)
        public static void Log(this Exception exception, bool clear = false, bool activate = false)
        {
            OutputWindowPane_Init();
            Pane.OutputString(text + "\r\n");
            // show buildPane if a build is in progress
            if (Dte.Solution.SolutionBuild.BuildState == vsBuildState.vsBuildStateInProgress)
                BuildPane?.Activate();
            msgQueue.Enqueue(new Msg()
            {
                Clear = clear,
                Text = ExceptionToString(exception),
                Activate = activate
            });
            FlushMessages();
        }
        /// <summary>
@@ -90,50 +83,44 @@
            FlushMessages();
        }
        static void OutputWindowPane_Activate()
        static async Task OutputWindowPane_ActivateAsync()
        {
            OutputWindowPane_Init();
            Pane?.Activate();
            await OutputWindowPane_InitAsync();
            await Pane?.ActivateAsync();
        }
        private static string ExceptionToString(System.Exception e)
        private static string ExceptionToString(System.Exception exception)
        {
            return e.Message + "\r\n" + "(" + e.StackTrace.Trim() + ")";
            return $"An exception ({exception.GetType().Name}) occurred.\r\n"
                   + $"Message:\r\n   {exception.Message}\r\n"
                   + $"Stack Trace:\r\n   {exception.StackTrace.Trim()}\r\n";
        }
        private static readonly string ErrorString = SR.GetString("Messages_ErrorOccured");
        private static readonly string WarningString = SR.GetString("Messages_Warning");
        private static readonly string SolutionString = SR.GetString("Messages_SolveProblem");
        static public void DisplayCriticalErrorMessage(System.Exception e)
        {
            MessageBox.Show(ErrorString +
                ExceptionToString(e),
                SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        static public void DisplayCriticalErrorMessage(string msg)
        public static void DisplayCriticalErrorMessage(string msg)
        {
            MessageBox.Show(ErrorString +
                msg,
                SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        static public void DisplayErrorMessage(System.Exception e)
        public static void DisplayErrorMessage(System.Exception e)
        {
            MessageBox.Show(ErrorString +
                ExceptionToString(e),
            MessageBox.Show(ExceptionToString(e),
                SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        static public void DisplayErrorMessage(string msg)
        public static void DisplayErrorMessage(string msg)
        {
            MessageBox.Show(ErrorString +
                msg,
                SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        static public void DisplayWarningMessage(System.Exception e, string solution)
        public static void DisplayWarningMessage(System.Exception e, string solution)
        {
            MessageBox.Show(WarningString +
                ExceptionToString(e) +
@@ -142,7 +129,7 @@
                SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
        static public void DisplayWarningMessage(string msg)
        public static void DisplayWarningMessage(string msg)
        {
            MessageBox.Show(WarningString +
                msg,
@@ -158,10 +145,10 @@
            FlushMessages();
        }
        static void OutputWindowPane_Clear()
        static async Task OutputWindowPane_ClearAsync()
        {
            OutputWindowPane_Init();
            Pane?.Clear();
            await OutputWindowPane_InitAsync();
            await Pane?.ClearAsync();
        }
        class Msg
@@ -171,33 +158,20 @@
            public bool Activate { get; set; } = false;
        }
        static bool shuttingDown = false;
        static ConcurrentQueue<Msg> msgQueue = new ConcurrentQueue<Msg>();
        static DTE Dte { get; set; } = null;
        static readonly ConcurrentQueue<Msg> msgQueue = new ConcurrentQueue<Msg>();
        private static void OnBeginShutdown()
        private static async Task OutputWindowPane_InitAsync()
        {
            shuttingDown = true;
        }
        private static void OutputWindowPane_Init()
        {
            if (Dte == null)
                Dte = VsServiceProvider.GetService<DTE>();
            var t = Stopwatch.StartNew();
            while (Pane == null && t.ElapsedMilliseconds < 5000) {
                try {
                    Window = Dte.Windows.Item(Constants.vsWindowKindOutput).Object as OutputWindow;
                    Pane = Window?.OutputWindowPanes.Add(SR.GetString("Resources_QtVsTools"));
                } catch {
                }
            try {
                if (Pane == null)
                    Thread.Yield();
                    Pane = await OutputWindowPane.CreateAsync(PaneName, PaneGuid);
            } catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine(ex);
            }
            Dte.Events.DTEEvents.OnBeginShutdown += OnBeginShutdown;
        }
        public static JoinableTaskFactory JoinableTaskFactory { get; set; }
        static readonly object staticCriticalSection = new object();
        static Task FlushTask { get; set; }
        static EventWaitHandle MessageReady { get; set; }
@@ -209,27 +183,21 @@
                    MessageReady = new EventWaitHandle(false, EventResetMode.AutoReset);
                    FlushTask = Task.Run(async () =>
                    {
                        while (!shuttingDown) {
                        var package = VsServiceProvider.Instance as Package;
                        while (!package.Zombied) {
                            if (!await MessageReady.ToTask(3000))
                                continue;
                            while (!msgQueue.IsEmpty) {
                                Msg msg;
                                if (!msgQueue.TryDequeue(out msg)) {
                                if (!msgQueue.TryDequeue(out Msg msg)) {
                                    await Task.Yield();
                                    continue;
                                }
                                ////////////////////////////////////////////////////////////////////
                                // Switch to main (UI) thread
                                await JoinableTaskFactory.SwitchToMainThreadAsync();
                                if (msg.Clear)
                                    OutputWindowPane_Clear();
                                    await OutputWindowPane_ClearAsync();
                                if (msg.Text != null)
                                    OutputWindowPane_Print(msg.Text);
                                    await OutputWindowPane_PrintAsync(msg.Text);
                                if (msg.Activate)
                                    OutputWindowPane_Activate();
                                ////////////////////////////////////////////////////////////////////
                                // Switch to background thread
                                await TaskScheduler.Default;
                                    await OutputWindowPane_ActivateAsync();
                            }
                        }
                    });
@@ -237,5 +205,15 @@
            }
            MessageReady.Set();
        }
        static async Task OutputWindowPane_PrintAsync(string text)
        {
            var active = await OutputWindowPane.GetActiveAsync();
            await OutputWindowPane_InitAsync();
            await Pane.PrintAsync(text);
            (active?.ActivateAsync()).Forget();
        }
    }
}
QtVsTools.Core/MocCmdChecker.cs
@@ -34,8 +34,8 @@
{
    class MocCmdChecker
    {
        private Regex backslashRegEx = new Regex(@"\\+\.?\\+");
        private Regex endRegEx = new Regex(@"\\\.?$");
        private readonly Regex backslashRegEx = new Regex(@"\\+\.?\\+");
        private readonly Regex endRegEx = new Regex(@"\\\.?$");
        private string NormalizePath(string path)
        {
QtVsTools.Core/MsBuildProject.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -30,22 +30,22 @@
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using QtVsTools.Core.QtMsBuild;
using System.Text.RegularExpressions;
using Microsoft.Build.Construction;
using Microsoft.Build.Execution;
using Microsoft.Build.Evaluation;
using QtVsTools.VisualStudio;
using QtVsTools.SyntaxAnalysis;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Core
{
    using QtMsBuild;
    using SyntaxAnalysis;
    using static HelperFunctions;
    using static RegExpr;
    using static SyntaxAnalysis.RegExpr;
    public class MsBuildProject
    {
@@ -65,7 +65,8 @@
            User,
            Count
        }
        MsBuildXmlFile[] files = new MsBuildXmlFile[(int)Files.Count];
        readonly MsBuildXmlFile[] files = new MsBuildXmlFile[(int)Files.Count];
        MsBuildProject()
        {
@@ -94,7 +95,7 @@
            }
        }
        private static XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
        private static readonly XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
        public static MsBuildProject Load(string pathToProject)
        {
@@ -293,12 +294,8 @@
            if (ConfigCondition == null)
                return false;
            // Get default Qt dir
            string defaultQtDir = null;
            var defaultVersionName = QtVersionManager.The().GetDefaultVersion();
            var defaultVersion = QtVersionManager.The().GetVersionInfo(defaultVersionName);
            if (defaultVersion != null)
                defaultQtDir = defaultVersion.qtDir;
            // Get project configurations
            var configs = this[Files.Project].xml
@@ -314,15 +311,6 @@
                .FirstOrDefault();
            if (globals == null)
                return false;
            // Get project configuration properties
            var configProps = this[Files.Project].xml
                .Elements(ns + "Project")
                .Elements(ns + "PropertyGroup")
                .Where(pg =>
                    (string)pg.Attribute("Label") == "Configuration"
                    && pg.Attribute("Condition") != null)
                .ToDictionary(pg => (string)pg.Attribute("Condition"));
            // Set Qt project format version
            var projKeyword = globals
@@ -386,10 +374,8 @@
                foreach (var pg in uncategorizedPropertyGroups) {
                    foreach (var p in pg.Elements().ToList()) {
                        var condition = p.Attribute("Condition") ?? pg.Attribute("Condition");
                        XElement configPropertyGroup = null;
                        if (condition != null)
                            propertyGroups.TryGetValue((string)condition, out configPropertyGroup);
                        if (configPropertyGroup != null) {
                        if (condition != null && propertyGroups
                            .TryGetValue((string)condition, out XElement configPropertyGroup)) {
                            p.Remove();
                            p.SetAttributeValue("Condition", null);
                            configPropertyGroup.Add(p);
@@ -535,12 +521,10 @@
                foreach (var configQtSettings in qtSettings) {
                    var configCondition = (string)configQtSettings.Attribute("Condition");
                    XElement oldConfigQtInstall;
                    if (oldQtInstall.TryGetValue(configCondition, out oldConfigQtInstall))
                    if (oldQtInstall.TryGetValue(configCondition, out XElement oldConfigQtInstall))
                        configQtSettings.Add(oldConfigQtInstall);
                    XElement oldConfigQtSettings;
                    if (oldQtSettings.TryGetValue(configCondition, out oldConfigQtSettings)) {
                    if (oldQtSettings.TryGetValue(configCondition, out XElement oldConfigQtSettings)) {
                        foreach (var qtSetting in oldConfigQtSettings.Elements())
                            configQtSettings.Add(qtSetting);
                    }
@@ -600,6 +584,11 @@
                .Elements(ns + "ItemDefinitionGroup")
                .Elements(ns + "Link");
            var resourceCompiler = this[Files.Project].xml
                .Elements(ns + "Project")
                .Elements(ns + "ItemDefinitionGroup")
                .Elements(ns + "ResourceCompile");
            // Qt module names, to copy to QtModules property
            var moduleNames = new HashSet<string>();
@@ -613,9 +602,9 @@
            var moduleLibs = new HashSet<string>();
            // Go through all known Qt modules and check which ones are currently being used
            foreach (var module in QtModules.Instance.GetAvailableModules()) {
            foreach (var module in QtModules.Instance.GetAvailableModules(defaultVersion.qtMajor)) {
                if (IsModuleUsed(module, compiler, linker)) {
                if (IsModuleUsed(module, compiler, linker, resourceCompiler)) {
                    // Qt module names, to copy to QtModules property
                    if (!string.IsNullOrEmpty(module.proVarQT))
@@ -670,6 +659,12 @@
                    .Select(x => Unquote(x))
                    // Exclude paths rooted on $(QTDIR)
                    .Where(x => !x.StartsWith("$(QTDIR)", IGNORE_CASE))));
            }
            // Remove Qt module macros from resource compiler properties
            foreach (var defines in resourceCompiler.Elements(ns + "PreprocessorDefinitions")) {
                defines.SetValue(string.Join(";", defines.Value.Split(';')
                    .Where(x => !moduleDefines.Contains(x))));
            }
            // Add Qt module names to QtModules project property
@@ -778,7 +773,8 @@
        bool IsModuleUsed(
            QtModule module,
            IEnumerable<XElement> compiler,
            IEnumerable<XElement> linker)
            IEnumerable<XElement> linker,
            IEnumerable<XElement> resourceCompiler)
        {
            // Module .lib is present in linker additional dependencies
            if (linker.Elements(ns + "AdditionalDependencies")
@@ -788,8 +784,15 @@
                return true;
            }
            // Module macro is present in pre-processor definitions
            // Module macro is present in the compiler pre-processor definitions
            if (compiler.Elements(ns + "PreprocessorDefinitions")
                .SelectMany(x => x.Value.Split(';'))
                .Any(x => module.Defines.Contains(x))) {
                return true;
            }
            // Module macro is present in resource compiler pre-processor definitions
            if (resourceCompiler.Elements(ns + "PreprocessorDefinitions")
                .SelectMany(x => x.Value.Split(';'))
                .Any(x => module.Defines.Contains(x))) {
                return true;
@@ -958,9 +961,9 @@
                        commandLine = replace(row.itemName, commandLine);
                    //
                    //   * Configuration/platform, e.g. x64\Debug --> $(Platform)\$(Configuration)
                    commandLine = commandLine
                        .Replace(configName, "$(Configuration)",
                            StringComparison.InvariantCultureIgnoreCase)
                    //   * ignore any word other than the expected configuration, e.g. lrelease.exe
                    commandLine = Regex.Replace(commandLine, @"\b" + configName + @"\b",
                            "$(Configuration)", RegexOptions.IgnoreCase)
                        .Replace(platformName, "$(Platform)",
                            StringComparison.InvariantCultureIgnoreCase);
@@ -969,8 +972,7 @@
                        evaluator.Properties.Add(configProp.Name.LocalName, (string)configProp);
                    if (!qtMsBuild.SetCommandLine(itemType, item, commandLine, evaluator)) {
                        int lineNumber = 1;
                        var errorLine = row.command as IXmlLineInfo;
                        if (errorLine != null && errorLine.HasLineInfo())
                        if (row.command is IXmlLineInfo errorLine && errorLine.HasLineInfo())
                            lineNumber = errorLine.LineNumber;
                        Messages.Print(string.Format(
@@ -1059,8 +1061,7 @@
                    return (string)cbt.Attribute("Include");
                }
            }
            string ouputFile;
            if (!properties.TryGetValue(QtMoc.Property.InputFile, out ouputFile))
            if (!properties.TryGetValue(QtMoc.Property.InputFile, out string ouputFile))
                return (string)cbt.Attribute("Include");
            return ouputFile;
        }
@@ -1085,8 +1086,7 @@
                        Path.IsPathRooted(x) ? x : Path.Combine(projDir, x)));
                var outputItems = new List<XElement>();
                foreach (var outputFile in outputFiles) {
                    List<XElement> mocOutput = null;
                    if (projItemsByPath.TryGetValue(outputFile, out mocOutput)) {
                    if (projItemsByPath.TryGetValue(outputFile, out List<XElement> mocOutput)) {
                        outputItems.AddRange(mocOutput);
                        hasGeneratedFiles |= hasGeneratedFiles ? true : mocOutput
                            .Where(x => !x.Elements(ns + "ExcludedFromBuild")
@@ -1469,17 +1469,16 @@
        class MSBuildEvaluator : IVSMacroExpander, IDisposable
        {
            MsBuildXmlFile projFile;
            string tempProjFilePath;
            XElement evaluateTarget;
            XElement evaluateProperty;
            ProjectRootElement projRoot;
            public Dictionary<string, string> expansionCache;
            private readonly MsBuildXmlFile projFile;
            private string tempProjFilePath;
            private XElement evaluateTarget;
            private XElement evaluateProperty;
            private ProjectRootElement projRoot;
            private readonly Dictionary<string, string> expansionCache;
            public Dictionary<string, string> Properties
            {
                get;
                private set;
            }
            public MSBuildEvaluator(MsBuildXmlFile projFile)
@@ -1522,8 +1521,7 @@
            public string ExpandString(string stringToExpand)
            {
                string expandedString;
                if (TryExpansionCache(stringToExpand, out expandedString))
                if (TryExpansionCache(stringToExpand, out string expandedString))
                    return expandedString;
                if (evaluateTarget == null) {
@@ -1649,7 +1647,7 @@
            return true;
        }
        static Regex ConditionParser =
        static readonly Regex ConditionParser =
            new Regex(@"\'\$\(Configuration[^\)]*\)\|\$\(Platform[^\)]*\)\'\=\=\'([^\']+)\'");
        class MsBuildConverterProvider : IPropertyStorageProvider
QtVsTools.Core/OutputWindowPane.cs
New file
@@ -0,0 +1,180 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Threading;
using Task = System.Threading.Tasks.Task;
namespace QtVsTools.Core
{
    using VisualStudio;
    public class OutputWindowPane
    {
        public enum VSOutputWindowPane
        {
            General,
            Build,
            Debug,
        }
        public static Task<OutputWindowPane> GetVSOutputWindowPaneAsync(VSOutputWindowPane pane)
        {
            switch (pane) {
            case VSOutputWindowPane.General:
                return GetAsync(VSConstants.OutputWindowPaneGuid.GeneralPane_guid);
            case VSOutputWindowPane.Build:
                return GetAsync(VSConstants.OutputWindowPaneGuid.BuildOutputPane_guid);
            case VSOutputWindowPane.Debug:
                return GetAsync(VSConstants.OutputWindowPaneGuid.DebugPane_guid);
            default:
                throw new InvalidOperationException("Unsupported Visual Studio output pane");
            };
        }
        public static async Task<OutputWindowPane> GetAsync(Guid guid)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            try {
                IVsOutputWindow w = null;
                if (guid == VSConstants.OutputWindowPaneGuid.GeneralPane_guid) {
                    w = await VsServiceProvider.GetServiceAsync<SVsGeneralOutputWindowPane,
                        IVsOutputWindow>();
                } else {
                    w = await VsServiceProvider.GetServiceAsync<SVsOutputWindow, IVsOutputWindow>();
                }
                ErrorHandler.ThrowOnFailure(w.GetPane(guid, out IVsOutputWindowPane pane));
                return new OutputWindowPane(guid, pane);
            } catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine(ex);
                return null;
            }
        }
        public static async Task<OutputWindowPane> CreateAsync(string name, Guid guid)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException($"{ nameof(name) } cannot be null");
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            try {
                var w = await VsServiceProvider.GetServiceAsync<SVsOutputWindow, IVsOutputWindow>();
                const int visible = 1, clear = 1;
                ErrorHandler.ThrowOnFailure(w.CreatePane(guid, name, visible, clear));
                ErrorHandler.ThrowOnFailure(w.GetPane(guid, out IVsOutputWindowPane pane));
                return new OutputWindowPane(guid, pane);
            } catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine(ex);
                return null;
            }
        }
        public static async Task<OutputWindowPane> GetActiveAsync()
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            try {
                var w2 = await VsServiceProvider
                    .GetServiceAsync<SVsOutputWindow, IVsOutputWindow>() as IVsOutputWindow2;
                ErrorHandler.ThrowOnFailure(w2.GetActivePaneGUID(out Guid guid));
                IVsOutputWindow w = w2 as IVsOutputWindow;
                ErrorHandler.ThrowOnFailure(w.GetPane(guid, out IVsOutputWindowPane pane));
                return new OutputWindowPane(guid, pane);
            } catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine(ex);
                return null;
            }
        }
        private Guid Guid { get; }
        private IVsOutputWindowPane Pane { get; set; } = null;
        private OutputWindowPane(Guid guid, IVsOutputWindowPane pane)
        {
            Guid = guid;
            Pane = pane;
        }
        public async Task ActivateAsync()
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            if (Pane == null)
                throw new InvalidOperationException($"{ nameof(Pane) } cannot be null");
            Pane.Activate();
        }
        public async Task HideAsync()
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            if (Pane == null)
                throw new InvalidOperationException($"{ nameof(Pane) } cannot be null");
            Pane.Hide();
        }
        public async Task ClearAsync()
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            if (Pane == null)
                throw new InvalidOperationException($"{ nameof(Pane) } cannot be null");
            Pane.Clear();
        }
        public void Print()
        {
            ThreadHelper.JoinableTaskFactory.Run(async () => { await PrintAsync(""); });
        }
        public void Print(string value)
        {
            ThreadHelper.JoinableTaskFactory.Run(async () => { await PrintAsync(value); });
        }
        public Task PrintAsync()
        {
            return PrintAsync("");
        }
        public async Task PrintAsync(string value)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            if (Pane is IVsOutputWindowPaneNoPump noPumpPane)
                noPumpPane.OutputStringNoPump(value + Environment.NewLine);
            else
                ErrorHandler.ThrowOnFailure(Pane.OutputStringThreadSafe(value + Environment.NewLine));
        }
    }
}
QtVsTools.Core/ProFileContent.cs
@@ -35,46 +35,20 @@
    {
        public ProFileContent(VCProject proj)
        {
            export = true;
            vcproj = proj;
            options = new List<ProFileOption>();
            Export = true;
            Project = proj;
            Options = new List<ProFileOption>();
        }
        public override string ToString()
        {
            return vcproj.Name;
            return Project.Name;
        }
        public VCProject Project
        {
            get
            {
                return vcproj;
            }
        }
        public VCProject Project { get; }
        public bool Export
        {
            get
            {
                return export;
            }
            set
            {
                export = value;
            }
        }
        public bool Export { get; set; }
        public List<ProFileOption> Options
        {
            get
            {
                return options;
            }
        }
        private VCProject vcproj;
        private bool export;
        private List<ProFileOption> options;
        public List<ProFileOption> Options { get; }
    }
}
QtVsTools.Core/ProFileOption.cs
@@ -34,95 +34,33 @@
    {
        public ProFileOption(string optname)
        {
            name = optname;
            astype = AssignType.AT_PlusEquals;
            comment = null;
            shortComment = "Default";
            incComment = false;
            newOpt = " \\\r\n    ";
            list = new List<string>();
            Name = optname;
            AssignSymbol = AssignType.AT_PlusEquals;
            Comment = null;
            ShortComment = "Default";
            IncludeComment = false;
            NewOption = " \\\r\n    ";
            List = new List<string>();
        }
        public override string ToString()
        {
            return shortComment;
            return ShortComment;
        }
        public string Comment
        {
            get
            {
                return comment;
            }
            set
            {
                comment = value;
            }
        }
        public string Comment { get; set; }
        public string ShortComment
        {
            get
            {
                return shortComment;
            }
            set
            {
                shortComment = value;
            }
        }
        public string ShortComment { get; set; }
        public AssignType AssignSymbol
        {
            get
            {
                return astype;
            }
            set
            {
                astype = value;
            }
        }
        public AssignType AssignSymbol { get; set; }
        public string NewOption
        {
            get
            {
                return newOpt;
            }
            set
            {
                newOpt = value;
            }
        }
        public string NewOption { get; set; }
        public string Name
        {
            get
            {
                return name;
            }
        }
        public string Name { get; }
        public List<string> List
        {
            get
            {
                return list;
            }
        }
        public List<string> List { get; }
        public bool IncludeComment
        {
            get
            {
                return incComment;
            }
            set
            {
                incComment = value;
            }
        }
        public bool IncludeComment { get; set; }
        public enum AssignType
        {
@@ -131,13 +69,5 @@
            AT_MinusEquals = 3,
            AT_Function = 4
        }
        private AssignType astype;
        private string shortComment;
        private bool incComment;
        private string comment;
        private string newOpt;
        private string name;
        private List<string> list;
    }
}
QtVsTools.Core/ProSolution.cs
@@ -35,27 +35,12 @@
    {
        public ProSolution(Solution sln)
        {
            prosln = sln;
            proFiles = new List<ProFileContent>();
            ProjectSolution = sln;
            ProFiles = new List<ProFileContent>();
        }
        public List<ProFileContent> ProFiles
        {
            get
            {
                return proFiles;
            }
        }
        public List<ProFileContent> ProFiles { get; }
        public Solution ProjectSolution
        {
            get
            {
                return prosln;
            }
        }
        private List<ProFileContent> proFiles;
        private Solution prosln;
        public Solution ProjectSolution { get; }
    }
}
QtVsTools.Core/ProjectExporter.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,15 +26,16 @@
**
****************************************************************************/
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.VCProjectEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
using EnvDTE;
using EnvDTE80;
namespace QtVsTools.Core
{
@@ -43,7 +44,7 @@
    /// </summary>
    public class ProjectExporter
    {
        private DTE dteObject;
        private readonly DTE dteObject;
        public ProjectExporter(DTE dte)
        {
@@ -91,13 +92,15 @@
        private ProSolution CreateProFileSolution(Solution sln)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            ProFileContent content;
            var prosln = new ProSolution(sln);
            foreach (var proj in HelperFunctions.ProjectsInSolution(sln.DTE)) {
                try {
                    // only add qt projects
                    if (HelperFunctions.IsQtProject(proj)) {
                    if (HelperFunctions.IsVsToolsProject(proj)) {
                        content = CreateProFileContent(proj);
                        prosln.ProFiles.Add(content);
                    } else if (proj.Kind == ProjectKinds.vsProjectKindSolutionFolder) {
@@ -113,9 +116,11 @@
        private void addProjectsInFolder(Project solutionFolder, ProSolution sln)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            foreach (ProjectItem pi in solutionFolder.ProjectItems) {
                var containedProject = pi.Object as Project;
                if (HelperFunctions.IsQtProject(containedProject)) {
                if (HelperFunctions.IsVsToolsProject(containedProject)) {
                    var content = CreateProFileContent(containedProject);
                    sln.ProFiles.Add(content);
                } else if (containedProject.Kind == ProjectKinds.vsProjectKindSolutionFolder) {
@@ -126,6 +131,8 @@
        private static ProFileContent CreateProFileContent(Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            ProFileOption option;
            var qtPro = QtProject.Create(project);
            var content = new ProFileContent(qtPro.VCProject);
@@ -192,8 +199,7 @@
            if (config.ConfigurationType == ConfigurationTypes.typeStaticLibrary)
                option.List.Add("staticlib");
            if (linker != null) {
                var linkerRule = linker as IVCRulePropertyStorage;
                var generateDebugInformation = (linkerRule != null) ?
                var generateDebugInformation = (linker is IVCRulePropertyStorage linkerRule) ?
                    linkerRule.GetUnevaluatedPropertyValue("GenerateDebugInformation") : null;
                if (generateDebugInformation != "false")
                    option.List.Add("debug");
@@ -213,10 +219,12 @@
                }
            }
            if (qtPro.IsDesignerPluginProject()) {
                option.List.Add("designer");
            var legacyDesigner = Legacy.QtProject.IsDesignerPluginProject(qtPro);
            var plugin = legacyDesigner | Core.QtProject.IsQtPlugin(qtPro);
            if (plugin)
                option.List.Add("plugin");
            }
            if (legacyDesigner)
                option.List.Add("designer");
            // add defines
            option = new ProFileOption("DEFINES");
@@ -323,12 +331,18 @@
                option.List.Add(project.Name + ".rc");
            }
            if (qtPro.IsDesignerPluginProject()) {
            if (plugin) {
                option = new ProFileOption("target.path");
                option.ShortComment = "Install the plugin in the designer plugins directory.";
                if (legacyDesigner)
                    option.ShortComment = "Installs the plugin in the designer plugins directory.";
                else
                    option.ShortComment = "Installs the plugin in the plugins directory.";
                option.IncludeComment = true;
                option.AssignSymbol = ProFileOption.AssignType.AT_Equals;
                option.List.Add("$$[QT_INSTALL_PLUGINS]/designer");
                if (legacyDesigner)
                    option.List.Add("$$[QT_INSTALL_PLUGINS]/designer");
                else
                    option.List.Add("$$[QT_INSTALL_PLUGINS]");
                content.Options.Add(option);
                option = new ProFileOption("INSTALLS");
@@ -343,6 +357,8 @@
        private static ProFileContent CreatePriFileContent(Project project, string priFileDirectory)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            ProFileOption option;
            var qtPro = QtProject.Create(project);
            var content = new ProFileContent(qtPro.VCProject);
@@ -417,6 +433,8 @@
        private static void AddIncludePaths(Project project, ProFileOption option, string includePaths)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_ClProperties)
                return;
@@ -437,10 +455,7 @@
                if (!d.StartsWith("$(qtdir)\\include", StringComparison.OrdinalIgnoreCase) &&
                    !d.StartsWith(qtDir + "\\include", StringComparison.OrdinalIgnoreCase) &&
                    !d.EndsWith("win32-msvc2005", StringComparison.OrdinalIgnoreCase)) {
                    var vcConfig = project.ConfigurationManager.ActiveConfiguration.Object
                        as VCConfiguration;
                    if (vcConfig != null)
                    if (project.ConfigurationManager.ActiveConfiguration.Object is VCConfiguration vcConfig)
                        HelperFunctions.ExpandString(ref d, vcConfig);
                    if (HelperFunctions.IsAbsoluteFilePath(d))
                        d = HelperFunctions.GetRelativePath(project.FullName, d);
@@ -461,8 +476,9 @@
                qtDir = Environment.GetEnvironmentVariable("QTDIR");
            if (qtDir == null)
                qtDir = "";
            qtDir = HelperFunctions.NormalizeRelativeFilePath(qtDir);
            ThreadHelper.ThrowIfNotOnUIThread();
            if (paths != null) {
                foreach (var s in paths.Split(';', ',')) {
@@ -491,7 +507,14 @@
        private static void AddModules(QtProject qtPrj, ProFileOption optionQT, ProFileOption optionCONFIG)
        {
            foreach (var module in QtModules.Instance.GetAvailableModules()) {
            ThreadHelper.ThrowIfNotOnUIThread();
            var vm = QtVersionManager.The();
            var versionInfo = vm.GetVersionInfo(qtPrj.Project);
            if (versionInfo == null)
                versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion());
            foreach (var module in QtModules.Instance.GetAvailableModules(versionInfo.qtMajor)) {
                if (!qtPrj.HasModule(module.Id))
                    continue;
@@ -504,6 +527,8 @@
        private void WriteProSolution(ProSolution prosln, bool openFile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var sln = prosln.ProjectSolution;
            if (string.IsNullOrEmpty(sln.FileName))
                return;
@@ -573,6 +598,8 @@
        private void WriteProFile(ProFileContent content, string proFile, string priFileToInclude, bool openFile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            StreamWriter sw;
            if (File.Exists(proFile)) {
                if (MessageBox.Show(SR.GetString("ExportProject_ExistsOverwriteQuestion", proFile),
@@ -605,8 +632,7 @@
                WriteProFileOptions(sw, content.Options);
            }
            // open the file in vs
            if (openFile)
            if (openFile) // open the file in vs
                dteObject.OpenFile(Constants.vsViewKindTextView, proFile).Activate();
        }
@@ -705,6 +731,8 @@
        public static void SyncIncludeFiles(VCProject vcproj, List<string> priFiles,
            List<string> projFiles, DTE dte, bool flat, FakeFilter fakeFilter)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var cmpPriFiles = new List<string>(priFiles.Count);
            foreach (var s in priFiles)
                cmpPriFiles.Add(HelperFunctions.NormalizeFilePath(s).ToLower());
@@ -779,7 +807,7 @@
        public void ExportToProFile()
        {
            var expDlg = new ExportProjectDialog();
            ThreadHelper.ThrowIfNotOnUIThread();
            var sln = dteObject.Solution;
            var prosln = CreateProFileSolution(sln);
@@ -789,6 +817,7 @@
                return;
            }
            var expDlg = new ExportProjectDialog();
            expDlg.ProFileSolution = prosln;
            expDlg.StartPosition = FormStartPosition.CenterParent;
            var ww = new MainWinWrapper(dteObject);
@@ -814,9 +843,10 @@
        public string ExportToPriFile(Project proj)
        {
            VCProject vcproj;
            ThreadHelper.ThrowIfNotOnUIThread();
            if (HelperFunctions.IsQtProject(proj)) {
            VCProject vcproj;
            if (HelperFunctions.IsVsToolsProject(proj)) {
                try {
                    vcproj = (VCProject)proj.Object;
                } catch (Exception e) {
@@ -844,8 +874,9 @@
        public void ExportToPriFile(Project proj, string fileName)
        {
            var priFile = new FileInfo(fileName);
            ThreadHelper.ThrowIfNotOnUIThread();
            var priFile = new FileInfo(fileName);
            var content = CreatePriFileContent(proj, priFile.DirectoryName);
            WritePriFile(content, priFile.FullName);
        }
QtVsTools.Core/ProjectImporter.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,18 +26,19 @@
**
****************************************************************************/
using EnvDTE;
using Microsoft.VisualStudio.VCProjectEngine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
using EnvDTE;
using Microsoft.VisualStudio.VCProjectEngine;
namespace QtVsTools.Core
{
    public class ProjectImporter
    {
        private DTE dteObject;
        private readonly DTE dteObject;
        const string projectFileExtension = ".vcxproj";
        public ProjectImporter(DTE dte)
@@ -47,6 +48,8 @@
        public void ImportProFile(string qtVersion)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            FileDialog toOpen = new OpenFileDialog();
            toOpen.FilterIndex = 1;
            toOpen.CheckFileExists = true;
@@ -78,6 +81,8 @@
        private void ImportSolution(FileInfo mainInfo, string qtVersion)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var versionInfo = QtVersionManager.The().GetVersionInfo(qtVersion);
            var VCInfo = RunQmake(mainInfo, ".sln", true, versionInfo);
            if (null == VCInfo)
@@ -88,7 +93,7 @@
                if (CheckQtVersion(versionInfo)) {
                    dteObject.Solution.Open(VCInfo.FullName);
                    if (qtVersion != null) {
                        QtVersionManager.The().SaveSolutionQtVersion(dteObject.Solution, qtVersion);
                        Legacy.QtVersionManager.SaveSolutionQtVersion(dteObject.Solution, qtVersion);
                        foreach (var prj in HelperFunctions.ProjectsInSolution(dteObject)) {
                            QtVersionManager.The().SaveProjectQtVersion(prj, qtVersion);
                            var qtPro = QtProject.Create(prj);
@@ -100,12 +105,14 @@
                Messages.Print("--- (Import): Finished opening " + VCInfo.Name);
            } catch (Exception e) {
                Messages.DisplayCriticalErrorMessage(e);
                Messages.DisplayErrorMessage(e);
            }
        }
        public void ImportProject(FileInfo mainInfo, string qtVersion)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var versionInfo = QtVersionManager.The().GetVersionInfo(qtVersion);
            var VCInfo = RunQmake(mainInfo, projectFileExtension, false, versionInfo);
            if (null == VCInfo)
@@ -116,9 +123,11 @@
            try {
                if (CheckQtVersion(versionInfo)) {
                    // no need to add the project again if it's already there...
                    if (!HelperFunctions.IsProjectInSolution(dteObject, VCInfo.FullName)) {
                    var fullName = VCInfo.FullName;
                    var pro = HelperFunctions.ProjectFromSolution(dteObject, fullName);
                    if (pro == null) {
                        try {
                            dteObject.Solution.AddFromFile(VCInfo.FullName, false);
                            pro = dteObject.Solution.AddFromFile(fullName, false);
                        } catch (Exception /*exception*/) {
                            Messages.Print("--- (Import): Generated project could not be loaded.");
                            Messages.Print("--- (Import): Please look in the output above for errors and warnings.");
@@ -129,13 +138,6 @@
                        Messages.Print("Project already in Solution");
                    }
                    Project pro = null;
                    foreach (var p in HelperFunctions.ProjectsInSolution(dteObject)) {
                        if (p.FullName.ToLower() == VCInfo.FullName.ToLower()) {
                            pro = p;
                            break;
                        }
                    }
                    if (pro != null) {
                        var qtPro = QtProject.Create(pro);
                        qtPro.SetQtEnvironment();
@@ -151,21 +153,16 @@
                                Messages.Print("Can't select the platform " + platformName + ".");
                        }
                        // try to figure out if the project is a plugin project
                        try {
                            var activeConfig = pro.ConfigurationManager.ActiveConfiguration.ConfigurationName;
                            var config = (VCConfiguration)((IVCCollection)qtPro.VCProject.Configurations).Item(activeConfig);
                            if (config.ConfigurationType == ConfigurationTypes.typeDynamicLibrary) {
                                var compiler = CompilerToolWrapper.Create(config);
                                var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                                if (compiler.GetPreprocessorDefinitions().IndexOf("QT_PLUGIN", StringComparison.Ordinal) > -1
                                    && compiler.GetPreprocessorDefinitions().IndexOf("QDESIGNER_EXPORT_WIDGETS", StringComparison.Ordinal) > -1
                                    && compiler.GetAdditionalIncludeDirectories().IndexOf("QtDesigner", StringComparison.Ordinal) > -1
                                    && linker.AdditionalDependencies.IndexOf("QtDesigner", StringComparison.Ordinal) > -1) {
                                    qtPro.MarkAsDesignerPluginProject();
                                }
                            }
                        } catch (Exception) { }
                        // figure out if the imported project is a plugin project
                        var tmp = qtPro.Project.ConfigurationManager.ActiveConfiguration
                            .ConfigurationName;
                        var vcConfig = (qtPro.VCProject.Configurations as IVCCollection).Item(tmp)
                            as VCConfiguration;
                        var def = CompilerToolWrapper.Create(vcConfig)?.GetPreprocessorDefinitions();
                        if (!string.IsNullOrEmpty(def)
                            && def.IndexOf("QT_PLUGIN", StringComparison.Ordinal) > -1) {
                            QtProject.MarkAsQtPlugin(qtPro);
                        }
                        qtPro.SetQtEnvironment();
                        ApplyPostImportSteps(qtPro);
@@ -222,18 +219,19 @@
            if (ok)
                ok = xmlProject.UpdateProjectFormatVersion();
            if (!ok) {
                Messages.Print(
                    SR.GetString("ImportProject_CannotConvertProject", projectFile.Name));
            if (ok) {
                xmlProject.Save();
                // Initialize Qt variables
                xmlProject.BuildTarget("QtVarsDesignTime");
            } else {
                Messages.Print($"Could not convert project file {projectFile.Name} to Qt/MSBuild.");
            }
            xmlProject.Save();
            // Initialize Qt variables
            xmlProject.BuildTarget("QtVarsDesignTime");
        }
        private static void ApplyPostImportSteps(QtProject qtProject)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            qtProject.RemoveResFilesFromGeneratedFilesFilter();
            qtProject.TranslateFilterNames();
@@ -249,32 +247,38 @@
        private FileInfo RunQmake(FileInfo mainInfo, string ext, bool recursive, VersionInformation vi)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var name = mainInfo.Name.Remove(mainInfo.Name.IndexOf('.'));
            var VCInfo = new FileInfo(mainInfo.DirectoryName + "\\" + name + ext);
            if (!VCInfo.Exists || DialogResult.Yes == MessageBox.Show(SR.GetString("ExportProject_ProjectExistsRegenerateOrReuse", VCInfo.Name),
                SR.GetString("ProjectExists"), MessageBoxButtons.YesNo, MessageBoxIcon.Question)) {
                Messages.Print("--- (Import): Generating new project of " + mainInfo.Name + " file");
                var waitDialog = WaitDialog.Start(
                    "Open Qt Project File",
                    "Generating Visual Studio project...", delay: 2);
                var qmake = new QMakeImport(vi, mainInfo.FullName, recursive, dteObject);
                int exitCode = qmake.Run(setVCVars: true);
                waitDialog.Stop();
                if (exitCode == 0)
                    return VCInfo;
            var vcxproj = new FileInfo(mainInfo.DirectoryName + "\\" + name + ext);
            if (vcxproj.Exists) {
                var result = MessageBox.Show($@"{vcxproj.Name} already exists. Select 'OK' to " +
                    "regenerate the file or 'Cancel' to quit importing the project.",
                    "Project file already exists.",
                    MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
                if (result == DialogResult.Cancel)
                    return null;
            }
            Messages.Print("--- (Import): Generating new project of " + mainInfo.Name + " file");
            var waitDialog = WaitDialog.Start("Open Qt Project File",
                "Generating Visual Studio project...", delay: 2);
            var qmake = new QMakeImport(vi, mainInfo.FullName, recursive, dteObject);
            int exitCode = qmake.Run(setVCVars: true);
            waitDialog.Stop();
            if (exitCode == 0)
                return vcxproj;
            return null;
        }
        private static bool CheckQtVersion(VersionInformation vi)
        {
            if (!vi.qt5Version) {
            if (vi.qtMajor < 5) {
                Messages.DisplayWarningMessage(SR.GetString("ExportProject_EditProjectFileManually"));
                return false;
            }
QtVsTools.Core/QMake.cs
@@ -32,23 +32,24 @@
using System.IO;
using System.Linq;
using System.Text;
using QtVsTools.VisualStudio;
namespace QtVsTools.Core
{
    using VisualStudio;
    public abstract class QMake
    {
        public Dictionary<string, string> Vars { get; protected set; }
        public string OutputFile { get; protected set; }
        public uint DebugLevel { get; protected set; }
        private uint DebugLevel { get; set; }
        public string TemplatePrefix { get; protected set; }
        public bool Recursive { get; protected set; }
        public string ProFile { get; protected set; }
        public string Query { get; protected set; }
        public bool DisableWarnings { get; set; }
        protected VersionInformation QtVersion { get; private set; }
        protected EnvDTE.DTE Dte { get; private set; }
        protected VersionInformation QtVersion { get; }
        private EnvDTE.DTE Dte { get; }
        public QMake(VersionInformation qtVersion, EnvDTE.DTE dte = null)
        {
@@ -196,14 +197,27 @@
                        exitCode = qmakeProc.ExitCode;
                        InfoExit(qmakeProc);
                    }
                } catch (Exception e) {
                    ErrMsg(string.Format("Exception \"{0}\":\r\n{1}",
                        e.Message,
                        e.StackTrace));
                } catch (Exception exception) {
                    exception.Log();
                }
            }
            return exitCode;
        }
        public static bool Exists(string path)
        {
            var possibleQMakePaths = new[] {
                // Path points to qmake.exe
                path,
                // Path points to folder containing qmake.exe
                Path.Combine(path, "qmake.exe"),
                // Path points to folder containing bin\qmake.exe
                Path.Combine(path, "bin", "qmake.exe"),
            };
            return possibleQMakePaths.Where(p => File.Exists(p)
                && Path.GetFileName(p).Equals("qmake.exe", StringComparison.OrdinalIgnoreCase))
                .Any();
        }
    }
    public class QMakeImport : QMake
QtVsTools.Core/QMakeConf.cs
@@ -29,13 +29,14 @@
using System;
using System.Collections;
using System.IO;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Core
{
    public class QMakeConf
    {
        public Hashtable Entries { get; private set; }
        public string QMakeSpecDirectory { get; private set; }
        public Hashtable Entries { get; }
        public string QMakeSpecDirectory { get; }
        public QMakeConf(VersionInformation versionInfo, QMakeQuery qmakeQuery = null)
        {
@@ -64,6 +65,17 @@
                qmakeConf = Path.Combine(qtPrefix, qtArchData, "mkspecs", qmakeXSpec, "qmake.conf");
                if (!File.Exists(qmakeConf)) {
                    // Check if this is a shadow build of Qt.
                    qtPrefix = qmakeQuery["QT_INSTALL_PREFIX/src"];
                    if (string.IsNullOrEmpty(qtPrefix))
                        throw new QtVSException("qmake error: no value for QT_INSTALL_PREFIX/src");
                    qtArchData = qmakeQuery["QT_INSTALL_ARCHDATA/src"];
                    if (string.IsNullOrEmpty(qtArchData))
                        throw new QtVSException("qmake error: no value for QT_INSTALL_ARCHDATA/src");
                    qmakeConf = Path.Combine(qtPrefix, qtArchData, "mkspecs", qmakeXSpec, "qmake.conf");
                }
                if (!File.Exists(qmakeConf))
                    throw new QtVSException("qmake.conf expected at " + qmakeConf + " not found");
            }
QtVsTools.Core/QMakeQuery.cs
@@ -26,18 +26,15 @@
**
****************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using QtVsTools.SyntaxAnalysis;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Core
{
    using static RegExpr;
    using static SyntaxAnalysis.RegExpr;
    public class QMakeQuery : QMake
    {
@@ -58,7 +55,6 @@
        public Dictionary<string, string> QueryAllValues()
        {
            string result = string.Empty;
            stdOutput = new StringBuilder();
            Query = " ";
@@ -78,16 +74,20 @@
        {
            get
            {
                string value = string.Empty;
                if (Properties.TryGetValue(name, out value))
                if (Properties.TryGetValue(name, out string value))
                    return value;
                else
                    return null;
                return null;
            }
        }
        Dictionary<string, string> _Properties;
        Dictionary<string, string> Properties => _Properties ?? (_Properties = QueryAllValues());
        Dictionary<string, string> Properties
        {
            get
            {
                return _Properties ?? (_Properties = QueryAllValues());
            }
        }
        Parser _PropertyParser;
        Parser PropertyParser
QtVsTools.Core/QrcPrefix.cs
@@ -34,7 +34,7 @@
    {
        public string Prefix { get; set; }
        public string Language { get; set; }
        public List<QrcItem> Items { get; private set; }
        public List<QrcItem> Items { get; }
        public QrcPrefix()
        {
QtVsTools.Core/QtConfig.cs
@@ -44,17 +44,18 @@
    /// </summary>
    class QtConfig
    {
        public BuildType BuildType { get; private set; }
        public BuildType BuildType { get; }
        public string LibInfix { get; private set; }
        public string LibInfix { get; }
        public bool Is64Bit { get; private set; }
        public bool Is64Bit { get; }
        public string Namespace { get; private set; }
        public string Namespace { get; }
        public uint VersionMajor { get; private set; }
        public uint VersionMinor { get; private set; }
        public uint VersionPatch { get; private set; }
        public uint VersionMajor { get; }
        public uint VersionMinor { get; }
        public uint VersionPatch { get; }
        public string VersionString { get; }
        public QtConfig(string qtdir)
        {
@@ -110,6 +111,8 @@
                        Is64Bit = (data == "x86_64");
                    } else if (name == "QT_NAMESPACE") {
                        Namespace = data;
                    } else if (name == "QT_VERSION") {
                        VersionString = data;
                    } else if (name == "QT_MAJOR_VERSION") {
                        if (uint.TryParse(data, out uint versionMajor))
                            VersionMajor = versionMajor;
QtVsTools.Core/QtModule.cs
@@ -28,7 +28,6 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace QtVsTools.Core
{
@@ -38,12 +37,12 @@
        public bool Selectable;
        public List<string> Defines = new List<string>();
        public string LibraryPrefix = string.Empty;
        public bool HasDLL = true;
        public List<string> AdditionalLibraries = new List<string>();
        public List<string> AdditionalLibrariesDebug = new List<string>();
        public List<string> IncludePath = new List<string>();
        public string proVarQT;
        public string proVarCONFIG;
        private string majorVersion;
        public string LibRelease
        {
@@ -51,7 +50,7 @@
            {
                return
                    LibraryPrefix.StartsWith("Qt", StringComparison.Ordinal)
                        ? "Qt5" + LibraryPrefix.Substring(2) + ".lib"
                        ? "Qt" + majorVersion + LibraryPrefix.Substring(2) + ".lib"
                        : LibraryPrefix + ".lib";
            }
        }
@@ -62,14 +61,15 @@
            {
                return
                    LibraryPrefix.StartsWith("Qt", StringComparison.Ordinal)
                        ? "Qt5" + LibraryPrefix.Substring(2) + "d.lib"
                        ? "Qt" + majorVersion + LibraryPrefix.Substring(2) + "d.lib"
                        : LibraryPrefix + "d.lib";
            }
        }
        public QtModule(int id)
        public QtModule(int id, string major)
        {
            Id = id;
            majorVersion = major;
        }
        public int Id { get; } = -1;
@@ -90,7 +90,7 @@
            var libs = new List<string>();
            var libName = LibraryPrefix;
            if (libName.StartsWith("Qt", StringComparison.Ordinal))
                libName = "Qt5" + libName.Substring(2);
                libName = "Qt" + majorVersion + libName.Substring(2);
            libName += libInfix;
            if (isDebugCfg)
                libName += "d";
QtVsTools.Core/QtModules.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -38,37 +38,58 @@
{
    public class QtModules
    {
        private static QtModules instance = new QtModules();
        private readonly Dictionary<int, QtModule> modules = new Dictionary<int, QtModule>();
        public static QtModules Instance { get; } = new QtModules();
        public static QtModules Instance
        {
            get { return instance; }
        }
        private List<QtModule> qt5list = null, qt6list = null;
        private readonly Dictionary<int, QtModule> qt5modules = new Dictionary<int, QtModule>();
        private readonly Dictionary<int, QtModule> qt6modules = new Dictionary<int, QtModule>();
        public QtModule Module(int id)
        public QtModule Module(int id, uint major)
        {
            QtModule module;
            modules.TryGetValue(id, out module);
            QtModule module = null;
            if (major < 6)
                qt5modules.TryGetValue(id, out module);
            if (major == 6)
                qt6modules.TryGetValue(id, out module);
            if (major > 6)
                throw new QtVSException("Unsupported Qt version.");
            return module;
        }
        public List<QtModule> GetAvailableModules()
        public List<QtModule> GetAvailableModules(uint major)
        {
            var lst = new List<QtModule>(modules.Count);
            foreach (var entry in modules)
                lst.Add(entry.Value);
            return lst;
            if (major < 6) {
                if (qt5list == null) {
                    qt5list = new List<QtModule>(qt5modules.Count);
                    foreach (var entry in qt5modules)
                        qt5list.Add(entry.Value);
                }
                return qt5list;
            }
            if (major == 6) {
                if (qt6list == null) {
                    qt6list = new List<QtModule>(qt6modules.Count);
                    foreach (var entry in qt6modules)
                        qt6list.Add(entry.Value);
                }
                return qt6list;
            }
            if (major > 6)
                throw new QtVSException("Unsupported Qt version.");
            return null;
        }
        private QtModules()
        {
            var uri = new Uri(
                System.Reflection.Assembly.GetExecutingAssembly().EscapedCodeBase);
            var pkgInstallPath = Path.GetDirectoryName(
                Uri.UnescapeDataString(uri.AbsolutePath)) + @"\";
            var uri = new Uri(System.Reflection.Assembly.GetExecutingAssembly().EscapedCodeBase);
            var pkgInstallPath = Path.GetDirectoryName(Uri.UnescapeDataString(uri.AbsolutePath));
            var modulesFile = Path.Combine(pkgInstallPath, "qtmodules.xml");
            FillModules(Path.Combine(pkgInstallPath, "qtmodules.xml"), "5", ref qt5modules);
            FillModules(Path.Combine(pkgInstallPath, "qt6modules.xml"), "6", ref qt6modules);
        }
        private void FillModules(string modulesFile, string major, ref Dictionary<int, QtModule> dict)
        {
            if (!File.Exists(modulesFile))
                return;
@@ -85,11 +106,10 @@
            foreach (var xModule in xml.Elements("QtVsTools").Elements("Module")) {
                int id = (int)xModule.Attribute("Id");
                QtModule module = new QtModule(id);
                QtModule module = new QtModule(id, major);
                module.Name = (string)xModule.Element("Name");
                module.Selectable = ((string)xModule.Element("Selectable") == "true");
                module.LibraryPrefix = (string)xModule.Element("LibraryPrefix");
                module.HasDLL = ((string)xModule.Element("HasDLL") == "true");
                module.proVarQT = (string)xModule.Element("proVarQT");
                module.proVarCONFIG = (string)xModule.Element("proVarCONFIG");
                module.IncludePath = xModule.Elements("IncludePath")
@@ -106,7 +126,7 @@
                    Messages.Print("\r\nCritical error: incorrect format of qtmodules.xml");
                    throw new QtVSException("qtmodules.xml");
                }
                modules.Add(id, module);
                dict.Add(id, module);
            }
        }
    }
QtVsTools.Core/QtMsBuild.cs
@@ -26,17 +26,17 @@
**
****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System;
using System.IO;
using CommandLineParser = QtVsTools.Core.CommandLine.Parser;
using CommandLineOption = QtVsTools.Core.CommandLine.Option;
namespace QtVsTools.Core.QtMsBuild
{
    using CommandLineParser = CommandLine.Parser;
    using CommandLineOption = CommandLine.Option;
    public interface IVSMacroExpander
    {
        string ExpandString(string stringToExpand);
@@ -84,8 +84,7 @@
    public class QtMsBuildContainer
    {
        IPropertyStorageProvider provider;
        readonly IPropertyStorageProvider provider;
        public QtMsBuildContainer(IPropertyStorageProvider provider)
        {
            this.provider = provider;
@@ -198,16 +197,15 @@
            return provider.GetProjectConfiguration(GetProject(), configName);
        }
        Dictionary<string, ItemPropertyChange> itemPropertyChanges
        readonly Dictionary<string, ItemPropertyChange> itemPropertyChanges
            = new Dictionary<string, ItemPropertyChange>();
        Dictionary<string, List<ItemPropertyChange>> itemPropertyChangesGrouped
        readonly Dictionary<string, List<ItemPropertyChange>> itemPropertyChangesGrouped
            = new Dictionary<string, List<ItemPropertyChange>>();
        bool pendingChanges = false;
        void AddChange(ItemPropertyChange newChange)
        {
            ItemPropertyChange oldChange;
            if (itemPropertyChanges.TryGetValue(newChange.Key, out oldChange)) {
            if (itemPropertyChanges.TryGetValue(newChange.Key, out ItemPropertyChange oldChange)) {
                if (oldChange.GroupKey == newChange.GroupKey) {
                    oldChange.CopyFrom(newChange);
                    return;
@@ -509,7 +507,8 @@
        #region QtRcc
        static QtRcc qtRccInstance;
        public static QtRcc QtRccInstance
        private static QtRcc QtRccInstance
        {
            get
            {
@@ -558,7 +557,8 @@
        #region QtRepc
        static QtRepc qtRepcInstance;
        public static QtRepc QtRepcInstance
        private static QtRepc QtRepcInstance
        {
            get
            {
@@ -607,7 +607,8 @@
        #region QtUic
        static QtUic qtUicInstance;
        public static QtUic QtUicInstance
        private static QtUic QtUicInstance
        {
            get
            {
@@ -658,10 +659,10 @@
    public abstract class QtTool
    {
        protected CommandLineParser parser;
        protected CommandLineOption outputOption;
        protected CommandLineOption helpOption;
        protected CommandLineOption versionOption;
        protected readonly CommandLineParser parser;
        private readonly CommandLineOption outputOption;
        private CommandLineOption helpOption;
        private CommandLineOption versionOption;
        protected QtTool(bool defaultInputOutput = true)
        {
@@ -779,7 +780,7 @@
            AdditionalDependencies,
        }
        Dictionary<Property, CommandLineOption> options
        readonly Dictionary<Property, CommandLineOption> options
            = new Dictionary<Property, CommandLineOption>();
        public QtMoc() : base()
@@ -881,14 +882,13 @@
        {
            properties = new Dictionary<Property, string>();
            string qtDir, inputPath, outputPath;
            if (!ParseCommandLine(
                commandLine,
                macros,
                ToolExecName,
                out qtDir,
                out inputPath,
                out outputPath)) {
                out string qtDir,
                out string inputPath,
                out string outputPath)) {
                return false;
            }
@@ -1078,7 +1078,7 @@
            AdditionalDependencies,
        }
        Dictionary<Property, CommandLineOption> options
        readonly Dictionary<Property, CommandLineOption> options
            = new Dictionary<Property, CommandLineOption>();
        public QtRcc() : base()
@@ -1130,14 +1130,13 @@
        {
            properties = new Dictionary<Property, string>();
            string qtDir, inputPath, outputPath;
            if (!ParseCommandLine(
                commandLine,
                macros,
                ToolExecName,
                out qtDir,
                out inputPath,
                out outputPath)) {
                out string qtDir,
                out string inputPath,
                out string outputPath)) {
                return false;
            }
@@ -1157,8 +1156,7 @@
                properties[Property.Root] = parser.Value(options[Property.Root]);
            if (parser.IsSet(options[Property.Compression])) {
                int level;
                if (!int.TryParse(parser.Value(options[Property.Compression]), out level))
                if (!int.TryParse(parser.Value(options[Property.Compression]), out int level))
                    return false;
                if (level < 1 || 9 < level)
                    return false;
@@ -1272,7 +1270,7 @@
            PrintDebug,
        }
        Dictionary<Property, CommandLineOption> options
        readonly Dictionary<Property, CommandLineOption> options
            = new Dictionary<Property, CommandLineOption>();
        public QtRepc() : base(defaultInputOutput: false)
@@ -1330,14 +1328,13 @@
        {
            properties = new Dictionary<Property, string>();
            string qtDir, inputPath, outputPath;
            if (!ParseCommandLine(
                commandLine,
                macros,
                ToolExecName,
                out qtDir,
                out inputPath,
                out outputPath)) {
                out string qtDir,
                out string inputPath,
                out string outputPath)) {
                return false;
            }
@@ -1385,8 +1382,8 @@
                GenerateCommandLineOption(cmd, options[Property.InputFileType], inputType);
            var outputType = container.GetPropertyValue(propertyStorage, Property.OutputFileType);
            if (!string.IsNullOrEmpty(inputType))
                GenerateCommandLineOption(cmd, options[Property.InputFileType], inputType);
            if (!string.IsNullOrEmpty(outputType))
                GenerateCommandLineOption(cmd, options[Property.OutputFileType], outputType);
            string value = container.GetPropertyValue(propertyStorage, Property.IncludePath);
            if (!string.IsNullOrEmpty(value))
@@ -1436,7 +1433,7 @@
            AdditionalDependencies,
        }
        Dictionary<Property, CommandLineOption> options
        readonly Dictionary<Property, CommandLineOption> options
            = new Dictionary<Property, CommandLineOption>();
        public QtUic() : base()
@@ -1473,14 +1470,13 @@
        {
            properties = new Dictionary<Property, string>();
            string qtDir, inputPath, outputPath;
            if (!ParseCommandLine(
                commandLine,
                macros,
                ToolExecName,
                out qtDir,
                out inputPath,
                out outputPath)) {
                out string qtDir,
                out string inputPath,
                out string outputPath)) {
                return false;
            }
QtVsTools.Core/QtProject.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,22 +26,22 @@
**
****************************************************************************/
using EnvDTE;
using Microsoft.VisualStudio.VCProjectEngine;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using QtVsTools.Core.QtMsBuild;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
using EnvDTE;
namespace QtVsTools.Core
{
    using QtMsBuild;
    /// <summary>
    /// QtProject holds the Qt specific properties for a Visual Studio project.
    /// There exists at most one QtProject per EnvDTE.Project.
@@ -54,13 +54,14 @@
        private VCProject vcPro;
        private MocCmdChecker mocCmdChecker;
        private Array lastConfigurationRowNames;
        private static Dictionary<Project, QtProject> instances = new Dictionary<Project, QtProject>();
        private QtMsBuildContainer qtMsBuild;
        private static readonly Dictionary<Project, QtProject> instances = new Dictionary<Project, QtProject>();
        private readonly QtMsBuildContainer qtMsBuild;
        public static QtVsTools.VisualStudio.IProjectTracker ProjectTracker { get; set; }
        public static QtProject Create(VCProject vcProject)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return Create((Project)vcProject.Object);
        }
@@ -81,6 +82,8 @@
        private QtProject(Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (project == null)
                throw new QtVSException(SR.GetString("QtProject_CannotConstructWithoutValidProject"));
            envPro = project;
@@ -105,35 +108,10 @@
                return string.Empty;
            try {
                return config.GetEvaluatedPropertyValue(itemType + "RuleName");
            } catch (Exception e) {
                System.Diagnostics.Debug.WriteLine(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
            } catch (Exception exception) {
                exception.Log();
                return string.Empty;
            }
        }
        public static string GetRuleName(VCProject project, string itemType)
        {
            if (project == null)
                return string.Empty;
            var configs = project.Configurations as IVCCollection;
            if (configs.Count == 0)
                return string.Empty;
            try {
                var firstConfig = configs.Item(1) as VCConfiguration;
                if (firstConfig == null)
                    return string.Empty;
                return GetRuleName(firstConfig, itemType);
            } catch (Exception e) {
                System.Diagnostics.Debug.WriteLine(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                return string.Empty;
            }
        }
        public string GetRuleName(string itemType)
        {
            return GetRuleName(vcPro, itemType);
        }
        public static bool IsQtMsBuildEnabled(VCProject project)
@@ -157,6 +135,8 @@
        public static bool IsQtMsBuildEnabled(Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (project == null)
                return false;
            return IsQtMsBuildEnabled(project.Object as VCProject);
@@ -165,6 +145,8 @@
        private bool? isQtMsBuildEnabled = null;
        public bool IsQtMsBuildEnabled()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!isQtMsBuildEnabled.HasValue) {
                if (vcPro != null)
                    isQtMsBuildEnabled = IsQtMsBuildEnabled(vcPro);
@@ -192,6 +174,8 @@
        {
            get
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                var ret = false;
                if (lastConfigurationRowNames == null) {
                    lastConfigurationRowNames = envPro.ConfigurationManager.ConfigurationRowNames as Array;
@@ -213,6 +197,8 @@
        /// <param name="uiFile">name of the ui file</param>
        public string GetUiGeneratedFileName(string uiFile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var fi = new FileInfo(uiFile);
            var file = fi.Name;
            if (HelperFunctions.IsUicFile(file)) {
@@ -248,8 +234,11 @@
        /// replaced by the value of configName.
        /// <param name="file">full file name of either the header or the source file</param>
        /// <returns></returns>
        private string GetRelativeMocFilePath(string file, string configName, string platformName)
        private string GetRelativeMocFilePath(string file, string configName = null,
                                              string platformName = null)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var fileName = GetMocFileName(file);
            if (fileName == null)
                return null;
@@ -260,65 +249,38 @@
            return mocDir;
        }
        /// <summary>
        /// Returns the file name of the generated moc file relative to the
        /// project directory.
        /// </summary>
        /// The returned file path may contain the macros $(ConfigurationName) and $(PlatformName).
        /// <param name="file">full file name of either the header or the source file</param>
        /// <returns></returns>
        private string GetRelativeMocFilePath(string file)
        {
            return GetRelativeMocFilePath(file, null, null);
        }
        /// <summary>
        /// Marks the specified project as a Qt project.
        /// </summary>
        public void MarkAsQtProject()
        {
            vcPro.keyword = string.Format("{0}_v{1}",
                Resources.qtProjectKeyword, Resources.qtProjectFormatVersion);
        }
        public static int GetFormatVersion(VCProject vcPro)
        {
            if (vcPro == null)
                return 0;
            if (vcPro.keyword.StartsWith(Resources.qtProjectKeyword,
                StringComparison.InvariantCultureIgnoreCase)) {
            if (vcPro.keyword.StartsWith(Resources.qtProjectKeyword, StringComparison.Ordinal))
                return Convert.ToInt32(vcPro.keyword.Substring(6));
            } else if (vcPro.keyword.StartsWith(Resources.qtProjectV2Keyword,
                StringComparison.InvariantCultureIgnoreCase)) {
            if (vcPro.keyword.StartsWith(Resources.qtProjectV2Keyword, StringComparison.Ordinal))
                return 200;
            } else {
                return 0;
            }
            return 0;
        }
        public static int GetFormatVersion(Project pro)
        {
            if (pro == null)
                return 0;
            return GetFormatVersion(pro.Object as VCProject);
            ThreadHelper.ThrowIfNotOnUIThread();
            return GetFormatVersion(pro?.Object as VCProject);
        }
        public int FormatVersion { get { return GetFormatVersion(Project); } }
        public string GetPropertyValue(string propName)
        public int FormatVersion
        {
            return GetPropertyValue(Project, propName);
        }
        public string GetPropertyValue(string configName, string platformName, string propName)
        {
            return GetPropertyValue(Project, configName, platformName, propName);
            get
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                return GetFormatVersion(Project);
            }
        }
        public static string GetPropertyValue(
            EnvDTE.Project dteProject,
            string propName)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var activeConfig = dteProject.ConfigurationManager?.ActiveConfiguration;
            if (activeConfig == null)
                return null;
@@ -331,6 +293,8 @@
            EnvDTE.Configuration dteConfig,
            string propName)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (dteProject == null || dteConfig == null)
                return null;
            return GetPropertyValue(
@@ -338,16 +302,6 @@
                dteConfig.ConfigurationName,
                dteConfig.PlatformName,
                propName);
        }
        public static string GetPropertyValue(
            EnvDTE.Project dteProject,
            string configName,
            string platformName,
            string propName)
        {
            return GetPropertyValue(
                dteProject.Object as VCProject, configName, platformName, propName);
        }
        public static string GetPropertyValue(
@@ -374,123 +328,10 @@
            return vcConfig.GetEvaluatedPropertyValue(propName);
        }
        public void AddDefine(string define, uint bldConf)
        {
            foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                var compiler = CompilerToolWrapper.Create(config);
                if (((!IsDebugConfiguration(config)) && ((bldConf & BuildConfig.Release) != 0)) ||
                    ((IsDebugConfiguration(config)) && ((bldConf & BuildConfig.Debug) != 0))) {
                    compiler.AddPreprocessorDefinition(define);
                }
            }
        }
        public void AddModule(int id)
        {
            if (HasModule(id))
                return;
            var vm = QtVersionManager.The();
            var versionInfo = vm.GetVersionInfo(Project);
            if (versionInfo == null)
                versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion());
            foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                var info = QtModules.Instance.Module(id);
                if (FormatVersion >= Resources.qtMinFormatVersion_Settings) {
                    var config3 = config as VCConfiguration3;
                    if (config3 == null)
                        continue;
                    if (!string.IsNullOrEmpty(info.proVarQT)) {
                        var qtModulesValue = config.GetUnevaluatedPropertyValue("QtModules");
                        var qtModules = new HashSet<string>(
                            !string.IsNullOrEmpty(qtModulesValue)
                                ? qtModulesValue.Split(';')
                                : new string[] { });
                        qtModules.UnionWith(info.proVarQT.Split(' '));
                        config3.SetPropertyValue(Resources.projLabelQtSettings, true,
                            "QtModules", string.Join(";", qtModules));
                    }
                    // In V3 project format, compiler and linker options
                    // required by modules are set by Qt/MSBuild.
                    continue;
                }
                var compiler = CompilerToolWrapper.Create(config);
                var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                if (compiler != null) {
                    foreach (var define in info.Defines)
                        compiler.AddPreprocessorDefinition(define);
                    var incPathList = info.GetIncludePath();
                    foreach (var incPath in incPathList)
                        compiler.AddAdditionalIncludeDirectories(incPath);
                }
                if (linker != null) {
                    var moduleLibs = info.GetLibs(IsDebugConfiguration(config), versionInfo);
                    var linkerWrapper = new LinkerToolWrapper(linker);
                    var additionalDeps = linkerWrapper.AdditionalDependencies;
                    var dependenciesChanged = false;
                    if (additionalDeps == null || additionalDeps.Count == 0) {
                        additionalDeps = moduleLibs;
                        dependenciesChanged = true;
                    } else {
                        foreach (var moduleLib in moduleLibs) {
                            if (!additionalDeps.Contains(moduleLib)) {
                                additionalDeps.Add(moduleLib);
                                dependenciesChanged = true;
                            }
                        }
                    }
                    if (dependenciesChanged)
                        linkerWrapper.AdditionalDependencies = additionalDeps;
                }
            }
        }
        public void RemoveModule(int id)
        {
            foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                var compiler = CompilerToolWrapper.Create(config);
                var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                var info = QtModules.Instance.Module(id);
                if (compiler != null) {
                    foreach (var define in info.Defines)
                        compiler.RemovePreprocessorDefinition(define);
                    var additionalIncludeDirs = compiler.AdditionalIncludeDirectories;
                    if (additionalIncludeDirs != null) {
                        var lst = new List<string>(additionalIncludeDirs);
                        foreach (var includePath in info.IncludePath) {
                            lst.Remove(includePath);
                            lst.Remove('\"' + includePath + '\"');
                        }
                        compiler.AdditionalIncludeDirectories = lst;
                    }
                }
                if (linker != null && linker.AdditionalDependencies != null) {
                    var linkerWrapper = new LinkerToolWrapper(linker);
                    var vm = QtVersionManager.The();
                    var versionInfo = vm.GetVersionInfo(Project);
                    if (versionInfo == null)
                        versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion());
                    var moduleLibs = info.GetLibs(IsDebugConfiguration(config), versionInfo);
                    var additionalDependencies = linkerWrapper.AdditionalDependencies;
                    var dependenciesChanged = false;
                    foreach (var moduleLib in moduleLibs)
                        dependenciesChanged |= additionalDependencies.Remove(moduleLib);
                    if (dependenciesChanged)
                        linkerWrapper.AdditionalDependencies = additionalDependencies;
                }
            }
        }
        public void UpdateModules(VersionInformation oldVersion, VersionInformation newVersion)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
@@ -500,7 +341,7 @@
                        var additionalDependencies = linkerWrapper.AdditionalDependencies;
                        var libsDesktop = new List<string>();
                        foreach (var module in QtModules.Instance.GetAvailableModules()) {
                        foreach (var module in QtModules.Instance.GetAvailableModules(newVersion.qtMajor)) {
                            if (HasModule(module.Id))
                                libsDesktop.AddRange(module.AdditionalLibraries);
                        }
@@ -538,133 +379,11 @@
            }
        }
        // TODO: remove once all callers are moved into Legacy namespace
        public bool HasModule(int id)
        {
            var foundInIncludes = false;
            var foundInLibs = false;
            var vm = QtVersionManager.The();
            var versionInfo = vm.GetVersionInfo(Project);
            if (versionInfo == null)
                versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion());
            if (versionInfo == null)
                return false; // neither a default or project Qt version
            var info = QtModules.Instance.Module(id);
            if (info == null)
                return false;
            foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                var compiler = CompilerToolWrapper.Create(config);
                var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                if (compiler != null) {
                    if (compiler.GetAdditionalIncludeDirectories() == null)
                        continue;
                    var incPathList = info.GetIncludePath();
                    var includeDirs = compiler.GetAdditionalIncludeDirectoriesList();
                    foundInIncludes = (incPathList.Count > 0);
                    foreach (var incPath in incPathList) {
                        var fixedIncludeDir = FixFilePathForComparison(incPath);
                        if (!includeDirs.Any(dir =>
                            FixFilePathForComparison(dir) == fixedIncludeDir)) {
                            foundInIncludes = false;
                            break;
                        }
                    }
                }
                if (foundInIncludes)
                    break;
                List<string> libs = null;
                if (linker != null) {
                    var linkerWrapper = new LinkerToolWrapper(linker);
                    libs = linkerWrapper.AdditionalDependencies;
                }
                if (libs != null) {
                    var moduleLibs = info.GetLibs(IsDebugConfiguration(config), versionInfo);
                    foundInLibs = moduleLibs.All(moduleLib => libs.Contains(moduleLib));
                }
            }
            return foundInIncludes || foundInLibs;
        }
        public void WriteProjectBasicConfigurations(uint type, bool usePrecompiledHeader)
        {
            WriteProjectBasicConfigurations(type, usePrecompiledHeader, null);
        }
        public void WriteProjectBasicConfigurations(uint type, bool usePrecompiledHeader, VersionInformation vi)
        {
            var configType = ConfigurationTypes.typeApplication;
            var targetExtension = ".exe";
            string qtVersion = null;
            var vm = QtVersionManager.The();
            if (vi == null) {
                qtVersion = vm.GetDefaultVersion();
                vi = vm.GetVersionInfo(qtVersion);
            }
            switch (type & TemplateType.ProjectType) {
            case TemplateType.DynamicLibrary:
                configType = ConfigurationTypes.typeDynamicLibrary;
                targetExtension = ".dll";
                break;
            case TemplateType.StaticLibrary:
                configType = ConfigurationTypes.typeStaticLibrary;
                targetExtension = ".lib";
                break;
            }
            foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                config.ConfigurationType = configType;
                var compiler = CompilerToolWrapper.Create(config);
                var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool");
                var librarian = (VCLibrarianTool)((IVCCollection)config.Tools).Item("VCLibrarianTool");
                if (linker != null) {
                    if ((type & TemplateType.ConsoleSystem) != 0)
                        linker.SubSystem = subSystemOption.subSystemConsole;
                    else
                        linker.SubSystem = subSystemOption.subSystemWindows;
                    linker.OutputFile = "$(OutDir)\\$(ProjectName)" + targetExtension;
                } else {
                    librarian.OutputFile = "$(OutDir)\\$(ProjectName)" + targetExtension;
                }
                if ((type & TemplateType.PluginProject) != 0)
                    compiler.AddPreprocessorDefinition("QT_PLUGIN");
                var isDebugConfiguration = false;
                if (config.Name.StartsWith("Release", StringComparison.Ordinal)) {
                    compiler.SetDebugInformationFormat(debugOption.debugDisabled);
                    compiler.RuntimeLibrary = runtimeLibraryOption.rtMultiThreadedDLL;
                } else if (config.Name.StartsWith("Debug", StringComparison.Ordinal)) {
                    isDebugConfiguration = true;
                    compiler.SetOptimization(optimizeOption.optimizeDisabled);
                    compiler.SetDebugInformationFormat(debugOption.debugEnabled);
                    compiler.RuntimeLibrary = runtimeLibraryOption.rtMultiThreadedDebugDLL;
                }
                compiler.SetTreatWChar_tAsBuiltInType(true);
                if (linker != null)
                    linker.GenerateDebugInformation = isDebugConfiguration;
                if (usePrecompiledHeader)
                    UsePrecompiledHeaders(config);
            }
            if ((type & TemplateType.PluginProject) != 0)
                MarkAsDesignerPluginProject();
        }
        public void MarkAsDesignerPluginProject()
        {
            Project.Globals["IsDesignerPlugin"] = true.ToString();
            if (!Project.Globals.get_VariablePersists("IsDesignerPlugin"))
                Project.Globals.set_VariablePersists("IsDesignerPlugin", true);
            ThreadHelper.ThrowIfNotOnUIThread();
            return Legacy.QtProject.HasModule(envPro, id);
        }
        public void AddUic4BuildStepMsBuild(
@@ -672,8 +391,7 @@
            string description,
            string outputFile)
        {
            var file = config.File as VCFile;
            if (file != null)
            if (config.File is VCFile file)
                file.ItemType = QtUic.ItemTypeName;
            qtMsBuild.SetItemProperty(config, QtUic.Property.ExecutionDescription, description);
            qtMsBuild.SetItemProperty(config, QtUic.Property.OutputFile, outputFile);
@@ -701,6 +419,8 @@
        /// <param name="file">file</param>
        public void AddUic4BuildStep(VCFile file)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (GetFormatVersion(vcPro) >= Resources.qtMinFormatVersion_Settings) {
                file.ItemType = QtUic.ItemTypeName;
                return;
@@ -736,7 +456,7 @@
                    }
                }
                if (toolSettings == CustomTool.CustomBuildStep && !uiFileExists)
                    AddFileInFilter(Filters.GeneratedFiles(), uiFile);
                    AddFileInFilter(Filters.GeneratedFiles(), uiFile, false);
            } catch {
                throw new QtVSException(SR.GetString("QtProject_CannotAddUicStep", file.FullPath));
            }
@@ -796,15 +516,15 @@
        public string GetDefines(VCFileConfiguration conf)
        {
            var defines = string.Empty;
            var propsFile = conf.Tool as IVCRulePropertyStorage;
            var projectConfig = conf.ProjectConfiguration as VCConfiguration;
            var propsProject = projectConfig.Rules.Item("CL") as IVCRulePropertyStorage;
            if (propsFile != null) {
            if (conf.Tool is IVCRulePropertyStorage propsFile) {
                try {
                    defines = propsFile.GetUnevaluatedPropertyValue("PreprocessorDefinitions");
                } catch { }
            }
            if (string.IsNullOrEmpty(defines) && propsProject != null) {
            var projectConfig = conf.ProjectConfiguration as VCConfiguration;
            if (string.IsNullOrEmpty(defines)
                && projectConfig?.Rules.Item("CL") is IVCRulePropertyStorage propsProject) {
                try {
                    defines = propsProject.GetUnevaluatedPropertyValue("PreprocessorDefinitions");
                } catch { }
@@ -846,12 +566,11 @@
            var projectConfig = conf.ProjectConfiguration as VCConfiguration;
            includeList.AddRange(GetIncludesFromCompilerTool(CompilerToolWrapper.Create(projectConfig)));
            var propertySheets = projectConfig.PropertySheets as IVCCollection;
            if (propertySheets != null) {
            if (projectConfig.PropertySheets is IVCCollection propertySheets) {
                foreach (VCPropertySheet sheet in propertySheets)
                    includeList.AddRange(GetIncludesFromPropertySheet(sheet));
            }
            var ompModified = new List<string>();
            string sDir = "$(SolutionDir)";
            foreach (string inc in includeList) {
@@ -884,8 +603,7 @@
        private List<string> GetIncludesFromPropertySheet(VCPropertySheet sheet)
        {
            var includeList = GetIncludesFromCompilerTool(CompilerToolWrapper.Create(sheet));
            var propertySheets = sheet.PropertySheets as IVCCollection;
            if (propertySheets != null) {
            if (sheet.PropertySheets is IVCCollection propertySheets) {
                foreach (VCPropertySheet subSheet in propertySheets)
                    includeList.AddRange(GetIncludesFromPropertySheet(subSheet));
            }
@@ -903,18 +621,10 @@
            return new List<string>();
        }
        private static bool IsDebugConfiguration(VCConfiguration conf)
        {
            var tool = CompilerToolWrapper.Create(conf);
            if (tool != null) {
                return tool.RuntimeLibrary == runtimeLibraryOption.rtMultiThreadedDebug
                    || tool.RuntimeLibrary == runtimeLibraryOption.rtMultiThreadedDebugDLL;
            }
            return false;
        }
        private string GetPCHMocOptions(VCFile file, CompilerToolWrapper compiler)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            // As .moc files are included, we should not add anything there
            if (!HelperFunctions.IsHeaderFile(file.Name))
                return string.Empty;
@@ -941,12 +651,13 @@
            VCFileConfiguration workFileConfig,
            VCFile mocFile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var hasDifferentMocFilePerConfig =
                QtVSIPSettings.HasDifferentMocFilePerConfig(envPro);
            var hasDifferentMocFilePerPlatform =
                QtVSIPSettings.HasDifferentMocFilePerPlatform(envPro);
            var workFile = workFileConfig.File as VCFile;
            var mocFileName = GetMocFileName(sourceFile.FullPath);
            var mocableIsCPP = HelperFunctions.IsMocFile(mocFileName);
            var vcConfig = workFileConfig.ProjectConfiguration as VCConfiguration;
@@ -1025,6 +736,8 @@
            string includes,
            string description)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var workFile = workFileConfig.File as VCFile;
            var mocFileName = GetMocFileName(sourceFile.FullPath);
            var mocableIsCPP = HelperFunctions.IsMocFile(mocFileName);
@@ -1068,6 +781,7 @@
                + mocFileName + "))";
            var regExp = new Regex(pattern);
            var matchList = regExp.Matches(tool.Outputs.Replace(ProjectMacros.Name, baseFileName));
            if (matchList.Count > 0) {
                if (matchList[0].Length > 0)
                    outputMocFile = matchList[0].ToString();
@@ -1202,6 +916,8 @@
            string includes,
            string description)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var baseFileName = sourceFile.Name.Remove(sourceFile.Name.LastIndexOf('.'));
            var outputMocFile = GetRelativeMocFilePath(sourceFile.FullPath);
            var outputMocPath = Path.GetDirectoryName(outputMocFile);
@@ -1233,7 +949,8 @@
            VCFileConfiguration workConfig,
            CustomTool toolSettings)
        {
            var workFile = workConfig.File as VCFile;
            ThreadHelper.ThrowIfNotOnUIThread();
            var mocFileName = GetMocFileName(sourceFile.FullPath);
            var mocableIsCPP = HelperFunctions.IsMocFile(mocFileName);
            var vcConfig = workConfig.ProjectConfiguration as VCConfiguration;
@@ -1316,6 +1033,8 @@
        /// <param name="file">file</param>
        public void AddMocStep(VCFile file)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (GetFormatVersion(vcPro) >= Resources.qtMinFormatVersion_Settings) {
                file.ItemType = QtMoc.ItemTypeName;
                if (HelperFunctions.IsSourceFile(file.FullPath)) {
@@ -1343,8 +1062,7 @@
                    File.WriteAllText(cbtFullPath, string.Format(
                        "This is a dummy file needed to create {0}", mocFileName));
                    file = AddFileInSubfilter(Filters.GeneratedFiles(), null, cbtFullPath, true);
                    var mocFileItem = file.Object as ProjectItem;
                    if (mocFileItem != null)
                    if (file.Object is ProjectItem mocFileItem)
                        HelperFunctions.EnsureCustomBuildToolAvailable(mocFileItem);
                }
@@ -1417,6 +1135,8 @@
        public bool HasMocStep(VCFile file, string mocOutDir = null)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (file.ItemType == QtMoc.ItemTypeName)
                return true;
@@ -1452,6 +1172,8 @@
        public void RefreshRccSteps()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            Messages.Print("\r\n=== Update rcc steps ===");
            var files = GetResourceFiles();
@@ -1482,6 +1204,8 @@
        public void RefreshRccSteps(string oldRccDir)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            RefreshRccSteps();
            UpdateCompilerIncludePaths(oldRccDir, QtVSIPSettings.GetRccDirectory(envPro));
        }
@@ -1493,8 +1217,7 @@
            string nameOnly,
            string qrcCppFile)
        {
            var file = vfc.File as VCFile;
            if (file != null)
            if (vfc.File is VCFile file)
                file.ItemType = QtRcc.ItemTypeName;
            qtMsBuild.SetItemProperty(vfc,
                QtRcc.Property.ExecutionDescription, "Rcc'ing " + ProjectMacros.FileName + "...");
@@ -1509,16 +1232,15 @@
            string nameOnly,
            string qrcCppFile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var qrcFile = vfc.File as VCFile;
            var rccOptsCfg = rccOpts;
            var cmdLine = string.Empty;
            var cbt = HelperFunctions.GetCustomBuildTool(vfc);
            cbt.AdditionalDependencies = filesInQrcFile;
            cbt.Description = "Rcc'ing " + ProjectMacros.FileName + "...";
            cbt.Outputs = qrcCppFile.Replace(nameOnly, ProjectMacros.Name);
            cmdLine += "\"" + Resources.rcc4Command + "\""
@@ -1539,6 +1261,8 @@
        public void UpdateRccStep(VCFile qrcFile, RccOptions rccOpts)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (GetFormatVersion(vcPro) >= Resources.qtMinFormatVersion_Settings) {
                qrcFile.ItemType = QtRcc.ItemTypeName;
                return;
@@ -1548,8 +1272,6 @@
                IsQtMsBuildEnabled() ? CustomTool.MSBuildTarget : CustomTool.CustomBuildStep;
            var vcpro = (VCProject)qrcFile.project;
            var dteObject = ((Project)vcpro.Object).DTE;
            var qtPro = Create(vcpro);
            var parser = new QrcParser(qrcFile.FullPath);
            var filesInQrcFile = ProjectMacros.Path;
@@ -1606,7 +1328,7 @@
            }
        }
        static public void ExcludeFromAllBuilds(VCFile file)
        public static void ExcludeFromAllBuilds(VCFile file)
        {
            if (file == null)
                return;
@@ -1672,8 +1394,7 @@
        List<VCFile> GetCppMocFiles(VCFile cppFile)
        {
            List<VCFile> mocFiles = new List<VCFile>();
            var vcProj = cppFile.project as VCProject;
            if (vcProj != null) {
            if (cppFile.project is VCProject vcProj) {
                mocFiles.AddRange(from VCFile vcFile
                                  in (IVCCollection)vcProj.Files
                                  where vcFile.ItemType == "CustomBuild"
@@ -1697,27 +1418,29 @@
        bool HasCppMocFiles(VCFile cppFile)
        {
            if (!IsQtMsBuildEnabled()) {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!IsQtMsBuildEnabled())
                return File.Exists(Path.ChangeExtension(cppFile.FullPath, ".cbt"));
            } else {
                var vcProj = cppFile.project as VCProject;
                if (vcProj != null) {
                    foreach (VCFile vcFile in (IVCCollection)vcProj.Files) {
                        if (vcFile.ItemType == "CustomBuild") {
                            if (IsCppMocFileCustomBuild(vcProj, vcFile, cppFile))
                                return true;
                        } else if (vcFile.ItemType == QtMoc.ItemTypeName) {
                            if (IsCppMocFileQtMsBuild(vcProj, vcFile, cppFile))
                                return true;
                        }
            if (cppFile.project is VCProject vcProj) {
                foreach (VCFile vcFile in (IVCCollection)vcProj.Files) {
                    if (vcFile.ItemType == "CustomBuild") {
                        if (IsCppMocFileCustomBuild(vcProj, vcFile, cppFile))
                            return true;
                    } else if (vcFile.ItemType == QtMoc.ItemTypeName) {
                        if (IsCppMocFileQtMsBuild(vcProj, vcFile, cppFile))
                            return true;
                    }
                }
                return false;
            }
            return false;
        }
        public void RemoveMocStep(VCFile file)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (file.ItemType == QtMoc.ItemTypeName) {
                RemoveMocStepQtMsBuild(file);
            } else if (HelperFunctions.IsHeaderFile(file.Name)) {
@@ -1756,6 +1479,7 @@
        /// <param name="file">file</param>
        public void RemoveMocStepCustomBuild(VCFile file)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            try {
                if (!HasMocStep(file))
                    return;
@@ -1890,14 +1614,11 @@
        public VCFile GetFileFromProject(string fileName)
        {
            fileName = HelperFunctions.NormalizeRelativeFilePath(fileName);
            var nf = fileName;
            if (!HelperFunctions.IsAbsoluteFilePath(fileName))
                nf = HelperFunctions.NormalizeFilePath(vcPro.ProjectDirectory + "\\" + fileName);
            nf = nf.ToLower();
                fileName = HelperFunctions.NormalizeFilePath(vcPro.ProjectDirectory + "\\" + fileName);
            foreach (VCFile f in (IVCCollection)vcPro.Files) {
                if (f.FullPath.ToLower() == nf)
                if (f.FullPath.Equals(fileName, StringComparison.OrdinalIgnoreCase))
                    return f;
            }
            return null;
@@ -1913,7 +1634,7 @@
        {
            var fi = new FileInfo(HelperFunctions.NormalizeRelativeFilePath(fileName));
            foreach (VCFile f in (IVCCollection)vcPro.Files) {
                if (string.Equals(f.Name, fi.Name, StringComparison.OrdinalIgnoreCase))
                if (f.Name.Equals(fi.Name, StringComparison.OrdinalIgnoreCase))
                    yield return f;
            }
        }
@@ -1924,34 +1645,6 @@
            foreach (VCFilter subfilter in (IVCCollection)filter.Filters)
                tmpList.AddRange(GetAllFilesFromFilter(subfilter));
            return tmpList;
        }
        /// <summary>
        /// Adds a file to a filter. If the filter doesn't exist yet, it
        /// will be created. (Doesn't check for duplicates)
        /// </summary>
        /// <param name="filter">fake filter</param>
        /// <param name="fileName">relative file name</param>
        /// <returns>A VCFile object of the added file.</returns>
        public VCFile AddFileInFilter(FakeFilter filter, string fileName)
        {
            return AddFileInFilter(filter, fileName, false);
        }
        public void RemoveItem(ProjectItem item)
        {
            foreach (ProjectItem tmpFilter in Project.ProjectItems) {
                if (tmpFilter.Name == item.Name) {
                    tmpFilter.Remove();
                    return;
                }
                foreach (ProjectItem tmpItem in tmpFilter.ProjectItems) {
                    if (tmpItem.Name == item.Name) {
                        tmpItem.Remove();
                        return;
                    }
                }
            }
        }
        /// <summary>
@@ -1972,7 +1665,8 @@
            return AddFileInSubfilter(filter, subfilterName, fileName, false);
        }
        public VCFile AddFileInSubfilter(FakeFilter filter, string subfilterName, string fileName, bool checkForDuplicates)
        public VCFile AddFileInSubfilter(FakeFilter filter, string subfilterName, string fileName,
            bool checkForDuplicates)
        {
            try {
                var vfilt = FindFilterFromGuid(filter.UniqueIdentifier);
@@ -1981,7 +1675,7 @@
                        // check if user already created this filter... then add guid
                        vfilt = FindFilterFromName(filter.Name);
                        if (vfilt == null)
                            throw new QtVSException(SR.GetString("QtProject_CannotAddFilter", filter.Name));
                            throw new QtVSException($"Project cannot add filter {filter.Name}");
                    } else {
                        vfilt = (VCFilter)vcPro.AddFilter(filter.Name);
                    }
@@ -2014,14 +1708,7 @@
                    }
                    if (!subfilterFound) {
                        if (!vfilt.CanAddFilter(subfilterName))
                            throw new QtVSException(SR.GetString("QtProject_CannotAddFilter", filter.Name));
#if !(VS2017 || VS2019 || VS2022)
                        // TODO: Enable once the freeze gets fixed in VS.
                        vfilt = (VCFilter)vfilt.AddFilter(subfilterName);
                        vfilt.Filter = "cpp;moc";
                        vfilt.SourceControlFiles = false;
#endif
                            throw new QtVSException($"Project cannot add filter {filter.Name}");
                    }
                }
@@ -2034,10 +1721,13 @@
                if (vfilt.CanAddFile(fileName))
                    return (VCFile)(vfilt.AddFile(fileName));
                throw new QtVSException(SR.GetString("QtProject_CannotAddFile", fileName));
            } catch {
                throw new QtVSException(SR.GetString("QtProject_CannotAddFile", fileName));
                throw new QtVSException($"Cannot add file {fileName} to filter.");
            } catch (QtVSException) {
                throw;
            } catch (Exception e){
                throw new QtVSException($"Cannot add file {fileName} to filter.", e);
            }
        }
        /// <summary>
@@ -2165,94 +1855,38 @@
            }
        }
        public void AddDirectories()
        public static bool IsQtPlugin(Core.QtProject qtPro)
        {
            try {
                // resource directory
                var fi = new FileInfo(envPro.FullName);
                var dfi = new DirectoryInfo(fi.DirectoryName + "\\" + Resources.resourceDir);
                dfi.Create();
            } catch {
                throw new QtVSException(SR.GetString("QtProject_CannotCreateResourceDir"));
            }
            AddFilterToProject(Filters.ResourceFiles());
        }
            ThreadHelper.ThrowIfNotOnUIThread();
        public void Finish()
        {
            try {
                var solutionExplorer = dte.Windows.Item(Constants.vsWindowKindSolutionExplorer);
                if (solutionExplorer != null) {
                    var hierarchy = (UIHierarchy)solutionExplorer.Object;
                    var projects = hierarchy.UIHierarchyItems.Item(1).UIHierarchyItems;
            if (qtPro.FormatVersion < Resources.qtMinFormatVersion_Settings)
                return false;
                    foreach (UIHierarchyItem itm in projects) {
                        if (itm.Name == envPro.Name) {
                            foreach (UIHierarchyItem i in itm.UIHierarchyItems) {
                                if (i.Name == Filters.GeneratedFiles().Name)
                                    i.UIHierarchyItems.Expanded = false;
                            }
                            break;
                        }
                    }
                }
            } catch { }
            ProjectTracker?.AddProject(envPro);
        }
        public bool IsDesignerPluginProject()
        {
            var b = false;
            if (Project.Globals.get_VariablePersists("IsDesignerPlugin")) {
                var s = (string)Project.Globals["IsDesignerPlugin"];
                try {
                    b = bool.Parse(s);
                } catch { }
            }
            return b;
        }
        /// <summary>
        /// Adds a file to a specified filter in a project.
        /// </summary>
        /// <param name="destName">name of the file in the project (relative to the project directory)</param>
        /// <param name="filter">filter</param>
        /// <returns>VCFile</returns>
        public VCFile AddFileToProject(string destName, FakeFilter filter)
        {
            VCFile file = null;
            if (filter != null)
                file = AddFileInFilter(filter, destName);
            else
                file = (VCFile)vcPro.AddFile(destName);
            if (file == null)
                return null;
            if (HelperFunctions.IsHeaderFile(file.Name)) {
                foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
                    var compiler = CompilerToolWrapper.Create(config);
                    if (compiler == null)
                        continue;
                    var paths = compiler.GetAdditionalIncludeDirectoriesList();
                    var fi = new FileInfo(file.FullPath);
                    var relativePath = HelperFunctions.GetRelativePath(ProjectDir, fi.Directory.ToString());
                    var fixedRelativePath = FixFilePathForComparison(relativePath);
                    if (!paths.Any(p => FixFilePathForComparison(p) == fixedRelativePath))
                        compiler.AddAdditionalIncludeDirectories(relativePath);
            foreach (VCConfiguration config in qtPro.VCProject.Configurations as IVCCollection) {
                if ((config.Rules.Item("QtRule10_Settings") as IVCRulePropertyStorage)
                        .GetEvaluatedPropertyValue("QtPlugin") == "true") {
                    return true;
                }
            }
            return file;
            return false;
        }
        public static void MarkAsQtPlugin(Core.QtProject qtPro)
        {
            foreach (VCConfiguration config in qtPro.VCProject.Configurations as IVCCollection) {
                (config.Rules.Item("QtRule10_Settings") as IVCRulePropertyStorage)
                    .SetPropertyValue("QtPlugin", "true");
            }
        }
        /// <summary>
        /// adjusts the whitespaces, tabs in the given file according to VS settings
        /// </summary>
        /// <param name="file"></param>
        public void AdjustWhitespace(string file)
        public static void AdjustWhitespace(DTE dte, string file)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!File.Exists(file))
                return;
@@ -2299,89 +1933,6 @@
            return whitespaces;
        }
        /// <summary>
        /// Copy a file to the projects folder. Does not add the file to the project.
        /// </summary>
        /// <param name="srcFile">full name of the file to add</param>
        /// <param name="destFolder">the name of the project folder</param>
        /// <param name="destName">name of the file in the project (relative to the project directory)</param>
        /// <returns>full name of the destination file</returns>
        public static string CopyFileToFolder(string srcFile, string destFolder, string destName)
        {
            var fullDestName = destFolder + "\\" + destName;
            var fi = new FileInfo(fullDestName);
            var replace = true;
            if (File.Exists(fullDestName)) {
                if (DialogResult.No == MessageBox.Show(SR.GetString("QtProject_FileExistsInProjectFolder", destName)
                    , SR.GetString("Resources_QtVsTools"), MessageBoxButtons.YesNo, MessageBoxIcon.Question)) {
                    replace = false;
                }
            }
            if (replace) {
                if (!fi.Directory.Exists)
                    fi.Directory.Create();
                File.Copy(srcFile, fullDestName, true);
                var attribs = File.GetAttributes(fullDestName);
                File.SetAttributes(fullDestName, attribs & (~FileAttributes.ReadOnly));
            }
            return fi.FullName;
        }
        public static void ReplaceTokenInFile(string file, string token, string replacement)
        {
            var text = string.Empty;
            try {
                var reader = new StreamReader(file);
                text = reader.ReadToEnd();
                reader.Close();
            } catch (Exception e) {
                Messages.DisplayErrorMessage(
                    SR.GetString("QtProject_CannotReplaceTokenRead", token, replacement, e.ToString()));
                return;
            }
            try {
                if (token.ToUpper() == "%PRE_DEF%" && !Char.IsLetter(replacement[0]))
                    replacement = "_" + replacement;
                text = text.Replace(token, replacement);
                var writer = new StreamWriter(file);
                writer.Write(text);
                writer.Close();
            } catch (Exception e) {
                Messages.DisplayErrorMessage(
                    SR.GetString("QtProject_CannotReplaceTokenWrite", token, replacement, e.ToString()));
            }
        }
        public void RepairGeneratedFilesStructure()
        {
            DeleteGeneratedFiles();
            var files = new ConcurrentBag<VCFile>();
            Task.WaitAll(
                Task.Run(() =>
                    Parallel.ForEach(((IVCCollection)vcPro.Files).Cast<VCFile>(), file =>
                    {
                        var name = file.Name;
                        if (!HelperFunctions.IsHeaderFile(name) && !HelperFunctions.IsSourceFile(name))
                            return;
                        if (HelperFunctions.HasQObjectDeclaration(file))
                            files.Add(file);
                    })
                )
            );
            qtMsBuild.BeginSetItemProperties();
            foreach (var file in files) {
                RemoveMocStep(file);
                AddMocStep(file);
            }
            qtMsBuild.EndSetItemProperties();
        }
        public void TranslateFilterNames()
        {
            var filters = vcPro.Filters as IVCCollection;
@@ -2402,37 +1953,10 @@
            }
        }
        public static string CreateQrcFile(string projectDir, string className, string destName)
        {
            var fullDestName = projectDir + "\\" + destName;
            if (!File.Exists(fullDestName)) {
                FileStream s = null;
                try {
                    s = File.Open(fullDestName, FileMode.CreateNew);
                    if (s.CanWrite) {
                        using (var sw = new StreamWriter(s)) {
                            s = null;
                            sw.WriteLine("<RCC>");
                            sw.WriteLine("    <qresource prefix=\"" + className + "\">");
                            sw.WriteLine("    </qresource>");
                            sw.WriteLine("</RCC>");
                        }
                    }
                } finally {
                    if (s != null)
                        s.Dispose();
                }
                var attribs = File.GetAttributes(fullDestName);
                File.SetAttributes(fullDestName, attribs & (~FileAttributes.ReadOnly));
            }
            var fi = new FileInfo(fullDestName);
            return fi.FullName;
        }
        public void AddActiveQtBuildStep(string version, string defFile = null)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (FormatVersion < Resources.qtMinFormatVersion_ClProperties)
                return;
@@ -2463,6 +1987,8 @@
        private void UpdateCompilerIncludePaths(string oldDir, string newDir)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var fixedOldDir = FixFilePathForComparison(oldDir);
            var dirs = new[] {
                FixFilePathForComparison(QtVSIPSettings.GetUicDirectory(envPro)),
@@ -2516,6 +2042,8 @@
        public void UpdateUicSteps(string oldUicDir, bool update_inc_path)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            Messages.Print("\r\n=== Update uic steps ===");
            var vcFilter = FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier);
            if (vcFilter != null) {
@@ -2538,7 +2066,7 @@
            qtMsBuild.BeginSetItemProperties();
            foreach (var file in files) {
                if (HelperFunctions.IsUicFile(file.Name) && !IsUic3File(file)) {
                if (HelperFunctions.IsUicFile(file.Name)) {
                    AddUic4BuildStep(file);
                    Messages.Print("Update uic step for " + file.Name + ".");
                    ++updatedFiles;
@@ -2549,42 +2077,6 @@
                UpdateCompilerIncludePaths(oldUicDir, QtVSIPSettings.GetUicDirectory(envPro));
            Messages.Print("\r\n=== " + updatedFiles + " uic steps updated. ===\r\n");
        }
        private static bool IsUic3File(VCFile file)
        {
            foreach (VCFileConfiguration config in (IVCCollection)file.FileConfigurations) {
                var tool = HelperFunctions.GetCustomBuildTool(config);
                if (tool == null)
                    return false;
                if (tool.CommandLine.IndexOf("uic3.exe", StringComparison.OrdinalIgnoreCase) > -1)
                    return true;
            }
            return false;
        }
        public bool UsePrecompiledHeaders(VCConfiguration config)
        {
            var compiler = CompilerToolWrapper.Create(config);
            return UsePrecompiledHeaders(compiler);
        }
        private bool UsePrecompiledHeaders(CompilerToolWrapper compiler)
        {
            try {
                compiler.SetUsePrecompiledHeader(pchOption.pchUseUsingSpecific);
                var pcHeaderThrough = GetPrecompiledHeaderThrough();
                if (string.IsNullOrEmpty(pcHeaderThrough))
                    pcHeaderThrough = "stdafx.h";
                compiler.SetPrecompiledHeaderThrough(pcHeaderThrough);
                var pcHeaderFile = GetPrecompiledHeaderFile();
                if (string.IsNullOrEmpty(pcHeaderFile))
                    pcHeaderFile = ".\\$(ConfigurationName)/" + Project.Name + ".pch";
                compiler.SetPrecompiledHeaderFile(pcHeaderFile);
                return true;
            } catch {
                return false;
            }
        }
        public bool UsesPrecompiledHeaders()
@@ -2637,32 +2129,6 @@
            return null;
        }
        public string GetPrecompiledHeaderFile()
        {
            foreach (VCConfiguration config in vcPro.Configurations as IVCCollection) {
                var file = GetPrecompiledHeaderFile(config);
                if (!string.IsNullOrEmpty(file))
                    return file;
            }
            return null;
        }
        public static string GetPrecompiledHeaderFile(VCConfiguration config)
        {
            var compiler = CompilerToolWrapper.Create(config);
            return GetPrecompiledHeaderFile(compiler);
        }
        private static string GetPrecompiledHeaderFile(CompilerToolWrapper compiler)
        {
            try {
                var file = compiler.GetPrecompiledHeaderFile();
                if (!string.IsNullOrEmpty(file))
                    return file;
            } catch { }
            return null;
        }
        public static void SetPCHOption(VCFile vcFile, pchOption option)
        {
            foreach (VCFileConfiguration config in vcFile.FileConfigurations as IVCCollection) {
@@ -2692,6 +2158,8 @@
        /// <returns></returns>
        private VCFile GetGeneratedMocFile(string fileName, VCFileConfiguration fileConfig)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (QtVSIPSettings.HasDifferentMocFilePerConfig(envPro)
                || QtVSIPSettings.HasDifferentMocFilePerPlatform(envPro)) {
                var projectConfig = (VCConfiguration)fileConfig.ProjectConfiguration;
@@ -2763,6 +2231,8 @@
        public void RefreshMocSteps()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            // Ignore when using shared compiler properties
            if (GetFormatVersion(vcPro) >= Resources.qtMinFormatVersion_ClProperties)
                return;
@@ -2790,6 +2260,7 @@
        public void RefreshMocStep(VCFile vcfile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            RefreshMocStep(vcfile, true);
        }
@@ -2803,6 +2274,8 @@
        /// <param name="vcfile"></param>
        private void RefreshMocStep(VCFile vcfile, bool singleFile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var isHeaderFile = HelperFunctions.IsHeaderFile(vcfile.FullPath);
            if (!isHeaderFile && !HelperFunctions.IsSourceFile(vcfile.FullPath))
                return;
@@ -2942,6 +2415,8 @@
        public void OnExcludedFromBuildChanged(VCFile vcFile, VCFileConfiguration vcFileCfg)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            // Update the ExcludedFromBuild flags of the mocced file
            // according to the ExcludedFromBuild flag of the mocable source file.
            var moccedFileName = GetMocFileName(vcFile.Name);
@@ -3011,6 +2486,8 @@
        public void UpdateMocSteps(string oldMocDir)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            Messages.Print("\r\n=== Update moc steps ===");
            var orgFiles = new List<VCFile>();
            var abandonedMocFiles = new List<string>();
@@ -3053,8 +2530,8 @@
                try {
                    RemoveMocStep(file);
                    AddMocStep(file);
                } catch (QtVSException e) {
                    Messages.Print(e.Message);
                } catch (QtVSException exception) {
                    exception.Log();
                    continue;
                }
                Messages.Print("Moc step updated successfully for " + file.Name + ".");
@@ -3073,6 +2550,8 @@
        private void Clean()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var solutionConfigs = envPro.DTE.Solution.SolutionBuild.SolutionConfigurations;
            var backup = new List<KeyValuePair<SolutionContext, bool>>();
            foreach (SolutionConfiguration config in solutionConfigs) {
@@ -3125,47 +2604,6 @@
            }
        }
        public bool isWinRT()
        {
            try {
                var vcProject = Project.Object as VCProject;
                var vcConfigs = vcProject.Configurations as IVCCollection;
                var vcConfig = vcConfigs.Item(1) as VCConfiguration;
                var appType = vcConfig.GetEvaluatedPropertyValue("ApplicationType");
                if (appType == "Windows Store")
                    return true;
            } catch { }
            return false;
        }
        public bool PromptChangeQtVersion(string oldVersion, string newVersion)
        {
            var versionManager = QtVersionManager.The();
            var viOld = versionManager.GetVersionInfo(oldVersion);
            var viNew = versionManager.GetVersionInfo(newVersion);
            if (viOld == null || viNew == null)
                return true;
            var oldIsWinRt = viOld.isWinRT();
            var newIsWinRt = viNew.isWinRT();
            if (newIsWinRt == oldIsWinRt || newIsWinRt == isWinRT())
                return true;
            var promptCaption = string.Format("Change Qt Version ({0})", Project.Name);
            var promptText = string.Format(
                "Changing Qt version from {0} to {1}.\r\n" +
                "Project might not build. Are you sure?",
                newIsWinRt ? "Win32" : "WinRT",
                newIsWinRt ? "WinRT" : "Win32"
                );
            return (MessageBox.Show(
                promptText, promptCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
                == DialogResult.Yes);
        }
        /// <summary>
        /// Changes the Qt version of this project.
        /// </summary>
@@ -3175,6 +2613,8 @@
        /// <returns>true, if the operation performed successfully</returns>
        public bool ChangeQtVersion(string oldVersion, string newVersion, ref bool newProjectCreated)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            newProjectCreated = false;
            var versionManager = QtVersionManager.The();
            var viNew = versionManager.GetVersionInfo(newVersion);
@@ -3243,6 +2683,8 @@
        public bool SelectSolutionPlatform(string platformName)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            foreach (SolutionConfiguration solutionCfg in dte.Solution.SolutionBuild.SolutionConfigurations) {
                var contexts = solutionCfg.SolutionContexts;
                for (var i = 1; i <= contexts.Count; ++i) {
@@ -3268,6 +2710,8 @@
        public void CreatePlatform(string oldPlatform, string newPlatform,
                                   VersionInformation viOld, VersionInformation viNew, ref bool newProjectCreated)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            try {
                var cfgMgr = envPro.ConfigurationManager;
                cfgMgr.AddPlatform(newPlatform, oldPlatform, true);
@@ -3329,9 +2773,7 @@
            if (genVCFilter == null)
                return;
            var error = false;
            error = DeleteFilesFromFilter(genVCFilter);
            if (error)
            if (DeleteFilesFromFilter(genVCFilter))
                Messages.Print(SR.GetString("DeleteGeneratedFilesError"));
        }
@@ -3389,7 +2831,7 @@
                resFile.Remove();
        }
        static private void AddPlatformToVCProj(string projectFileName, string oldPlatformName, string newPlatformName)
        private static void AddPlatformToVCProj(string projectFileName, string oldPlatformName, string newPlatformName)
        {
            var tempFileName = Path.GetTempFileName();
            var fi = new FileInfo(projectFileName);
@@ -3404,7 +2846,7 @@
            fi.Delete();
        }
        static private void AddPlatformToVCProj(XmlDocument doc, string oldPlatformName, string newPlatformName)
        private static void AddPlatformToVCProj(XmlDocument doc, string oldPlatformName, string newPlatformName)
        {
            var vsProj = doc.DocumentElement.SelectSingleNode("/VisualStudioProject");
            var platforms = vsProj.SelectSingleNode("Platforms");
@@ -3438,7 +2880,7 @@
            }
        }
        static private void SetTargetMachine(VCLinkerTool linker, VersionInformation versionInfo)
        private static void SetTargetMachine(VCLinkerTool linker, VersionInformation versionInfo)
        {
            var qMakeLFlagsWindows = versionInfo.GetQMakeConfEntry("QMAKE_LFLAGS_WINDOWS");
            var rex = new Regex("/MACHINE:(\\S+)");
@@ -3476,6 +2918,8 @@
        public void CollapseFilter(string filterName)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var solutionExplorer = (UIHierarchy)dte.Windows.Item(Constants.vsext_wk_SProjectWindow).Object;
            if (solutionExplorer.UIHierarchyItems.Count == 0)
                return;
@@ -3489,6 +2933,8 @@
        private UIHierarchyItem FindProjectHierarchyItem(UIHierarchy hierarchy)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (hierarchy.UIHierarchyItems.Count == 0)
                return null;
@@ -3504,6 +2950,8 @@
        private UIHierarchyItem FindProjectHierarchyItem(UIHierarchyItem root)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            UIHierarchyItem projectItem = null;
            try {
                if (root.Name == envPro.Name)
@@ -3524,6 +2972,7 @@
        /// </summary>
        public string GetQtVersion()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return QtVersionManager.The().GetProjectQtVersion(envPro);
        }
@@ -3532,6 +2981,7 @@
        /// </summary>
        public void SetQtEnvironment()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            SetQtEnvironment(QtVersionManager.The().GetProjectQtVersion(envPro));
        }
@@ -3540,6 +2990,7 @@
        /// </summary>
        public void SetQtEnvironment(string qtVersion)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            SetQtEnvironment(qtVersion, string.Empty);
        }
@@ -3548,6 +2999,8 @@
        /// </summary>
        public void SetQtEnvironment(string qtVersion, string solutionConfig, bool build = false)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (string.IsNullOrEmpty(qtVersion))
                return;
@@ -3558,6 +3011,7 @@
            if (qtVersion != "$(QTDIR)")
                qtDir = QtVersionManager.The().GetInstallPath(qtVersion);
            HelperFunctions.SetEnvironmentVariableEx("QTDIR", qtDir);
            try {
                var propertyAccess = (IVCBuildPropertyStorage)vcPro;
                var vcprj = envPro.Object as VCProject;
@@ -3590,8 +3044,7 @@
                            debuggerEnv = propertyAccess.GetPropertyValue(
                                "LocalDebuggerEnvironment", cur_solution, "UserFile");
                            if (!string.IsNullOrEmpty(debuggerEnv)) {
                                var debugSettings = conf.DebugSettings as VCDebugSettings;
                                if (debugSettings != null) {
                                if (conf.DebugSettings is VCDebugSettings debugSettings) {
                                    //Get original value without expanded properties
                                    debuggerEnv = debugSettings.Environment;
                                }
@@ -3624,9 +3077,8 @@
                var projProps = vcProj as IVCBuildPropertyStorage;
                try {
                    return projProps.GetPropertyValue(pszPropName, Config.Name, "UserFile");
                } catch (Exception e) {
                    System.Diagnostics.Debug.WriteLine(
                        e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                } catch (Exception exception) {
                    exception.Log();
                    return string.Empty;
                }
            }
@@ -3637,9 +3089,8 @@
                var projProps = vcProj as IVCBuildPropertyStorage;
                try {
                    projProps.SetPropertyValue(pszPropName, Config.Name, "UserFile", pszPropValue);
                } catch (Exception e) {
                    System.Diagnostics.Debug.WriteLine(
                        e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                } catch (Exception exception) {
                    exception.Log();
                }
            }
@@ -3649,9 +3100,8 @@
                var projProps = vcProj as IVCBuildPropertyStorage;
                try {
                    projProps.RemoveProperty(pszPropName, Config.Name, "UserFile");
                } catch (Exception e) {
                    System.Diagnostics.Debug.WriteLine(
                        e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                } catch (Exception exception) {
                    exception.Log();
                }
            }
        }
@@ -3970,10 +3420,10 @@
        {
            if (propertyStorage == null)
                return null;
            if (propertyStorage is VCFileConfiguration)
                return GetParentProject(propertyStorage as VCFileConfiguration);
            else if (propertyStorage is VCConfiguration)
                return GetParentProject(propertyStorage as VCConfiguration);
            if (propertyStorage is VCFileConfiguration configuration)
                return GetParentProject(configuration);
            else if (propertyStorage is VCConfiguration storage)
                return GetParentProject(storage);
            return null;
        }
@@ -4026,8 +3476,7 @@
    public class VCMacroExpander : IVSMacroExpander
    {
        object config;
        readonly object config;
        public VCMacroExpander(object config)
        {
            this.config = config;
@@ -4042,14 +3491,15 @@
    public class QtCustomBuildTool
    {
        QtMsBuildContainer qtMsBuild;
        VCFileConfiguration vcConfig;
        VCFile vcFile;
        VCCustomBuildTool tool;
        VCMacroExpander macros;
        readonly QtMsBuildContainer qtMsBuild;
        readonly VCFileConfiguration vcConfig;
        readonly VCFile vcFile;
        readonly VCCustomBuildTool tool;
        readonly VCMacroExpander macros;
        enum FileItemType { Other = 0, CustomBuild, QtMoc, QtRcc, QtRepc, QtUic };
        FileItemType itemType = FileItemType.Other;
        readonly FileItemType itemType = FileItemType.Other;
        public QtCustomBuildTool(VCFileConfiguration vcConfig, QtMsBuildContainer container = null)
        {
            if (container != null)
QtVsTools.Core/QtVSIPSettings.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,11 +26,9 @@
**
****************************************************************************/
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
using Microsoft.Win32;
using QtVsTools.Core.QtMsBuild;
using System;
using System.Collections;
using QtVsTools.Common;
namespace QtVsTools.Core
{
@@ -46,41 +44,26 @@
    {
        public static IQtVsToolsOptions Options { get; set; }
        static Hashtable mocDirCache = new Hashtable();
        static Hashtable uicDirCache = new Hashtable();
        static Hashtable rccDirCache = new Hashtable();
        public static bool GetDisableAutoMocStepsUpdate()
        {
            return GetBoolValue(Resources.disableAutoMocStepsUpdateKeyword, false);
        }
        public static void SaveDisableAutoMocStepsUpdate(bool b)
        {
            SetBoolValue(Resources.disableAutoMocStepsUpdateKeyword, b);
            return QtVSIPSettingsShared.GetBoolValue(Resources.disableAutoMocStepsUpdateKeyword, false);
        }
        public static string GetUicDirectory(EnvDTE.Project project)
        {
            return GetDirectory(project, Resources.uicDirKeyword);
        }
        public static void SaveUicDirectory(EnvDTE.Project project, string directory)
        {
            if (directory == null)
                SaveDirectory(project, Resources.uicDirKeyword, GetDirectory(project, Resources.uicDirKeyword));
            else
                SaveDirectory(project, Resources.uicDirKeyword, directory);
            ThreadHelper.ThrowIfNotOnUIThread();
            return QtVSIPSettingsShared.GetDirectory(project, Resources.uicDirKeyword);
        }
        public static string GetMocDirectory()
        {
            return GetDirectory(Resources.mocDirKeyword);
            return QtVSIPSettingsShared.GetDirectory(Resources.mocDirKeyword);
        }
        public static string GetMocDirectory(EnvDTE.Project project)
        {
            return GetDirectory(project, Resources.mocDirKeyword);
            ThreadHelper.ThrowIfNotOnUIThread();
            return QtVSIPSettingsShared.GetDirectory(project, Resources.mocDirKeyword);
        }
        public static string GetMocDirectory(
@@ -88,6 +71,8 @@
            string configName,
            string platformName, VCFile vCFile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            string filePath = null;
            if (vCFile != null)
                filePath = vCFile.FullPath;
@@ -100,7 +85,9 @@
            string platformName,
            string filePath = null)
        {
            var dir = GetDirectory(project, Resources.mocDirKeyword);
            ThreadHelper.ThrowIfNotOnUIThread();
            var dir = QtVSIPSettingsShared.GetDirectory(project, Resources.mocDirKeyword);
            if (!string.IsNullOrEmpty(configName)
                && !string.IsNullOrEmpty(platformName))
                HelperFunctions.ExpandString(ref dir, project, configName, platformName, filePath);
@@ -109,458 +96,56 @@
        public static bool HasDifferentMocFilePerConfig(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var mocDir = GetMocDirectory(project);
            return mocDir.Contains("$(ConfigurationName)");
        }
        public static bool HasDifferentMocFilePerPlatform(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var mocDir = GetMocDirectory(project);
            return mocDir.Contains("$(PlatformName)");
        }
        public static string GetMocOptions()
        {
            return GetOption(Resources.mocOptionsKeyword);
        }
        public static string GetMocOptions(EnvDTE.Project project)
        {
            return GetOption(project, Resources.mocOptionsKeyword);
            ThreadHelper.ThrowIfNotOnUIThread();
            return QtVSIPSettingsShared.GetOption(project, Resources.mocOptionsKeyword);
        }
        public static bool GetLUpdateOnBuild(EnvDTE.Project project)
        {
            if (GetProjectQtSetting(project, "QtRunLUpdateOnBuild") == "true")
            ThreadHelper.ThrowIfNotOnUIThread();
            if (QtVSIPSettingsShared.GetProjectQtSetting(project, "QtRunLUpdateOnBuild") == "true")
                return true;
            return GetBoolValue(project, Resources.lupdateKeyword);
        }
        public static string GetLUpdateOptions()
        {
            return GetOption(Resources.lupdateOptionsKeyword);
        }
        static string GetProjectQtSetting(EnvDTE.Project project, string propertyName)
        {
            var vcProject = project.Object as VCProject;
            if (vcProject == null)
                return null;
            var vcConfigs = vcProject.Configurations as IVCCollection;
            if (vcConfigs == null)
                return null;
            var activeConfig = project.ConfigurationManager.ActiveConfiguration;
            if (activeConfig == null)
                return null;
            var activeConfigId = string.Format("{0}|{1}",
                activeConfig.ConfigurationName, activeConfig.PlatformName);
            var props = vcProject as IVCBuildPropertyStorage;
            if (props == null)
                return null;
            try {
                return props.GetPropertyValue(propertyName, activeConfigId, "ProjectFile");
            } catch {
                return null;
            }
        }
        public static string GetLUpdateOptions(EnvDTE.Project project)
        {
            string qtLUpdateOptions = GetProjectQtSetting(project, "QtLUpdateOptions");
            if (!string.IsNullOrEmpty(qtLUpdateOptions))
                return qtLUpdateOptions;
            return GetOption(project, Resources.lupdateOptionsKeyword);
        }
        public static string GetLReleaseOptions()
        {
            return GetOption(Resources.lreleaseOptionsKeyword);
        }
        public static string GetLReleaseOptions(EnvDTE.Project project)
        {
            string qtLReleaseOptions = GetProjectQtSetting(project, "QtLReleaseOptions");
            if (!string.IsNullOrEmpty(qtLReleaseOptions))
                return qtLReleaseOptions;
            return GetOption(project, Resources.lreleaseOptionsKeyword);
        }
        public static bool GetAskBeforeCheckoutFile()
        {
            return GetBoolValue(Resources.askBeforeCheckoutFileKeyword, true);
        }
        public static void SaveAskBeforeCheckoutFile(bool value)
        {
            SetBoolValue(Resources.askBeforeCheckoutFileKeyword, value);
        }
        public static bool GetDisableCheckoutFiles()
        {
            return GetBoolValue(Resources.disableCheckoutFilesKeyword, false);
        }
        public static void SaveDisableCheckoutFiles(bool value)
        {
            SetBoolValue(Resources.disableCheckoutFilesKeyword, value);
        }
        public static void SaveMocDirectory(EnvDTE.Project project, string directory)
        {
            if (directory == null)
                SaveDirectory(project, Resources.mocDirKeyword, GetDirectory(project, Resources.mocDirKeyword));
            else
                SaveDirectory(project, Resources.mocDirKeyword, directory);
        }
        public static void SaveMocOptions(EnvDTE.Project project, string options)
        {
            if (options == null)
                options = GetMocOptions();
            SaveOption(project, Resources.mocOptionsKeyword, options);
        }
        public static void SaveMocOptions(string options)
        {
            SaveOption(Resources.mocOptionsKeyword, options);
        }
        public static void SaveLUpdateOnBuild(EnvDTE.Project project)
        {
            SetBoolValue(project, Resources.lupdateKeyword, GetLUpdateOnBuild());
        }
        public static void SaveLUpdateOnBuild(EnvDTE.Project project, bool value)
        {
            SetBoolValue(project, Resources.lupdateKeyword, value);
        }
        public static void SaveLUpdateOptions(EnvDTE.Project project, string options)
        {
            if (options == null)
                options = GetLUpdateOptions();
            SaveOption(project, Resources.lupdateOptionsKeyword, options);
        }
        public static void SaveLUpdateOptions(string options)
        {
            SaveOption(Resources.lupdateOptionsKeyword, options);
        }
        public static void SaveLReleaseOptions(EnvDTE.Project project, string options)
        {
            if (options == null)
                options = GetLReleaseOptions();
            SaveOption(project, Resources.lreleaseOptionsKeyword, options);
        }
        public static void SaveLReleaseOptions(string options)
        {
            SaveOption(Resources.lreleaseOptionsKeyword, options);
            return QtVSIPSettingsShared.GetBoolValue(project, Resources.lupdateKeyword);
        }
        public static string GetRccDirectory(EnvDTE.Project project)
        {
            return GetDirectory(project, Resources.rccDirKeyword);
        }
        public static void SaveRccDirectory(string dir)
        {
            SaveDirectory(Resources.rccDirKeyword, dir);
        }
        public static void SaveRccDirectory(EnvDTE.Project project, string directory)
        {
            if (directory == null)
                SaveDirectory(project, Resources.rccDirKeyword, GetDirectory(project, Resources.rccDirKeyword));
            else
                SaveDirectory(project, Resources.rccDirKeyword, directory);
        }
        private static string GetDirectory(string type)
        {
            try {
                var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + Resources.registryPackagePath);
                if (key != null) {
                    var path = (string)key.GetValue(type, null);
                    if (path != null)
                        return HelperFunctions.NormalizeRelativeFilePath(path);
                }
            } catch { }
            if (type == Resources.mocDirKeyword)
                return Resources.generatedFilesDir + "\\$(ConfigurationName)";
            return Resources.generatedFilesDir;
        }
        private static string GetOption(string type)
        {
            try {
                var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + Resources.registryPackagePath);
                if (key != null) {
                    var opt = (string)key.GetValue(type, null);
                    if (opt != null)
                        return opt;
                }
            } catch { }
            return null;
        }
        public static bool GetLUpdateOnBuild()
        {
            return GetBoolValue(Resources.lupdateKeyword, false);
            ThreadHelper.ThrowIfNotOnUIThread();
            return QtVSIPSettingsShared.GetDirectory(project, Resources.rccDirKeyword);
        }
        public static string GetRccDirectory()
        {
            return GetDirectory(Resources.rccDirKeyword);
            return QtVSIPSettingsShared.GetDirectory(Resources.rccDirKeyword);
        }
        public static string GetUicDirectory()
        {
            return GetDirectory(Resources.uicDirKeyword);
        }
        private static string GetDirectory(EnvDTE.Project project, string type)
        {
            // check for directory in following order:
            // - stored in project
            // - stored in cache
            // - retrieve from moc/uic steps
            // - globally defined default directory
            // - fallback on hardcoded directory
            if (project != null) {
                if (project.Globals.get_VariablePersists(type))
                    return HelperFunctions.NormalizeRelativeFilePath((string)project.Globals[type]);
                try {
                    if (type == Resources.mocDirKeyword && mocDirCache.Contains(project.FullName))
                        return (string)mocDirCache[project.FullName];
                    if (type == Resources.uicDirKeyword && uicDirCache.Contains(project.FullName))
                        return (string)uicDirCache[project.FullName];
                    if (type == Resources.rccDirKeyword && rccDirCache.Contains(project.FullName))
                        return (string)rccDirCache[project.FullName];
                    QtCustomBuildTool tool = null;
                    string configName = null;
                    string platformName = null;
                    var vcpro = (VCProject)project.Object;
                    foreach (VCFile vcfile in (IVCCollection)vcpro.Files) {
                        var name = vcfile.Name;
                        if ((type == Resources.mocDirKeyword && HelperFunctions.IsHeaderFile(name))
                            || (type == Resources.mocDirKeyword && HelperFunctions.IsMocFile(name))
                            || (type == Resources.uicDirKeyword && HelperFunctions.IsUicFile(name))
                            || (type == Resources.rccDirKeyword && HelperFunctions.IsQrcFile(name))) {
                            foreach (VCFileConfiguration config in (IVCCollection)vcfile.FileConfigurations) {
                                tool = new QtCustomBuildTool(config);
                                configName = config.Name.Remove(config.Name.IndexOf('|'));
                                var vcConfig = config.ProjectConfiguration as VCConfiguration;
                                var platform = vcConfig.Platform as VCPlatform;
                                platformName = platform.Name;
                                if (tool != null && (tool.CommandLine.IndexOf("moc.exe", StringComparison.OrdinalIgnoreCase) != -1
                                    || (tool.CommandLine.IndexOf("uic.exe", StringComparison.OrdinalIgnoreCase) != -1)
                                    || (tool.CommandLine.IndexOf("rcc.exe", StringComparison.OrdinalIgnoreCase) != -1)))
                                    break;
                                tool = null;
                            }
                            if (tool != null)
                                break;
                        }
                    }
                    if (tool != null) {
                        string dir = null;
                        var lastindex = tool.Outputs.LastIndexOf('\\');
                        if (tool.Outputs.LastIndexOf('/') > lastindex)
                            lastindex = tool.Outputs.LastIndexOf('/');
                        if (lastindex == -1)
                            dir = ".";
                        else
                            dir = tool.Outputs.Substring(0, lastindex);
                        dir = dir.Replace("\"", "");
                        if (type == Resources.mocDirKeyword) {
                            int index;
                            if ((index = dir.IndexOf(configName, StringComparison.OrdinalIgnoreCase)) != -1)
                                dir = dir.Replace(dir.Substring(index, configName.Length), "$(ConfigurationName)");
                            if ((index = dir.IndexOf(platformName, StringComparison.OrdinalIgnoreCase)) != -1)
                                dir = dir.Replace(dir.Substring(index, platformName.Length), "$(PlatformName)");
                            mocDirCache.Add(project.FullName, HelperFunctions.NormalizeRelativeFilePath(dir));
                        } else if (type == Resources.uicDirKeyword)
                            uicDirCache.Add(project.FullName, HelperFunctions.NormalizeRelativeFilePath(dir));
                        else if (type == Resources.rccDirKeyword)
                            rccDirCache.Add(project.FullName, HelperFunctions.NormalizeRelativeFilePath(dir));
                        cleanUpCache(project);
                        return HelperFunctions.NormalizeRelativeFilePath(dir);
                    }
                } catch { }
            }
            return GetDirectory(type);
        }
        private static string GetOption(EnvDTE.Project project, string type)
        {
            // check for directory in following order:
            // - stored in project
            // - globally defined default option
            // - empty options
            if (project != null && project.Globals.get_VariablePersists(type))
                return (string)project.Globals[type];
            return GetOption(type);
        }
        private static bool GetBoolValue(EnvDTE.Project project, string type)
        {
            // check for directory in following order:
            // - stored in project
            // - globally defined default option
            // - empty options
            if (project != null && project.Globals.get_VariablePersists(type))
                return Convert.ToInt32(project.Globals[type] as string) > 0;
            return GetBoolValue(type, false);
        }
        private static void SaveDirectory(EnvDTE.Project project, string type, string dir)
        {
            dir = HelperFunctions.NormalizeRelativeFilePath(dir);
            project.Globals[type] = dir;
            if (!project.Globals.get_VariablePersists(type))
                project.Globals.set_VariablePersists(type, true);
            cleanUpCache(project);
        }
        private static void SaveOption(EnvDTE.Project project, string type, string option)
        {
            project.Globals[type] = option;
            if (!project.Globals.get_VariablePersists(type))
                project.Globals.set_VariablePersists(type, true);
        }
        private static void SetBoolValue(EnvDTE.Project project, string type, bool value)
        {
            project.Globals[type] = Convert.ToInt32(value).ToString();
            if (!project.Globals.get_VariablePersists(type))
                project.Globals.set_VariablePersists(type, true);
        }
        public static void SaveUicDirectory(string dir)
        {
            SaveDirectory(Resources.uicDirKeyword, dir);
        }
        public static void SaveMocDirectory(string dir)
        {
            SaveDirectory(Resources.mocDirKeyword, dir);
        }
        public static void SaveLUpdateOnBuild(bool val)
        {
            SetBoolValue(Resources.lupdateKeyword, val);
        }
        public static void cleanUpCache(EnvDTE.Project project)
        {
            try {
                var mocEnumerator = mocDirCache.GetEnumerator();
                while (mocEnumerator.MoveNext()) {
                    if (!HelperFunctions.IsProjectInSolution(project.DTE, (string)mocEnumerator.Key)) {
                        mocDirCache.Remove(mocEnumerator.Key);
                        mocEnumerator = mocDirCache.GetEnumerator();
                    }
                }
                var uicEnumerator = uicDirCache.GetEnumerator();
                while (uicEnumerator.MoveNext()) {
                    if (!HelperFunctions.IsProjectInSolution(project.DTE, (string)uicEnumerator.Key)) {
                        uicDirCache.Remove(uicEnumerator.Key);
                        uicEnumerator = uicDirCache.GetEnumerator();
                    }
                }
                var rccEnumerator = rccDirCache.GetEnumerator();
                while (rccEnumerator.MoveNext()) {
                    if (!HelperFunctions.IsProjectInSolution(project.DTE, (string)rccEnumerator.Key)) {
                        rccDirCache.Remove(rccEnumerator.Key);
                        rccEnumerator = rccDirCache.GetEnumerator();
                    }
                }
            } catch { }
        }
        private static void SaveDirectory(string type, string dir)
        {
            dir = HelperFunctions.NormalizeRelativeFilePath(dir);
            var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\" + Resources.registryPackagePath);
            if (key == null)
                return;
            key.SetValue(type, dir);
        }
        private static void SaveOption(string type, string option)
        {
            var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\" + Resources.registryPackagePath);
            if (key == null)
                return;
            if (option == null)
                option = "";
            key.SetValue(type, option);
            return QtVSIPSettingsShared.GetDirectory(Resources.uicDirKeyword);
        }
        public static bool AutoUpdateUicSteps()
        {
            if (ValueExists("AutoUpdateUicSteps"))
                return GetBoolValue("AutoUpdateUicSteps", true);
            return GetBoolValue("AutoUpdateBuildSteps", true);
        }
        private static bool GetBoolValue(string key, bool defaultValue)
        {
            var regKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + Resources.registryPackagePath);
            if (regKey == null)
                return defaultValue;
            return ((int)regKey.GetValue(key, defaultValue ? 1 : 0)) > 0;
        }
        private static bool ValueExists(string key)
        {
            var regKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + Resources.registryPackagePath);
            if (regKey != null) {
                foreach (var s in regKey.GetValueNames()) {
                    if (s == key)
                        return true;
                }
            }
            return false;
        }
        private static void SetBoolValue(string key, bool val)
        {
            var regKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\" + Resources.registryPackagePath);
            if (regKey == null)
                return;
            regKey.SetValue(key, val ? 1 : 0);
        }
        public static bool GetQmlDebug(EnvDTE.Project project)
        {
            return QtProject.Create(project).QmlDebug;
        }
        public static void SaveQmlDebug(EnvDTE.Project project, bool enabled)
        {
            QtProject.Create(project).QmlDebug = enabled;
            if (QtVSIPSettingsShared.ValueExists("AutoUpdateUicSteps"))
                return QtVSIPSettingsShared.GetBoolValue("AutoUpdateUicSteps", true);
            return QtVSIPSettingsShared.GetBoolValue("AutoUpdateBuildSteps", true);
        }
    }
}
QtVsTools.Core/QtVersionManager.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,16 +26,14 @@
**
****************************************************************************/
using Microsoft.VisualStudio.VCProjectEngine;
using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using QtVsTools.VisualStudio;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
using Microsoft.Win32;
namespace QtVsTools.Core
{
@@ -45,37 +43,20 @@
    public class QtVersionManager
    {
        private static QtVersionManager instance;
        private string regVersionPath;
        private string strVersionKey;
        private readonly string regVersionPath;
        private readonly string strVersionKey;
        private Hashtable versionCache;
        protected QtVersionManager()
        {
            strVersionKey = "Versions";
            regVersionPath = Resources.registryVersionPath;
            RefreshVersionNames();
        }
        void RefreshVersionNames()
        {
            var rootKeyPath = "SOFTWARE\\" + Resources.registryRootPath;
            try {
                using (var rootKey = Registry.CurrentUser.OpenSubKey(rootKeyPath, true))
                using (var versionsKey = rootKey.OpenSubKey(strVersionKey, true)) {
                    versionsKey.SetValue("VersionNames", string.Join(";", GetVersions()));
                }
        private static readonly EventWaitHandle packageInit = new EventWaitHandle(false, EventResetMode.ManualReset);
        private static EventWaitHandle packageInitDone = null;
            } catch (Exception e) {
                Messages.Print(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
            }
        }
        static EventWaitHandle
            packageInit = new EventWaitHandle(false, EventResetMode.ManualReset),
            packageInitDone = null;
        static public QtVersionManager The(EventWaitHandle initDone = null)
        public static QtVersionManager The(EventWaitHandle initDone = null)
        {
            if (initDone == null) {
                packageInit.WaitOne();
@@ -92,10 +73,10 @@
        public VersionInformation GetVersionInfo(string name)
        {
            if (name == "$(DefaultQtVersion)")
                name = GetDefaultVersion();
            if (name == null)
                return null;
            if (name == "$(DefaultQtVersion)")
                name = GetDefaultVersion();
            if (versionCache == null)
                versionCache = new Hashtable();
@@ -111,13 +92,8 @@
        public VersionInformation GetVersionInfo(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return GetVersionInfo(GetProjectQtVersion(project));
        }
        public void ClearVersionCache()
        {
            if (versionCache != null)
                versionCache.Clear();
        }
        public string[] GetVersions()
@@ -130,13 +106,12 @@
            if (qtDir == null)
                return null;
            qtDir = qtDir.ToLower();
            var versions = GetVersions();
            foreach (var version in versions) {
                var installPath = GetInstallPath(version);
                if (installPath == null)
                    continue;
                if (installPath.ToLower() == qtDir)
                if (installPath.Equals(qtDir, StringComparison.OrdinalIgnoreCase))
                    return version;
            }
@@ -157,84 +132,61 @@
        /// <summary>
        /// Check if all Qt versions are valid and readable.
        /// </summary>
        /// Also sets the default Qt version to the newest version, if needed.
        /// <param name="errorMessage"></param>
        /// <returns>true, if we found an invalid version</returns>
        public bool HasInvalidVersions(out string errorMessage)
        /// <returns>true, if there are one or more invalid Qt version</returns>
        public bool HasInvalidVersions(out string errorMessage, out bool defaultVersionInvalid)
        {
            var validVersions = new Dictionary<string, QtConfig>();
            var invalidVersions = new List<string>();
            var defaultVersion = GetDefaultVersionString();
            defaultVersionInvalid = string.IsNullOrEmpty(defaultVersion);
            foreach (var v in GetVersions()) {
                if (v == "$(DefaultQtVersion)")
                    continue;
                var vPath = GetInstallPath(v);
                if (string.IsNullOrEmpty(vPath)) {
                    invalidVersions.Add(v);
                    continue;
                }
                if (vPath.StartsWith("SSH:") || vPath.StartsWith("WSL:"))
                    continue;
                var qmakePath = Path.Combine(vPath, "bin", "qmake.exe");
                if (!File.Exists(qmakePath))
                    qmakePath = Path.Combine(vPath, "qmake.exe");
                if (!File.Exists(qmakePath)) {
                    invalidVersions.Add(v);
                    continue;
                }
                validVersions[v] = new QtConfig(vPath);
            }
            if (invalidVersions.Count > 0) {
                errorMessage = "These Qt version are inaccessible:\n";
                foreach (var invalidVersion in invalidVersions)
                    errorMessage += invalidVersion + " in " + GetInstallPath(invalidVersion) + "\n";
                errorMessage += "Make sure that you have read access to all files in your Qt directories.";
                // Is the default Qt version invalid?
                var isDefaultQtVersionInvalid = false;
                var defaultQtVersionName = GetDefaultVersion();
                if (string.IsNullOrEmpty(defaultQtVersionName)) {
                    isDefaultQtVersionInvalid = true;
                } else {
                    foreach (var name in invalidVersions) {
                        if (name == defaultQtVersionName) {
                            isDefaultQtVersionInvalid = true;
                            break;
                        }
                    }
                }
                // find the newest valid Qt version that can be used as default version
                if (isDefaultQtVersionInvalid && validVersions.Count > 0) {
                    QtConfig defaultQtVersionConfig = null;
                    foreach (var vNameConfig in validVersions) {
                        var vName = vNameConfig.Key;
                        var v = vNameConfig.Value;
                        if (defaultQtVersionConfig == null) {
                            defaultQtVersionConfig = v;
                            defaultQtVersionName = vName;
                            continue;
                        }
                        if (defaultQtVersionConfig.VersionMajor < v.VersionMajor ||
                               (defaultQtVersionConfig.VersionMajor == v.VersionMajor && (defaultQtVersionConfig.VersionMinor < v.VersionMinor ||
                                   (defaultQtVersionConfig.VersionMinor == v.VersionMinor && defaultQtVersionConfig.VersionPatch < v.VersionPatch)))) {
                            defaultQtVersionConfig = v;
                            defaultQtVersionName = vName;
                        }
                    }
                    if (defaultQtVersionConfig != null)
                        SaveDefaultVersion(defaultQtVersionName);
                }
                return true;
            }
            errorMessage = null;
            return false;
            foreach (var version in GetVersions()) {
                if (version == "$(DefaultQtVersion)")
                    continue;
                var path = GetInstallPath(version);
                if (path != null && (path.StartsWith("SSH:") || path.StartsWith("WSL:")))
                    continue;
                if (string.IsNullOrEmpty(path) || !QMake.Exists(path)) {
                    errorMessage += version + " in " + path + "\n";
                    defaultVersionInvalid |= version == defaultVersion;
                }
                if (!string.IsNullOrEmpty(errorMessage)) {
                    errorMessage = "These Qt version are inaccessible:\n"
                        + errorMessage
                        + "Make sure that you have read access to all files in your Qt directories.";
                }
            }
            return errorMessage != null;
        }
        public void SetLatestQtVersionAsDefault()
        {
            var validVersions = new Dictionary<string, Version>();
            foreach (var version in GetVersions()) {
                if (version == "$(DefaultQtVersion)")
                    continue;
                var path = GetInstallPath(version);
                if (!string.IsNullOrEmpty(path) && QMake.Exists(path))
                    validVersions[version] = new Version(new QtConfig(path).VersionString);
            }
            if (validVersions.Count <= 0)
                return;
            var defaultName = "";
            Version defaultVersion = null;
            foreach (var tmp in validVersions) {
                var version = tmp.Value;
                if (defaultVersion == null || defaultVersion < version) {
                    defaultName = tmp.Key;
                    defaultVersion = version;
                }
            }
            SaveDefaultVersion(defaultName);
        }
        public string GetInstallPath(string version)
@@ -252,16 +204,14 @@
                return Environment.GetEnvironmentVariable("QTDIR");
            var key = root.OpenSubKey("SOFTWARE\\" + Resources.registryRootPath, false);
            if (key == null)
                return null;
            var versionKey = key.OpenSubKey(strVersionKey + "\\" + version, false);
            if (versionKey == null)
                return null;
            return (string)versionKey.GetValue("InstallDir");
            var versionKey = key?.OpenSubKey(strVersionKey + "\\" + version, false);
            return versionKey?.GetValue("InstallDir") as string;
        }
        public string GetInstallPath(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var version = GetProjectQtVersion(project);
            if (version == "$(DefaultQtVersion)")
                version = GetDefaultVersion();
@@ -309,7 +259,6 @@
                    versionKey.SetValue("InstallDir", dir);
                }
            }
            RefreshVersionNames();
            return true;
        }
@@ -320,10 +269,9 @@
                return;
            key.DeleteSubKey(versionName);
            key.Close();
            RefreshVersionNames();
        }
        private bool IsVersionAvailable(string version)
        internal bool IsVersionAvailable(string version)
        {
            var versionAvailable = false;
            var versions = GetVersions();
@@ -338,13 +286,17 @@
        public bool SaveProjectQtVersion(EnvDTE.Project project, string version)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return SaveProjectQtVersion(project, version, project.ConfigurationManager.ActiveConfiguration.PlatformName);
        }
        public bool SaveProjectQtVersion(EnvDTE.Project project, string version, string platform)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!IsVersionAvailable(version) && version != "$(DefaultQtVersion)")
                return false;
            if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) {
                var vcPro = project.Object as VCProject;
                if (vcPro == null)
@@ -365,6 +317,8 @@
        public string GetProjectQtVersion(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            EnvDTE.Configuration config = null;
            try {
                config = project.ConfigurationManager.ActiveConfiguration;
@@ -382,13 +336,15 @@
            }
            if (version == null)
                version = GetSolutionQtVersion(project.DTE.Solution);
                version = Legacy.QtVersionManager.GetSolutionQtVersion(project.DTE.Solution);
            return version;
        }
        public string GetProjectQtVersion(EnvDTE.Project project, EnvDTE.Configuration config)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings)
                return QtProject.GetPropertyValue(project, config, "QtInstall");
@@ -402,6 +358,8 @@
        public string GetProjectQtVersion(EnvDTE.Project project, string platform)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings)
                return GetProjectQtVersion(project);
@@ -426,29 +384,6 @@
                    version = Environment.GetEnvironmentVariable(env);
                }
            }
        }
        public bool SaveSolutionQtVersion(EnvDTE.Solution solution, string version)
        {
            if (!IsVersionAvailable(version) && version != "$(DefaultQtVersion)")
                return false;
            solution.Globals["Qt5Version"] = version;
            if (!solution.Globals.get_VariablePersists("Qt5Version"))
                solution.Globals.set_VariablePersists("Qt5Version", true);
            return true;
        }
        public string GetSolutionQtVersion(EnvDTE.Solution solution)
        {
            if (solution == null)
                return null;
            if (solution.Globals.get_VariableExists("Qt5Version")) {
                var version = (string)solution.Globals["Qt5Version"];
                return VerifyIfQtVersionExists(version) ? version : null;
            }
            return null;
        }
        public string GetDefaultVersion()
@@ -489,6 +424,25 @@
                }
            }
            return VerifyIfQtVersionExists(defaultVersion) ? defaultVersion : null;
        }
        public string GetDefaultVersionString()
        {
            string defaultVersion = null;
            try {
                var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + regVersionPath, false);
                if (key != null)
                    defaultVersion = key.GetValue("DefaultQtVersion") as string;
            } catch {
                Messages.Print("Cannot read the default Qt version from registry.");
            }
            if (defaultVersion == null) {
                var qtDir = Environment.GetEnvironmentVariable("QTDIR");
                if (string.IsNullOrEmpty(qtDir))
                    return defaultVersion;
            }
            return defaultVersion;
        }
        public bool SaveDefaultVersion(string version)
@@ -540,7 +494,7 @@
            }
        }
        private bool VerifyIfQtVersionExists(string version)
        internal bool VerifyIfQtVersionExists(string version)
        {
            if (version == "$(DefaultQtVersion)")
                version = GetDefaultVersion();
QtVsTools.Core/QtVsTools.Core.csproj
@@ -69,36 +69,45 @@
  // -->
  <ItemGroup>
    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
    <Reference Include="PresentationFramework" />
    <Reference Include="System" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="WindowsBase" />
  </ItemGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Version specific references
  // General package references
  // -->
  <Import Project="$(SolutionDir)\references.props" />
  <ItemGroup>
    <PackageReference Include="Microsoft.VisualStudio.SDK"
      Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" />
    <PackageReference Include="Microsoft.VSSDK.BuildTools"
      Version="$(Version_Microsoft_VSSDK_BuildTools)" />
    <PackageReference Include="Microsoft.Build"
      Version="$(Version_Microsoft_Build)" />
    <PackageReference Include="$(Name_Microsoft_VSSDK_BuildTools)" Version="$(Version_Microsoft_VSSDK_BuildTools)" />
    <PackageReference Include="$(Name_Microsoft_VisualStudio_SDK)" Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" />
    <PackageReference Include="$(Name_Microsoft_Build)" Version="$(Version_Microsoft_Build)" />
  </ItemGroup>
  <ItemGroup Condition="'$(VisualStudioVersion)'=='17.0'">
    <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)"
      Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" />
  </ItemGroup>
  <ItemGroup Condition="'$(VisualStudioVersion)'=='16.0'">
    <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)"
      Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" />
  </ItemGroup>
  <ItemGroup Condition="'$(VisualStudioVersion)'=='15.0'">
    <Reference Include="Microsoft.VisualStudio.VCProjectEngine" />
  </ItemGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Version specific package references
  // -->
  <Choose>
    <When Condition="'$(VisualStudioVersion)'=='17.0'">
      <ItemGroup>
      </ItemGroup>
    </When>
    <When Condition="'$(VisualStudioVersion)'=='16.0'">
      <ItemGroup>
        <PackageReference Include="$(Name_Microsoft_VisualStudio_Validation)" Version="$(Version_Microsoft_VisualStudio_Validation)" />
        <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" />
      </ItemGroup>
    </When>
    <When Condition="'$(VisualStudioVersion)'=='15.0'">
      <ItemGroup>
        <Reference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" />
      </ItemGroup>
    </When>
  </Choose>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Solution project references
@@ -117,6 +126,8 @@
    <Compile Include="BuildConfig.cs" />
    <Compile Include="CommandLineParser.cs" />
    <Compile Include="Common\EnumExt.cs" />
    <Compile Include="Common\LazyFactory.cs" />
    <Compile Include="Common\QtVSIPSettingsShared.cs" />
    <Compile Include="CompilerToolWrapper.cs" />
    <Compile Include="CxxStreamReader.cs" />
    <Compile Include="ExportProjectDialog.cs">
@@ -130,12 +141,16 @@
    <Compile Include="ImageButton.cs">
      <SubType>Component</SubType>
    </Compile>
    <Compile Include="Legacy\QtProject.cs" />
    <Compile Include="Legacy\QtVersionManager.cs" />
    <Compile Include="Legacy\QtVSIPSettings.cs" />
    <Compile Include="LinkerToolWrapper.cs" />
    <Compile Include="MainWinWrapper.cs" />
    <Compile Include="Messages.cs" />
    <Compile Include="MocCmdChecker.cs" />
    <Compile Include="MsBuildProject.cs" />
    <Compile Include="Observable.cs" />
    <Compile Include="OutputWindowPane.cs" />
    <Compile Include="ProFileContent.cs" />
    <Compile Include="ProFileOption.cs" />
    <Compile Include="ProjectExporter.cs" />
@@ -170,10 +185,12 @@
    <Compile Include="RccOptions.cs" />
    <Compile Include="Resources.cs" />
    <Compile Include="SR.cs" />
    <Compile Include="TemplateType.cs" />
    <Compile Include="VersionInformation.cs" />
    <Compile Include="VisualStudio\InfoBarMessage.cs" />
    <Compile Include="VisualStudio\IProjectTracker.cs" />
    <Compile Include="VisualStudio\VsSearch.cs" />
    <Compile Include="VisualStudio\VsServiceProvider.cs" />
    <Compile Include="VisualStudio\VsShell.cs" />
    <Compile Include="WaitDialog.cs" />
    <EmbeddedResource Include="ExportProjectDialog.resx">
      <DependentUpon>ExportProjectDialog.cs</DependentUpon>
@@ -188,4 +205,4 @@
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="$(SolutionDir)\transform.targets" />
</Project>
</Project>
QtVsTools.Core/RccOptions.cs
@@ -26,8 +26,9 @@
**
****************************************************************************/
using Microsoft.VisualStudio.VCProjectEngine;
using System;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
namespace QtVsTools.Core
{
@@ -57,6 +58,8 @@
        {
            get
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                if (project.Globals.get_VariablePersists("RccCompressFiles" + id)
                    && (string)project.Globals["RccCompressFiles" + id] == "true")
                    return true;
@@ -64,6 +67,8 @@
            }
            set
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                if (value)
                    project.Globals["RccCompressFiles" + id] = "true";
                else
@@ -77,12 +82,16 @@
        {
            get
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                if (project.Globals.get_VariablePersists("RccCompressLevel" + id))
                    return Convert.ToInt32((string)project.Globals["RccCompressLevel" + id], 10);
                return 0;
            }
            set
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                project.Globals["RccCompressLevel" + id] = value.ToString();
                if (!project.Globals.get_VariablePersists("RccCompressLevel" + id))
                    project.Globals.set_VariablePersists("RccCompressLevel" + id, true);
@@ -93,12 +102,16 @@
        {
            get
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                if (project.Globals.get_VariablePersists("RccCompressThreshold" + id))
                    return Convert.ToInt32((string)project.Globals["RccCompressThreshold" + id], 10);
                return 0;
            }
            set
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                project.Globals["RccCompressThreshold" + id] = value.ToString();
                if (!project.Globals.get_VariablePersists("RccCompressThreshold" + id))
                    project.Globals.set_VariablePersists("RccCompressThreshold" + id, true);
QtVsTools.Core/Resources.cs
@@ -100,18 +100,11 @@
        public const int qtMinFormatVersion_PropertyEval = 303;
        // Project properties labels
        public const string projLabelGlobals = "Globals";
        public const string projLabelQtSettings = "QtSettings";
        public const string uic4Command = "$(QTDIR)\\bin\\uic.exe";
        public const string moc4Command = "$(QTDIR)\\bin\\moc.exe";
        public const string rcc4Command = "$(QTDIR)\\bin\\rcc.exe";
        public const string lupdateCommand = "\\bin\\lupdate.exe";
        public const string lreleaseCommand = "\\bin\\lrelease.exe";
        // All defined paths have to be relative to the project directory!!!
        public const string resourceDir = "Resources";
        // If those directories do not equal to the project directory
        // they have to be added to the include directories for
QtVsTools.Core/Resources.resx
@@ -123,9 +123,6 @@
  <data name="OpenSolution" xml:space="preserve">
    <value>Open Solution</value>
  </data>
  <data name="ProjectExists" xml:space="preserve">
    <value>Project file already exists.</value>
  </data>
  <data name="ExportProject_EditProjectFileManually" xml:space="preserve">
    <value>qmake has generated a .vcproj file, but it needs to be converted.
To do this you must open the .vcproj file manually.
@@ -140,17 +137,13 @@
    <value>Export to .pri File</value>
  </data>
  <data name="ExportProject_NoProjectsToExport" xml:space="preserve">
    <value>Cannot find any Qt 4 projects to export.</value>
    <value>Cannot find any Qt projects to export.</value>
  </data>
  <data name="ExportProject_PriFileContainsSpaces" xml:space="preserve">
    <value>The generated .pri file contains paths with spaces. You will not be able to import from this file.
1. Manually edit the generated .pri file.
2. Move your project to a location without spaces in the path.
3. Place the .pri file in another directory.</value>
  </data>
  <data name="ExportProject_ProjectExistsRegenerateOrReuse" xml:space="preserve">
    <value>{0} already exists.
Select 'Yes' to regenerate the file, and 'No' to use the existing one.</value>
  </data>
  <data name="ExportProject_ProjectOrSolutionCorrupt" xml:space="preserve">
    <value>{0}
@@ -216,12 +209,6 @@
  <data name="QtProject_CannotRemoveMocStep" xml:space="preserve">
    <value>Cannot remove a moc step from file {0}</value>
  </data>
  <data name="QtProject_CannotAddFilter" xml:space="preserve">
    <value>Project cannot add filter {0}</value>
  </data>
  <data name="QtProject_CannotAddFile" xml:space="preserve">
    <value>Cannot add file {0} to filter.</value>
  </data>
  <data name="QtProject_CannotRemoveFile" xml:space="preserve">
    <value>Cannot remove file {0} from filter.</value>
  </data>
@@ -238,25 +225,9 @@
  <data name="QtProject_ProjectCannotAddResourceFilter" xml:space="preserve">
    <value>Cannot add a resource filter.</value>
  </data>
  <data name="QtProject_CannotCreateResourceDir" xml:space="preserve">
    <value>Cannot create a resource directory.</value>
  </data>
  <data name="QtProject_CannotAdjustWhitespaces" xml:space="preserve">
    <value>Cannot adjust whitespace or tabs in file (write).
({0})</value>
  </data>
  <data name="QtProject_CannotReplaceTokenRead" xml:space="preserve">
    <value>Cannot replace token ({0} -&gt; {1}) in file (read).
({3})</value>
  </data>
  <data name="QtProject_CannotReplaceTokenWrite" xml:space="preserve">
    <value>Cannot replace token ({0} -&gt; {1}) in file (write).
({3})</value>
  </data>
  <data name="QtProject_FileExistsInProjectFolder" xml:space="preserve">
    <value>The file {0} exists in your project folder. Qt VS Tools will overwrite the existing file.
Select 'Yes' to overwrite, select 'No' to keep the existing file and automatically add it to the project.</value>
  </data>
  <data name="QtVersionManager_CannotLoadQtVersion" xml:space="preserve">
    <value>Cannot load the default Qt version.</value>
@@ -276,9 +247,6 @@
  <data name="Resources_ResourceFiles" xml:space="preserve">
    <value>Resource Files</value>
  </data>
  <data name="Resources_TranslationFiles" xml:space="preserve">
    <value>Translation Files</value>
  </data>
  <data name="Resources_GeneratedFiles" xml:space="preserve">
    <value>Generated Files</value>
  </data>
@@ -287,12 +255,6 @@
  </data>
  <data name="QtProject_CannotAccessUserFile" xml:space="preserve">
    <value>Could not add QTDIR to {0}'s .user file.</value>
  </data>
  <data name="ImportProject_CannotConvertProject" xml:space="preserve">
    <value>Could not convert project file {0} to Qt/MSBuild.</value>
  </data>
  <data name="Resources_OtherFiles" xml:space="preserve">
    <value>Other Files</value>
  </data>
  <data name="WaitDialogRefreshMoc" xml:space="preserve">
    <value>Updating moc steps...</value>
QtVsTools.Core/SR.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,10 +26,7 @@
**
****************************************************************************/
using System;
using System.Globalization;
using System.Resources;
using System.Threading;
namespace QtVsTools.Core
{
@@ -37,7 +34,7 @@
    {
        static SR loader;
        readonly ResourceManager resources;
        static readonly Object obj = new Object();
        static readonly object obj = new object();
        internal SR()
        {
@@ -52,20 +49,7 @@
                        loader = new SR();
                }
            }
            return loader;
        }
        private static CultureInfo Culture
        {
            get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; }
            //get { return new CultureInfo("de-DE"); }
        }
        public static string LanguageName
        {
            get { return Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName; }
            //get { return Culture.TwoLetterISOLanguageName; }
        }
        public static string GetString(string name, params object[] args)
@@ -73,19 +57,16 @@
            var sys = GetLoader();
            if (sys == null)
                return null;
            var res = sys.resources.GetString(name, Culture);
            if (args != null && args.Length > 0)
            var res = sys.resources.GetString(name, null);
            if (args != null && args.Length > 0 && !string.IsNullOrEmpty(res))
                return string.Format(res, args);
            return res;
        }
        public static string GetString(string name)
        {
            var sys = GetLoader();
            if (sys == null)
                return null;
            return sys.resources.GetString(name, Culture);
            return GetString(name, null);
        }
    }
}
QtVsTools.Core/VersionInformation.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -34,6 +34,7 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Core
{
@@ -42,11 +43,10 @@
    {
        //fields
        public string name;
        public string qtDir;
        public readonly string qtDir;
        public uint qtMajor; // X in version x.y.z
        public uint qtMinor; // Y in version x.y.z
        public uint qtPatch; // Z in version x.y.z
        public bool qt5Version = true;
        private QtConfig qtConfig;
        private QMakeConf qmakeConf;
        private string vsPlatformName;
@@ -79,18 +79,18 @@
            _cache.Clear();
        }
        Dictionary<string, bool> _IsModuleAvailable;
        readonly Dictionary<string, bool> _IsModuleAvailable;
        public bool IsModuleAvailable(string module)
        {
            return _IsModuleAvailable?[module] ?? false;
        }
        public string VC_MinimumVisualStudioVersion { get; private set; }
        public string VC_ApplicationTypeRevision { get; private set; }
        public string VC_WindowsTargetPlatformMinVersion { get; private set; }
        public string VC_WindowsTargetPlatformVersion { get; private set; }
        public string VC_Link_TargetMachine { get; private set; }
        public string VC_PlatformToolset { get; private set; }
        public string VC_MinimumVisualStudioVersion { get; }
        public string VC_ApplicationTypeRevision { get; }
        public string VC_WindowsTargetPlatformMinVersion { get; }
        public string VC_WindowsTargetPlatformVersion { get; }
        public string VC_Link_TargetMachine { get; }
        private string VC_PlatformToolset { get; }
        private VersionInformation(string qtDirIn)
        {
@@ -128,7 +128,6 @@
                    qtMinor = (version >> 8) & 0xFF;
                    qtPatch = version & 0xFF;
                }
                qt5Version = (qtMajor == 5);
                try {
                    QtInstallDocs = qmakeQuery["QT_INSTALL_DOCS"];
@@ -143,7 +142,7 @@
                var tempProData = new StringBuilder();
                tempProData.AppendLine("SOURCES = main.cpp");
                var modules = QtModules.Instance.GetAvailableModules()
                var modules = QtModules.Instance.GetAvailableModules(qtMajor)
                    .Where((QtModule mi) => mi.Selectable);
                foreach (QtModule mi in modules) {
@@ -194,7 +193,7 @@
        public string QtInstallDocs
        {
            get; private set;
            get;
        }
        public string QMakeSpecDirectory
QtVsTools.Core/VisualStudio/InfoBarMessage.cs
New file
@@ -0,0 +1,166 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.VisualStudio.Imaging.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
namespace QtVsTools.VisualStudio
{
    using Common;
    public abstract class InfoBarMessage
    {
        protected abstract ImageMoniker Icon { get; }
        protected abstract TextSpan[] Text { get; }
        protected abstract Hyperlink[] Hyperlinks { get; }
        protected class TextSpan
        {
            public string Text { get; set; }
            public bool Bold { get; set; }
            public bool Italic { get; set; }
            public bool Underline { get; set; }
            public static implicit operator TextSpan(string text) => new TextSpan { Text = text };
        }
        protected class TextSpacer : TextSpan
        {
            public TextSpacer(int spaces)
            {
                Text = new string(' ', spaces);
            }
        }
        protected class Hyperlink
        {
            public string Text { get; set; }
            public bool CloseInfoBar { get; set; }
            public Action OnClicked { get; set; }
        }
        private MessageUI UI { get; set; }
        public InfoBarMessage()
        {
            UI = new MessageUI { Message = this };
        }
        public virtual void Show()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            UI.Show();
        }
        public virtual void Close()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            UI.Close();
        }
        public bool IsOpen => UI.IsOpen;
        protected virtual void OnClosed()
        { }
        private class MessageUI : IVsInfoBarUIEvents
        {
            static LazyFactory StaticLazy { get; } = new LazyFactory();
            static IVsInfoBarUIFactory Factory => StaticLazy.Get(() =>
                Factory, () => VsServiceProvider
                    .GetService<SVsInfoBarUIFactory, IVsInfoBarUIFactory>());
            private IVsInfoBarUIElement UIElement { get; set; }
            private uint eventNotificationCookie;
            public bool IsOpen => UIElement != null;
            public InfoBarMessage Message { get; set; }
            public void Show()
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                if (Factory == null)
                    return;
                if (UIElement != null) // Message already shown
                    return;
                var textSpans = Enumerable.Empty<InfoBarTextSpan>();
                if (Message.Text != null) {
                    textSpans = Message.Text
                        .Select(x => new InfoBarTextSpan(x.Text, x.Bold, x.Italic, x.Underline));
                }
                var hyperlinks = Enumerable.Empty<InfoBarHyperlink>();
                if (Message.Hyperlinks != null) {
                    hyperlinks = Message.Hyperlinks
                        .Select(x => new InfoBarHyperlink(x.Text, x));
                }
                var model = new InfoBarModel(textSpans, hyperlinks, Message.Icon, true);
                UIElement = Factory.CreateInfoBar(model);
                if (UIElement != null) {
                    UIElement.Advise(this, out eventNotificationCookie);
                    VsShell.InfoBarHost?.AddInfoBar(UIElement);
                }
            }
            public void Close()
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                UIElement?.Close();
            }
            void IVsInfoBarUIEvents.OnActionItemClicked(
                IVsInfoBarUIElement infoBarUIElement,
                IVsInfoBarActionItem actionItem)
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                Debug.Assert(infoBarUIElement == UIElement);
                var hyperlink = actionItem.ActionContext as Hyperlink;
                if (hyperlink == null)
                    return;
                if (hyperlink.CloseInfoBar)
                    Close();
                hyperlink.OnClicked();
            }
            void IVsInfoBarUIEvents.OnClosed(IVsInfoBarUIElement infoBarUIElement)
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                Debug.Assert(infoBarUIElement == UIElement);
                if (UIElement != null) {
                    UIElement.Unadvise(eventNotificationCookie);
                    UIElement = null;
                    eventNotificationCookie = 0;
                    Message.OnClosed();
                }
            }
        }
    }
}
QtVsTools.Core/VisualStudio/VsSearch.cs
New file
@@ -0,0 +1,146 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Data;
using Microsoft.Internal.VisualStudio.PlatformUI;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
namespace QtVsTools.VisualStudio
{
    public class SearchTask : VsSearchTask
    {
        private readonly Action _clearCallback = null;
        private readonly Func<IEnumerable<string>, uint> _searchCallBack = null;
        public SearchTask(uint cookie, IVsSearchQuery query, IVsSearchCallback callback,
                Action clearCallback, Func<IEnumerable<string>, uint> searchCallBack)
            : base(cookie, query, callback)
        {
            _clearCallback = clearCallback;
            _searchCallBack = searchCallBack;
        }
        protected override void OnStartSearch()
        {
            ErrorCode = VSConstants.S_OK;
            try {
                _ = ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
                {
                    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                    if (TaskStatus != VSConstants.VsSearchTaskStatus.Stopped) {
                        SearchResults = _searchCallBack(
                            SearchUtilities.ExtractSearchTokens(SearchQuery).Select(token =>
                            {
                                ThreadHelper.ThrowIfNotOnUIThread();
                                return token.ParsedTokenText;
                            })
                        );
                    } else if (TaskStatus == VSConstants.VsSearchTaskStatus.Stopped) {
                        _clearCallback();
                    }
                });
            } catch {
                ErrorCode = VSConstants.E_FAIL;
            }
            base.OnStartSearch();
        }
        protected override void OnStopSearch()
        {
            SearchResults = 0;
        }
    }
    public abstract class VsWindowSearch : IVsWindowSearch
    {
        public abstract IVsSearchTask CreateSearch(uint cookie, IVsSearchQuery query,
                                                   IVsSearchCallback callback);
        public virtual void ClearSearch()
        {}
        public virtual void ProvideSearchSettings(IVsUIDataSource settings)
        {
            Utilities.SetValue(settings, SearchSettingsDataSource.PropertyNames.ControlMaxWidth,
                (uint)10000);
            Utilities.SetValue(settings, SearchSettingsDataSource.PropertyNames.SearchStartType,
                (uint)VSSEARCHSTARTTYPE.SST_INSTANT);
        }
        public virtual bool OnNavigationKeyDown(uint dwNavigationKey, uint dwModifiers) => false;
        public virtual bool SearchEnabled => true;
        public virtual Guid Category => Guid.Empty;
        public virtual IVsEnumWindowSearchFilters SearchFiltersEnum => null;
        public virtual IVsEnumWindowSearchOptions SearchOptionsEnum => null;
    }
    public class ListBoxSearch : VsWindowSearch
    {
        private readonly ListBox _listBox = null;
        private Action<string> _setSearchText = null;
        public ListBoxSearch(ListBox listBox, Action<string> action)
        {
            _listBox = listBox;
            _setSearchText = action;
        }
        public override IVsSearchTask CreateSearch(uint cookie, IVsSearchQuery query,
                                                   IVsSearchCallback callback)
        {
            return new SearchTask(cookie, query, callback, ClearSearch,
                searchCallBack: (IEnumerable<string> tokens) =>
                {
                    _setSearchText(string.Join(" ", tokens));
                    var view = CollectionViewSource.GetDefaultView(_listBox.ItemsSource);
                    view.Refresh();
                    if (_listBox.Items.Count == 1 || _listBox.SelectedItem == null)
                        _listBox.SelectedIndex = 0;
                    return (uint)view.Cast<object>().Count();
                });
        }
        public override void ClearSearch()
        {
            _setSearchText(string.Empty);
            CollectionViewSource.GetDefaultView(_listBox.ItemsSource).Refresh();
        }
    }
}
QtVsTools.Core/VisualStudio/VsServiceProvider.cs
@@ -26,7 +26,6 @@
**
****************************************************************************/
using Microsoft.VisualStudio.Shell;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
@@ -45,7 +44,7 @@
    {
        public static IVsServiceProvider Instance { get; set; }
        static ConcurrentDictionary<ServiceType, object> services
        static readonly ConcurrentDictionary<ServiceType, object> services
            = new ConcurrentDictionary<ServiceType, object>();
        public static I GetService<I>()
@@ -61,8 +60,7 @@
            if (Instance == null)
                return null;
            object serviceObj;
            if (services.TryGetValue(new ServiceType(typeof(T), typeof(I)), out serviceObj))
            if (services.TryGetValue(new ServiceType(typeof(T), typeof(I)), out object serviceObj))
                return serviceObj as I;
            var serviceInterface = Instance.GetService<T, I>();
@@ -83,8 +81,7 @@
            if (Instance == null)
                return null;
            object serviceObj;
            if (services.TryGetValue(new ServiceType(typeof(T), typeof(I)), out serviceObj))
            if (services.TryGetValue(new ServiceType(typeof(T), typeof(I)), out object serviceObj))
                return serviceObj as I;
            var serviceInterface = await Instance.GetServiceAsync<T, I>();
QtVsTools.Core/VisualStudio/VsShell.cs
New file
@@ -0,0 +1,107 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
namespace QtVsTools.VisualStudio
{
    public static class VsShell
    {
        public static string InstallRootDir
        {
            get
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                Initialize();
                return _InstallRootDir;
            }
        }
        private static IVsShell vsShell;
        private static string _InstallRootDir;
        private static void Initialize()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (vsShell != null)
                return;
            vsShell = VsServiceProvider.GetService<IVsShell>();
            int res = vsShell.GetProperty((int)__VSSPROPID2.VSSPROPID_InstallRootDir, out object o);
            if (res == VSConstants.S_OK && o is string property)
                _InstallRootDir = property;
        }
        public static EnvDTE.Project GetProject(IVsHierarchy context)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            int res = context.GetProperty(
                (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_ExtObject, out object value);
            if (res == VSConstants.S_OK && value is EnvDTE.Project project)
                return project;
            return null;
        }
        public static EnvDTE.ProjectItem GetProjectItem(IVsHierarchy context, uint itemid)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            int res = context.GetProperty(
                itemid, (int)__VSHPROPID.VSHPROPID_ExtObject, out object value);
            if (res == VSConstants.S_OK && value is EnvDTE.ProjectItem item)
                return item;
            return null;
        }
        public static EnvDTE.Document GetDocument(IVsHierarchy context, uint itemid)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return GetProjectItem(context, itemid)?.Document;
        }
        private static IVsInfoBarHost _InfoBarHost = null;
        public static IVsInfoBarHost InfoBarHost
        {
            get
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                if (_InfoBarHost != null)
                    return _InfoBarHost;
                Initialize();
                object host = null;
                vsShell?.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out host);
                return _InfoBarHost = host as IVsInfoBarHost;
            }
        }
    }
}
QtVsTools.Core/WaitDialog.cs
@@ -27,25 +27,21 @@
****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.VCProjectEngine;
using QtVsTools.VisualStudio;
namespace QtVsTools.Core
{
    using VisualStudio;
    public class WaitDialog : IDisposable
    {
        static IVsThreadedWaitDialogFactory factory = null;
        public IVsThreadedWaitDialog2 VsWaitDialog { get; private set; }
        private IVsThreadedWaitDialog2 VsWaitDialog { get; set; }
        public bool Running { get; private set; }
        private bool Running { get; set; }
        bool? vsDialogCanceled = null;
@@ -53,14 +49,15 @@
        {
            get
            {
                ThreadHelper.ThrowIfNotOnUIThread();
                if (vsDialogCanceled.HasValue)
                    return vsDialogCanceled.Value;
                if (VsWaitDialog == null)
                    return false;
                bool canceled = false;
                int res = VsWaitDialog.HasCanceled(out canceled);
                int res = VsWaitDialog.HasCanceled(out bool canceled);
                if (res != VSConstants.S_OK)
                    return false;
@@ -76,6 +73,8 @@
        static WaitDialog Create(IVsThreadedWaitDialogFactory dialogFactory)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (factory == null) {
                factory = dialogFactory ?? VsServiceProvider
                    .GetService<SVsThreadedWaitDialogFactory, IVsThreadedWaitDialogFactory>();
@@ -83,8 +82,7 @@
                    return null;
            }
            IVsThreadedWaitDialog2 vsWaitDialog = null;
            factory.CreateInstance(out vsWaitDialog);
            factory.CreateInstance(out IVsThreadedWaitDialog2 vsWaitDialog);
            if (vsWaitDialog == null)
                return null;
@@ -105,6 +103,8 @@
            bool showMarqueeProgress = true,
            IVsThreadedWaitDialogFactory dialogFactory = null)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var dialog = Create(dialogFactory);
            if (dialog == null)
                return null;
@@ -129,6 +129,8 @@
            bool isCancelable = false,
            IVsThreadedWaitDialogFactory dialogFactory = null)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var dialog = Create(dialogFactory);
            if (dialog == null)
                return null;
@@ -151,12 +153,13 @@
            string statusBarText = null,
            bool disableCancel = false)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!Running)
                return;
            bool canceled = false;
            int res = VsWaitDialog.UpdateProgress(message, progressText,
                statusBarText, currentStep, totalSteps, disableCancel, out canceled);
                statusBarText, currentStep, totalSteps, disableCancel, out bool canceled);
            if (res != VSConstants.S_OK)
                return;
@@ -167,18 +170,22 @@
        public void Stop()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!Running)
                return;
            Running = false;
            int canceled = 0;
            VsWaitDialog.EndWaitDialog(out canceled);
            VsWaitDialog.EndWaitDialog(out int canceled);
            Canceled = (canceled != 0);
        }
        void IDisposable.Dispose()
        {
            Stop();
            ThreadHelper.JoinableTaskFactory.Run(async () => {
                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                Stop();
            });
        }
    }
}
QtVsTools.Package/Common/Concurrent.cs
@@ -40,12 +40,9 @@
    public abstract class Concurrent<TSubClass>
        where TSubClass : Concurrent<TSubClass>
    {
        private static readonly object _StaticCriticalSection = new object();
        protected static object StaticCriticalSection => _StaticCriticalSection;
        protected static object StaticCriticalSection { get; } = new object();
        private object _CriticalSection = null;
        protected object CriticalSection =>
            StaticThreadSafeInit(() => _CriticalSection, () => _CriticalSection = new object());
        protected object CriticalSection { get; } = new object();
        protected T ThreadSafeInit<T>(Func<T> getValue, Action init)
            where T : class
QtVsTools.Package/Common/ConcurrentStopwatch.cs
@@ -33,7 +33,7 @@
{
    public class ConcurrentStopwatch : Concurrent<ConcurrentStopwatch>
    {
        Stopwatch Stopwatch { get; set; }
        Stopwatch Stopwatch { get; }
        public ConcurrentStopwatch()
        {
QtVsTools.Package/Common/Json/DeferredObject.cs
@@ -26,8 +26,6 @@
**
****************************************************************************/
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace QtVsTools.Json
QtVsTools.Package/Common/Json/Serializable.cs
@@ -48,7 +48,7 @@
    {
        #region //////////////////// Prototype ////////////////////////////////////////////////////
        protected Serializer Serializer { get; set; }
        private Serializer Serializer { get; set; }
        protected Serializable()
        { }
@@ -192,9 +192,11 @@
                var container = toDo.Dequeue();
                foreach (var defObj in container.PendingObjects) {
                    defObj.Deserialize();
                    var subContainer = defObj.Object as IDeferredObjectContainer;
                    if (subContainer != null && subContainer.PendingObjects.Any())
                    if (defObj.Object is IDeferredObjectContainer subContainer
                        && subContainer.PendingObjects.Any()) {
                        toDo.Enqueue(subContainer);
                    }
                }
            }
            return obj;
QtVsTools.Package/Common/Json/SerializableEnum.cs
@@ -27,7 +27,6 @@
****************************************************************************/
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
@@ -106,7 +105,7 @@
    class EnumStringAttribute : Attribute
    {
        public string ValueString { get; private set; }
        public string ValueString { get; }
        public EnumStringAttribute(string enumValueString)
        {
QtVsTools.Package/Common/Json/Serializer.cs
@@ -36,6 +36,7 @@
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml;
using QtVsTools.Core;
/// <summary>
/// The classes in this namespace provide support to the serialization and deserialization of
@@ -127,11 +128,10 @@
                    serializer.WriteObject(writer, obj);
                    writer.Close();
                    return new JsonData() { Stream = stream };
                } catch (Exception e) {
                } catch (Exception exception) {
                    exception.Log();
                    if (stream != null && stream.CanRead && stream.Length > 0)
                        stream.Dispose();
                    System.Diagnostics.Debug.WriteLine(
                        e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                    return null;
                }
            }
@@ -151,16 +151,14 @@
                    using (reader = XmlReader.Create(data.XmlStream)) {
                        var obj = serializer.ReadObject(reader, false);
                        var container = obj as IDeferredObjectContainer;
                        if (container != null)
                        if (obj is IDeferredObjectContainer container)
                            deferredObjects.ForEach(x => container.Add(x));
                        return obj;
                    }
                } catch (Exception e) {
                    System.Diagnostics.Debug.WriteLine(
                        e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                } catch (Exception exception) {
                    exception.Log();
                    return null;
                } finally {
@@ -206,9 +204,8 @@
                    data.XmlStream = new MemoryStream(xmlData);
                }
                return true;
            } catch (Exception e) {
                System.Diagnostics.Debug.WriteLine(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
            } catch (Exception exception) {
                exception.Log();
                return false;
            }
        }
@@ -245,9 +242,9 @@
        #region //////////////////// Data Contract Surrogate //////////////////////////////////////
        static Exclusive<Serializer> sharedInstance = new Exclusive<Serializer>();
        static readonly Exclusive<Serializer> sharedInstance = new Exclusive<Serializer>();
        private XmlReader reader = null;
        private List<IDeferredObject> deferredObjects = new List<IDeferredObject>();
        private readonly List<IDeferredObject> deferredObjects = new List<IDeferredObject>();
        public static IJsonData GetCurrentJsonData()
        {
@@ -260,9 +257,8 @@
                root.Append("</root>");
                var xmlData = Encoding.UTF8.GetBytes(root.ToString());
                return new JsonData { XmlStream = new MemoryStream(xmlData) };
            } catch (Exception e) {
                System.Diagnostics.Debug.WriteLine(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
            } catch (Exception exception) {
                exception.Log();
                return null;
            }
        }
QtVsTools.Package/Common/NativeAPI.cs
@@ -127,12 +127,12 @@
                szTypeName = "";
            }
            public IntPtr hIcon;
            public int iIcon;
            public uint dwAttributes;
            private readonly int iIcon;
            private readonly uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
            public string szDisplayName;
            private readonly string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_TYPE)]
            public string szTypeName;
            private readonly string szTypeName;
        };
        [Flags]
QtVsTools.Package/Common/PriorityQueue.cs
@@ -51,8 +51,8 @@
    public abstract class BasePriorityQueue<T, TPriority> : Concurrent, IEnumerable<T>
        where TPriority : IComparable<TPriority>
    {
        SortedDictionary<TPriority, T> ItemsByPriority { get; set; }
        Dictionary<object, TPriority> ItemPriority { get; set; }
        SortedDictionary<TPriority, T> ItemsByPriority { get; }
        Dictionary<object, TPriority> ItemPriority { get; }
        T Head { get; set; }
        public int Count { get; private set; }
        public bool IsEmpty => (Count == 0);
@@ -61,7 +61,7 @@
        IEnumerator<T> IEnumerable<T>.GetEnumerator() => Items.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => Items.GetEnumerator();
        Func<T, object> GetItemKey { get; set; }
        Func<T, object> GetItemKey { get; }
        public BasePriorityQueue() : this(x => x)
        { }
@@ -102,11 +102,10 @@
            if (item == null)
                throw new InvalidOperationException("Item cannot be null.");
            lock (CriticalSection) {
                T oldItem;
                if (ItemsByPriority.TryGetValue(priority, out oldItem) && !item.Equals(oldItem))
                if (ItemsByPriority.TryGetValue(priority, out T oldItem) && !item.Equals(oldItem))
                    throw new InvalidOperationException("An item with the same priority exists.");
                TPriority oldPriority;
                if (ItemPriority.TryGetValue(GetItemKey(item), out oldPriority)) {
                if (ItemPriority.TryGetValue(GetItemKey(item), out TPriority oldPriority)) {
                    ItemsByPriority.Remove(oldPriority);
                    --Count;
                }
@@ -128,8 +127,7 @@
        public T Peek()
        {
            lock (CriticalSection) {
                T result;
                if (!TryPeek(out result))
                if (!TryPeek(out T result))
                    throw new InvalidOperationException("Queue is empty.");
                return result;
            }
@@ -167,8 +165,7 @@
        public T Dequeue()
        {
            lock (CriticalSection) {
                T result;
                if (!TryDequeue(out result))
                if (!TryDequeue(out T result))
                    throw new InvalidOperationException("Queue is empty.");
                return result;
            }
QtVsTools.Package/Common/Prototyped.cs
@@ -134,7 +134,7 @@
            static readonly object classCriticalSection = new object();
            static Dictionary<Type, List<Type>> types = GetTypeHierarchy(typeof(TBase));
            static readonly Dictionary<Type, List<Type>> types = GetTypeHierarchy(typeof(TBase));
            static Dictionary<Type, List<Type>> GetTypeHierarchy(Type baseType)
            {
@@ -174,7 +174,7 @@
                return subTypes;
            }
            static Dictionary<Type, SubClass> classes = types
            static readonly Dictionary<Type, SubClass> classes = types
                .ToDictionary(x => x.Key, x => Create(x.Key, x.Value));
            static SubClass Create(Type type, IEnumerable<Type> subTypes)
@@ -194,8 +194,7 @@
                lock (classCriticalSection) {
                    SubClass subClass = null;
                    if (!classes.TryGetValue(type, out subClass)) {
                    if (!classes.TryGetValue(type, out SubClass subClass)) {
                        var newTypes = GetTypeHierarchy(type)
                            .Where(x => !classes.ContainsKey(x.Key));
@@ -213,7 +212,7 @@
                }
            }
            public static SubClass baseClass = Get(typeof(TBase));
            public static readonly SubClass baseClass = Get(typeof(TBase));
        }
        #endregion //////////////////// SubClass //////////////////////////////////////////////////
QtVsTools.Package/Common/Timestamp.cs
@@ -30,8 +30,12 @@
namespace QtVsTools
{
    using Common;
    public class Timestamp : Concurrent<Timestamp>
    {
        static LazyFactory StaticLazy { get; } = new LazyFactory();
        long LastTimestamp { get; set; }
        long GetStrictMonotonicTimestamp()
        {
@@ -43,9 +47,8 @@
            }
        }
        static Timestamp _Instance;
        static Timestamp Instance =>
            StaticThreadSafeInit(() => _Instance, () => _Instance = new Timestamp());
        static Timestamp Instance => StaticLazy.Get(() =>
            Instance, () => new Timestamp());
        public static long Next()
        {
QtVsTools.Package/Editors/Editor.QtDesigner.cs
@@ -31,12 +31,15 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Shell.Interop;
using QtVsTools.QtMsBuild;
using QtVsTools.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Task = System.Threading.Tasks.Task;
namespace QtVsTools.Editors
{
    using QtMsBuild;
    using VisualStudio;
    [Guid(GuidString)]
    public class QtDesigner : Editor
    {
@@ -58,27 +61,31 @@
        protected override void OnStart(Process process)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            base.OnStart(process);
            var document = VsShell.GetDocument(Context, ItemId);
            if (document == null)
                return;
            var project = document.ProjectItem?.ContainingProject;
            if (project == null || !QtProjectTracker.IsTracked(project))
            if (project == null || !QtProjectTracker.IsTracked(project.FullName))
                return;
            string projectPath = project.FullName;
            string filePath = document.FullName;
            string[] itemId = new[] { document.ProjectItem?.Name };
            var lastWriteTime = File.GetLastWriteTime(filePath);
            Task.Run(() =>
            _ = Task.Run(async () =>
            {
                while (!process.WaitForExit(1000)) {
                    var latestWriteTime = File.GetLastWriteTime(filePath);
                    if (lastWriteTime != latestWriteTime) {
                        lastWriteTime = latestWriteTime;
                        QtProjectIntellisense.Refresh(project, selectedFiles: itemId);
                        await QtProjectIntellisense.RefreshAsync(project, projectPath);
                    }
                }
                if (lastWriteTime != File.GetLastWriteTime(filePath)) {
                    QtProjectIntellisense.Refresh(project, selectedFiles: itemId);
                    await QtProjectIntellisense.RefreshAsync(project, projectPath);
                }
            });
        }
QtVsTools.Package/Editors/Editor.QtResourceEditor.cs
@@ -29,7 +29,7 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Editors
{
@@ -44,18 +44,13 @@
        public override string ExecutableName => "QrcEditor.exe";
        protected override string GetToolsPath()
        {
            return QtVsToolsPackage.Instance?.PkgInstallPath;
        }
        protected override string GetToolsPath() =>
            QtVsToolsPackage.Instance?.PkgInstallPath;
        public override Func<string, bool> WindowFilter =>
            caption => caption.StartsWith(Title);
        protected override string GetTitle(Process editorProcess)
        {
            return Title;
        }
        protected override string GetTitle(Process editorProcess) => Title;
        protected override bool Detached => QtVsToolsPackage.Instance.Options.ResourceEditorDetached;
    }
QtVsTools.Package/Editors/Editor.cs
@@ -40,11 +40,12 @@
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.VCProjectEngine;
using QtVsTools.Core;
using QtVsTools.VisualStudio;
namespace QtVsTools.Editors
{
    using Core;
    using VisualStudio;
    using static Core.HelperFunctions;
    public abstract class Editor : IVsEditorFactory
@@ -61,6 +62,7 @@
        protected virtual string GetToolsPath()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return GetQtToolsPath() ?? GetDefaultQtToolsPath();
        }
@@ -69,33 +71,37 @@
        string GetQtToolsPath()
        {
            var project = VsShell.GetProject(Context);
            if (project == null)
                return null;
            return ThreadHelper.JoinableTaskFactory.Run(async () =>
            {
                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                var project = VsShell.GetProject(Context);
                if (project == null)
                    return null;
            var vcProject = project.Object as VCProject;
            if (vcProject == null)
                return null;
                var vcProject = project.Object as VCProject;
                if (vcProject == null)
                    return null;
            var vcConfigs = vcProject.Configurations as IVCCollection;
            if (vcConfigs == null)
                return null;
                var vcConfigs = vcProject.Configurations as IVCCollection;
                if (vcConfigs == null)
                    return null;
            var activeConfig = project.ConfigurationManager?.ActiveConfiguration;
            if (activeConfig == null)
                return null;
                var activeConfig = project.ConfigurationManager?.ActiveConfiguration;
                if (activeConfig == null)
                    return null;
            var activeConfigId = string.Format("{0}|{1}",
                activeConfig.ConfigurationName, activeConfig.PlatformName);
            var vcConfig = vcConfigs.Item(activeConfigId) as VCConfiguration;
            if (vcConfig == null)
                return null;
                var activeConfigId = string.Format("{0}|{1}",
                    activeConfig.ConfigurationName, activeConfig.PlatformName);
                var vcConfig = vcConfigs.Item(activeConfigId) as VCConfiguration;
                if (vcConfig == null)
                    return null;
            var qtToolsPath = vcConfig.GetEvaluatedPropertyValue("QtToolsPath");
            if (string.IsNullOrEmpty(qtToolsPath))
                return null;
                var qtToolsPath = vcConfig.GetEvaluatedPropertyValue("QtToolsPath");
                if (string.IsNullOrEmpty(qtToolsPath))
                    return null;
            return qtToolsPath;
                return qtToolsPath;
            });
        }
        string GetDefaultQtToolsPath()
@@ -126,6 +132,8 @@
            out Guid pguidCmdUI,
            out int pgrfCDW)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            // Initialize to null
            ppunkDocView = IntPtr.Zero;
            ppunkDocData = IntPtr.Zero;
@@ -202,11 +210,15 @@
        {
            if (string.IsNullOrEmpty(qtToolsPath))
                qtToolsPath = GetDefaultQtToolsPath();
            var st = GetStartInfo(filePath, qtToolsPath, hideWindow);
            try {
                return Process.Start(GetStartInfo(filePath, qtToolsPath, hideWindow));
            } catch (Exception e) {
                Messages.Print(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                return Process.Start(st);
            } catch (Exception exception) {
                exception.Log();
                if (!File.Exists(st.Arguments))
                    Messages.Print("The system cannot find the file: " + st.Arguments);
                if (!File.Exists(st.FileName))
                    Messages.Print("The system cannot find the file: " + st.FileName);
                return null;
            }
        }
@@ -223,20 +235,20 @@
        private class EditorPane : WindowPane, IVsPersistDocData
        {
            public Editor Editor { get; private set; }
            public string QtToolsPath { get; private set; }
            private Editor Editor { get; }
            private string QtToolsPath { get; }
            public TableLayoutPanel EditorContainer { get; private set; }
            public Label EditorTitle { get; private set; }
            public LinkLabel EditorDetachButton { get; private set; }
            public Panel EditorControl { get; private set; }
            private TableLayoutPanel EditorContainer { get; set; }
            private Label EditorTitle { get; }
            private LinkLabel EditorDetachButton { get; }
            private Panel EditorControl { get; }
            public override IWin32Window Window => EditorContainer;
            public Process EditorProcess { get; private set; }
            public IntPtr EditorWindow { get; private set; }
            public int EditorWindowStyle { get; private set; }
            public int EditorWindowStyleExt { get; private set; }
            public IntPtr EditorIcon { get; private set; }
            private Process EditorProcess { get; set; }
            private IntPtr EditorWindow { get; set; }
            private int EditorWindowStyle { get; set; }
            private int EditorWindowStyleExt { get; set; }
            private IntPtr EditorIcon { get; set; }
            public EditorPane(Editor editor, string qtToolsPath)
            {
@@ -306,11 +318,11 @@
            int IVsPersistDocData.LoadDocData(string pszMkDocument)
            {
                var solution = GetService(typeof(SVsSolution)) as IVsSolution;
                EditorProcess = Editor.Start(pszMkDocument, QtToolsPath,
                    hideWindow: !Editor.Detached);
                if (EditorProcess == null)
                    return VSConstants.E_FAIL;
                if (Editor.Detached) {
                    Editor.OnStart(EditorProcess);
                    CloseParentFrame();
@@ -395,8 +407,12 @@
            {
                EditorProcess = null;
                EditorWindow = IntPtr.Zero;
                var parentFrame = GetService(typeof(SVsWindowFrame)) as IVsWindowFrame;
                parentFrame?.CloseFrame((uint)__FRAMECLOSE.FRAMECLOSE_NoSave);
                ThreadHelper.JoinableTaskFactory.Run(async () =>
                {
                    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                    var parentFrame = GetService(typeof(SVsWindowFrame)) as IVsWindowFrame;
                    parentFrame?.CloseFrame((uint)__FRAMECLOSE.FRAMECLOSE_NoSave);
                });
            }
            private void EditorProcess_Exited(object sender, EventArgs e)
@@ -452,7 +468,7 @@
                    EditorWindow = IntPtr.Zero;
                    // Close editor window
                    System.Threading.Tasks.Task.Run(() =>
                    _ = System.Threading.Tasks.Task.Run(() =>
                    {
                        NativeAPI.SendMessage(editorWindow, NativeAPI.WM_CLOSE, 0, 0);
                        if (!editorProcess.WaitForExit(500)) {
QtVsTools.Package/Icons/Monikers.imagemanifest
New file
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://schemas.microsoft.com/VisualStudio/ImageManifestSchema/2014">
  <Symbols>
    <String Name="Resources" Value="/QtVsTools;Component/Icons" />
    <Guid Name="QtMonikersGuid" Value="{0d2e443f-6dbb-4001-99dc-9cd7d5c924e7}" />
    <ID Name="prf" Value="0" />
    <ID Name="pri" Value="1" />
    <ID Name="pro" Value="2" />
    <ID Name="qml" Value="3" />
    <ID Name="qrc" Value="4" />
    <ID Name="ts" Value="5" />
    <ID Name="ui" Value="6" />
  </Symbols>
  <Images>
    <Image Guid="$(QtMonikersGuid)" ID="$(prf)">
      <Source Uri="$(Resources)/prf32.png">
        <Size Value="32" />
      </Source>
    </Image>
    <Image Guid="$(QtMonikersGuid)" ID="$(pri)">
      <Source Uri="$(Resources)/pri32.png">
        <Size Value="32" />
      </Source>
    </Image>
    <Image Guid="$(QtMonikersGuid)" ID="$(pro)">
      <Source Uri="$(Resources)/pro32.png">
        <Size Value="32" />
      </Source>
    </Image>
    <Image Guid="$(QtMonikersGuid)" ID="$(qml)">
      <Source Uri="$(Resources)/qml32.png">
        <Size Value="32" />
      </Source>
    </Image>
    <Image Guid="$(QtMonikersGuid)" ID="$(qrc)">
      <Source Uri="$(Resources)/qrc32.png">
        <Size Value="32" />
      </Source>
    </Image>
    <Image Guid="$(QtMonikersGuid)" ID="$(ts)">
      <Source Uri="$(Resources)/ts32.png">
        <Size Value="32" />
      </Source>
    </Image>
    <Image Guid="$(QtMonikersGuid)" ID="$(ui)">
      <Source Uri="$(Resources)/ui32.png">
        <Size Value="32" />
      </Source>
    </Image>
  </Images>
  <ImageLists />
</ImageManifest>
QtVsTools.Package/Icons/prf32.png
QtVsTools.Package/Icons/pri32.png
QtVsTools.Package/Icons/pro32.png
QtVsTools.Package/Icons/qml32.png
QtVsTools.Package/Icons/qrc32.png
QtVsTools.Package/Icons/ts32.png
QtVsTools.Package/Icons/ui32.png
QtVsTools.Package/Legacy/ChangeFor.cs
New file
@@ -0,0 +1,32 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
namespace QtVsTools.Legacy
{
    internal enum ChangeFor { Solution, Project }
}
QtVsTools.Package/Legacy/FormChangeQtVersion.Designer.cs
New file
@@ -0,0 +1,87 @@
namespace QtVsTools.Legacy
{
    partial class FormChangeQtVersion
    {
        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            btnOK = new System.Windows.Forms.Button();
            btnCancel = new System.Windows.Forms.Button();
            lbQtVersions = new System.Windows.Forms.ListBox();
            lQtVersions = new System.Windows.Forms.Label();
            SuspendLayout();
            //
            // btnOK
            //
            btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
            btnOK.Location = new System.Drawing.Point(124, 231);
            btnOK.Name = "btnOK";
            btnOK.Size = new System.Drawing.Size(75, 23);
            btnOK.TabIndex = 1;
            btnOK.Text = "&OK";
            btnOK.UseVisualStyleBackColor = true;
            //
            // btnCancel
            //
            btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            btnCancel.Location = new System.Drawing.Point(205, 231);
            btnCancel.Name = "btnCancel";
            btnCancel.Size = new System.Drawing.Size(75, 23);
            btnCancel.TabIndex = 2;
            btnCancel.Text = "&Cancel";
            btnCancel.UseVisualStyleBackColor = true;
            //
            // lbQtVersions
            //
            lbQtVersions.FormattingEnabled = true;
            lbQtVersions.Location = new System.Drawing.Point(13, 39);
            lbQtVersions.Name = "lbQtVersions";
            lbQtVersions.Size = new System.Drawing.Size(267, 173);
            lbQtVersions.TabIndex = 0;
            //
            // lQtVersions
            //
            lQtVersions.AutoSize = true;
            lQtVersions.Location = new System.Drawing.Point(13, 20);
            lQtVersions.Name = "lQtVersions";
            lQtVersions.Size = new System.Drawing.Size(103, 13);
            lQtVersions.TabIndex = 3;
            lQtVersions.Text = "Installed Qt Versions";
            //
            // FormChangeQtVersion
            //
            AcceptButton = btnOK;
            AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            CancelButton = btnCancel;
            ClientSize = new System.Drawing.Size(292, 266);
            Controls.Add(lQtVersions);
            Controls.Add(lbQtVersions);
            Controls.Add(btnCancel);
            Controls.Add(btnOK);
            FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            KeyPreview = true;
            MaximizeBox = false;
            MinimizeBox = false;
            Name = "FormChangeQtVersion";
            ShowInTaskbar = false;
            Text = "FormChangeQtVersion";
            ResumeLayout(false);
            PerformLayout();
        }
        #endregion
        private System.Windows.Forms.Button btnOK;
        private System.Windows.Forms.Button btnCancel;
        private System.Windows.Forms.ListBox lbQtVersions;
        private System.Windows.Forms.Label lQtVersions;
    }
}
QtVsTools.Package/Legacy/FormChangeQtVersion.cs
New file
@@ -0,0 +1,107 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Legacy
{
    using Core;
    using Legacy = Core.Legacy;
    public partial class FormChangeQtVersion : Form
    {
        public FormChangeQtVersion()
        {
            InitializeComponent();
            btnOK.Text = "&OK";
            btnCancel.Text = "&Cancel";
            lQtVersions.Text = "Installed Qt Versions";
            lbQtVersions.DoubleClick += OnQtVersions_DoubleClick;
            KeyPress += FormChangeQtVersion_KeyPress;
            Shown += FormChangeQtVersion_Shown;
        }
        private void FormChangeQtVersion_Shown(object sender, EventArgs e)
        {
            Text = "Set Solution's Qt Version";
        }
        void OnQtVersions_DoubleClick(object sender, EventArgs e)
        {
            DialogResult = DialogResult.OK;
            Close();
        }
        void FormChangeQtVersion_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 27) {
                DialogResult = DialogResult.Cancel;
                Close();
            }
        }
        internal void UpdateContent(ChangeFor change)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            lbQtVersions.Items.Clear();
            var vm = QtVersionManager.The();
            foreach (var versionName in vm.GetVersions())
                lbQtVersions.Items.Add(versionName);
            lbQtVersions.Items.Add("$(DefaultQtVersion)");
            if (change == ChangeFor.Solution) {
                var qtVer = Legacy.QtVersionManager
                    .GetSolutionQtVersion(QtVsToolsPackage.Instance.Dte.Solution);
                if (qtVer == null)
                    qtVer = vm.GetDefaultVersion();
                if (qtVer != null)
                    lbQtVersions.SelectedItem = qtVer;
                Text = "Set Solution's Qt Version";
            } else {
                var pro = Core.HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte);
                var qtVer = vm.GetProjectQtVersion(pro);
                if (qtVer == null)
                    qtVer = vm.GetDefaultVersion();
                if (qtVer != null)
                    lbQtVersions.SelectedItem = qtVer;
                Text = "Set Project's Qt Version";
            }
        }
        public string GetSelectedQtVersion()
        {
            var idx = lbQtVersions.SelectedIndex;
            if (idx < 0)
                return null;
            return lbQtVersions.Items[idx].ToString();
        }
    }
}
QtVsTools.Package/Legacy/FormChangeQtVersion.resx
New file
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
QtVsTools.Package/Legacy/FormProjectQtSettings.Designer.cs
New file
@@ -0,0 +1,159 @@
using System.Windows.Forms;
namespace QtVsTools.Legacy
{
    partial class FormProjectQtSettings
    {
        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.OptionsPropertyGrid = new System.Windows.Forms.PropertyGrid();
            this.panel1 = new System.Windows.Forms.Panel();
            this.okButton = new System.Windows.Forms.Button();
            this.cancelButton = new System.Windows.Forms.Button();
            this.tabControl1 = new System.Windows.Forms.TabControl();
            this.tabPage1 = new System.Windows.Forms.TabPage();
            this.tabPage2 = new System.Windows.Forms.TabPage();
            this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
            this.panel1.SuspendLayout();
            this.tabControl1.SuspendLayout();
            this.tabPage1.SuspendLayout();
            this.tabPage2.SuspendLayout();
            this.SuspendLayout();
            //
            // OptionsPropertyGrid
            //
            this.OptionsPropertyGrid.Dock = System.Windows.Forms.DockStyle.Fill;
            this.OptionsPropertyGrid.HelpVisible = false;
            this.OptionsPropertyGrid.Location = new System.Drawing.Point(6, 6);
            this.OptionsPropertyGrid.Margin = new System.Windows.Forms.Padding(6);
            this.OptionsPropertyGrid.Name = "OptionsPropertyGrid";
            this.OptionsPropertyGrid.PropertySort = System.Windows.Forms.PropertySort.Alphabetical;
            this.OptionsPropertyGrid.Size = new System.Drawing.Size(898, 603);
            this.OptionsPropertyGrid.TabIndex = 8;
            this.OptionsPropertyGrid.ToolbarVisible = false;
            //
            // panel1
            //
            this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.panel1.Controls.Add(this.okButton);
            this.panel1.Controls.Add(this.cancelButton);
            this.panel1.Location = new System.Drawing.Point(620, 700);
            this.panel1.Margin = new System.Windows.Forms.Padding(6);
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size(336, 73);
            this.panel1.TabIndex = 9;
            //
            // okButton
            //
            this.okButton.Location = new System.Drawing.Point(16, 15);
            this.okButton.Margin = new System.Windows.Forms.Padding(6);
            this.okButton.Name = "okButton";
            this.okButton.Size = new System.Drawing.Size(150, 44);
            this.okButton.TabIndex = 0;
            this.okButton.Click += new System.EventHandler(this.OkButton_Click);
            this.okButton.Text = "&OK";
            //
            // cancelButton
            //
            this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.cancelButton.Location = new System.Drawing.Point(176, 15);
            this.cancelButton.Margin = new System.Windows.Forms.Padding(6);
            this.cancelButton.Name = "cancelButton";
            this.cancelButton.Size = new System.Drawing.Size(150, 44);
            this.cancelButton.TabIndex = 1;
            this.cancelButton.Text = "Cancel";
            //
            // tabControl1
            //
            this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
            | System.Windows.Forms.AnchorStyles.Left)
            | System.Windows.Forms.AnchorStyles.Right)));
            this.tabControl1.Controls.Add(this.tabPage1);
            this.tabControl1.Controls.Add(this.tabPage2);
            this.tabControl1.Location = new System.Drawing.Point(24, 23);
            this.tabControl1.Margin = new System.Windows.Forms.Padding(6);
            this.tabControl1.Name = "tabControl1";
            this.tabControl1.SelectedIndex = 0;
            this.tabControl1.Size = new System.Drawing.Size(926, 662);
            this.tabControl1.TabIndex = 10;
            //
            // tabPage1
            //
            this.tabPage1.BackColor = System.Drawing.SystemColors.Control;
            this.tabPage1.Controls.Add(this.OptionsPropertyGrid);
            this.tabPage1.Location = new System.Drawing.Point(8, 39);
            this.tabPage1.Margin = new System.Windows.Forms.Padding(6);
            this.tabPage1.Name = "tabPage1";
            this.tabPage1.Padding = new System.Windows.Forms.Padding(6);
            this.tabPage1.Size = new System.Drawing.Size(910, 615);
            this.tabPage1.TabIndex = 0;
            this.tabPage1.Text = "Properties";
            //
            // tabPage2
            //
            this.tabPage2.BackColor = System.Drawing.SystemColors.Control;
            this.tabPage2.Controls.Add(this.flowLayoutPanel1);
            this.tabPage2.Location = new System.Drawing.Point(8, 39);
            this.tabPage2.Margin = new System.Windows.Forms.Padding(6);
            this.tabPage2.Name = "tabPage2";
            this.tabPage2.Padding = new System.Windows.Forms.Padding(6);
            this.tabPage2.Size = new System.Drawing.Size(910, 615);
            this.tabPage2.TabIndex = 1;
            this.tabPage2.Text = "Qt Modules";
            //
            // flowLayoutPanel1
            //
            this.flowLayoutPanel1.AutoScroll = true;
            this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
            this.flowLayoutPanel1.Location = new System.Drawing.Point(6, 6);
            this.flowLayoutPanel1.Name = "flowLayoutPanel1";
            this.flowLayoutPanel1.Size = new System.Drawing.Size(898, 603);
            this.flowLayoutPanel1.TabIndex = 66;
            //
            // FormProjectQtSettings
            //
            this.AcceptButton = this.okButton;
            this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.CancelButton = this.cancelButton;
            this.ClientSize = new System.Drawing.Size(962, 801);
            this.Controls.Add(this.tabControl1);
            this.Controls.Add(this.panel1);
            this.KeyPreview = true;
            this.Margin = new System.Windows.Forms.Padding(6);
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.MinimumSize = new System.Drawing.Size(988, 750);
            this.Name = "FormProjectQtSettings";
            this.ShowIcon = false;
            this.ShowInTaskbar = false;
            this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show;
            this.Text = "Qt Project Settings";
            this.panel1.ResumeLayout(false);
            this.tabControl1.ResumeLayout(false);
            this.tabPage1.ResumeLayout(false);
            this.tabPage2.ResumeLayout(false);
            this.ResumeLayout(false);
        }
        #endregion
        private PropertyGrid OptionsPropertyGrid;
        private Panel panel1;
        private Button okButton;
        private Button cancelButton;
        private TabControl tabControl1;
        private TabPage tabPage1;
        private TabPage tabPage2;
        private FlowLayoutPanel flowLayoutPanel1;
    }
}
QtVsTools.Package/Legacy/FormProjectQtSettings.cs
New file
@@ -0,0 +1,166 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using EnvDTE;
namespace QtVsTools.Legacy
{
    using Core;
    using Legacy = Core.Legacy;
    public partial class FormProjectQtSettings : Form
    {
        private readonly Project project;
        private readonly ProjectQtSettings qtSettings;
        private readonly List<ModuleItem> moduleMap = new List<ModuleItem>();
        private struct ModuleItem
        {
            public readonly CheckBox checkbox;
            public readonly int moduleId;
            public bool initialValue;
            public ModuleItem(CheckBox cb, int mid, bool init)
            {
                checkbox = cb;
                moduleId = mid;
                initialValue = init;
            }
        }
        public FormProjectQtSettings(Project pro)
        {
            InitializeComponent();
            project = pro;
            qtSettings = new ProjectQtSettings(project);
            qtSettings.PropertyChanged += OnPropertyChanged;
            OptionsPropertyGrid.SelectedObject = qtSettings;
            InitModules(QtVersionManager.The().GetProjectQtVersion(project));
        }
        protected override bool ProcessDialogKey(Keys keyData)
        {
            if (ModifierKeys == Keys.None && keyData == Keys.Escape) {
                Close();
                return true;
            }
            return base.ProcessDialogKey(keyData);
        }
        private void OkButton_Click(object sender, EventArgs e)
        {
            // Disable the buttons since some operations are quite expensive (e.g. changing
            // the Qt version) and take some to finish. Keeping the buttons enabled allows to hit
            // the buttons several times resulting in successive executions of these operations.
            okButton.Enabled = false;
            cancelButton.Enabled = false;
            qtSettings.SaveSettings();
            SaveModules();
            okButton.DialogResult = DialogResult.OK;
            Close();
        }
        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "Version")
                throw new ArgumentException();
            InitModules(qtSettings.Version);
        }
        private void InitModules(string qtVersion)
        {
            moduleMap.Clear();
            flowLayoutPanel1.Controls.Clear();
            var vm = QtVersionManager.The();
            var versionInfo = vm.GetVersionInfo(qtVersion);
            if (string.IsNullOrEmpty(qtVersion) || versionInfo == null) {
                flowLayoutPanel1.Controls.Add(new Label
                {
                    Text = Environment.NewLine
                        + "Please set a valid Qt version first.",
                    AutoSize = true
                });
                return;
            }
            var installPath = vm.GetInstallPath(qtVersion) ?? string.Empty;
            var modules = QtModules.Instance.GetAvailableModules(versionInfo.qtMajor)
                .Where(x => x.Selectable)
                .OrderBy(x => x.Name);
            foreach (var module in modules) {
                var checkBox = new CheckBox
                {
                    Margin = new Padding(6),
                    Location = new System.Drawing.Point(150, 150),
                    Name = module.LibraryPrefix,
                    UseVisualStyleBackColor = true,
                    AutoSize = true,
                    Text = module.Name,
                    Checked = Legacy.QtProject.HasModule(project, module.Id, qtVersion)
                };
                flowLayoutPanel1.Controls.Add(checkBox);
                moduleMap.Add(new ModuleItem(checkBox, module.Id, checkBox.Checked));
                var info = QtModules.Instance.Module(module.Id, versionInfo.qtMajor);
                var libraryPrefix = info?.LibraryPrefix;
                if (libraryPrefix.StartsWith("Qt", StringComparison.Ordinal))
                    libraryPrefix = "Qt" + versionInfo.qtMajor + libraryPrefix.Substring(2);
                checkBox.Enabled = new FileInfo(
                    Path.Combine(installPath, "lib", $"{libraryPrefix}{versionInfo.LibInfix()}.lib")
                ).Exists; // Disable the check-box if the module is not installed.
                if (!checkBox.Enabled)
                    checkBox.Checked = false; // Uncheck if the module is not installed.
            }
        }
        private void SaveModules()
        {
            for (var i = 0; i < moduleMap.Count; ++i) {
                var item = moduleMap[i];
                var isModuleChecked = item.checkbox.Checked;
                if (isModuleChecked != item.initialValue) {
                    if (isModuleChecked)
                        Legacy.QtProject.AddModule(project, item.moduleId);
                    else
                        Legacy.QtProject.RemoveModule(project, item.moduleId);
                }
            }
        }
    }
}
QtVsTools.Package/Legacy/FormProjectQtSettings.resx
New file
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!--
    Microsoft ResX Schema
    Version 2.0
    The primary goals of this format is to allow a simple XML format
    that is mostly human readable. The generation and parsing of the
    various data types are done through the TypeConverter classes
    associated with the data types.
    Example:
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
    There are any number of "resheader" rows that contain simple
    name/value pairs.
    Each data row contains a name, and value. The row also contains a
    type or mimetype. Type corresponds to a .NET class that support
    text/value conversion through the TypeConverter architecture.
    Classes that don't support this are serialized and stored with the
    mimetype set.
    The mimetype is used for serialized objects, and tells the
    ResXResourceReader how to depersist the object. This is currently not
    extensible. For a given mimetype the value must be set accordingly:
    Note - application/x-microsoft.net.object.binary.base64 is the format
    that the ResXResourceWriter will generate, however the reader can
    read any of the formats listed below.
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
QtVsTools.Package/Legacy/ProjectQtSettings.cs
New file
@@ -0,0 +1,336 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.ComponentModel;
using System.Globalization;
using System.Text.RegularExpressions;
namespace QtVsTools.Legacy
{
    using Core;
    using Legacy = Core.Legacy;
    public class ProjectQtSettings : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            var eventHandler = PropertyChanged;
            if (eventHandler != null)
                eventHandler.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public ProjectQtSettings(EnvDTE.Project proj)
        {
            versionManager = QtVersionManager.The();
            project = proj;
            newMocDir = oldMocDir = Legacy.QtVSIPSettings.GetMocDirectory(project);
            newMocOptions = oldMocOptions = Legacy.QtVSIPSettings.GetMocOptions(project);
            newRccDir = oldRccDir = Legacy.QtVSIPSettings.GetRccDirectory(project);
            newUicDir = oldUicDir = Legacy.QtVSIPSettings.GetUicDirectory(project);
            newLUpdateOnBuild = oldLUpdateOnBuild = Legacy.QtVSIPSettings.GetLUpdateOnBuild(project);
            newLUpdateOptions = oldLUpdateOptions = Legacy.QtVSIPSettings.GetLUpdateOptions(project);
            newLReleaseOptions = oldLReleaseOptions = Legacy.QtVSIPSettings.GetLReleaseOptions(project);
            newQtVersion = oldQtVersion = versionManager.GetProjectQtVersion(project);
            QmlDebug = oldQmlDebug = Legacy.QtVSIPSettings.GetQmlDebug(project);
        }
        private readonly QtVersionManager versionManager;
        private EnvDTE.Project project;
        private readonly string oldMocDir;
        private readonly string oldMocOptions;
        private readonly string oldRccDir;
        private readonly string oldUicDir;
        private readonly string oldQtVersion;
        private readonly bool oldLUpdateOnBuild;
        private readonly string oldLUpdateOptions;
        private readonly string oldLReleaseOptions;
        private readonly bool oldQmlDebug;
        private string newMocDir;
        private string newMocOptions;
        private string newRccDir;
        private string newUicDir;
        private string newQtVersion;
        private bool newLUpdateOnBuild;
        private string newLUpdateOptions;
        private string newLReleaseOptions;
        public void SaveSettings()
        {
            var updateMoc = false;
            var qtPro = QtProject.Create(project);
            if (oldMocDir != newMocDir) {
                Legacy.QtVSIPSettings.SaveMocDirectory(project, newMocDir);
                updateMoc = true;
            }
            if (oldMocOptions != newMocOptions) {
                Legacy.QtVSIPSettings.SaveMocOptions(project, newMocOptions);
                updateMoc = true;
            }
            if (updateMoc)
                qtPro.UpdateMocSteps(oldMocDir);
            if (oldUicDir != newUicDir) {
                Legacy.QtVSIPSettings.SaveUicDirectory(project, newUicDir);
                qtPro.UpdateUicSteps(oldUicDir, true);
            }
            if (oldRccDir != newRccDir) {
                Legacy.QtVSIPSettings.SaveRccDirectory(project, newRccDir);
                qtPro.RefreshRccSteps(oldRccDir);
            }
            if (oldLUpdateOnBuild != newLUpdateOnBuild)
                Legacy.QtVSIPSettings.SaveLUpdateOnBuild(project, newLUpdateOnBuild);
            if (oldLUpdateOptions != newLUpdateOptions)
                Legacy.QtVSIPSettings.SaveLUpdateOptions(project, newLUpdateOptions);
            if (oldLReleaseOptions != newLReleaseOptions)
                Legacy.QtVSIPSettings.SaveLReleaseOptions(project, newLReleaseOptions);
            if (oldQmlDebug != QmlDebug)
                Legacy.QtVSIPSettings.SaveQmlDebug(project, QmlDebug);
            if (oldQtVersion != newQtVersion) {
                if (Legacy.QtProject.PromptChangeQtVersion(project, oldQtVersion, newQtVersion)) {
                    var newProjectCreated = false;
                    var versionChanged = qtPro.ChangeQtVersion(
                        oldQtVersion, newQtVersion, ref newProjectCreated);
                    if (versionChanged && newProjectCreated)
                        project = qtPro.Project;
                }
            }
        }
        public string MocDirectory
        {
            get
            {
                return newMocDir;
            }
            set
            {
                var tmp = HelperFunctions.NormalizeRelativeFilePath(value);
                if (tmp.Equals(oldMocDir, StringComparison.OrdinalIgnoreCase))
                    return;
                string badMacros = IncompatibleMacros(tmp);
                if (!string.IsNullOrEmpty(badMacros))
                    Messages.DisplayErrorMessage(SR.GetString("IncompatibleMacros", badMacros));
                else
                    newMocDir = tmp;
            }
        }
        public string MocOptions
        {
            get
            {
                return newMocOptions;
            }
            set
            {
                newMocOptions = value;
            }
        }
        public string UicDirectory
        {
            get
            {
                return newUicDir;
            }
            set
            {
                var tmp = HelperFunctions.NormalizeRelativeFilePath(value);
                if (tmp.Equals(oldUicDir, StringComparison.OrdinalIgnoreCase))
                    return;
                string badMacros = IncompatibleMacros(tmp);
                if (!string.IsNullOrEmpty(badMacros))
                    Messages.DisplayErrorMessage(SR.GetString("IncompatibleMacros", badMacros));
                else
                    newUicDir = tmp;
            }
        }
        public string RccDirectory
        {
            get
            {
                return newRccDir;
            }
            set
            {
                var tmp = HelperFunctions.NormalizeRelativeFilePath(value);
                if (tmp.Equals(oldRccDir, StringComparison.OrdinalIgnoreCase))
                    return;
                string badMacros = IncompatibleMacros(tmp);
                if (!string.IsNullOrEmpty(badMacros))
                    Messages.DisplayErrorMessage(SR.GetString("IncompatibleMacros", badMacros));
                else
                    newRccDir = tmp;
            }
        }
        public bool lupdateOnBuild
        {
            get
            {
                return newLUpdateOnBuild;
            }
            set
            {
                newLUpdateOnBuild = value;
            }
        }
        public string LUpdateOptions
        {
            get
            {
                return newLUpdateOptions;
            }
            set
            {
                newLUpdateOptions = value;
            }
        }
        public string LReleaseOptions
        {
            get
            {
                return newLReleaseOptions;
            }
            set
            {
                newLReleaseOptions = value;
            }
        }
        [DisplayName("QML Debug")]
        [TypeConverter(typeof(QmlDebugConverter))]
        private bool QmlDebug { get; }
        private static string IncompatibleMacros(string stringToExpand)
        {
            string incompatibleMacros = "";
            foreach (Match metaNameMatch in Regex.Matches(stringToExpand, @"\%\(([^\)]+)\)")) {
                string metaName = metaNameMatch.Groups[1].Value;
                if (!incompatibleMacros.Contains(string.Format("%({0})", metaName))) {
                    switch (metaName) {
                    case "RecursiveDir":
                    case "ModifiedTime":
                    case "CreatedTime":
                    case "AccessedTime":
                        if (!string.IsNullOrEmpty(incompatibleMacros))
                            incompatibleMacros += ", ";
                        incompatibleMacros += string.Format("%({0})", metaName);
                        break;
                    }
                }
            }
            return incompatibleMacros;
        }
        internal class QmlDebugConverter : BooleanConverter
        {
            public override object ConvertTo(
                ITypeDescriptorContext context,
                CultureInfo culture,
                object value,
                Type destinationType)
            {
                return (bool)value ? "Enabled" : "Disabled";
            }
            public override object ConvertFrom(
                ITypeDescriptorContext context,
                CultureInfo culture,
                object value)
            {
                return (string)value == "Enabled";
            }
        }
        [TypeConverter(typeof(VersionConverter))]
        public string Version
        {
            get
            {
                return newQtVersion;
            }
            set
            {
                if (newQtVersion != value) {
                    newQtVersion = value;
                    OnPropertyChanged("Version");
                }
            }
        }
        internal class VersionConverter : StringConverter
        {
            private readonly QtVersionManager versionManager;
            public VersionConverter()
            {
                versionManager = QtVersionManager.The();
            }
            public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
            {
                return true;
            }
            public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
            {
                var versions = versionManager.GetVersions();
                Array.Resize(ref versions, versions.Length + 1);
                versions[versions.Length - 1] = "$(DefaultQtVersion)";
                return new StandardValuesCollection(versions);
            }
            public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
            {
                return true;
            }
        }
    }
}
QtVsTools.Package/Legacy/QtMenu.cs
New file
@@ -0,0 +1,109 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System.Windows.Forms;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Legacy
{
    using Core;
    using Legacy = Core.Legacy;
    internal static class QtMenu
    {
        internal static void ShowFormProjectQtSettings(Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            using (var form = new FormProjectQtSettings(project)) {
                form.StartPosition = FormStartPosition.CenterParent;
                form.ShowDialog(new MainWinWrapper(QtVsToolsPackage.Instance.Dte));
            }
        }
        internal static void ShowFormChangeProjectQtVersion()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte);
            if (!HelperFunctions.IsQtProject(pro))
                return;
            using (var formChangeQtVersion = new FormChangeQtVersion()) {
                formChangeQtVersion.UpdateContent(ChangeFor.Project);
                var ww = new MainWinWrapper(QtVsToolsPackage.Instance.Dte);
                if (formChangeQtVersion.ShowDialog(ww) == DialogResult.OK) {
                    var qtVersion = formChangeQtVersion.GetSelectedQtVersion();
                    HelperFunctions.SetDebuggingEnvironment(pro, "PATH=" + QtVersionManager
                        .The().GetInstallPath(qtVersion) + @"\bin;$(PATH)", true);
                }
            }
        }
        internal static void ShowFormChangeSolutionQtVersion()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            string newQtVersion = null;
            using (var formChangeQtVersion = new FormChangeQtVersion()) {
                formChangeQtVersion.UpdateContent(ChangeFor.Solution);
                if (formChangeQtVersion.ShowDialog() != DialogResult.OK)
                    return;
                newQtVersion = formChangeQtVersion.GetSelectedQtVersion();
            }
            if (newQtVersion == null)
                return;
            string platform = null;
            var dte = QtVsToolsPackage.Instance.Dte;
            try {
                platform = (dte.Solution.SolutionBuild.ActiveConfiguration as SolutionConfiguration2)
                    .PlatformName;
            } catch { }
            if (string.IsNullOrEmpty(platform))
                return;
            var vm = QtVersionManager.The();
            foreach (var project in HelperFunctions.ProjectsInSolution(dte)) {
                if (HelperFunctions.IsVsToolsProject(project)) {
                    var OldQtVersion = vm.GetProjectQtVersion(project, platform);
                    if (OldQtVersion == null)
                        OldQtVersion = vm.GetDefaultVersion();
                    var created = false;
                    var qtProject = QtProject.Create(project);
                    if (Legacy.QtProject.PromptChangeQtVersion(project, OldQtVersion, newQtVersion))
                        qtProject.ChangeQtVersion(OldQtVersion, newQtVersion, ref created);
                }
            }
            Legacy.QtVersionManager.SaveSolutionQtVersion(dte.Solution, newQtVersion);
        }
    }
}
QtVsTools.Package/Legacy/QtOptionsPage.cs
New file
@@ -0,0 +1,161 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System;
using System.ComponentModel;
using Microsoft.Win32;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools.Legacy
{
    using Core;
    public class QtOptionsPage : DialogPage
    {
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Source control: Ask before checking out files")]
        public bool CheckoutPrompt { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Source control: Enable file check-out")]
        public bool Checkout { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Linguist: Default lrelease options")]
        public string DefaultLReleaseOptions { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Linguist: Default lupdate options")]
        public string DefaultLUpdateOptions { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Linguist: Run lupdate during build")]
        public bool EnableLUpdateOnBuild { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Meta-Object Compiler: Default moc generated files directory")]
        public string DefaultMocDir { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Meta-Object Compiler: Default additional moc options ")]
        public string AdditionalMocOptions { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Meta-Object Compiler: Enable automatic moc")]
        public bool AutoMoc { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Resource Compiler: Default rcc generated files directory")]
        public string DefaultRccDir { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("User Interface Compiler: Default uic generated files directory")]
        public string DefaultUicDir { get; set; }
        [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")]
        [DisplayName("Build: Run pre-build setup")]
        public bool PreBuildSetup { get; set; }
        const string VALUENAME_LegacyPreBuild = "LegacyPreBuild";
        public override void ResetSettings()
        {
            CheckoutPrompt = true;
            Checkout = true;
            DefaultLReleaseOptions = "";
            DefaultLUpdateOptions = "";
            EnableLUpdateOnBuild = false;
            DefaultMocDir = "";
            AdditionalMocOptions = "";
            AutoMoc = true;
            DefaultRccDir = "";
            DefaultUicDir = "";
            PreBuildSetup = false;
        }
        public override void LoadSettingsFromStorage()
        {
            ResetSettings();
            try {
                using (var key = Registry.CurrentUser
                    .OpenSubKey(@"SOFTWARE\" + Resources.registryPackagePath, writable: false)) {
                    if (key == null)
                        return;
                    if (key.GetValue(Resources.askBeforeCheckoutFileKeyword) is int checkoutPrompt)
                        CheckoutPrompt = (checkoutPrompt != 0);
                    if (key.GetValue(Resources.disableCheckoutFilesKeyword) is int disableCheckout)
                        Checkout = (disableCheckout == 0);
                    if (key.GetValue(Resources.lreleaseOptionsKeyword) is string lreleaseOptions)
                        DefaultLReleaseOptions = lreleaseOptions;
                    if (key.GetValue(Resources.lupdateOptionsKeyword) is string lupdateOptions)
                        DefaultLUpdateOptions = lupdateOptions;
                    if (key.GetValue(Resources.lupdateKeyword) is int lupdateOnBuild)
                        EnableLUpdateOnBuild = (lupdateOnBuild != 0);
                    if (key.GetValue(Resources.mocDirKeyword) is string mocDir)
                        DefaultMocDir = mocDir;
                    if (key.GetValue(Resources.mocOptionsKeyword) is string mocOptions)
                        AdditionalMocOptions = mocOptions;
                    if (key.GetValue(Resources.disableAutoMocStepsUpdateKeyword) is int autoMocOff)
                        AutoMoc = (autoMocOff == 0);
                    if (key.GetValue(Resources.rccDirKeyword) is string rccDir)
                        DefaultRccDir = rccDir;
                    if (key.GetValue(Resources.uicDirKeyword) is string uicDir)
                        DefaultUicDir = uicDir;
                    if (key.GetValue(VALUENAME_LegacyPreBuild) is int preBuild)
                        PreBuildSetup = (preBuild != 0);
                }
            } catch (Exception exception) {
                exception.Log();
            }
        }
        public override void SaveSettingsToStorage()
        {
            try {
                using (var key = Registry.CurrentUser
                    .CreateSubKey(@"SOFTWARE\" + Resources.registryPackagePath)) {
                    if (key == null)
                        return;
                    key.SetValue(Resources.askBeforeCheckoutFileKeyword, CheckoutPrompt ? 1 : 0);
                    key.SetValue(Resources.disableCheckoutFilesKeyword, Checkout ? 0 : 1);
                    key.SetValue(Resources.lreleaseOptionsKeyword, DefaultLReleaseOptions);
                    key.SetValue(Resources.lupdateOptionsKeyword, DefaultLUpdateOptions);
                    key.SetValue(Resources.lupdateKeyword, EnableLUpdateOnBuild ? 1 : 0);
                    key.SetValue(Resources.mocDirKeyword, DefaultMocDir);
                    key.SetValue(Resources.mocOptionsKeyword, AdditionalMocOptions);
                    key.SetValue(Resources.disableAutoMocStepsUpdateKeyword, AutoMoc ? 0 : 1);
                    key.SetValue(Resources.rccDirKeyword, DefaultRccDir);
                    key.SetValue(Resources.uicDirKeyword, DefaultUicDir);
                    key.SetValue(VALUENAME_LegacyPreBuild, PreBuildSetup ? 1 : 0);
                }
            } catch (Exception exception) {
                exception.Log();
            }
        }
    }
}
QtVsTools.Package/Legacy/Translation.cs
New file
@@ -0,0 +1,134 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.VCProjectEngine;
namespace QtVsTools.Legacy
{
    using Core;
    using Legacy = Core.Legacy;
    using static Core.HelperFunctions;
    using BuildAction = QtVsTools.Translation.BuildAction;
    internal static class Translation
    {
        internal static void Run(BuildAction buildAction, QtProject qtProject,
            IEnumerable<string> tsFiles)
        {
            var qtInstallPath = QtVersionManager.The().GetInstallPath(qtProject.GetQtVersion());
            if (string.IsNullOrEmpty(qtInstallPath)) {
                Messages.Print("translation: Error accessing Qt installation");
                return;
            }
            if (tsFiles == null) {
                tsFiles = (qtProject.VCProject
                    .GetFilesEndingWith(".ts") as IVCCollection)
                    .Cast<VCFile>()
                    .Select(vcFile => vcFile.RelativePath);
            }
            if (tsFiles != null) {
                var project = qtProject.Project;
                var tempFile = Path.GetTempFileName();
                File.WriteAllLines(tempFile, GetProjectFiles(project, FilesToList.FL_HFiles)
                    .Union(GetProjectFiles(project, FilesToList.FL_CppFiles))
                    .Union(GetProjectFiles(project, FilesToList.FL_UiFiles))
                    .Union(GetProjectFiles(project, FilesToList.FL_QmlFiles)));
                var procInfo = new ProcessStartInfo
                {
                    WorkingDirectory = qtProject.ProjectDir,
                    CreateNoWindow = true,
                    UseShellExecute = false,
                    RedirectStandardError = true,
                    RedirectStandardOutput = true,
                    Arguments = ""
                };
                procInfo.FileName = Path.Combine(qtInstallPath, "bin",
                    buildAction == BuildAction.Update ? "lupdate.exe" : "lrelease.exe");
                foreach (var file in tsFiles.Where(file => file != null))
                    Run(buildAction, file, tempFile, procInfo);
            } else {
                Messages.Print("translation: No translation files found");
            }
        }
        private static void Run(BuildAction buildAction, string tsFile, string tempFile,
            ProcessStartInfo procInfo)
        {
            switch (buildAction) {
            case BuildAction.Update:
                Messages.Print("\r\n--- (lupdate) file: " + tsFile);
                var options = Legacy.QtVSIPSettings.GetLUpdateOptions();
                if (!string.IsNullOrEmpty(options))
                    procInfo.Arguments += options + " ";
                procInfo.Arguments += string.Format("\"@{0}\" -ts \"{1}\"", tempFile, tsFile);
                break;
            case BuildAction.Release:
                Messages.Print("\r\n--- (lrelease) file: " + tsFile);
                options = Legacy.QtVSIPSettings.GetLReleaseOptions();
                if (!string.IsNullOrEmpty(options))
                    procInfo.Arguments += options + " ";
                procInfo.Arguments += string.Format("\"{0}\"", tsFile);
                break;
            }
            using (var proc = Process.Start(procInfo)) {
                proc.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
                {
                    if (!string.IsNullOrEmpty(e.Data))
                        Messages.Print(e.Data);
                };
                proc.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
                {
                    if (!string.IsNullOrEmpty(e.Data))
                        Messages.Print(e.Data);
                };
                proc.BeginOutputReadLine();
                proc.BeginErrorReadLine();
                proc.WaitForExit();
                switch (proc.ExitCode) {
                case 0:
                    Messages.Print("translation: ok");
                    break;
                default:
                    Messages.Print(string.Format("translation: ERROR {0}", proc.ExitCode));
                    break;
                }
            }
        }
    }
}
QtVsTools.Package/Marketplace/Overview.html_TT
@@ -94,7 +94,7 @@
<p><h3><span>The main features of Qt VS Tools are:</span></h3></p>
<ul>
    <li>
        Wizards for creating new Qt projects and classes.
        Wizards for creating new Qt and Qt Quick projects and files.
    </li>
    <li>
        Automated build setup for the <a href="http://doc.qt.io/qt-5/moc.html">
@@ -122,6 +122,9 @@
    <li>
        Debugging extensions for Qt data types.
    </li>
    <li>
        QML debug engine for debugging Qt Quick applications.
    </li>
</ul>
<p><h3>How to set up F1 help</h3></p>
<ol class="1" type="1">
@@ -140,13 +143,41 @@
        Select <strong>Assign</strong>, and then select <strong>OK</strong>.
    </li>
</ol>
<p><h3>How to report bugs and contribute code?</h3></p>
<p><h3>How to contribute via GitHub?</h3></p>
<ul>
    <li>
        <a title="Bug reports" href="https://bugreports.qt.io/browse/QTVSADDINBUG">Bug reports</a>
        <a title="Source code on GitHub"
           href="https://github.com/qt-labs/vstools">Source code on GitHub</a>
    </li>
    <li>
        <a title="Source code" href="https://code.qt.io/cgit/qt-labs/vstools.git">Source code</a>
        <a title="Pull requests on GitHub"
           href="https://github.com/qt-labs/vstools/pulls">Pull requests on GitHub</a>
    </li>
</ul>
<p><h3>How to contribute via Qt Labs?</h3></p>
<ul>
    <li>
        <a title="Source code on Qt Labs"
           href="https://code.qt.io/cgit/qt-labs/vstools.git">Source code on Qt Labs</a>
    </li>
    <li>
        <a title="Qt VS Tools Code Review"
           href="https://codereview.qt-project.org/q/project:qt-labs/vstools">Qt VS Tools Code Review</a>
    </li>
</ul>
For more information on how to contribute to the Qt Visual Tools via Qt Labs, please look at the
        <a title="Qt contribution Guidelines"
           href="https://wiki.qt.io/Qt_Contribution_Guidelines">
                Qt contribution Guidelines</a> first.
<p><h3>How to report bugs?</h3></p>
<ul>
    <li>
        <a title="Qt Bug Tracker"
           href="https://bugreports.qt.io/browse/QTVSADDINBUG">Qt Bug Tracker</a>
    </li>
    <li>
        <a title="GitHub Issue Tracker"
           href="https://github.com/qt-labs/vstools/issues">GitHub Issue Tracker</a>
    </li>
</ul>
<p>
QtVsTools.Package/Options/QtOptionsPage.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -30,20 +30,20 @@
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using Microsoft.Win32;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.Build.Framework;
using Microsoft.Win32;
using EnvDTE;
using QtVsTools.Core;
using QtVsTools.Common;
using QtVsTools.VisualStudio;
using System.Reflection;
using System.Linq.Expressions;
namespace QtVsTools.Options
{
    using static EnumExt;
    using Core;
    using VisualStudio;
    using static Common.EnumExt;
    public class QtOptionsPage : DialogPage, IQtVsToolsOptions
    {
@@ -85,6 +85,16 @@
            [String("BkgBuild_RunQtTools")] RunQtTools,
            [String("BkgBuild_DebugInfo")] DebugInfo,
            [String("BkgBuild_LoggerVerbosity")] LoggerVerbosity,
        }
        public enum Notifications
        {
            [String("Notifications_Installed")] Installed,
        }
        public enum Natvis
        {
            [String("LinkNatvis")] Link,
        }
        public enum Timeout : uint { Disabled = 0 }
@@ -144,8 +154,8 @@
                object value,
                Type destinationType)
            {
                if (value.GetType() == typeof(bool) && destinationType == typeof(string))
                    return ((bool)value) ? "Enable" : "Disable";
                if (value is bool b && destinationType == typeof(string))
                    return b ? "Enable" : "Disable";
                return base.ConvertTo(context, culture, value, destinationType);
            }
        }
@@ -170,7 +180,7 @@
        [DisplayName("Keyboard shortcut")]
        [Description("To change keyboard mapping, go to: Tools > Options > Keyboard")]
        [ReadOnly(true)]
        public string QtHelpKeyBinding { get; set; }
        private string QtHelpKeyBinding { get; set; }
        [Category("Help")]
        [DisplayName("Preferred source")]
@@ -222,8 +232,25 @@
        [Description("Configure verbosity level of background build log.")]
        public LoggerVerbosity BuildLoggerVerbosity { get; set; }
        [Category("Notifications")]
        [DisplayName("New version installed")]
        [Description("Show notification when a new version was recently installed.")]
        [TypeConverter(typeof(EnableDisableConverter))]
        public bool NotifyInstalled { get; set; }
        [Category("Natvis")]
        [DisplayName("Embed .natvis file into PDB")]
        [Description("Embeds the debugger visualizations (.natvis file) into the PDB file"
            + "generated by LINK. While setting this option, the embedded Natvis file will"
            + "take precedence over user-specific Natvis files(for example the files"
            + "located in %USERPROFILE%\\Documents\\Visual Studio 2022\\Visualizers).")]
        [TypeConverter(typeof(EnableDisableConverter))]
        public bool LinkNatvis { get; set; }
        public override void ResetSettings()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            QtMsBuildPath = "";
            QmlDebuggerEnabled = true;
            QmlDebuggerTimeout = (Timeout)60000;
@@ -234,6 +261,8 @@
            BuildRunQtTools = ProjectTracking = true;
            BuildDebugInformation = false;
            BuildLoggerVerbosity = LoggerVerbosity.Quiet;
            NotifyInstalled = true;
            LinkNatvis = true;
            ////////
            // Get Qt Help keyboard shortcut
@@ -252,6 +281,8 @@
        public override void LoadSettingsFromStorage()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            ResetSettings();
            try {
                QtMsBuildPath = Environment.GetEnvironmentVariable("QTMSBUILD");
@@ -271,10 +302,11 @@
                    Load(() => BuildRunQtTools, key, BkgBuild.RunQtTools);
                    Load(() => BuildDebugInformation, key, BkgBuild.DebugInfo);
                    Load(() => BuildLoggerVerbosity, key, BkgBuild.LoggerVerbosity);
                    Load(() => NotifyInstalled, key, Notifications.Installed);
                    Load(() => LinkNatvis, key, Natvis.Link);
                }
            } catch (Exception exception) {
                Messages.Print(
                    exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace);
                exception.Log();
            }
        }
@@ -296,7 +328,7 @@
                    Save(QmlDebuggerEnabled, key, QmlDebug.Enable);
                    Save(QmlDebuggerTimeout, key, QmlDebug.Timeout);
                    Save(HelpPreference, key, Help.Preference);
                    Save(TryQtHelpOnF1Pressed, key, Help.Preference);
                    Save(TryQtHelpOnF1Pressed, key, Help.TryOnF1Pressed);
                    Save(DesignerDetached, key, Designer.Detached);
                    Save(LinguistDetached, key, Linguist.Detached);
                    Save(ResourceEditorDetached, key, ResEditor.Detached);
@@ -304,10 +336,11 @@
                    Save(BuildRunQtTools, key, BkgBuild.RunQtTools);
                    Save(BuildDebugInformation, key, BkgBuild.DebugInfo);
                    Save(BuildLoggerVerbosity, key, BkgBuild.LoggerVerbosity);
                    Save(NotifyInstalled, key, Notifications.Installed);
                    Save(LinkNatvis, key, Natvis.Link);
                }
            } catch (Exception exception) {
                Messages.Print(
                    exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace);
                exception.Log();
            }
        }
QtVsTools.Package/Options/QtVersionsPage.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -28,29 +28,37 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using QtVsTools.Core;
namespace QtVsTools.Options
{
    using Common;
    using Core;
    using static QtVsTools.Options.QtVersionsTable;
    public class QtVersionsPage : UIElementDialogPage
    {
        static LazyFactory Lazy { get; } = new LazyFactory();
        QtVersionManager VersionManager => QtVersionManager.The();
        QtVersionsTable _VersionsTable;
        QtVersionsTable VersionsTable => _VersionsTable
            ?? (_VersionsTable = new QtVersionsTable());
        QtVersionsTable VersionsTable => Lazy.Get(() =>
            VersionsTable, () => new QtVersionsTable());
        protected override UIElement Child => VersionsTable;
        public override void LoadSettingsFromStorage()
        {
            var versions = new List<QtVersionsTable.Row>();
            var versions = new List<Row>();
            foreach (var versionName in VersionManager.GetVersions()) {
                var versionPath = VersionManager.GetInstallPath(versionName);
                if (string.IsNullOrEmpty(versionPath))
                    continue;
                BuildHost host = BuildHost.Windows;
                string compiler = "msvc";
                if (versionPath.StartsWith("SSH:") || versionPath.StartsWith("WSL:")) {
@@ -65,13 +73,15 @@
                        compiler = linuxPaths[2];
                }
                var defaultVersion = VersionManager.GetDefaultVersion();
                versions.Add(new QtVersionsTable.Row()
                versions.Add(new Row()
                {
                    IsDefault = (versionName == defaultVersion),
                    VersionName = versionName,
                    InitialVersionName = versionName,
                    Path = versionPath,
                    Host = host,
                    Compiler = compiler,
                    State = State.Unknown
                });
            }
            VersionsTable.UpdateVersions(versions);
@@ -79,23 +89,40 @@
        public override void SaveSettingsToStorage()
        {
            foreach (var versionName in VersionManager.GetVersions()) {
            void RemoveVersion(string versionName)
            {
                try {
                    VersionManager.RemoveVersion(versionName);
                } catch (Exception exception) {
                    Messages.Print(
                        exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace);
                    exception.Log();
                }
            }
            foreach (var version in VersionsTable.Versions) {
            var versions = VersionsTable.Versions;
            foreach (var version in versions) {
                if (version.State.HasFlag(State.Removed))
                    RemoveVersion(version.VersionName);
                if (!version.State.HasFlag(State.Modified))
                    continue;
                try {
                    if (version.Host == BuildHost.Windows) {
                        var versionInfo = VersionInformation.Get(version.Path);
                        var generator = versionInfo.GetQMakeConfEntry("MAKEFILE_GENERATOR");
                        if (generator != "MSVC.NET" && generator != "MSBUILD")
                            throw new Exception(SR.GetString(
                                "AddQtVersionDialog_IncorrectMakefileGenerator", generator));
                        VersionManager.SaveVersion(version.VersionName, version.Path);
                        if (version.State.HasFlag((State)Column.Path)) {
                            var versionPath = version.Path;
                            var ignoreCase = StringComparison.CurrentCultureIgnoreCase;
                            if (Path.GetFileName(versionPath).Equals("qmake.exe", ignoreCase))
                                versionPath = Path.GetDirectoryName(versionPath);
                            if (Path.GetFileName(versionPath).Equals("bin", ignoreCase))
                                versionPath = Path.GetDirectoryName(versionPath);
                            var versionInfo = VersionInformation.Get(versionPath);
                            var generator = versionInfo.GetQMakeConfEntry("MAKEFILE_GENERATOR");
                            if (generator != "MSVC.NET" && generator != "MSBUILD")
                                throw new Exception(string.Format(
                                    "This Qt version uses an unsupported makefile generator (used: "
                                    + "{0}, supported: MSVC.NET, MSBUILD)", generator));
                            VersionManager.SaveVersion(version.VersionName, versionPath);
                        }
                    } else {
                        string name = version.VersionName;
                        string access =
@@ -107,21 +134,34 @@
                        path = string.Format("{0}:{1}:{2}", access, path, compiler);
                        VersionManager.SaveVersion(name, path, checkPath: false);
                    }
                    if (version.State.HasFlag((State)Column.VersionName)) {
                        try {
                            VersionManager.SaveVersion(version.VersionName, version.Path);
                            if (!string.IsNullOrEmpty(version.InitialVersionName))
                                VersionManager.RemoveVersion(version.InitialVersionName);
                        } catch (Exception exception) {
                            exception.Log();
                        }
                    }
                } catch (Exception exception) {
                    Messages.Print(
                        exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace);
                    exception.Log();
                    version.State = State.Removed;
                    RemoveVersion(version.VersionName);
                }
            }
            try {
                var defaultVersion = VersionsTable.Versions
                    .Where(version => version.IsDefault)
                    .FirstOrDefault();
                if (defaultVersion != null)
                    VersionManager.SaveDefaultVersion(defaultVersion.VersionName);
                var defaultVersion =
                    versions.FirstOrDefault(v => v.IsDefault && v.State != State.Removed)
                        ?? versions.FirstOrDefault(v => v.State != State.Removed);
                VersionManager.SaveDefaultVersion(defaultVersion?.VersionName ?? "");
            } catch (Exception exception) {
                Messages.Print(
                    exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace);
                exception.Log();
            }
            if (Notifications.NoQtVersion.IsOpen && VersionManager.GetVersions()?.Any() == true)
                Notifications.NoQtVersion.Close();
        }
        protected override void OnApply(PageApplyEventArgs e)
QtVsTools.Package/Options/QtVersionsTable.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -37,11 +37,12 @@
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using QtVsTools.Common;
namespace QtVsTools.Options
{
    using static EnumExt;
    using Common;
    using QtVsTools.Core;
    using static Common.EnumExt;
    public enum BuildHost
    {
@@ -52,9 +53,28 @@
    public partial class QtVersionsTable : UserControl
    {
        LazyFactory Lazy { get; } = new LazyFactory();
        public QtVersionsTable()
        {
            InitializeComponent();
        }
        [Flags] public enum Column
        {
            IsDefault = 0x10,
            VersionName = 0x20,
            Host = 0x40,
            Path = 0x80,
            Compiler = 0x100
        }
        [Flags] public enum State
        {
            Unknown = 0x00,
            Existing = 0x01,
            Removed = 0x02,
            Modified = 0x04
        }
        public class Field
@@ -62,57 +82,66 @@
            public string Value { get; set; }
            public Control Control { get; set; }
            public DataGridCell Cell { get; set; }
            public string ValidationError { get; set; }
            private string error;
            public string ValidationError {
                set {
                    UpdateUi = value != error;
                    error = value;
                }
                get { return error; }
            }
            public bool IsValid => string.IsNullOrEmpty(ValidationError);
            public ToolTip ToolTip
                => IsValid ? null : new ToolTip() { Content = ValidationError };
            public int SelectionStart { get; set; }
            public bool UpdateUi { get; private set; } = false;
        }
        public class Row
        {
            public enum FieldNames { IsDefault, VersionName, Host, Path, Compiler }
            static LazyFactory StaticLazy { get; } = new LazyFactory();
            LazyFactory Lazy { get; } = new LazyFactory();
            public Dictionary<FieldNames, Field> _Fields;
            public Dictionary<FieldNames, Field> Fields => _Fields
                ?? (_Fields = GetValues<FieldNames>()
                    .Select(field => new KeyValuePair<FieldNames, Field>(field, null))
            public Dictionary<Column, Field> Fields => Lazy.Get(() =>
                Fields, () => GetValues<Column>()
                    .Select(field => new KeyValuePair<Column, Field>(field, null))
                    .ToDictionary(keyValue => keyValue.Key, keyValue => keyValue.Value));
            public Field FieldDefault => Fields[FieldNames.IsDefault]
                ?? (Fields[FieldNames.IsDefault] = new Field());
            public Field FieldDefault => Fields[Column.IsDefault]
                ?? (Fields[Column.IsDefault] = new Field());
            public bool IsDefault
            {
                get => (FieldDefault.Value == true.ToString());
                set => FieldDefault.Value = value.ToString();
            }
            public Field FieldVersionName => Fields[FieldNames.VersionName]
                ?? (Fields[FieldNames.VersionName] = new Field());
            public Field FieldVersionName => Fields[Column.VersionName]
                ?? (Fields[Column.VersionName] = new Field());
            public string VersionName
            {
                get => FieldVersionName.Value;
                set => FieldVersionName.Value = value;
            }
            public string InitialVersionName { get; set; }
            public Field FieldHost => Fields[FieldNames.Host]
                ?? (Fields[FieldNames.Host] = new Field());
            public Field FieldHost => Fields[Column.Host]
                ?? (Fields[Column.Host] = new Field());
            public BuildHost Host
            {
                get => FieldHost.Value.Cast(defaultValue: BuildHost.Windows);
                set => FieldHost.Value = value.Cast<string>();
            }
            public Field FieldPath => Fields[FieldNames.Path]
                ?? (Fields[FieldNames.Path] = new Field());
            public Field FieldPath => Fields[Column.Path]
                ?? (Fields[Column.Path] = new Field());
            public string Path
            {
                get => FieldPath.Value;
                set => FieldPath.Value = value;
            }
            public Field FieldCompiler => Fields[FieldNames.Compiler]
                ?? (Fields[FieldNames.Compiler] = new Field());
            public Field FieldCompiler => Fields[Column.Compiler]
                ?? (Fields[Column.Compiler] = new Field());
            public string Compiler
            {
                get => FieldCompiler.Value;
@@ -124,7 +153,7 @@
            public bool DefaultEnabled => !IsDefault && !LastRow;
            public bool NameEnabled => !LastRow;
            public bool CompilerEnabled => (Host != BuildHost.Windows);
            public Visibility RowVisibility
            public Visibility RowContentVisibility
                => LastRow ? Visibility.Hidden : Visibility.Visible;
            public Visibility ButtonAddVisibility
                => LastRow ? Visibility.Visible : Visibility.Hidden;
@@ -135,17 +164,16 @@
            public FontWeight FontWeight
                => IsDefault ? FontWeights.Bold : FontWeights.Normal;
            public static ImageSource _ExplorerIcon;
            public static ImageSource ExplorerIcon => _ExplorerIcon
                ?? (_ExplorerIcon = GetExplorerIcon());
        }
            public static ImageSource ExplorerIcon => StaticLazy.Get(() =>
                ExplorerIcon, () => GetExplorerIcon());
        public bool IsValid { get; private set; }
            public State State { get; set; } = State.Unknown;
            public bool RowVisible => State != State.Removed;
        }
        Field FocusedField { get; set; }
        List<Row> _Rows;
        List<Row> Rows => _Rows ?? (_Rows = new List<Row>());
        List<Row> Rows => Lazy.Get(() => Rows, () => new List<Row>());
        public IEnumerable<Row> Versions => Rows.TakeWhile(item => !item.LastRow);
        public void UpdateVersions(IEnumerable<Row> versions)
@@ -154,15 +182,16 @@
            Rows.AddRange(versions);
            Rows.Add(new Row { LastRow = true });
            DataGrid.ItemsSource = Rows;
            IsValid = true;
            FocusedField = null;
            Validate(true);
            Rows.ForEach(item => item.State = State.Existing);
        }
        public IEnumerable<string> GetErrorMessages()
        {
            Validate(true);
            return Versions
                .Where(v => v.State != State.Removed)
                .SelectMany(v => v.Fields.Values.Select(f => f.ValidationError))
                .Where(s => !string.IsNullOrEmpty(s))
                .Distinct();
@@ -170,95 +199,73 @@
        void Validate(bool mustRefresh)
        {
            /////////////////////////
            // Automatic cell values
            foreach (var version in Versions) {
                if (version.Host != BuildHost.Windows && version.Compiler == "msvc") {
                    version.Compiler = "g++";
                    version.FieldCompiler.SelectionStart = version.Compiler.Length;
                    mustRefresh = true;
                } else if (version.Host == BuildHost.Windows && version.Compiler != "msvc") {
                    version.Compiler = "msvc";
                    version.FieldCompiler.SelectionStart = version.Compiler.Length;
                    mustRefresh = true;
                }
            }
            ////////////////////////
            // Validate cell values
            string previousValidation;
            bool wasValid = IsValid;
            IsValid = true;
            foreach (var version in Versions) {
                if (!version.State.HasFlag(State.Modified))
                    continue;
                //////////////////////
                // Default validation
                previousValidation = version.FieldDefault.ValidationError;
                version.FieldDefault.ValidationError = null;
                if (version.IsDefault && version.Host != BuildHost.Windows) {
                    version.FieldDefault.ValidationError = "Default version: host must be Windows";
                    IsValid = false;
                if (version.State.HasFlag((State)Column.IsDefault)) {
                    version.FieldDefault.ValidationError = null;
                    if (version.IsDefault && version.Host != BuildHost.Windows)
                        version.FieldDefault.ValidationError = "Default version: Host must be Windows";
                    mustRefresh |= version.FieldDefault.UpdateUi;
                }
                if (previousValidation != version.FieldDefault.ValidationError)
                    mustRefresh = true;
                ///////////////////
                // Name validation
                previousValidation = version.FieldVersionName.ValidationError;
                version.FieldVersionName.ValidationError = null;
                if (string.IsNullOrEmpty(version.VersionName)) {
                    version.FieldVersionName.ValidationError = "Name cannot be empty";
                    IsValid = false;
                } else if (Versions
                    .Where(otherVersion => otherVersion != version
                        && otherVersion.VersionName == version.VersionName)
                    .Any()) {
                    version.FieldVersionName.ValidationError = "Duplicate version names";
                    IsValid = false;
                if (version.State.HasFlag((State)Column.VersionName)) {
                    version.FieldVersionName.ValidationError = null;
                    if (string.IsNullOrEmpty(version.VersionName)) {
                        version.FieldVersionName.ValidationError = "Name cannot be empty";
                    } else if (Versions.Where(otherVersion => otherVersion != version
                        && otherVersion.VersionName == version.VersionName).Any()) {
                        version.FieldVersionName.ValidationError = "Duplicate version names";
                    }
                    mustRefresh |= version.FieldVersionName.UpdateUi;
                }
                if (previousValidation != version.FieldVersionName.ValidationError)
                    mustRefresh = true;
                ///////////////////
                // Host validation
                previousValidation = version.FieldHost.ValidationError;
                version.FieldHost.ValidationError = null;
                if (version.IsDefault && version.Host != BuildHost.Windows) {
                    version.FieldHost.ValidationError = "Default version: host must be Windows";
                    IsValid = false;
                if (version.State.HasFlag((State)Column.Host)) {
                    version.FieldHost.ValidationError = null;
                    if (version.IsDefault && version.Host != BuildHost.Windows)
                        version.FieldHost.ValidationError = "Default version: Host must be Windows";
                    mustRefresh |= version.FieldHost.UpdateUi;
                }
                if (previousValidation != version.FieldHost.ValidationError)
                    mustRefresh = true;
                ///////////////////
                // Path validation
                previousValidation = version.FieldPath.ValidationError;
                version.FieldPath.ValidationError = null;
                if (string.IsNullOrEmpty(version.Path)) {
                    version.FieldPath.ValidationError = "Path cannot be empty";
                    IsValid = false;
                } else if (version.Host == BuildHost.Windows && !Directory.Exists(version.Path)) {
                    version.FieldPath.ValidationError = "Path does not exist";
                    IsValid = false;
                if (version.State.HasFlag((State)Column.Path)) {
                    version.FieldPath.ValidationError = null;
                    if (string.IsNullOrEmpty(version.Path)) {
                        version.FieldPath.ValidationError = "Path cannot be empty";
                    } else if (version.Host == BuildHost.Windows) {
                        string path = NormalizePath(version.Path);
                        if (path == null) {
                            version.FieldPath.ValidationError = "Invalid path format";
                        } else {
                            if (!QMake.Exists(path))
                                version.FieldPath.ValidationError = "Cannot find qmake.exe";
                        }
                    }
                    mustRefresh |= version.FieldPath.UpdateUi;
                }
                if (previousValidation != version.FieldPath.ValidationError)
                    mustRefresh = true;
                ///////////////////////
                // Compiler validation
                previousValidation = version.FieldCompiler.ValidationError;
                version.FieldCompiler.ValidationError = null;
                if (string.IsNullOrEmpty(version.Compiler)) {
                    version.FieldCompiler.ValidationError = "Compiler cannot be empty";
                    IsValid = false;
                if (version.State.HasFlag((State)Column.Compiler)) {
                    version.FieldCompiler.ValidationError = null;
                    if (string.IsNullOrEmpty(version.Compiler))
                        version.FieldCompiler.ValidationError = "Compiler cannot be empty";
                    mustRefresh |= version.FieldCompiler.UpdateUi;
                }
                if (previousValidation != version.FieldCompiler.ValidationError)
                    mustRefresh = true;
            }
            //////////////////////////////////////
            // Refresh versions table if required
            mustRefresh |= (wasValid != IsValid);
            if (mustRefresh) {
                // Reset bindings
                foreach (var version in Versions) {
@@ -304,22 +311,20 @@
            if (sender is Control control && GetBinding(control) is Row version) {
                if (version.LastRow)
                    return;
                Row.FieldNames field;
                if (string.IsNullOrEmpty(control.Name) || !control.Name.TryCast(out field))
                if (string.IsNullOrEmpty(control.Name) || !control.Name.TryCast(out Column column))
                    return;
                var fieldBinding = version.Fields[field];
                fieldBinding.Control = control;
                fieldBinding.Cell = FindContainingCell(control);
                if (fieldBinding.Cell != null) {
                    fieldBinding.Cell.Background =
                        fieldBinding.IsValid ? Brushes.Transparent : InvalidCellBackground;
                var field = version.Fields[column];
                field.Control = control;
                field.Cell = FindContainingCell(control);
                if (field.Cell != null) {
                    field.Cell.Background =
                        field.IsValid ? Brushes.Transparent : InvalidCellBackground;
                }
                if (fieldBinding == FocusedField)
                if (field == FocusedField)
                    control.Focus();
                if (control is TextBox textBox && fieldBinding.SelectionStart >= 0)
                    textBox.Select(fieldBinding.SelectionStart, 0);
                if (control is TextBox textBox && field.SelectionStart >= 0)
                    textBox.Select(field.SelectionStart, 0);
            }
        }
@@ -339,24 +344,24 @@
        void Control_GotFocus(object sender, RoutedEventArgs e)
        {
            if (sender is Control control && GetBinding(control) is Row version) {
                Row.FieldNames field;
                if (string.IsNullOrEmpty(control.Name) || !control.Name.TryCast(out field))
                if (string.IsNullOrEmpty(control.Name) || !control.Name.TryCast(out Column column))
                    return;
                var fieldBinding = version.Fields[field];
                if (fieldBinding.Control != control)
                var field = version.Fields[column];
                if (field.Control != control)
                    return;
                FocusedField = fieldBinding;
                FocusedField = field;
            }
        }
        void Control_LostFocus(object sender, RoutedEventArgs e)
        {
            if (sender is Control control && GetBinding(control) is Row version) {
                Row.FieldNames field;
                if (string.IsNullOrEmpty(control.Name) || !control.Name.TryCast(out field))
                if (string.IsNullOrEmpty(control.Name) || !control.Name.TryCast(out Column column))
                    return;
                var fieldBinding = version.Fields[field];
                if (fieldBinding != FocusedField || fieldBinding.Control != control)
                var field = version.Fields[column];
                if (field != FocusedField || field.Control != control)
                    return;
                FocusedField = null;
            }
@@ -365,29 +370,32 @@
        void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
        {
            if (sender is TextBox textBox && GetBinding(textBox) is Row version) {
                Row.FieldNames field;
                if (string.IsNullOrEmpty(textBox.Name) || !textBox.Name.TryCast(out field))
                if (string.IsNullOrEmpty(textBox.Name) || !textBox.Name.TryCast(out Column column))
                    return;
                var fieldBinding = version.Fields[field];
                if (fieldBinding.Control != textBox)
                var field = version.Fields[column];
                if (field.Control != textBox)
                    return;
                fieldBinding.SelectionStart = textBox.SelectionStart;
                field.SelectionStart = textBox.SelectionStart;
            }
        }
        void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (sender is TextBox textBox && GetBinding(textBox) is Row version) {
                Row.FieldNames field;
                if (string.IsNullOrEmpty(textBox.Name) || !textBox.Name.TryCast(out field))
                if (string.IsNullOrEmpty(textBox.Name) || !textBox.Name.TryCast(out Column column))
                    return;
                var fieldBinding = version.Fields[field];
                if (fieldBinding == null
                    || fieldBinding.Control != textBox
                    || fieldBinding.Value == textBox.Text)
                var field = version.Fields[column];
                if (field == null
                    || field.Control != textBox
                    || field.Value == textBox.Text)
                    return;
                fieldBinding.SelectionStart = textBox.SelectionStart;
                fieldBinding.Value = textBox.Text;
                field.SelectionStart = textBox.SelectionStart;
                field.Value = textBox.Text;
                version.State |= State.Modified | (State)column;
                Validate(false);
            }
        }
@@ -397,28 +405,49 @@
            if (sender is ComboBox comboBox && GetBinding(comboBox) is Row version) {
                if (!comboBox.IsEnabled || comboBox.SelectedIndex < 0)
                    return;
                if (string.IsNullOrEmpty(comboBox.Name) || !comboBox.Name.TryCast(out Column column))
                    return;
                string comboBoxValue = comboBox.Items[comboBox.SelectedIndex] as string;
                string controlName = comboBox.Name;
                Row.FieldNames field;
                if (string.IsNullOrEmpty(controlName) || !controlName.TryCast(out field))
                var field = version.Fields[column];
                if (field == null
                    || field.Control != comboBox
                    || field.Value == comboBoxValue)
                    return;
                var fieldBinding = version.Fields[field];
                if (fieldBinding == null
                    || fieldBinding.Control != comboBox
                    || fieldBinding.Value == comboBoxValue)
                    return;
                fieldBinding.Value = comboBoxValue;
                Validate(false);
                field.Value = comboBoxValue;
                version.State |= State.Modified | (State)Column.Host;
                bool mustRefresh = false;
                if (version.Host != BuildHost.Windows && version.Compiler == "msvc") {
                    version.Compiler = "g++";
                    version.FieldCompiler.SelectionStart = version.Compiler.Length;
                    version.State |= (State)Column.Compiler;
                    mustRefresh = true;
                } else if (version.Host == BuildHost.Windows && version.Compiler != "msvc") {
                    version.Compiler = "msvc";
                    version.FieldCompiler.SelectionStart = version.Compiler.Length;
                    version.State |= (State)Column.Compiler;
                    mustRefresh = true;
                }
                Validate(mustRefresh);
            }
        }
        static void SetDefaultState(ref Row version, bool value)
        {
            version.IsDefault = value;
            version.State |= State.Modified | (State)Column.IsDefault;
        }
        void Default_Click(object sender, RoutedEventArgs e)
        {
            if (sender is CheckBox checkBox && GetBinding(checkBox) is Row version) {
                var defaultVersion = Rows.Where(row => row.IsDefault).FirstOrDefault();
                var defaultVersion = Rows.FirstOrDefault(row => row.IsDefault);
                if (defaultVersion != null)
                    defaultVersion.IsDefault = false;
                version.IsDefault = true;
                    SetDefaultState(ref defaultVersion, false);
                SetDefaultState(ref version, true);
                Validate(true);
            }
        }
@@ -427,12 +456,16 @@
        {
            var version = new Row()
            {
                IsDefault = !Versions.Any(),
                IsDefault = !Versions.Any(x => x.State != State.Removed),
                Host = BuildHost.Windows,
                Path = "",
                Compiler = "msvc",
                LastRow = false
                LastRow = false,
                State = State.Modified | (State)Column.VersionName | (State)Column.Host
                                       | (State)Column.Path | (State)Column.Compiler
            };
            if (version.IsDefault)
                version.State |= (State)Column.IsDefault;
            Rows.Insert(Rows.Count - 1, version);
            FocusedField = version.FieldVersionName;
            Validate(true);
@@ -441,10 +474,26 @@
        void Remove_Click(object sender, RoutedEventArgs e)
        {
            if (sender is Button button && GetBinding(button) is Row version) {
                Rows.Remove(version);
                if (version.IsDefault && Versions.Any())
                    Versions.First().IsDefault = true;
                version.State = State.Removed;
                if (version.IsDefault) {
                    var first = Versions.FirstOrDefault(x => x.State != State.Removed);
                    if (first != null)
                        SetDefaultState(ref first, true);
                }
                Validate(true);
            }
        }
        static string NormalizePath(string path)
        {
            if (string.IsNullOrEmpty(path))
                return path;
            try {
                return Path.GetFullPath(new Uri(path).LocalPath)
                    .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
                    .ToUpperInvariant();
            } catch (UriFormatException) {
                return null;
            }
        }
@@ -462,6 +511,7 @@
                if (openFileDialog.ShowDialog() == true) {
                    var qmakePath = openFileDialog.FileName;
                    var qmakeDir = Path.GetDirectoryName(qmakePath);
                    var previousPath = NormalizePath(version.Path);
                    if (Path.GetFileName(qmakeDir)
                        .Equals("bin", StringComparison.InvariantCultureIgnoreCase)) {
                        qmakeDir = Path.GetDirectoryName(qmakeDir);
@@ -469,12 +519,18 @@
                    } else {
                        version.Path = qmakePath;
                    }
                    if (previousPath != NormalizePath(version.Path))
                        version.State |= State.Modified | (State)Column.Path;
                    if (string.IsNullOrEmpty(version.VersionName)) {
                        version.VersionName = string.Format("{0}_{1}",
                            Path.GetFileName(Path.GetDirectoryName(qmakeDir)),
                            Path.GetFileName(qmakeDir))
                            .Replace(" ", "_");
                        version.State |= State.Modified | (State)Column.VersionName;
                    }
                    Validate(true);
                }
            }
@@ -503,9 +559,9 @@
        static object GetBinding(FrameworkElement control)
        {
            if (control == null
            || control.BindingGroup == null
            || control.BindingGroup.Items == null
            || control.BindingGroup.Items.Count == 0) {
                || control.BindingGroup == null
                || control.BindingGroup.Items == null
                || control.BindingGroup.Items.Count == 0) {
                return null;
            }
            return control.BindingGroup.Items[0];
@@ -515,7 +571,7 @@
        {
            while (control != null) {
                if (control is ContentPresenter contentPresenter
                && contentPresenter.Parent is DataGridCell cell) {
                    && contentPresenter.Parent is DataGridCell cell) {
                    return cell;
                }
                control = VisualTreeHelper.GetParent(control);
QtVsTools.Package/Options/QtVersionsTable.xaml
@@ -1,7 +1,7 @@
<!--
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -32,7 +32,6 @@
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:QtVsTools.Options"
             mc:Ignorable="d"
             d:DesignHeight="450"
             d:DesignWidth="800">
@@ -58,7 +57,13 @@
                       Color="Transparent" />
      <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}"
                       Color="Transparent" />
      <BooleanToVisibilityConverter x:Key="b2v" />
    </DataGrid.Resources>
    <DataGrid.RowStyle>
      <Style TargetType="{x:Type DataGridRow}">
        <Setter Property="Visibility" Value="{Binding RowVisible, Converter={StaticResource b2v}}"/>
      </Style>
    </DataGrid.RowStyle>
    <DataGrid.CellStyle>
      <Style TargetType="DataGridCell">
        <Setter Property="BorderThickness"
@@ -76,8 +81,7 @@
            <CheckBox x:Name="IsDefault"
                      IsChecked="{Binding IsDefault}"
                      Focusable="{Binding DefaultEnabled}"
                      IsHitTestVisible="{Binding DefaultEnabled}"
                      Visibility="{Binding RowVisibility}"
                      Visibility="{Binding RowContentVisibility}"
                      BorderThickness="1"
                      Background="Transparent"
                      VerticalAlignment="Center"
@@ -94,7 +98,7 @@
            <!--//// Name ////-->
            <Grid>
              <Button Cursor="Hand"
                      Visibility="{Binding RowVisibility}"
                      Visibility="{Binding RowContentVisibility}"
                      HorizontalAlignment="Left"
                      VerticalAlignment="Center"
                      Click="Remove_Click">
@@ -153,7 +157,7 @@
              </Button>
              <TextBox x:Name="VersionName"
                       Text="{Binding VersionName}"
                       Visibility="{Binding RowVisibility}"
                       Visibility="{Binding RowContentVisibility}"
                       IsEnabled="{Binding NameEnabled}"
                       FontWeight="{Binding FontWeight}"
                       Margin="20,4,2,4"
@@ -166,7 +170,7 @@
                       LostFocus="Control_LostFocus"
                       TextChanged="TextBox_TextChanged"
                       SelectionChanged="TextBox_SelectionChanged"
                       ToolTip="{Binding FieldName.ToolTip}">
                       ToolTip="{Binding FieldVersionName.ToolTip}">
                <TextBox.Resources>
                  <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
                                   Color="LimeGreen" />
@@ -181,7 +185,7 @@
          <DataTemplate>
            <!--//// Host ////-->
            <ComboBox x:Name="Host"
                      Visibility="{Binding RowVisibility}"
                      Visibility="{Binding RowContentVisibility}"
                      IsEditable="True"
                      IsReadOnly="True"
                      BorderThickness="0"
@@ -233,7 +237,7 @@
              </Button>
              <TextBox x:Name="Path"
                       Text="{Binding Path}"
                       Visibility="{Binding RowVisibility}"
                       Visibility="{Binding RowContentVisibility}"
                       BorderThickness="0"
                       Background="Transparent"
                       Margin="{Binding PathMargin}"
@@ -260,7 +264,7 @@
            <!--//// Compiler ////-->
            <TextBox x:Name="Compiler"
                     Text="{Binding Compiler}"
                     Visibility="{Binding RowVisibility}"
                     Visibility="{Binding RowContentVisibility}"
                     IsEnabled="{Binding CompilerEnabled}"
                     BorderThickness="0"
                     Background="Transparent"
QtVsTools.Package/Package/DteEventsHandler.cs
@@ -26,44 +26,45 @@
**
****************************************************************************/
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.VCProjectEngine;
using QtVsTools.Core;
using QtVsTools.Core.QtMsBuild;
using QtVsTools.QtMsBuild;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
using EnvDTE;
using EnvDTE80;
namespace QtVsTools
{
    using Core;
    using QtMsBuild;
    class DteEventsHandler
    {
        private DTE dte;
        private SolutionEvents solutionEvents;
        private BuildEvents buildEvents;
        private DocumentEvents documentEvents;
        private ProjectItemsEvents projectItemsEvents;
        private readonly DTE dte;
        private readonly SolutionEvents solutionEvents;
        private readonly BuildEvents buildEvents;
        private readonly DocumentEvents documentEvents;
        private readonly ProjectItemsEvents projectItemsEvents;
        private vsBuildAction currentBuildAction = vsBuildAction.vsBuildActionBuild;
        private VCProjectEngineEvents vcProjectEngineEvents;
        private CommandEvents debugStartEvents;
        private CommandEvents debugStartWithoutDebuggingEvents;
        private CommandEvents f1HelpEvents;
        private int dispId_VCFileConfiguration_ExcludedFromBuild;
        private int dispId_VCCLCompilerTool_UsePrecompiledHeader;
        private int dispId_VCCLCompilerTool_PrecompiledHeaderThrough;
        private int dispId_VCCLCompilerTool_PreprocessorDefinitions;
        private int dispId_VCCLCompilerTool_AdditionalIncludeDirectories;
        private readonly CommandEvents debugStartEvents;
        private readonly CommandEvents debugStartWithoutDebuggingEvents;
        private readonly CommandEvents f1HelpEvents;
        private WindowEvents windowEvents;
        private readonly int dispId_VCFileConfiguration_ExcludedFromBuild;
        private readonly int dispId_VCCLCompilerTool_UsePrecompiledHeader;
        private readonly int dispId_VCCLCompilerTool_PrecompiledHeaderThrough;
        private readonly int dispId_VCCLCompilerTool_PreprocessorDefinitions;
        private readonly int dispId_VCCLCompilerTool_AdditionalIncludeDirectories;
        public DteEventsHandler(DTE _dte)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            dte = _dte;
            var events = dte.Events as Events2;
@@ -85,6 +86,9 @@
            solutionEvents.Opened += SolutionEvents_Opened;
            solutionEvents.AfterClosing += SolutionEvents_AfterClosing;
            windowEvents = events.WindowEvents;
            windowEvents.WindowActivated += WindowEvents_WindowActivated;
            var debugCommandsGUID = "{5EFC7975-14BC-11CF-9B2B-00AA00573819}";
            debugStartEvents = events.get_CommandEvents(debugCommandsGUID, 295);
            debugStartEvents.BeforeExecute += debugStartEvents_BeforeExecute;
@@ -105,15 +109,33 @@
            InitializeVCProjects();
        }
        private void WindowEvents_WindowActivated(Window gotFocus, Window lostFocus)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (dte.MainWindow?.Visible == true) {
                windowEvents.WindowActivated -= WindowEvents_WindowActivated;
                windowEvents = null;
                QtVsToolsPackage.Instance.VsMainWindowActivated();
            }
        }
        private void F1HelpEvents_BeforeExecute(
            string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault)
        {
            if (QtVsToolsPackage.Instance.Options.TryQtHelpOnF1Pressed && QtHelp.QueryEditorContextHelp())
            ThreadHelper.ThrowIfNotOnUIThread();
            if (QtVsToolsPackage.Instance.Options.TryQtHelpOnF1Pressed) {
                if (!QtHelp.ShowEditorContextHelp()) {
                    Messages.Print("No help match was found. You can still try to search online at "
                        + "https://doc.qt.io" + ".", false, true);
                }
                CancelDefault = true;
            }
        }
        void debugStartEvents_BeforeExecute(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var debugger = dte.Debugger;
            if (debugger != null && debugger.CurrentMode != dbgDebugMode.dbgDesignMode)
                return;
@@ -147,6 +169,8 @@
        public void Disconnect()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (buildEvents != null) {
                buildEvents.OnBuildBegin -= buildEvents_OnBuildBegin;
                buildEvents.OnBuildProjConfigBegin -= OnBuildProjConfigBegin;
@@ -174,12 +198,19 @@
            if (debugStartWithoutDebuggingEvents != null)
                debugStartWithoutDebuggingEvents.BeforeExecute -= debugStartWithoutDebuggingEvents_BeforeExecute;
            if (vcProjectEngineEvents != null)
            if (vcProjectEngineEvents != null) {
                vcProjectEngineEvents.ItemPropertyChange -= OnVCProjectEngineItemPropertyChange;
                vcProjectEngineEvents.ItemPropertyChange2 -= OnVCProjectEngineItemPropertyChange2;
            }
            if (windowEvents != null)
                windowEvents.WindowActivated -= WindowEvents_WindowActivated;
        }
        public void OnBuildProjConfigBegin(string projectName, string projectConfig, string platform, string solutionConfig)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!QtVsToolsPackage.Instance.LegacyOptions.PreBuildSetup)
                return;
@@ -195,7 +226,7 @@
                    break;
                }
            }
            if (project == null || !HelperFunctions.IsQtProject(project))
            if (project == null || !HelperFunctions.IsVsToolsProject(project))
                return;
            if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings)
@@ -229,9 +260,11 @@
        public void DocumentSaved(Document document)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var qtPro = QtProject.Create(document.ProjectItem.ContainingProject);
            if (!HelperFunctions.IsQtProject(qtPro.VCProject))
            if (!HelperFunctions.IsVsToolsProject(qtPro.VCProject))
                return;
            var file = (VCFile)((IVCCollection)qtPro.VCProject.Files).Item(document.FullName);
@@ -323,10 +356,13 @@
        public void ProjectItemsEvents_ItemAdded(ProjectItem projectItem)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var project = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte);
            var qtPro = QtProject.Create(project);
            if (!HelperFunctions.IsQtProject(project))
            if (!HelperFunctions.IsVsToolsProject(project))
                return;
            var vcFile = GetVCFileFromProject(projectItem.Name, qtPro.VCProject);
            if (vcFile == null)
                return;
@@ -373,12 +409,15 @@
                        HelperFunctions.EnsureCustomBuildToolAvailable(projectItem);
                    qtPro.UpdateRccStep(vcFile, null);
                } else if (HelperFunctions.IsTranslationFile(vcFile.Name)) {
                    Translation.RunlUpdate(vcFile);
                }
            } catch { }
        }
        void ProjectItemsEvents_ItemRemoved(ProjectItem ProjectItem)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte);
            if (pro == null)
                return;
@@ -389,6 +428,8 @@
        void ProjectItemsEvents_ItemRenamed(ProjectItem ProjectItem, string OldName)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (OldName == null)
                return;
            var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte);
@@ -397,12 +438,15 @@
            var qtPro = QtProject.Create(pro);
            qtPro.RemoveGeneratedFiles(OldName);
            ProjectItemsEvents_ItemAdded(ProjectItem);
        }
        void SolutionEvents_ProjectAdded(Project project)
        {
            if (HelperFunctions.IsQMakeProject(project)) {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (HelperFunctions.IsQtProject(project)) {
                InitializeVCProject(project);
                QtProjectTracker.Add(project);
                var vcpro = project.Object as VCProject;
@@ -446,9 +490,11 @@
        public void SolutionEvents_Opened()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            QtProjectTracker.SolutionPath = QtVsToolsPackage.Instance.Dte.Solution.FullName;
            foreach (var p in HelperFunctions.ProjectsInSolution(QtVsToolsPackage.Instance.Dte)) {
                if (HelperFunctions.IsQtProject(p)) {
                if (HelperFunctions.IsVsToolsProject(p)) {
                    InitializeVCProject(p);
                    QtProjectTracker.Add(p);
                }
@@ -464,14 +510,18 @@
        void InitializeVCProjects()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            foreach (var project in HelperFunctions.ProjectsInSolution(dte)) {
                if (project != null && HelperFunctions.IsQtProject(project))
                if (project != null && HelperFunctions.IsVsToolsProject(project))
                    InitializeVCProject(project);
            }
        }
        void InitializeVCProject(Project p)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (vcProjectEngineEvents != null)
                return;
@@ -480,12 +530,12 @@
                return;
            // Retrieves the VCProjectEngine from the given project and registers the handlers for VCProjectEngineEvents.
            var prjEngine = vcPrj.VCProjectEngine as VCProjectEngine;
            if (prjEngine != null) {
            if (vcPrj.VCProjectEngine is VCProjectEngine prjEngine) {
                vcProjectEngineEvents = prjEngine.Events as VCProjectEngineEvents;
                if (vcProjectEngineEvents != null) {
                    try {
                        vcProjectEngineEvents.ItemPropertyChange += OnVCProjectEngineItemPropertyChange;
                        vcProjectEngineEvents.ItemPropertyChange2 += OnVCProjectEngineItemPropertyChange2;
                    } catch {
                        Messages.DisplayErrorMessage("VCProjectEngine events could not be registered.");
                    }
@@ -495,7 +545,6 @@
        private void OnVCProjectEngineItemPropertyChange(object item, object tool, int dispid)
        {
            //System.Diagnostics.Debug.WriteLine("OnVCProjectEngineItemPropertyChange " + dispid.ToString());
            var vcFileCfg = item as VCFileConfiguration;
            if (vcFileCfg == null) {
                // A global or project specific property has changed.
@@ -506,7 +555,7 @@
                var vcPrj = vcCfg.project as VCProject;
                if (vcPrj == null)
                    return;
                if (!HelperFunctions.IsQtProject(vcPrj))
                if (!HelperFunctions.IsVsToolsProject(vcPrj))
                    return;
                // Ignore property events when using shared compiler properties
                if (QtProject.GetFormatVersion(vcPrj) >= Resources.qtMinFormatVersion_ClProperties)
@@ -538,7 +587,7 @@
                var vcPrj = vcFile.project as VCProject;
                if (vcPrj == null)
                    return;
                if (!HelperFunctions.IsQtProject(vcPrj))
                if (!HelperFunctions.IsVsToolsProject(vcPrj))
                    return;
                // Ignore property events when using shared compiler properties
                if (QtProject.GetFormatVersion(vcPrj) >= Resources.qtMinFormatVersion_ClProperties)
@@ -557,10 +606,27 @@
            }
        }
        private void OnVCProjectEngineItemPropertyChange2(
            object item,
            string propertySheet,
            string itemType,
            string propertyName)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!propertyName.StartsWith("Qt") || propertyName == "QtLastBackgroundBuild")
                return;
            if (item is VCConfiguration vcConfig
                && vcConfig.project is VCProject vcProject
                && vcProject.Object is Project project) {
                QtProjectIntellisense.Refresh(
                    QtProjectTracker.Get(project, project.FullName).Project, vcConfig.Name);
            }
        }
        private static VCFile GetVCFileFromProject(string absFileName, VCProject project)
        {
            foreach (VCFile f in (IVCCollection)project.Files) {
                if (f.Name.ToLower() == absFileName.ToLower())
                if (f.Name.Equals(absFileName, StringComparison.OrdinalIgnoreCase))
                    return f;
            }
            return null;
@@ -574,8 +640,7 @@
            var pi = type.GetProperty(propertyName);
            if (pi != null) {
                foreach (Attribute attribute in pi.GetCustomAttributes(true)) {
                    var dispIdAttribute = attribute as DispIdAttribute;
                    if (dispIdAttribute != null)
                    if (attribute is DispIdAttribute dispIdAttribute)
                        return dispIdAttribute.Value;
                }
            }
QtVsTools.Package/Package/ExtLoader.cs
@@ -26,22 +26,27 @@
**
****************************************************************************/
using Microsoft.VisualStudio.VCProjectEngine;
using QtVsTools.Core;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
namespace QtVsTools
{
    using Core;
    public static class ExtLoader
    {
        public static void ImportProFile()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var vm = QtVersionManager.The();
            var qtVersion = vm.GetDefaultVersion();
            var qtDir = vm.GetInstallPath(qtVersion);
            if (qtDir == null) {
                Messages.DisplayErrorMessage(SR.GetString("CannotFindQMake"));
                return;
@@ -59,11 +64,13 @@
        public static void ImportPriFile(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (project == null)
                return;
            VCProject vcproj;
            if (!HelperFunctions.IsQtProject(project))
            if (!HelperFunctions.IsVsToolsProject(project))
                return;
            vcproj = project.Object as VCProject;
@@ -87,10 +94,12 @@
        public static void ImportPriFile(EnvDTE.Project project, string fileName)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (project == null)
                return;
            if (!HelperFunctions.IsQtProject(project))
            if (!HelperFunctions.IsVsToolsProject(project))
                return;
            var vcproj = project.Object as VCProject;
@@ -134,6 +143,8 @@
        private static List<string> ResolveFilesFromQMake(string[] files, EnvDTE.Project project, string path)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var lst = new List<string>();
            foreach (var file in files) {
                var s = ResolveEnvironmentVariables(file, project);
QtVsTools.Package/Package/Notifications.cs
New file
@@ -0,0 +1,111 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools
{
    using Common;
    using Microsoft.VisualStudio.Imaging.Interop;
    using VisualStudio;
    public static class Notifications
    {
        static LazyFactory StaticLazy { get; } = new LazyFactory();
        public static NoQtVersion NoQtVersion
            => StaticLazy.Get(() => NoQtVersion, () => new NoQtVersion());
        public static NotifyInstall NotifyInstall
            => StaticLazy.Get(() => NotifyInstall, () => new NotifyInstall());
    }
    public class NoQtVersion : InfoBarMessage
    {
        protected override ImageMoniker Icon => KnownMonikers.StatusWarning;
        protected override TextSpan[] Text => new TextSpan[]
        {
            new TextSpan { Bold = true, Text = "Qt Visual Studio Tools" },
            new TextSpacer(2),
            "\u2014", // Em dash
            new TextSpacer(2),
            "You must select a Qt version to use for development."
        };
        protected override Hyperlink[] Hyperlinks => new Hyperlink[]
        {
            new Hyperlink
            {
                Text = "Select Qt version...",
                CloseInfoBar = false,
                OnClicked = () =>
                    QtVsToolsPackage.Instance.ShowOptionPage(typeof(Options.QtVersionsPage))
            }
        };
    }
    public class NotifyInstall : InfoBarMessage
    {
        protected override ImageMoniker Icon => KnownMonikers.StatusInformation;
        protected override TextSpan[] Text => new TextSpan[]
        {
            new TextSpan { Bold = true, Text = "Qt Visual Studio Tools" },
            new TextSpacer(2),
            "\u2014", // Em dash
            new TextSpacer(2),
            $"Version {Version.USER_VERSION} was recently installed."
        };
        protected override Hyperlink[] Hyperlinks => new Hyperlink[]
        {
            new Hyperlink
            {
                Text = "Release Notes",
                CloseInfoBar = false,
                OnClicked= () =>
                {
                    VsShellUtilities.OpenSystemBrowser(
                        "https://code.qt.io/cgit/qt-labs/vstools.git/tree/Changelog");
                }
            },
            new Hyperlink
            {
                Text = "Don't show again",
                CloseInfoBar = true,
                OnClicked = () =>
                {
                    QtVsToolsPackage.Instance.Options.NotifyInstalled = false;
                    QtVsToolsPackage.Instance.Options.SaveSettingsToStorage();
                }
            }
        };
    }
}
QtVsTools.Package/Package/QMakeWrapper.cs
@@ -37,7 +37,7 @@
        public string QtDir { get; set; }
        public bool IsFlat { get; private set; }
        public bool IsValid { get; private set; }
        private bool IsValid { get; set; }
        public string[] SourceFiles { get; private set; }
        public string[] HeaderFiles { get; private set; }
QtVsTools.Package/Package/QtHelp.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,13 +26,6 @@
**
****************************************************************************/
using EnvDTE;
using Microsoft.VisualStudio.Settings;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.Settings;
using QtVsTools.Core;
using QtVsTools.VisualStudio;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
@@ -40,37 +33,36 @@
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Task = System.Threading.Tasks.Task;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
namespace QtVsTools
{
    using Core;
    using VisualStudio;
    public class QtHelp
    {
        public enum SourcePreference { Online, Offline }
        public static QtHelp Instance
        private static QtHelp Instance
        {
            get;
            private set;
            set;
        }
        public static void Initialize(Package package)
        public static void Initialize()
        {
            Instance = new QtHelp(package);
            Instance = new QtHelp();
        }
        const int F1QtHelpId = 0x0502;
        readonly Package package;
        public static readonly Guid MainMenuGuid = new Guid("58f83fff-d39d-4c66-810b-2702e1f04e73");
        private static readonly Guid MainMenuGuid = new Guid("58f83fff-d39d-4c66-810b-2702e1f04e73");
        QtHelp(Package pkg)
        private QtHelp()
        {
            if (pkg == null)
                throw new ArgumentNullException("package");
            package = pkg;
            var commandService = VsServiceProvider
                .GetService<IMenuCommandService, OleMenuCommandService>();
            if (commandService == null)
@@ -79,12 +71,6 @@
            var menuCommandID = new CommandID(MainMenuGuid, F1QtHelpId);
            commandService.AddCommand(new MenuCommand(F1QtHelpEventHandler, menuCommandID));
        }
        IServiceProvider ServiceProvider
        {
            get { return package; }
        }
        static bool IsSuperfluousCharacter(string text)
        {
            switch (text) {
@@ -131,11 +117,17 @@
        void F1QtHelpEventHandler(object sender, EventArgs args)
        {
            QueryEditorContextHelp(true);
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!ShowEditorContextHelp()) {
                Messages.Print("No help match was found. You can still try to search online at "
                    + "https://doc.qt.io" + ".", false, true);
            }
        }
        public static bool QueryEditorContextHelp(bool defaultTryOnline = false)
        public static bool ShowEditorContextHelp()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            try {
                var dte = VsServiceProvider.GetService<SDTE, DTE>();
                var objTextDocument = dte?.ActiveDocument?.Object() as TextDocument;
@@ -173,7 +165,7 @@
                var project = HelperFunctions.GetSelectedQtProject(dte);
                if (project == null) {
                    project = HelperFunctions.GetSelectedProject(dte);
                    if (project != null && HelperFunctions.IsQMakeProject(project)) {
                    if (project != null && HelperFunctions.IsQtProject(project)) {
                        var qmakeQtDir = HelperFunctions.GetQtDirFromQMakeProject(project);
                        qtVersion = QtVersionManager.The().GetQtVersionFromInstallDir(qmakeQtDir);
                    }
@@ -188,7 +180,7 @@
                var qchFiles = Directory.GetFiles(docPath, "*?.qch");
                if (qchFiles.Length == 0)
                    return false;
                    return TryShowGenericSearchResultsOnline(keyword, info.qtMajor);
                var offline = QtVsToolsPackage.Instance.Options.HelpPreference == SourcePreference.Offline;
@@ -207,8 +199,9 @@
                    using (var connection = new SQLiteConnection(builder.ToString())) {
                        connection.Open();
                        using (var command = new SQLiteCommand(linksForKeyword, connection)) {
                            using (var reader =
                                Task.Run(async () => await command.ExecuteReaderAsync()).Result) {
                            var reader = QtVsToolsPackage.Instance.JoinableTaskFactory
                                .Run(async () => await command.ExecuteReaderAsync());
                            using (reader) {
                                while (reader.Read()) {
                                    var title = GetString(reader, 0);
                                    if (string.IsNullOrWhiteSpace(title))
@@ -233,15 +226,7 @@
                var uri = string.Empty;
                switch (links.Values.Count) {
                case 0:
                    if (!offline && defaultTryOnline) {
                        uri = new UriBuilder($"https://doc.qt.io/qt-{info.qtMajor}/search-results.html")
                        {
                            Query = "q=" + keyword
                        }.ToString();
                    } else {
                        return false;
                    }
                    break;
                    return TryShowGenericSearchResultsOnline(keyword, info.qtMajor);
                case 1:
                    uri = links.First().Value;
                    break;
@@ -254,34 +239,40 @@
                    };
                    if (!dialog.ShowModal().GetValueOrDefault())
                        return false;
                    uri = dialog.Link
                        .Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
                    uri = dialog.Link;
                    break;
                }
                if (string.IsNullOrEmpty(uri)) { // offline mode without a single search hit
                    VsShellUtilities.ShowMessageBox(Instance.ServiceProvider,
                        "Your search - " + keyword + " - did not match any documents.",
                uri = HelperFunctions.FromNativeSeparators(uri);
                var helpUri = new Uri(uri);
                if (helpUri.IsFile && !File.Exists(helpUri.LocalPath)) {
                    VsShellUtilities.ShowMessageBox(QtVsToolsPackage.Instance,
                        "Your search - " + keyword + " - did match a document, but it could "
                        + "not be found on disk. To use the online help, select: "
                        + "Tools | Options | Qt | Preferred source | Online",
                        string.Empty, OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK,
                        OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
                } else {
                    var helpUri = new Uri(uri.Replace('\\', '/'));
                    if (helpUri.IsFile && !File.Exists(helpUri.LocalPath)) {
                        VsShellUtilities.ShowMessageBox(Instance.ServiceProvider,
                            "Your search - " + keyword + " - did match a document, but it could "
                            + "not be found on disk. To use the online help, select: "
                            + "Help | Set Qt Help Preference | Use Online Documentation",
                            string.Empty, OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK,
                            OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
                    } else {
                        VsShellUtilities.OpenSystemBrowser(HelperFunctions.ChangePathFormat(uri));
                    }
                    VsShellUtilities.OpenSystemBrowser(uri);
                }
            } catch (Exception e) {
                Messages.Print(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
            } catch (Exception exception) {
                exception.Log();
            }
            return true;
        }
        private static bool TryShowGenericSearchResultsOnline(string keyword, uint version)
        {
            if (QtVsToolsPackage.Instance.Options.HelpPreference != SourcePreference.Online)
                return false;
            VsShellUtilities.OpenSystemBrowser(HelperFunctions.FromNativeSeparators(
                new UriBuilder($"https://doc.qt.io/qt-{version}/search-results.html")
                {
                    Query = "q=" + keyword
                }.ToString())
            );
            return true;
        }
    }
}
QtVsTools.Package/Package/QtHelpLinkChooser.xaml
@@ -1,7 +1,7 @@
<!--
    *****************************************************************************
    **
    ** Copyright (C) 2016 The Qt Company Ltd.
    ** Copyright (C) 2022 The Qt Company Ltd.
    ** Contact: https://www.qt.io/licensing/
    **
    ** This file is part of the Qt VS Tools.
@@ -28,33 +28,31 @@
    *****************************************************************************
-->
<local:VsToolsDialogWindow x:Class="QtVsTools.QtHelpLinkChooser"
                           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                           xmlns:local="clr-namespace:QtVsTools"
                           Width="400"
                           Height="250"
                           MinWidth="400"
                           MinHeight="250"
                           mc:Ignorable="d"
                           Title="Choose Topic"
                           ShowInTaskbar="False"
                           HasHelpButton="False"
                           HasMinimizeButton="False"
                           ResizeMode="CanResizeWithGrip"
                           WindowStartupLocation="CenterOwner">
    <local:VsToolsDialogWindow.Resources>
        <BooleanToVisibilityConverter x:Key="b2v" />
<vsui:DialogWindow x:Class="QtVsTools.QtHelpLinkChooser"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vsui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
        Width="400"
        Height="250"
        MinWidth="400"
        MinHeight="250"
        mc:Ignorable="d"
        Title="Choose Topic"
        ShowInTaskbar="False"
        HasHelpButton="False"
        HasMinimizeButton="False"
        ResizeMode="CanResizeWithGrip"
        WindowStartupLocation="CenterOwner">
    <vsui:DialogWindow.Resources>
        <Style x:Key="ListBoxDoubleClickStyle"
               TargetType="ListBoxItem">
            <EventSetter Event="MouseDoubleClick"
                         Handler="OnListBoxItem_DoubleClick" />
        </Style>
    </local:VsToolsDialogWindow.Resources>
    <Grid Margin="10"
          FocusManager.FocusedElement="{Binding ElementName=searchBox}">
    </vsui:DialogWindow.Resources>
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
@@ -68,18 +66,8 @@
                 Text="{Binding Path=Keyword}" />
            <Run Text=":" />
        </TextBlock>
        <Grid Grid.Row="1"
              MinHeight="22"
              Background="White">
            <TextBlock Text=" Filter..."
                       Foreground="LightSteelBlue"
                       VerticalAlignment="Center"
                       Visibility="{Binding ElementName=searchBox,
                                    Path=Text.IsEmpty, Converter={StaticResource b2v}}" />
            <TextBox Name="searchBox"
                     Background="Transparent"
                     TextChanged="OnSearchBox_TextChanged"
                     VerticalContentAlignment="Center" />
        <Grid Grid.Row="1">
            <Grid Name="searchControlHost" />
        </Grid>
        <ListBox Grid.Row="2"
                 Margin="0,10,0,0"
@@ -104,4 +92,4 @@
                    Margin="0,10,0,0" />
        </StackPanel>
    </Grid>
</local:VsToolsDialogWindow>
</vsui:DialogWindow>
QtVsTools.Package/Package/QtHelpLinkChooser.xaml.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -32,10 +32,14 @@
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using Microsoft.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using QtVsTools.VisualStudio;
namespace QtVsTools
{
    partial class QtHelpLinkChooser : VsToolsDialogWindow
    partial class QtHelpLinkChooser : DialogWindow
    {
        public QtHelpLinkChooser()
        {
@@ -46,28 +50,32 @@
        }
        public string Link { get; set; }
        public string SearchText { get; set; }
        public string Keyword { private get; set; }
        public Dictionary<string, string> Links { private get; set; }
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var view = CollectionViewSource.GetDefaultView(linkListBox.ItemsSource);
            view.Filter = obj =>
            {
                if (string.IsNullOrEmpty(searchBox.Text))
                if (string.IsNullOrEmpty(SearchText))
                    return true;
                var item = (KeyValuePair<string, string>)obj;
                return item.Key.IndexOf(searchBox.Text, StringComparison.OrdinalIgnoreCase) >= 0;
                return item.Key.IndexOf(SearchText, StringComparison.OrdinalIgnoreCase) >= 0;
            };
            linkListBox.SelectedIndex = 0;
        }
        private void OnSearchBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            CollectionViewSource.GetDefaultView(linkListBox.ItemsSource).Refresh();
            if (linkListBox.Items.Count == 1 || linkListBox.SelectedItem == null)
                linkListBox.SelectedIndex = 0;
            var factory = VsServiceProvider
                .GetService<SVsWindowSearchHostFactory, IVsWindowSearchHostFactory>();
            var host = factory.CreateWindowSearchHost(searchControlHost);
            host.SetupSearch(new ListBoxSearch(linkListBox, value => SearchText = value));
            host.Activate(); // set focus
        }
        private void OnListBoxItem_DoubleClick(object sender, MouseButtonEventArgs e)
QtVsTools.Package/Package/QtItemContextMenu.cs
@@ -26,15 +26,16 @@
**
****************************************************************************/
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using QtVsTools.Core;
using QtVsTools.VisualStudio;
using System;
using System.ComponentModel.Design;
using Microsoft.VisualStudio.Shell;
using EnvDTE;
namespace QtVsTools
{
    using Core;
    using VisualStudio;
    /// <summary>
    /// Command handler
    /// </summary>
@@ -43,30 +44,24 @@
        /// <summary>
        /// Command menu group (command set GUID).
        /// </summary>
        public static readonly Guid ItemContextMenuGuid = new Guid("9f67a0bd-ee0a-47e3-b656-5efb12e3c770");
        private static readonly Guid ItemContextMenuGuid = new Guid("9f67a0bd-ee0a-47e3-b656-5efb12e3c770");
        /// <summary>
        /// Gets the instance of the command.
        /// </summary>
        public static QtItemContextMenu Instance
        private static QtItemContextMenu Instance
        {
            get;
            private set;
            set;
        }
        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        public static void Initialize(Package package)
        public static void Initialize()
        {
            Instance = new QtItemContextMenu(package);
            Instance = new QtItemContextMenu();
        }
        /// <summary>
        /// VS Package that provides this command, not null.
        /// </summary>
        private readonly Package m_package;
        /// <summary>
        /// Command ID.
@@ -78,14 +73,8 @@
        /// Initializes a new instance of the <see cref="QtMainMenu"/> class.
        /// Adds our command handlers for menu (commands must exist in the command table file)
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        private QtItemContextMenu(Package package)
        private QtItemContextMenu()
        {
            if (package == null)
                throw new ArgumentNullException("package");
            m_package = package;
            var commandService = VsServiceProvider
                .GetService<IMenuCommandService, OleMenuCommandService>();
            if (commandService == null)
@@ -104,6 +93,8 @@
        private void execHandler(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var command = sender as OleMenuCommand;
            if (command == null)
                return;
@@ -120,6 +111,8 @@
        private void beforeQueryStatus(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var command = sender as OleMenuCommand;
            if (command == null)
                return;
@@ -128,7 +121,7 @@
            command.Visible = false;
            var prj = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte);
            if (!HelperFunctions.IsQtProject(prj) || QtVsToolsPackage.Instance.Dte.SelectedItems.Count <= 0)
            if (!HelperFunctions.IsVsToolsProject(prj) || QtVsToolsPackage.Instance.Dte.SelectedItems.Count <= 0)
                return;
            foreach (SelectedItem si in QtVsToolsPackage.Instance.Dte.SelectedItems) {
@@ -136,7 +129,7 @@
                    return; // Don't display commands if one of the selected files is not a .ts file.
            }
            command.Enabled = true;
            command.Enabled = Translation.ToolsAvailable(prj);
            command.Visible = true;
        }
    }
QtVsTools.Package/Package/QtMainMenu.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,16 +26,17 @@
**
****************************************************************************/
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using QtVsTools.Core;
using QtVsTools.VisualStudio;
using System;
using System.ComponentModel.Design;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
using EnvDTE;
namespace QtVsTools
{
    using Core;
    using VisualStudio;
    /// <summary>
    /// Command handler
    /// </summary>
@@ -44,24 +45,23 @@
        /// <summary>
        /// Command menu group (command set GUID).
        /// </summary>
        public static readonly Guid MainMenuGuid = new Guid("58f83fff-d39d-4c66-810b-2702e1f04e73");
        private static readonly Guid MainMenuGuid = new Guid("58f83fff-d39d-4c66-810b-2702e1f04e73");
        /// <summary>
        /// Gets the instance of the command.
        /// </summary>
        public static QtMainMenu Instance
        private static QtMainMenu Instance
        {
            get;
            private set;
            set;
        }
        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        public static void Initialize(Package package)
        public static void Initialize()
        {
            Instance = new QtMainMenu(package);
            Instance = new QtMainMenu();
        }
        /// <summary>
@@ -71,16 +71,14 @@
        {
            QtVersionId = 0x0500,
            ViewQtHelpId = 0x0501,
            ViewGettingStartedId = 0x0503,
            LaunchDesignerId = 0x0100,
            LaunchLinguistId = 0x0101,
            OpenProFileId = 0x0102,
            ImportPriFileId = 0x0103,
            ExportPriFileId = 0x0104,
            ExportProFileId = 0x0105,
            CreateNewTsFileId = 0x0107,
            ConvertToQtMsBuild = 0x0130,
            ConvertToQtId = 0x0124,
            ConvertToQmakeId = 0x0108,
            QtProjectSettingsId = 0x0109,
            ChangeProjectQtVersionId = 0x0126,
            QtOptionsId = 0x0110,
@@ -88,22 +86,12 @@
        }
        /// <summary>
        /// VS Package that provides this command, not null.
        /// </summary>
        private readonly Package m_package;
        /// <summary>
        /// Initializes a new instance of the <see cref="QtMainMenu"/> class.
        /// Adds our command handlers for menu (commands must exist in the command table file)
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        private QtMainMenu(Package package)
        private QtMainMenu()
        {
            if (package == null)
                throw new ArgumentNullException("package");
            m_package = package;
            var commandService = VsServiceProvider
                .GetService<IMenuCommandService, OleMenuCommandService>();
            if (commandService == null)
@@ -119,6 +107,8 @@
        private void execHandler(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var command = sender as OleMenuCommand;
            if (command == null)
                return;
@@ -126,6 +116,9 @@
            switch ((CommandId)command.CommandID.ID) {
            case CommandId.ViewQtHelpId:
                VsShellUtilities.OpenSystemBrowser("https://www.qt.io/developers");
                break;
            case CommandId.ViewGettingStartedId:
                VsShellUtilities.OpenSystemBrowser("https://doc.qt.io/qtvstools/qtvstools-getting-started.html");
                break;
            case CommandId.LaunchDesignerId:
                QtVsToolsPackage.Instance.QtDesigner.Start(hideWindow: false);
@@ -145,23 +138,8 @@
            case CommandId.ExportProFileId:
                ExtLoader.ExportProFile();
                break;
            case CommandId.CreateNewTsFileId:
                Translation.CreateNewTranslationFile(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage
                    .Instance.Dte));
                break;
            case CommandId.ConvertToQtId:
            case CommandId.ConvertToQmakeId: {
                    var caption = SR.GetString("ConvertTitle");
                    var text = SR.GetString("ConvertConfirmation");
                    if (MessageBox.Show(text, caption, MessageBoxButtons.YesNo) == DialogResult.Yes) {
                        HelperFunctions.ToggleProjectKind(HelperFunctions.GetSelectedProject(QtVsToolsPackage
                            .Instance.Dte));
                    }
                }
                break;
            case CommandId.ConvertToQtMsBuild: {
                    QtMsBuildConverter.SolutionToQtMsBuild();
                }
            case CommandId.ConvertToQtMsBuild:
                QtMsBuildConverter.SolutionToQtMsBuild();
                break;
            case CommandId.QtProjectSettingsId: {
                    var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte);
@@ -169,31 +147,14 @@
                    if (projectVersion >= Resources.qtMinFormatVersion_Settings) {
                        QtVsToolsPackage.Instance.Dte.ExecuteCommand("Project.Properties");
                    } else if (pro != null) {
                        using (var formProjectQtSettings = new FormProjectQtSettings()) {
                            formProjectQtSettings.SetProject(pro);
                            formProjectQtSettings.StartPosition = FormStartPosition.CenterParent;
                            var ww = new MainWinWrapper(QtVsToolsPackage.Instance.Dte);
                            formProjectQtSettings.ShowDialog(ww);
                        }
                        Legacy.QtMenu.ShowFormProjectQtSettings(pro);
                    } else {
                        MessageBox.Show(SR.GetString("NoProjectOpened"));
                    }
                }
                break;
            case CommandId.ChangeProjectQtVersionId: {
                    var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte);
                    if (HelperFunctions.IsQMakeProject(pro)) {
                        using (var formChangeQtVersion = new FormChangeQtVersion()) {
                            formChangeQtVersion.UpdateContent(ChangeFor.Project);
                            var ww = new MainWinWrapper(QtVsToolsPackage.Instance.Dte);
                            if (formChangeQtVersion.ShowDialog(ww) == DialogResult.OK) {
                                var qtVersion = formChangeQtVersion.GetSelectedQtVersion();
                                HelperFunctions.SetDebuggingEnvironment(pro, "PATH=" + QtVersionManager
                                    .The().GetInstallPath(qtVersion) + @"\bin;$(PATH)", true);
                            }
                        }
                    }
                }
            case CommandId.ChangeProjectQtVersionId:
                Legacy.QtMenu.ShowFormChangeProjectQtVersion();
                break;
            case CommandId.QtOptionsId:
                QtVsToolsPackage.Instance.ShowOptionPage(typeof(Options.QtOptionsPage));
@@ -206,12 +167,17 @@
        private void beforeQueryStatus(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var command = sender as OleMenuCommand;
            if (command == null)
                return;
            var project = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte);
            switch ((CommandId)command.CommandID.ID) {
            case CommandId.ViewQtHelpId:
            case CommandId.ViewGettingStartedId:
                command.Visible = command.Enabled = true;
                break;
            case CommandId.QtVersionId:
@@ -230,39 +196,26 @@
            case CommandId.ImportPriFileId:
            case CommandId.ExportPriFileId:
            case CommandId.ExportProFileId:
            case CommandId.CreateNewTsFileId: {
                    command.Visible = true;
                    command.Enabled = HelperFunctions.IsQtProject(HelperFunctions
                        .GetSelectedProject(QtVsToolsPackage.Instance.Dte));
                }
                command.Visible = true;
                command.Enabled = HelperFunctions.IsVsToolsProject(project);
                break;
            // TODO: Fix these functionality and re-enable the menu items
            case CommandId.ConvertToQtId:
            case CommandId.ConvertToQmakeId: {
                    command.Visible = false;
                }
                break;
            //case CommandId.ConvertToQmakeId:
            case CommandId.QtProjectSettingsId: {
                    var status = vsCommandStatus.vsCommandStatusSupported;
                    var project = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte);
                    if (project != null) {
                        if (HelperFunctions.IsQtProject(project))
                        if (HelperFunctions.IsVsToolsProject(project))
                            status |= vsCommandStatus.vsCommandStatusEnabled;
                        else if (HelperFunctions.IsQMakeProject(project))
                        else if (HelperFunctions.IsQtProject(project))
                            status |= vsCommandStatus.vsCommandStatusInvisible;
                    }
                    command.Enabled = ((status & vsCommandStatus.vsCommandStatusEnabled) != 0);
                    command.Visible = ((status & vsCommandStatus.vsCommandStatusInvisible) == 0);
                }
                break;
            //case CommandId.ConvertToQtId:
            case CommandId.ChangeProjectQtVersionId: {
                    var status = vsCommandStatus.vsCommandStatusSupported;
                    var project = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte);
                    if ((project == null) || HelperFunctions.IsQtProject(project))
                    if ((project == null) || HelperFunctions.IsVsToolsProject(project))
                        status |= vsCommandStatus.vsCommandStatusInvisible;
                    else if (HelperFunctions.IsQMakeProject(project))
                    else if (HelperFunctions.IsQtProject(project))
                        status |= vsCommandStatus.vsCommandStatusEnabled;
                    else
                        status |= vsCommandStatus.vsCommandStatusInvisible;
QtVsTools.Package/Package/QtMsBuildConverter.cs
@@ -32,17 +32,21 @@
using System.Linq;
using System.Windows.Forms;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.VCProjectEngine;
using QtVsTools.Core;
using QtVsTools.VisualStudio;
namespace QtVsTools
{
    using Core;
    using VisualStudio;
    static class QtMsBuildConverter
    {
        public static bool SolutionToQtMsBuild()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var solution = QtVsToolsPackage.Instance.Dte.Solution;
            if (solution == null)
                return ErrorMessage(string.Format(SR.GetString("ErrorConvertingProject"), ""));
@@ -53,8 +57,8 @@
                return WarningMessage(SR.GetString("NoProjectsToConvert"));
            foreach (EnvDTE.Project project in allProjects) {
                if ((HelperFunctions.IsQtProject(project)
                    || HelperFunctions.IsQMakeProject(project))
                if ((HelperFunctions.IsVsToolsProject(project)
                    || HelperFunctions.IsQtProject(project))
                    && !QtProject.IsQtMsBuildEnabled(project)) {
                    projects.Add(project);
                }
@@ -68,7 +72,14 @@
                MessageBoxButtons.YesNo) != DialogResult.Yes)
                return WarningMessage(SR.GetString("CancelConvertingProject"));
            if (projects.Where(project => project.IsDirty).Any()) {
            bool hasDirtyProjects = projects
                .Where(project =>
                {
                    ThreadHelper.ThrowIfNotOnUIThread();
                    return project.IsDirty;
                })
                .Any();
            if (hasDirtyProjects) {
                if (MessageBox.Show(
                    SR.GetString("ConvertSaveConfirmation"),
                    SR.GetString("ConvertTitle"),
@@ -76,7 +87,13 @@
                    return WarningMessage(SR.GetString("CancelConvertingProject"));
            }
            var projectPaths = projects.Select(x => x.FullName).ToList();
            var projectPaths = projects
                .Select(x =>
                {
                    ThreadHelper.ThrowIfNotOnUIThread();
                    return x.FullName;
                })
                .ToList();
            string solutionPath = solution.FileName;
            solution.Close(true);
@@ -142,6 +159,8 @@
        public static bool ProjectToQtMsBuild(EnvDTE.Project project, bool askConfirmation = true)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (project == null)
                return ErrorMessage(string.Format(SR.GetString("ErrorConvertingProject"), ""));
            var pathToProject = project.FullName;
@@ -178,7 +197,7 @@
            try {
                if (solution.UnloadProject(
                    ref projectGuid,
                    (uint)_VSProjectUnloadStatus.UNLOADSTATUS_LoadPendingIfNeeded)
                    (uint)_VSProjectUnloadStatus.UNLOADSTATUS_UnloadedByUser)
                    != VSConstants.S_OK)
                    return ErrorMessage(
                        string.Format(SR.GetString("ErrorConvertingProject"), projectName));
QtVsTools.Package/Package/QtProjectContextMenu.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,20 +26,17 @@
**
****************************************************************************/
using EnvDTE;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.VCProjectEngine;
using QtVsTools.Core;
using QtVsTools.VisualStudio;
using System;
using System.ComponentModel.Design;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
using EnvDTE;
namespace QtVsTools
{
    using Core;
    using QtMsBuild;
    using VisualStudio;
    /// <summary>
    /// Command handler
@@ -49,24 +46,23 @@
        /// <summary>
        /// Command menu group (command set GUID).
        /// </summary>
        public static readonly Guid ProjectContextMenuGuid = new Guid("5732faa9-6074-4e07-b035-2816e809f50e");
        private static readonly Guid ProjectContextMenuGuid = new Guid("5732faa9-6074-4e07-b035-2816e809f50e");
        /// <summary>
        /// Gets the instance of the command.
        /// </summary>
        public static QtProjectContextMenu Instance
        private static QtProjectContextMenu Instance
        {
            get;
            private set;
            set;
        }
        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        public static void Initialize(Package package)
        public static void Initialize()
        {
            Instance = new QtProjectContextMenu(package);
            Instance = new QtProjectContextMenu();
        }
        /// <summary>
@@ -77,35 +73,20 @@
            ImportPriFileProjectId = 0x0114,
            ExportPriFileProjectId = 0x0115,
            ExportProFileProjectId = 0x0116,
            CreateNewTsFileProjectId = 0x0117,
            lUpdateOnProjectId = 0x0118,
            lReleaseOnProjectId = 0x0119,
            ProjectConvertToQtMsBuild = 0x0130,
            ProjectRefreshIntelliSense = 0x0131,
            ConvertToQtProjectId = 0x0120,
            ConvertToQmakeProjectId = 0x0121,
            QtProjectSettingsProjectId = 0x0122,
            ChangeProjectQtVersionProjectId = 0x0123,
            ProjectAddNewQtClassProjectId = 0x200
            ChangeProjectQtVersionProjectId = 0x0123
        }
        /// <summary>
        /// VS Package that provides this command, not null.
        /// </summary>
        private readonly Package m_package;
        /// <summary>
        /// Initializes a new instance of the <see cref="QtMainMenu"/> class.
        /// Adds our command handlers for menu (commands must exist in the command table file)
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        private QtProjectContextMenu(Package package)
        private QtProjectContextMenu()
        {
            if (package == null)
                throw new ArgumentNullException("package");
            m_package = package;
            var commandService = VsServiceProvider
                .GetService<IMenuCommandService, OleMenuCommandService>();
            if (commandService == null)
@@ -121,6 +102,8 @@
        private void execHandler(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var command = sender as OleMenuCommand;
            if (command == null)
                return;
@@ -135,25 +118,11 @@
            case CommandId.ExportProFileProjectId:
                ExtLoader.ExportProFile();
                break;
            case CommandId.CreateNewTsFileProjectId:
                Translation.CreateNewTranslationFile(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage
                    .Instance.Dte));
                break;
            case CommandId.lUpdateOnProjectId:
                Translation.RunlUpdate(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte));
                break;
            case CommandId.lReleaseOnProjectId:
                Translation.RunlRelease(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte));
                break;
            case CommandId.ConvertToQtProjectId:
            case CommandId.ConvertToQmakeProjectId: {
                    var caption = SR.GetString("ConvertTitle");
                    var text = SR.GetString("ConvertConfirmation");
                    if (MessageBox.Show(text, caption, MessageBoxButtons.YesNo) == DialogResult.Yes) {
                        HelperFunctions.ToggleProjectKind(HelperFunctions.GetSelectedProject(QtVsToolsPackage
                            .Instance.Dte));
                    }
                }
                break;
            case CommandId.QtProjectSettingsProjectId: {
                    var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte);
@@ -161,31 +130,14 @@
                    if (projectVersion >= Resources.qtMinFormatVersion_Settings) {
                        QtVsToolsPackage.Instance.Dte.ExecuteCommand("Project.Properties");
                    } else if (pro != null) {
                        using (var formProjectQtSettings = new FormProjectQtSettings()) {
                            formProjectQtSettings.SetProject(pro);
                            formProjectQtSettings.StartPosition = FormStartPosition.CenterParent;
                            var ww = new MainWinWrapper(QtVsToolsPackage.Instance.Dte);
                            formProjectQtSettings.ShowDialog(ww);
                        }
                        Legacy.QtMenu.ShowFormProjectQtSettings(pro);
                    } else {
                        MessageBox.Show(SR.GetString("NoProjectOpened"));
                    }
                }
                break;
            case CommandId.ChangeProjectQtVersionProjectId: {
                    var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte);
                    if (HelperFunctions.IsQMakeProject(pro)) {
                        using (var formChangeQtVersion = new FormChangeQtVersion()) {
                            formChangeQtVersion.UpdateContent(ChangeFor.Project);
                            var ww = new MainWinWrapper(QtVsToolsPackage.Instance.Dte);
                            if (formChangeQtVersion.ShowDialog(ww) == DialogResult.OK) {
                                var qtVersion = formChangeQtVersion.GetSelectedQtVersion();
                                HelperFunctions.SetDebuggingEnvironment(pro, "PATH=" + QtVersionManager
                                    .The().GetInstallPath(qtVersion) + @"\bin;$(PATH)", true);
                            }
                        }
                    }
                }
            case CommandId.ChangeProjectQtVersionProjectId:
                Legacy.QtMenu.ShowFormChangeProjectQtVersion();
                break;
            case CommandId.ProjectConvertToQtMsBuild: {
                    QtMsBuildConverter.ProjectToQtMsBuild(
@@ -194,29 +146,8 @@
                break;
            case CommandId.ProjectRefreshIntelliSense: {
                    var selectedProject = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte);
                    var tracker = QtProjectTracker.Get(selectedProject);
                    var tracker = QtProjectTracker.Get(selectedProject, selectedProject.FullName);
                    QtProjectIntellisense.Refresh(tracker.Project);
                }
                break;
            case CommandId.ProjectAddNewQtClassProjectId: {
                    try {
                        var project = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte);
                        if (!HelperFunctions.IsQtProject(project))
                            return;
                        var vcProject = project.Object as VCProject;
                        if (vcProject == null)
                            return;
                        var loop = true;
                        do {
                            var classWizard = new Wizards.ClassWizard.AddClassWizard();
                            loop = classWizard.Run(QtVsToolsPackage.Instance.Dte, vcProject.Name,
                                vcProject.ProjectDirectory) == Wizards.WizardResult.Exception;
                        } while (loop);
                    } catch {
                        // Deliberately ignore any kind of exception but close the dialog.
                    }
                }
                break;
            }
@@ -229,8 +160,8 @@
                return;
            var project = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte);
            var isQtProject = HelperFunctions.IsQtProject(project);
            var isQMakeProject = HelperFunctions.IsQMakeProject(project);
            var isQtProject = HelperFunctions.IsVsToolsProject(project);
            var isQMakeProject = HelperFunctions.IsQtProject(project);
            var isQtMsBuildEnabled = QtProject.IsQtMsBuildEnabled(project);
            if (!isQtProject && !isQMakeProject) {
@@ -239,25 +170,19 @@
            }
            switch ((CommandId)command.CommandID.ID) {
            // TODO: Fix these functionality and re-enable the menu items
            case CommandId.ConvertToQtProjectId:
            case CommandId.ConvertToQmakeProjectId: {
                    command.Visible = false;
                }
                break;
            case CommandId.ImportPriFileProjectId:
            case CommandId.ExportPriFileProjectId:
            case CommandId.ExportProFileProjectId:
            case CommandId.CreateNewTsFileProjectId:
                command.Visible = true;
                command.Enabled = HelperFunctions.IsVsToolsProject(HelperFunctions
                    .GetSelectedProject(QtVsToolsPackage.Instance.Dte));
                break;
            case CommandId.lUpdateOnProjectId:
            case CommandId.lReleaseOnProjectId:
                command.Visible = true;
                command.Enabled = HelperFunctions.IsQtProject(HelperFunctions
                    .GetSelectedProject(QtVsToolsPackage.Instance.Dte));
                command.Enabled = Translation.ToolsAvailable(project);
                break;
            //case CommandId.ConvertToQmakeProjectId:
            case CommandId.QtProjectSettingsProjectId:
            case CommandId.ProjectAddNewQtClassProjectId: {
            case CommandId.QtProjectSettingsProjectId: {
                    var status = vsCommandStatus.vsCommandStatusSupported;
                    if (project != null) {
                        if (isQtProject)
@@ -269,7 +194,6 @@
                    command.Visible = ((status & vsCommandStatus.vsCommandStatusInvisible) == 0);
                }
                break;
            //case CommandId.ConvertToQtProjectId:
            case CommandId.ChangeProjectQtVersionProjectId: {
                    var status = vsCommandStatus.vsCommandStatusSupported;
                    if ((project == null) || isQtProject)
QtVsTools.Package/Package/QtSolutionContextMenu.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,17 +26,15 @@
**
****************************************************************************/
using EnvDTE80;
using Microsoft.VisualStudio.Shell;
using QtVsTools.Core;
using QtVsTools.VisualStudio;
using System;
using System.ComponentModel.Design;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
namespace QtVsTools
{
    using Core;
    using QtMsBuild;
    using VisualStudio;
    /// <summary>
    /// Command handler
@@ -46,30 +44,24 @@
        /// <summary>
        /// Command menu group (command set GUID).
        /// </summary>
        public static readonly Guid SolutionContextMenuGuid = new Guid("6dcda34f-4d22-4d6a-a176-5507069c5a3e");
        private static readonly Guid SolutionContextMenuGuid = new Guid("6dcda34f-4d22-4d6a-a176-5507069c5a3e");
        /// <summary>
        /// Gets the instance of the command.
        /// </summary>
        public static QtSolutionContextMenu Instance
        private static QtSolutionContextMenu Instance
        {
            get;
            private set;
            set;
        }
        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        public static void Initialize(Package package)
        public static void Initialize()
        {
            Instance = new QtSolutionContextMenu(package);
            Instance = new QtSolutionContextMenu();
        }
        /// <summary>
        /// VS Package that provides this command, not null.
        /// </summary>
        private readonly Package m_package;
        /// <summary>
        /// Command ID.
@@ -87,14 +79,8 @@
        /// Initializes a new instance of the <see cref="QtMainMenu"/> class.
        /// Adds our command handlers for menu (commands must exist in the command table file)
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        private QtSolutionContextMenu(Package package)
        private QtSolutionContextMenu()
        {
            if (package == null)
                throw new ArgumentNullException("package");
            m_package = package;
            var commandService = VsServiceProvider
                .GetService<IMenuCommandService, OleMenuCommandService>();
            if (commandService == null)
@@ -113,66 +99,51 @@
            var command = sender as OleMenuCommand;
            if (command == null)
                return;
            command.Enabled = command.Visible = true;
            switch (command.CommandID.ID) {
            case (int)CommandId.ChangeSolutionQtVersionId:
                var projects = HelperFunctions.ProjectsInSolution(QtVsToolsPackage.Instance.Dte);
                foreach (var project in projects) {
                    if (!HelperFunctions.IsVsToolsProject(project)
                        && HelperFunctions.IsQtProject(project)) {
                        command.Enabled = command.Visible = true;
                        return;
                    }
                }
                command.Enabled = command.Visible = false;
                break;
            default:
                command.Enabled = command.Visible = true;
                break;
            }
        }
        private void execHandler(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var command = sender as OleMenuCommand;
            if (command == null)
                return;
            var dte = QtVsToolsPackage.Instance.Dte;
            switch (command.CommandID.ID) {
            case (int)CommandId.lUpdateOnSolutionId:
            switch ((CommandId)command.CommandID.ID) {
            case CommandId.lUpdateOnSolutionId:
                Translation.RunlUpdate(QtVsToolsPackage.Instance.Dte.Solution);
                break;
            case (int)CommandId.lReleaseOnSolutionId:
            case CommandId.lReleaseOnSolutionId:
                Translation.RunlRelease(QtVsToolsPackage.Instance.Dte.Solution);
                break;
            case (int)CommandId.ChangeSolutionQtVersionId:
                string newQtVersion = null;
                using (var formChangeQtVersion = new FormChangeQtVersion()) {
                    formChangeQtVersion.UpdateContent(ChangeFor.Solution);
                    if (formChangeQtVersion.ShowDialog() != DialogResult.OK)
                        return;
                    newQtVersion = formChangeQtVersion.GetSelectedQtVersion();
                }
                if (newQtVersion == null)
                    return;
                string currentPlatform = null;
                try {
                    var config2 = QtVsToolsPackage.Instance.Dte.Solution.SolutionBuild
                        .ActiveConfiguration as SolutionConfiguration2;
                    currentPlatform = config2.PlatformName;
                } catch { }
                if (string.IsNullOrEmpty(currentPlatform))
                    return;
                foreach (var project in HelperFunctions.ProjectsInSolution(dte)) {
                    if (HelperFunctions.IsQtProject(project)) {
                        var OldQtVersion = QtVersionManager.The().GetProjectQtVersion(project,
                            currentPlatform);
                        if (OldQtVersion == null)
                            OldQtVersion = QtVersionManager.The().GetDefaultVersion();
                        var created = false;
                        var qtProject = QtProject.Create(project);
                        if (qtProject.PromptChangeQtVersion(OldQtVersion, newQtVersion))
                            qtProject.ChangeQtVersion(OldQtVersion, newQtVersion, ref created);
                    }
                }
                QtVersionManager.The().SaveSolutionQtVersion(dte.Solution, newQtVersion);
            case CommandId.ChangeSolutionQtVersionId:
                Legacy.QtMenu.ShowFormChangeSolutionQtVersion();
                break;
            case (int)CommandId.SolutionConvertToQtMsBuild: {
                    QtMsBuildConverter.SolutionToQtMsBuild();
                }
            case CommandId.SolutionConvertToQtMsBuild:
                QtMsBuildConverter.SolutionToQtMsBuild();
                break;
            case (int)CommandId.SolutionEnableProjectTracking: {
            case CommandId.SolutionEnableProjectTracking: {
                    foreach (var project in HelperFunctions.ProjectsInSolution(dte)) {
                        if (HelperFunctions.IsQtProject(project))
                            QtProjectTracker.Get(project);
                        if (HelperFunctions.IsVsToolsProject(project))
                            QtProjectTracker.Get(project, project.FullName);
                    }
                }
                break;
QtVsTools.Package/Package/SR.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,8 +26,6 @@
**
****************************************************************************/
using System;
using System.Globalization;
using System.Resources;
namespace QtVsTools
@@ -36,66 +34,39 @@
    {
        static SR loader;
        readonly ResourceManager resources;
        static readonly Object obj = new Object();
        static readonly object obj = new object();
        internal const string OK = "OK";
        internal const string Cancel = "Cancel";
        internal static CultureInfo appCultureInfo;
        internal static CultureInfo defaultCultureInfo;
        internal SR(int localeId)
        internal SR()
        {
            defaultCultureInfo = CultureInfo.GetCultureInfo("en");
            appCultureInfo = CultureInfo.GetCultureInfo(localeId);
            if (appCultureInfo.Name.StartsWith("en", StringComparison.Ordinal))
                appCultureInfo = null;
            resources = new ResourceManager("QtVsTools.Package.Resources", GetType().Assembly);
        }
        private static SR GetLoader(int localeId)
        private static SR GetLoader()
        {
            if (loader == null) {
                lock (obj) {
                    if (loader == null)
                        loader = new SR(localeId);
                        loader = new SR();
                }
            }
            return loader;
        }
        private static CultureInfo Culture
        {
            get { return appCultureInfo; }
        }
        public static string GetString(string name, params object[] args)
        {
            var res = GetString(name);
            if (!string.IsNullOrEmpty(res) && args != null && args.Length > 0)
            var sys = GetLoader();
            if (sys == null)
                return null;
            var res = sys.resources.GetString(name, null);
            if (args != null && args.Length > 0 && !string.IsNullOrEmpty(res))
                return string.Format(res, args);
            return res;
        }
        public static string GetString(string name)
        {
            return GetString(name, QtVsToolsPackage.Instance);
        }
        public static string GetString(string name, QtVsToolsPackage vsixInstance)
        {
            var sys = GetLoader(vsixInstance.Dte.LocaleID);
            if (sys == null)
                return null;
            string result;
            try {
                result = sys.resources.GetString(name, Culture);
            } catch (Exception) {
                result = sys.resources.GetString(name, defaultCultureInfo);
            }
            return result;
            return GetString(name, null);
        }
    }
}
QtVsTools.Package/Package/Translation.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -26,24 +26,16 @@
**
****************************************************************************/
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.VCProjectEngine;
using QtVsTools.Core;
using QtVsTools.VisualStudio;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
namespace QtVsTools
{
    using static Core.HelperFunctions;
    using Core;
    using QtMsBuild;
    /// <summary>
@@ -51,16 +43,10 @@
    /// </summary>
    public static class Translation
    {
        public static void RunlRelease(VCFile vcFile)
        {
            var vcProj = vcFile.project as VCProject;
            var project = vcProj?.Object as EnvDTE.Project;
            RunTranslationTarget(BuildAction.Release,
                project, new[] { vcFile.RelativePath });
        }
        public static void RunlRelease(VCFile[] vcFiles)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var vcProj = vcFiles.FirstOrDefault()?.project as VCProject;
            var project = vcProj?.Object as EnvDTE.Project;
            RunTranslationTarget(BuildAction.Release,
@@ -69,11 +55,14 @@
        public static void RunlRelease(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            RunTranslationTarget(BuildAction.Release, project);
        }
        public static void RunlRelease(EnvDTE.Solution solution)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (solution == null)
                return;
@@ -83,6 +72,8 @@
        public static void RunlUpdate(VCFile vcFile)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var vcProj = vcFile.project as VCProject;
            var project = vcProj?.Object as EnvDTE.Project;
            RunTranslationTarget(BuildAction.Update,
@@ -91,6 +82,8 @@
        public static void RunlUpdate(VCFile[] vcFiles)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var vcProj = vcFiles.FirstOrDefault()?.project as VCProject;
            var project = vcProj?.Object as EnvDTE.Project;
            RunTranslationTarget(BuildAction.Update,
@@ -99,16 +92,19 @@
        public static void RunlUpdate(EnvDTE.Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            RunTranslationTarget(BuildAction.Update, project);
        }
        enum BuildAction { Update, Release }
        internal enum BuildAction { Update, Release }
        static void RunTranslationTarget(
            BuildAction buildAction,
            EnvDTE.Project project,
            IEnumerable<string> selectedFiles = null)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            using (WaitDialog.Start(
                "Qt Visual Studio Tools", "Running translation tool...")) {
@@ -122,10 +118,9 @@
                if (qtPro.FormatVersion < Resources.qtMinFormatVersion_Settings) {
                    Messages.Print("translation: Legacy project format");
                    try {
                        Legacy_RunTranslation(buildAction, qtPro, selectedFiles);
                    } catch (Exception e) {
                        Messages.Print(
                            e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                        Legacy.Translation.Run(buildAction, qtPro, selectedFiles);
                    } catch (Exception exception) {
                        exception.Log();
                    }
                    return;
                }
@@ -152,12 +147,15 @@
                if (selectedFiles != null)
                    properties["SelectedFiles"] = string.Join(";", selectedFiles);
                QtProjectBuild.StartBuild(project, activeConfigId, properties, new[] { target });
                QtProjectBuild.StartBuild(
                    project, project.FullName, activeConfigId, properties, new[] { target });
            }
        }
        public static void RunlUpdate(EnvDTE.Solution solution)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (solution == null)
                return;
@@ -165,118 +163,23 @@
                RunlUpdate(project);
        }
        static void Legacy_RunTranslation(
            BuildAction buildAction,
            QtProject qtProject,
            IEnumerable<string> tsFiles)
        {
            if (tsFiles == null) {
                tsFiles = (qtProject.VCProject
                    .GetFilesEndingWith(".ts") as IVCCollection)
                    .Cast<VCFile>()
                    .Select(vcFile => vcFile.RelativePath);
                if (tsFiles == null) {
                    Messages.Print("translation: no translation files found");
                    return;
                }
            }
            string tempFile = null;
            foreach (var file in tsFiles.Where(file => file != null))
                Legacy_RunTranslation(buildAction, qtProject, file, ref tempFile);
        }
        static void Legacy_RunTranslation(
            BuildAction buildAction,
            QtProject qtProject,
            string tsFile,
            ref string tempFile)
        {
            var qtVersion = qtProject.GetQtVersion();
            var qtInstallPath = QtVersionManager.The().GetInstallPath(qtVersion);
            if (string.IsNullOrEmpty(qtInstallPath)) {
                Messages.Print("translation: Error accessing Qt installation");
                return;
            }
            var procInfo = new ProcessStartInfo
            {
                WorkingDirectory = qtProject.ProjectDir,
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                Arguments = ""
            };
            switch (buildAction) {
            case BuildAction.Update:
                Messages.Print("\r\n--- (lupdate) file: " + tsFile);
                procInfo.FileName = Path.Combine(qtInstallPath, "bin", "lupdate.exe");
                var options = QtVSIPSettings.GetLUpdateOptions();
                if (!string.IsNullOrEmpty(options))
                    procInfo.Arguments += options + " ";
                if (tempFile == null) {
                    var inputFiles = GetProjectFiles(qtProject.Project, FilesToList.FL_HFiles)
                        .Union(GetProjectFiles(qtProject.Project, FilesToList.FL_CppFiles))
                        .Union(GetProjectFiles(qtProject.Project, FilesToList.FL_UiFiles))
                        .Union(GetProjectFiles(qtProject.Project, FilesToList.FL_QmlFiles));
                    tempFile = Path.GetTempFileName();
                    File.WriteAllLines(tempFile, inputFiles);
                }
                procInfo.Arguments += string.Format("\"@{0}\" -ts \"{1}\"", tempFile, tsFile);
                break;
            case BuildAction.Release:
                Messages.Print("\r\n--- (lrelease) file: " + tsFile);
                procInfo.FileName = Path.Combine(qtInstallPath, "bin", "lrelease.exe");
                options = QtVSIPSettings.GetLReleaseOptions();
                if (!string.IsNullOrEmpty(options))
                    procInfo.Arguments += options + " ";
                procInfo.Arguments += string.Format("\"{0}\"", tsFile);
                break;
            }
            using (var proc = Process.Start(procInfo)) {
                proc.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
                {
                    if (!string.IsNullOrEmpty(e.Data))
                        Messages.Print(e.Data);
                };
                proc.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
                {
                    if (!string.IsNullOrEmpty(e.Data))
                        Messages.Print(e.Data);
                };
                proc.BeginOutputReadLine();
                proc.BeginErrorReadLine();
                proc.WaitForExit();
                switch (proc.ExitCode) {
                case 0:
                    Messages.Print("translation: ok");
                    break;
                default:
                    Messages.Print(string.Format("translation: ERROR {0}", proc.ExitCode));
                    break;
                }
            }
        }
        public static void CreateNewTranslationFile(EnvDTE.Project project)
        public static bool ToolsAvailable(EnvDTE.Project project)
        {
            if (project == null)
                return;
                return false;
            if (QtProject.GetPropertyValue(project, "ApplicationType") == "Linux")
                return true;
            using (var transDlg = new AddTranslationDialog(project)) {
                if (transDlg.ShowDialog() == DialogResult.OK) {
                    try {
                        var qtPro = QtProject.Create(project);
                        var file = qtPro.AddFileInFilter(Filters.TranslationFiles(),
                            transDlg.TranslationFile, true);
                        RunlUpdate(file);
                    } catch (QtVSException e) {
                        Messages.DisplayErrorMessage(e.Message);
                    } catch (System.Exception ex) {
                        Messages.DisplayErrorMessage(ex.Message);
                    }
                }
            var qtToolsPath = QtProject.GetPropertyValue(project, "QtToolsPath");
            if (string.IsNullOrEmpty(qtToolsPath)) {
                var qtVersion = QtVersionManager.The().GetProjectQtVersion(project);
                var qtInstallPath = QtVersionManager.The().GetInstallPath(qtVersion);
                if (string.IsNullOrEmpty(qtInstallPath))
                    return false;
                qtToolsPath = Path.Combine(qtInstallPath, "bin");
            }
            return File.Exists(Path.Combine(qtToolsPath, "lupdate.exe"))
                && File.Exists(Path.Combine(qtToolsPath, "lrelease.exe"));
        }
    }
}
QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs
@@ -33,7 +33,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using Microsoft.VisualStudio.Text;
@@ -52,7 +51,7 @@
    /// </summary>
    class SharedTagList : Concurrent
    {
        SortedList<int, TrackingTag> data = new SortedList<int, TrackingTag>();
        readonly SortedList<int, TrackingTag> data = new SortedList<int, TrackingTag>();
        object owner;
        public bool Ready { get; private set; }
@@ -97,7 +96,7 @@
        class TrackingTagComparer : Comparer<TrackingTag>
        {
            ITextSnapshot snapshot;
            readonly ITextSnapshot snapshot;
            public TrackingTagComparer(ITextSnapshot snapshot)
            {
                this.snapshot = snapshot;
@@ -232,17 +231,16 @@
        /// <returns>Instance of T corresponding to the given TrackingTag</returns>
        protected abstract T GetClassification(TrackingTag tag);
        protected ITextView TextView { get; private set; }
        protected ITextBuffer Buffer { get; private set; }
        private ITextView TextView { get; }
        private ITextBuffer Buffer { get; }
        readonly object criticalSection = new object();
        string classificationType;
        readonly string classificationType;
        ParserKey currentParserKey;
        TagListKey currentTagListKey;
        SharedTagList currentTagList;
        Dispatcher dispatcher;
        DispatcherTimer timer;
        readonly Dispatcher dispatcher;
        readonly DispatcherTimer timer;
        bool flag = false;
        protected QmlAsyncClassifier(
@@ -267,7 +265,7 @@
            currentTagList = null;
            this.classificationType = classificationType;
            AsyncParse(buffer.CurrentSnapshot);
            Parse(buffer.CurrentSnapshot);
        }
        private void TextView_Closed(object sender, EventArgs e)
@@ -286,16 +284,16 @@
        private void Buffer_Changed(object sender, TextContentChangedEventArgs e)
        {
            timer.Stop();
            AsyncParse(e.After);
            Parse(e.After);
        }
        private void Timer_Tick(object sender, EventArgs e)
        {
            timer.Stop();
            AsyncParse(Buffer.CurrentSnapshot);
            Parse(Buffer.CurrentSnapshot);
        }
        private async void AsyncParse(ITextSnapshot snapshot)
        private void Parse(ITextSnapshot snapshot)
        {
            lock (criticalSection) {
                if (flag)
@@ -311,7 +309,7 @@
            ParserKey oldParserKey = null;
            TagListKey oldTagListKey = null;
            await Task.Run(() =>
            _ = Task.Run(() =>
            {
                var parser = ParserStore.Instance.Get(this, newParserKey);
@@ -344,7 +342,7 @@
                flag = false;
            }
            await Task.Run(() =>
            _ = Task.Run(() =>
            {
                if (oldParserKey != null)
                    ParserStore.Instance.Release(this, oldParserKey);
@@ -511,7 +509,7 @@
                public TValue Value { get; set; }
                public HashSet<object> ClientObjects { get; set; }
            }
            Dictionary<TKey, ValueRef> data = new Dictionary<TKey, ValueRef>();
            readonly Dictionary<TKey, ValueRef> data = new Dictionary<TKey, ValueRef>();
            static readonly object staticCriticalSection = new object();
            readonly object criticalSection = new object();
@@ -524,8 +522,7 @@
            public TValue Get(object client, TKey key)
            {
                lock (criticalSection) {
                    ValueRef valueRef;
                    if (!data.TryGetValue(key, out valueRef)) {
                    if (!data.TryGetValue(key, out ValueRef valueRef)) {
                        valueRef = new ValueRef
                        {
                            Value = GetDefaultValue(key),
@@ -543,8 +540,7 @@
            {
                IDisposable disposable = null;
                lock (criticalSection) {
                    ValueRef valueRef;
                    if (data.TryGetValue(key, out valueRef)) {
                    if (data.TryGetValue(key, out ValueRef valueRef)) {
                        valueRef.ClientObjects.Remove(client);
                        if (valueRef.ClientObjects.Count == 0) {
                            data.Remove(key);
@@ -573,8 +569,8 @@
        class TagListKey
        {
            public string Classification { get; private set; }
            public ITextSnapshot Snapshot { get; private set; }
            private string Classification { get; }
            private ITextSnapshot Snapshot { get; }
            public TagListKey(string classification, ITextSnapshot snapshot)
            {
                Classification = classification;
@@ -614,7 +610,7 @@
        class ParserKey
        {
            public ITextSnapshot Snapshot { get; private set; }
            public ITextSnapshot Snapshot { get; }
            public ParserKey(ITextSnapshot snapshot)
            {
                Snapshot = snapshot;
QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs
@@ -28,11 +28,7 @@
/// Highlighting of syntax errors
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using System.Windows.Threading;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs
@@ -41,11 +41,12 @@
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Utilities;
using QtVsTools.Qml.Syntax;
using QtVsTools.VisualStudio;
namespace QtVsTools.Qml.Classification
{
    using QtVsTools.VisualStudio;
    using Syntax;
    [Export(typeof(IViewTaggerProvider))]
    [ContentType("qml")]
    [TagType(typeof(ClassificationTag))]
@@ -169,6 +170,8 @@
        int IVsTextViewFilter.GetDataTipText(TextSpan[] pSpan, out string pbstrText)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            pbstrText = "";
            var dbgMode = new DBGMODE[1];
            if (debugger.GetMode(dbgMode) != VSConstants.S_OK
@@ -215,6 +218,7 @@
            OLECMD[] prgCmds,
            IntPtr pCmdText)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return nextTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
        }
@@ -225,12 +229,13 @@
            IntPtr pvaIn,
            IntPtr pvaOut)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return nextTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
        }
        class ExprTrackingTag : TrackingTag
        {
            public IOrderedEnumerable<AstNode> Exprs { get; private set; }
            public IOrderedEnumerable<AstNode> Exprs { get; }
            public ExprTrackingTag(
                ITextSnapshot snapshot,
@@ -245,7 +250,7 @@
        class ExprTag : ClassificationTag
        {
            public IOrderedEnumerable<AstNode> Exprs { get; private set; }
            public IOrderedEnumerable<AstNode> Exprs { get; }
            public ExprTag(IOrderedEnumerable<AstNode> exprs, IClassificationType type)
                : base(type)
QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs
@@ -29,11 +29,7 @@
/// This file implements the actual highlighting of the text according to the
/// classification of the syntax elements recognized by the QML parser.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using System.Windows.Threading;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
QtVsTools.Package/QML/Classification/QmlTag.cs
@@ -47,10 +47,10 @@
    /// </summary>
    public class TrackingTag : ITag
    {
        public ITextSnapshot Snapshot { get; private set; }
        public int Start { get; private set; }
        public int Length { get; private set; }
        public ITrackingSpan Span { get; private set; }
        public ITextSnapshot Snapshot { get; }
        public int Start { get; }
        private int Length { get; }
        public ITrackingSpan Span { get; }
        public TrackingTag(ITextSnapshot snapshot, int start, int length)
        {
            Snapshot = snapshot;
@@ -76,9 +76,9 @@
        public const string TypeName = "typename.qml";
        public const string Binding = "binding.qml";
        public SyntaxElement SyntaxElement { get; private set; }
        public SourceLocation SourceLocation { get; private set; }
        public IClassificationType ClassificationType { get; private set; }
        private SyntaxElement SyntaxElement { get; }
        private SourceLocation SourceLocation { get; }
        public IClassificationType ClassificationType { get; }
        private QmlSyntaxTag(ITextSnapshot snapshot, SourceLocation location)
            : base(snapshot, location.Offset, location.Length)
@@ -118,7 +118,7 @@
            return new QmlSyntaxTag(snapshot, parentNode, classificationType, fullNameLocation);
        }
        public static readonly HashSet<string> QmlBasicTypes = new HashSet<string> {
        private static readonly HashSet<string> QmlBasicTypes = new HashSet<string> {
            "bool", "double", "enumeration", "int",
            "list", "real", "string", "url", "var",
            "date", "point", "rect", "size", "alias"
@@ -223,7 +223,7 @@
    public class QmlDiagnosticsTag : TrackingTag
    {
        public DiagnosticMessage DiagnosticMessage { get; private set; }
        private DiagnosticMessage DiagnosticMessage { get; }
        public QmlDiagnosticsTag(ITextSnapshot snapshot, DiagnosticMessage diagnosticMessage)
            : base(snapshot, diagnosticMessage.Location.Offset, diagnosticMessage.Location.Length)
        {
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Breakpoint.cs
@@ -44,12 +44,12 @@
        static readonly string[] ValidExtensions = new string[] { ".qml", ".js" };
        public QmlEngine Engine { get; private set; }
        public IDebugBreakpointRequest2 Request { get; private set; }
        public enum_BP_LOCATION_TYPE LocationType { get; private set; }
        public BP_REQUEST_INFO RequestInfo { get; private set; }
        private IDebugBreakpointRequest2 Request { get; set; }
        private enum_BP_LOCATION_TYPE LocationType { get; set; }
        private BP_REQUEST_INFO RequestInfo { get; set; }
        public string FileName { get; private set; }
        public TEXT_POSITION BeginPosition { get; private set; }
        public TEXT_POSITION EndPosition { get; private set; }
        private TEXT_POSITION EndPosition { get; set; }
        public bool Enabled { get; private set; }
        HashSet<Breakpoint> breakpoints;
@@ -85,8 +85,7 @@
            if (docPosition == null)
                return false;
            string fileName;
            if (docPosition.GetFileName(out fileName) != VSConstants.S_OK)
            if (docPosition.GetFileName(out string fileName) != VSConstants.S_OK)
                return false;
            if (!ValidExtensions.Where(x => string.Equals(x, Path.GetExtension(fileName))).Any())
@@ -186,12 +185,12 @@
        IDebugBreakpointResolution2 // "This interface represents the information that describes a
                                    //  bound breakpoint."
    {
        public QmlDebugger Debugger { get; private set; }
        public QmlEngine Engine { get; private set; }
        private QmlDebugger Debugger { get; set; }
        private QmlEngine Engine { get; set; }
        public Program Program { get; private set; }
        public PendingBreakpoint Parent { get; private set; }
        public CodeContext CodeContext { get; private set; }
        public bool Enabled { get; set; }
        private CodeContext CodeContext { get; set; }
        private bool Enabled { get; set; }
        bool supressNotify;
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Engine.cs
@@ -78,13 +78,13 @@
            FileSystem = FileSystem.Create();
        }
        Dictionary<Guid, Program> programs = new Dictionary<Guid, Program>();
        readonly Dictionary<Guid, Program> programs = new Dictionary<Guid, Program>();
        public IEnumerable<Program> Programs
        {
            get { return ThreadSafe(() => programs.Values.ToList()); }
        }
        HashSet<PendingBreakpoint> pendingBreakpoints = new HashSet<PendingBreakpoint>();
        readonly HashSet<PendingBreakpoint> pendingBreakpoints = new HashSet<PendingBreakpoint>();
        public IEnumerable<PendingBreakpoint> PendingBreakpoints
        {
            get { return ThreadSafe(() => pendingBreakpoints.ToList()); }
@@ -106,8 +106,7 @@
            if (string.IsNullOrEmpty(pszOptions))
                return VSConstants.E_FAIL;
            uint procId;
            if (!uint.TryParse(pszOptions, out procId))
            if (!uint.TryParse(pszOptions, out uint procId))
                return VSConstants.E_FAIL;
            var env = bstrEnv.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
@@ -120,13 +119,12 @@
                    FileSystem.RegisterRccFile(rccFile);
            }
            IDebugProcess2 nativeProc;
            var nativeProcId = new AD_PROCESS_ID
            {
                ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_SYSTEM,
                dwProcessId = procId
            };
            if (pPort.GetProcess(nativeProcId, out nativeProc) != VSConstants.S_OK)
            if (pPort.GetProcess(nativeProcId, out IDebugProcess2 nativeProc) != VSConstants.S_OK)
                return VSConstants.E_FAIL;
            var program = Program.Create(this, nativeProc, pszExe, pszArgs);
@@ -144,20 +142,11 @@
            if (program == null)
                return VSConstants.E_FAIL;
            IDebugPort2 port;
            if (process.GetPort(out port) != VSConstants.S_OK)
            if (process.GetPort(out IDebugPort2 port) != VSConstants.S_OK)
                return VSConstants.E_FAIL;
            string portName;
            port.GetPortName(out portName);
            Guid guidPort;
            port.GetPortId(out guidPort);
            IDebugDefaultPort2 defaultPort = (IDebugDefaultPort2)port;
            IDebugPortNotify2 portNotify;
            if (defaultPort.GetPortNotify(out portNotify) != VSConstants.S_OK)
            if (defaultPort.GetPortNotify(out IDebugPortNotify2 portNotify) != VSConstants.S_OK)
                return VSConstants.E_FAIL;
            if (portNotify.AddProgramNode(program) != VSConstants.S_OK)
@@ -181,8 +170,7 @@
            DebugEvent.Send(new EngineCreateEvent(this));
            Guid pguidProgramId;
            if (rgpPrograms[0].GetProgramId(out pguidProgramId) != VSConstants.S_OK)
            if (rgpPrograms[0].GetProgramId(out Guid pguidProgramId) != VSConstants.S_OK)
                return VSConstants.E_FAIL;
            program.ProgramId = pguidProgramId;
@@ -199,15 +187,10 @@
        int IDebugEngineLaunch2.CanTerminateProcess(IDebugProcess2 pProcess)
        {
            Guid procId;
            if (pProcess.GetProcessId(out procId) != VSConstants.S_OK)
            if (pProcess.GetProcessId(out Guid procId) != VSConstants.S_OK)
                return VSConstants.E_FAIL;
            Program program;
            if (!programs.TryGetValue(procId, out program))
                return VSConstants.S_FALSE;
            return VSConstants.S_OK;
            return programs.TryGetValue(procId, out _) ? VSConstants.S_OK : VSConstants.S_FALSE;
        }
        public bool ProgramIsRunning(Program program)
@@ -217,12 +200,10 @@
        int IDebugEngineLaunch2.TerminateProcess(IDebugProcess2 pProcess)
        {
            Guid procId;
            if (pProcess.GetProcessId(out procId) != VSConstants.S_OK)
            if (pProcess.GetProcessId(out Guid procId) != VSConstants.S_OK)
                return VSConstants.E_FAIL;
            Program program;
            if (!programs.TryGetValue(procId, out program))
            if (!programs.TryGetValue(procId, out Program program))
                return VSConstants.S_FALSE;
            programs.Remove(procId);
@@ -235,8 +216,7 @@
        int IDebugEngine2.ContinueFromSynchronousEvent(IDebugEvent2 pEvent)
        {
            var evtProgramDestroy = pEvent as ProgramDestroyEvent;
            if (evtProgramDestroy != null)
            if (pEvent is ProgramDestroyEvent evtProgramDestroy)
                evtProgramDestroy.Program.Dispose();
            return VSConstants.S_OK;
@@ -271,7 +251,7 @@
        #region //////////////////// Concurrent ///////////////////////////////////////////////////
        LocalConcurrent concurrent = new LocalConcurrent();
        readonly LocalConcurrent concurrent = new LocalConcurrent();
        class LocalConcurrent : Concurrent
        {
            public void LocalThreadSafe(Action action)
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Events.cs
@@ -38,8 +38,8 @@
                     //  such as stopping at a breakpoint, and non-critical information, such as a
                     //  debugging message."
    {
        public Guid InterfaceId { get; protected set; }
        public uint Attributes { get; protected set; }
        private Guid InterfaceId { get; }
        private uint Attributes { get; }
        protected const uint ASYNCHRONOUS = (uint)enum_EVENTATTRIBUTES.EVENT_ASYNCHRONOUS;
        protected const uint STOPPING = (uint)enum_EVENTATTRIBUTES.EVENT_ASYNC_STOP;
@@ -48,10 +48,10 @@
              (uint)enum_EVENTATTRIBUTES.EVENT_STOPPING
            | (uint)enum_EVENTATTRIBUTES.EVENT_SYNCHRONOUS;
        protected QmlEngine Engine { get; set; }
        protected IDebugProgram2 Program { get; set; }
        protected IDebugThread2 Thread { get; set; }
        protected IDebugEventCallback2 Callback { get; set; }
        protected QmlEngine Engine { get; }
        private IDebugProgram2 Program { get; }
        private IDebugThread2 Thread { get; }
        private IDebugEventCallback2 Callback { get; }
        protected DebugEvent(
            QmlEngine engine,
@@ -114,8 +114,8 @@
    class ProgramDestroyEvent : DebugEvent, IDebugProgramDestroyEvent2
    {
        uint exitCode;
        public new Program Program { get; private set; }
        readonly uint exitCode;
        public Program Program { get; }
        public ProgramDestroyEvent(Program program, uint exitCode)
        : base(program.Engine, typeof(IDebugProgramDestroyEvent2).GUID,
@@ -142,7 +142,7 @@
    class ThreadDestroyEvent : DebugEvent, IDebugThreadDestroyEvent2
    {
        uint exitCode;
        readonly uint exitCode;
        public ThreadDestroyEvent(Program program, uint exitCode)
        : base(program.Engine, typeof(IDebugThreadDestroyEvent2).GUID,
@@ -176,7 +176,7 @@
    class BreakpointBoundEvent : DebugEvent, IDebugBreakpointBoundEvent2
    {
        public Breakpoint Breakpoint { get; private set; }
        private Breakpoint Breakpoint { get; }
        public BreakpointBoundEvent(Breakpoint breakpoint)
        : base(breakpoint.Program.Engine, typeof(IDebugBreakpointBoundEvent2).GUID,
              ASYNCHRONOUS, breakpoint.Program, breakpoint.Program)
@@ -201,7 +201,7 @@
    class BreakpointEvent : DebugEvent, IDebugBreakpointEvent2
    {
        IEnumDebugBoundBreakpoints2 boundBreakpoints;
        readonly IEnumDebugBoundBreakpoints2 boundBreakpoints;
        public BreakpointEvent(Program program,
            IEnumDebugBoundBreakpoints2 boundBreakpoints)
@@ -228,8 +228,8 @@
    class ExpressionEvaluationCompleteEvent : DebugEvent, IDebugExpressionEvaluationCompleteEvent2
    {
        public Expression Expression { get; private set; }
        public Property Property { get; private set; }
        private Expression Expression { get; }
        private Property Property { get; }
        public ExpressionEvaluationCompleteEvent(
            IDebugEventCallback2 callback,
@@ -257,7 +257,7 @@
    class OutputStringEvent : DebugEvent, IDebugOutputStringEvent2
    {
        string outputString;
        readonly string outputString;
        public OutputStringEvent(QmlEngine engine, string outputString)
        : base(engine, typeof(IDebugOutputStringEvent2).GUID, ASYNCHRONOUS)
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Expression.cs
@@ -26,7 +26,6 @@
**
****************************************************************************/
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Debugger.Interop;
@@ -40,13 +39,13 @@
        IDebugExpression2 // "This interface represents a parsed expression ready for binding
                          //  and evaluating."
    {
        public string ExpressionString { get; private set; }
        private string ExpressionString { get; set; }
        public StackFrame StackFrame { get; private set; }
        private StackFrame StackFrame { get; set; }
        public QmlEngine Engine { get; private set; }
        public Program Program { get; private set; }
        public QmlDebugger Debugger { get; private set; }
        public CodeContext CodeContext { get; private set; }
        private QmlDebugger Debugger { get; set; }
        private CodeContext CodeContext { get; set; }
        public static Expression Create(StackFrame frame, string expr)
        {
@@ -86,7 +85,7 @@
            enum_EVALFLAGS dwFlags,
            IDebugEventCallback2 pExprCallback)
        {
            Task.Run(() =>
            _ = Task.Run(() =>
            {
                var value = Debugger.Evaluate(StackFrame.FrameNumber, ExpressionString);
                if (value != null)
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7InfoHelpers.cs
@@ -26,12 +26,9 @@
**
****************************************************************************/
using Microsoft.VisualStudio.Debugger.Interop;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace QtVsTools.Qml.Debug.AD7
{
@@ -76,9 +73,9 @@
        public class Mapping<TStruct, TFieldMask> : Mapping,
            IEnumerable<MapField<TStruct, TFieldMask>>
        {
            List<MapField<TStruct, TFieldMask>> fieldMaps;
            readonly List<MapField<TStruct, TFieldMask>> fieldMaps;
            protected static Action<Ref<TStruct>, TFieldMask> UpdateMask { get; set; }
            private static Action<Ref<TStruct>, TFieldMask> UpdateMask { get; set; }
            public Mapping(Action<Ref<TStruct>, TFieldMask> updateMask)
            {
@@ -188,8 +185,7 @@
            TFieldMask fieldMask,
            out TStruct infoStruct)
        {
            var mappingToStruct = mapping as Mapping<TStruct, TFieldMask>;
            if (mappingToStruct != null)
            if (mapping is Mapping<TStruct, TFieldMask> mappingToStruct)
                mappingToStruct.Map(this as TDerived, fieldMask, out infoStruct);
            else
                infoStruct = default(TStruct);
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Program.cs
@@ -29,18 +29,16 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Threading;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Debugger.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using QtVsTools.VisualStudio;
namespace QtVsTools.Qml.Debug.AD7
{
    using VisualStudio;
    sealed partial class Program : Disposable, IDebuggerEventSink,
        IDebugProgramNode2,  // "This interface represents a program that can be debugged."
@@ -64,21 +62,21 @@
        public QmlEngine Engine { get; private set; }
        public List<StackFrame> CurrentFrames { get; private set; }
        private List<StackFrame> CurrentFrames { get; set; }
        public const string Name = "QML Debugger";
        public Guid ProcessId { get; private set; }
        private const string Name = "QML Debugger";
        public Guid ProcessId { get; set; }
        public Guid ProgramId { get; set; }
        public IDebugProcess2 NativeProc { get; private set; }
        public uint NativeProcId { get; private set; }
        public string ExecPath { get; private set; }
        public string ExecArgs { get; private set; }
        public IVsDebugger VsDebugger { get; private set; }
        Dispatcher vsDebuggerThreadDispatcher;
        private IDebugProcess2 NativeProc { get; set; }
        private uint NativeProcId { get; set; }
        private string ExecPath { get; set; }
        private string ExecArgs { get; set; }
        private IVsDebugger VsDebugger { get; set; }
        private Dispatcher vsDebuggerThreadDispatcher;
        private readonly static object criticalSectionGlobal = new object();
        static bool originalBreakAllProcesses = BreakAllProcesses;
        static int runningPrograms = 0;
        private static readonly object criticalSectionGlobal = new object();
        private static bool originalBreakAllProcesses = BreakAllProcesses;
        private static int runningPrograms = 0;
        public static Program Create(
            QmlEngine engine,
@@ -114,8 +112,13 @@
                return false;
            VsDebugger = VsServiceProvider.GetService<IVsDebugger>();
            if (VsDebugger != null)
                VsDebugger.AdviseDebugEventCallback(this as IDebugEventCallback2);
            if (VsDebugger != null) {
                ThreadHelper.JoinableTaskFactory.Run(async () =>
                {
                    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                    VsDebugger.AdviseDebugEventCallback(this);
                });
            }
            vsDebuggerThreadDispatcher = Dispatcher.CurrentDispatcher;
            ProcessId = Guid.NewGuid();
@@ -141,8 +144,13 @@
        protected override void DisposeManaged()
        {
            Debugger.Dispose();
            if (VsDebugger != null)
                VsDebugger.UnadviseDebugEventCallback(this as IDebugEventCallback2);
            if (VsDebugger != null) {
                ThreadHelper.JoinableTaskFactory.Run(async () =>
                {
                    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                    VsDebugger.UnadviseDebugEventCallback(this as IDebugEventCallback2);
                });
            }
            lock (criticalSectionGlobal) {
                runningPrograms--;
@@ -161,9 +169,12 @@
        {
            var debugMode = new DBGMODE[1];
            int res = VSConstants.S_FALSE;
            vsDebuggerThreadDispatcher
                .BeginInvoke(new Action(() => res = VsDebugger.GetMode(debugMode)), new object[0])
                .Wait();
            QtVsToolsPackage.Instance.JoinableTaskFactory.Run(async () =>
            {
                await QtVsToolsPackage.Instance.JoinableTaskFactory.SwitchToMainThreadAsync();
                res = VsDebugger.GetMode(debugMode);
            });
            if (res != VSConstants.S_OK)
                return false;
@@ -295,17 +306,24 @@
        {
            get
            {
                return ((bool)QtVsToolsPackage.Instance.Dte
                    .Properties["Debugging", "General"]
                    .Item("BreakAllProcesses")
                    .Value);
                return ThreadHelper.JoinableTaskFactory.Run(async () => {
                    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                    return ((bool)QtVsToolsPackage.Instance.Dte
                        .Properties["Debugging", "General"]
                        .Item("BreakAllProcesses")
                        .Value);
                });
            }
            set
            {
                QtVsToolsPackage.Instance.Dte
                    .Properties["Debugging", "General"]
                    .Item("BreakAllProcesses")
                    .let_Value(value ? "True" : "False");
                ThreadHelper.JoinableTaskFactory.Run(async () =>
                {
                    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                    QtVsToolsPackage.Instance.Dte
                        .Properties["Debugging", "General"]
                        .Item("BreakAllProcesses")
                        .let_Value(value ? "True" : "False");
                });
            }
        }
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Property.cs
@@ -29,7 +29,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Debugger.Interop;
@@ -43,23 +42,23 @@
                        //  property, or some other property. The property is usually the result of
                        //  an expression evaluation."
    {
        public QmlDebugger Debugger { get; private set; }
        private QmlDebugger Debugger { get; set; }
        public QmlEngine Engine { get; private set; }
        public Program Program { get; private set; }
        public StackFrame StackFrame { get; private set; }
        public CodeContext CodeContext { get; private set; }
        private QmlEngine Engine { get; set; }
        private Program Program { get; set; }
        private StackFrame StackFrame { get; set; }
        private CodeContext CodeContext { get; set; }
        public Property Parent { get; private set; }
        public SortedDictionary<string, Property> Children { get; private set; }
        private Property Parent { get; set; }
        private SortedDictionary<string, Property> Children { get; set; }
        public int FrameNumber { get; private set; }
        public int ScopeNumber { get; private set; }
        public JsValue JsValue { get; private set; }
        public string Name { get; private set; }
        public string FullName { get; private set; }
        public string Type { get; private set; }
        public string Value { get; private set; }
        private int FrameNumber { get; set; }
        private int ScopeNumber { get; set; }
        private JsValue JsValue { get; set; }
        private string Name { get; set; }
        private string FullName { get; set; }
        private string Type { get; set; }
        private string Value { get; set; }
        public static Property Create(
            StackFrame frame,
@@ -120,11 +119,9 @@
        static string GetChildKey(string childName)
        {
            int childIndex;
            if (int.TryParse(childName, out childIndex))
            if (int.TryParse(childName, out int childIndex))
                return string.Format("{0:D9}", childIndex);
            else
                return childName;
            return childName;
        }
        int IDebugProperty2.SetValueAsString(string pszValue, uint dwRadix, uint dwTimeout)
@@ -242,8 +239,7 @@
        public DEBUG_PROPERTY_INFO GetInfo(enum_DEBUGPROP_INFO_FLAGS dwFields)
        {
            DEBUG_PROPERTY_INFO info;
            Info.Map(MappingToDEBUG_PROPERTY_INFO, dwFields, out info);
            Info.Map(MappingToDEBUG_PROPERTY_INFO, dwFields, out DEBUG_PROPERTY_INFO info);
            return info;
        }
@@ -275,19 +271,19 @@
            public static readonly Guid Registers
                = new Guid("223ae797-bd09-4f28-8241-2763bdc5f713");
            public static readonly Guid Locals
            private static readonly Guid Locals
                = new Guid("b200f725-e725-4c53-b36a-1ec27aef12ef");
            public static readonly Guid AllLocals
            private static readonly Guid AllLocals
                = new Guid("196db21f-5f22-45a9-b5a3-32cddb30db06");
            public static readonly Guid Args
                = new Guid("804bccea-0475-4ae7-8a46-1862688ab863");
            public static readonly Guid LocalsPlusArgs
            private static readonly Guid LocalsPlusArgs
                = new Guid("e74721bb-10c0-40f5-807f-920d37f95419");
            public static readonly Guid AllLocalsPlusArgs
            private static readonly Guid AllLocalsPlusArgs
                = new Guid("939729a8-4cb0-4647-9831-7ff465240d5f");
            public static bool LocalsSelected(ref Guid guidFilter)
QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7StackFrame.cs
@@ -32,6 +32,7 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Debugger.Interop;
using Microsoft.VisualStudio.Threading;
namespace QtVsTools.Qml.Debug.AD7
{
@@ -52,14 +53,14 @@
        public Program Program { get; private set; }
        public CodeContext Context { get; private set; }
        public Dictionary<int, Dictionary<string, Property>> Properties { get; private set; }
        private Dictionary<int, Dictionary<string, Property>> Properties { get; set; }
        public string Name { get; private set; }
        public int FrameNumber { get; private set; }
        public IEnumerable<int> Scopes { get; private set; }
        public Task InitThread { get; private set; }
        private string Name { get; set; }
        public int FrameNumber { get; set; }
        private IEnumerable<int> Scopes { get; set; }
        private JoinableTask InitThread { get; set; }
        static public StackFrame Create(
        public static StackFrame Create(
            string name,
            int number,
            IEnumerable<int> scopes,
@@ -85,7 +86,11 @@
            Name = string.Format("{0}@{1}:{2}", name, context.FilePath, context.FileLine + 1);
            FrameNumber = number;
            Scopes = scopes;
            InitThread = Task.Run(() => InitializeProperties());
            InitThread = QtVsToolsPackage.Instance.JoinableTaskFactory.RunAsync(async () =>
            {
                InitializeProperties();
                await Task.Yield();
            });
            return true;
        }
@@ -134,7 +139,7 @@
            if (guidFilter != Guid.Empty && !Property.Filter.LocalsSelected(ref guidFilter))
                return VSConstants.S_OK;
            InitThread.Wait();
            InitThread.Join();
            pcelt = 0;
            ppEnum = PropertyEnum.Create(Properties
                .SelectMany(x => x.Value
@@ -152,9 +157,8 @@
            uint dwTimeout,
            out IEnumDebugPropertyInfo2 ppEnum)
        {
            uint pcelt;
            return ((IDebugStackFrame2)this)
                .EnumProperties(dwFields, dwRadix, guidFilter, dwTimeout, out pcelt, out ppEnum);
                .EnumProperties(dwFields, dwRadix, guidFilter, dwTimeout, out _, out ppEnum);
        }
        #region //////////////////// Info /////////////////////////////////////////////////////////
QtVsTools.Package/QML/Debugging/QmlDebugLauncher.cs
@@ -35,39 +35,50 @@
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.VCProjectEngine;
using QtVsTools.Core;
using QtVsTools.Core.QtMsBuild;
using QtVsTools.SyntaxAnalysis;
using static QtVsTools.SyntaxAnalysis.RegExpr;
namespace QtVsTools.Qml.Debug
{
    using AD7;
    using Common;
    using Core;
    using Core.QtMsBuild;
    using SyntaxAnalysis;
    using VisualStudio;
    using static SyntaxAnalysis.RegExpr;
    class Launcher : Disposable, IDebugEventCallback2
    {
        public static Launcher Instance { get; private set; }
        LazyFactory Lazy { get; } = new LazyFactory();
        private static Launcher Instance { get; set; }
        IVsDebugger debugger;
        IVsDebugger4 debugger4;
        HashSet<Guid> _ExcludedProcesses;
        HashSet<Guid> ExcludedProcesses => _ExcludedProcesses
            ?? (_ExcludedProcesses = new HashSet<Guid>());
        HashSet<Guid> ExcludedProcesses => Lazy.Get(() =>
            ExcludedProcesses, () => new HashSet<Guid>());
        public static void Initialize()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            Instance = new Launcher();
            Instance.debugger = VsServiceProvider.GetService<IVsDebugger>();
            Instance.debugger4 = VsServiceProvider.GetService<IVsDebugger, IVsDebugger4>();
            if (Instance.debugger != null && Instance.debugger4 != null)
                Instance.debugger.AdviseDebugEventCallback(Instance);
        }
        protected override void DisposeManaged()
        {
            if (debugger != null)
                debugger.UnadviseDebugEventCallback(this);
            if (debugger != null) {
                ThreadHelper.JoinableTaskFactory.Run(async () =>
                {
                    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                    debugger.UnadviseDebugEventCallback(this);
                });
            }
        }
        int IDebugEventCallback2.Event(
@@ -79,6 +90,8 @@
            ref Guid riidEvent,
            uint dwAttrib)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (!QtVsToolsPackage.Instance.Options.QmlDebuggerEnabled)
                return VSConstants.S_OK;
@@ -90,8 +103,7 @@
            if (pProcess == null && pProgram.GetProcess(out pProcess) != VSConstants.S_OK)
                return VSConstants.S_OK;
            Guid procGuid;
            if (pProcess.GetProcessId(out procGuid) != VSConstants.S_OK)
            if (pProcess.GetProcessId(out Guid procGuid) != VSConstants.S_OK)
                return VSConstants.S_OK;
            // Run only once per process
@@ -119,14 +131,10 @@
            else
                return VSConstants.S_OK;
            string execPath;
            uint procId;
            if (!GetProcessInfo(pProcess, native, out execPath, out procId))
            if (!GetProcessInfo(pProcess, native, out string execPath, out uint procId))
                return VSConstants.S_OK;
            string execCmd;
            IEnumerable<string> rccItems;
            if (!GetProjectInfo(execPath, native, out execCmd, out rccItems))
            if (!GetProjectInfo(execPath, native, out string execCmd, out IEnumerable<string> rccItems))
                return VSConstants.S_OK;
            LaunchDebug(execPath, execCmd, procId, rccItems);
@@ -135,9 +143,7 @@
        Guid GetEngineId(IDebugProgram2 pProgram)
        {
            string engineName;
            Guid engineGuid;
            if (pProgram.GetEngineInfo(out engineName, out engineGuid) != VSConstants.S_OK)
            if (pProgram.GetEngineInfo(out _, out Guid engineGuid) != VSConstants.S_OK)
                return Guid.Empty;
            return engineGuid;
        }
@@ -152,7 +158,7 @@
            }
        }
        static RegExpr wslPathRegex = new Token("WSLPATH", SkipWs_Disable, StartOfFile
        static readonly RegExpr wslPathRegex = new Token("WSLPATH", SkipWs_Disable, StartOfFile
            & "/mnt/" & new Token("DRIVE", CharWord) & "/" & new Token("PATH", AnyChar.Repeat()))
        {
            new Rule<WslPath>
@@ -161,15 +167,14 @@
                Update("PATH", (WslPath wslPath, string path) => wslPath.Path = path),
            }
        };
        static RegExpr.Parser wslPathParser = wslPathRegex.Render();
        static readonly RegExpr.Parser wslPathParser = wslPathRegex.Render();
        bool GetProcessInfo(IDebugProcess2 pProcess, bool native, out string execPath, out uint procId)
        {
            execPath = "";
            procId = 0;
            string fileName;
            if (pProcess.GetName(enum_GETNAME_TYPE.GN_FILENAME, out fileName) != VSConstants.S_OK)
            if (pProcess.GetName(enum_GETNAME_TYPE.GN_FILENAME, out string fileName) != VSConstants.S_OK)
                return false;
            var pProcessId = new AD_PROCESS_ID[1];
@@ -193,6 +198,8 @@
        bool GetProjectInfo(string execPath, bool native, out string execCmd, out IEnumerable<string> rccItems)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            execCmd = "";
            rccItems = null;
@@ -290,6 +297,8 @@
            uint procId,
            IEnumerable<string> rccItems)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var targets = new[] { new VsDebugTargetInfo4
            {
                dlo = (uint)DEBUG_LAUNCH_OPERATION.DLO_CreateProcess,
@@ -305,8 +314,8 @@
            try {
                debugger4.LaunchDebugTargets4((uint)targets.Length, targets, processInfo);
            } catch (System.Exception e) {
                OutputWriteLine(e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
            } catch (Exception exception) {
                exception.Log();
            }
        }
    }
QtVsTools.Package/QML/Debugging/QmlDebugger.cs
@@ -26,18 +26,19 @@
**
****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using QtVsTools.SyntaxAnalysis;
using static QtVsTools.SyntaxAnalysis.RegExpr;
using RegExprParser = QtVsTools.SyntaxAnalysis.RegExpr.Parser;
namespace QtVsTools.Qml.Debug
{
    using Common;
    using SyntaxAnalysis;
    using V4;
    using RegExprParser = SyntaxAnalysis.RegExpr.Parser;
    using static SyntaxAnalysis.RegExpr;
    struct FrameInfo
    {
@@ -69,6 +70,8 @@
    class QmlDebugger : Disposable, IMessageEventSink
    {
        static LazyFactory StaticLazy { get; } = new LazyFactory();
        IDebuggerEventSink sink;
        ProtocolDriver driver;
        string connectionHostName;
@@ -80,11 +83,11 @@
        List<Request> outbox;
        Dictionary<int, IBreakpoint> breakpoints;
        public bool Started { get; private set; }
        private bool Started { get; set; }
        public bool Running { get; private set; }
        private bool Running { get; set; }
        public string Version { get; private set; }
        private string Version { get; set; }
        public uint? ThreadId { get { return driver.ThreadId; } }
@@ -160,7 +163,7 @@
            if (!Started) {
                Running = Started = true;
                LeaveCriticalSection();
                Task.Run(() => ConnectToDebugger());
                _ = Task.Run(() => ConnectToDebugger());
            } else if (!Running) {
                Running = true;
@@ -201,7 +204,7 @@
            setBreakpoint.Arguments.Line = (int)breakpoint.Line;
            setBreakpoint.Tag = breakpoint;
            if (driver.ConnectionState == DebugClientState.Connected)
                setBreakpoint.SendAsync();
                setBreakpoint.SendRequest();
            else
                ThreadSafe(() => outbox.Add(setBreakpoint));
        }
@@ -231,7 +234,7 @@
            var reqClearBreak = Message.Create<ClearBreakpointRequest>(driver);
            reqClearBreak.Arguments.Breakpoint = breakpointNum[breakpoint];
            reqClearBreak.SendAsync();
            reqClearBreak.SendRequest();
        }
        void RefreshFrames()
@@ -286,8 +289,7 @@
            } else {
                foreach (int breakpointId in evtBreak.Body.Breakpoints) {
                    IBreakpoint breakpoint;
                    if (!breakpoints.TryGetValue(breakpointId, out breakpoint))
                    if (!breakpoints.TryGetValue(breakpointId, out IBreakpoint breakpoint))
                        continue;
                    breakpoint.NotifyBreak();
                }
@@ -406,20 +408,20 @@
        {
            if (oldState != DebugClientState.Unavailable
                && newState == DebugClientState.Disconnected) {
                Task.Run(() => sink.NotifyClientDisconnected());
                _ = Task.Run(() => sink.NotifyClientDisconnected());
            }
        }
        void IMessageEventSink.NotifyRequestResponded(Request msgRequest)
        {
            if (msgRequest is SetBreakpointRequest)
                Task.Run(() => SetBreakpointResponded(msgRequest as SetBreakpointRequest));
                _ = Task.Run(() => SetBreakpointResponded(msgRequest as SetBreakpointRequest));
        }
        void IMessageEventSink.NotifyEvent(Event msgEvent)
        {
            if (msgEvent is BreakEvent)
                Task.Run(() => BreakNotified(msgEvent as BreakEvent));
                _ = Task.Run(() => BreakNotified(msgEvent as BreakEvent));
        }
        void IMessageEventSink.NotifyMessage(Message msg)
@@ -430,13 +432,7 @@
        public static bool CheckCommandLine(string execPath, string args)
        {
            ushort portFrom;
            ushort portTo;
            string hostName;
            string fileName;
            bool block;
            return ParseCommandLine(
                execPath, args, out portFrom, out portTo, out hostName, out fileName, out block);
            return ParseCommandLine(execPath, args, out _, out _, out _, out _, out _);
        }
        /// <summary>
@@ -456,8 +452,8 @@
        /// <summary>
        /// Regex-based parser for QML debug connection parameters
        /// </summary>
        static RegExprParser ConnectParamsParser => _ConnectParamsParser ?? (
            _ConnectParamsParser = new Token(TokenId.ConnectParams, RxConnectParams)
        static RegExprParser ConnectParamsParser => StaticLazy.Get(() =>
            ConnectParamsParser, () => new Token(TokenId.ConnectParams, RxConnectParams)
            {
                new Rule<ConnectParams>
                {
@@ -469,7 +465,6 @@
                }
            }
            .Render());
        static RegExprParser _ConnectParamsParser;
        /// <summary>
        /// Regular expression for parsing connection parameters string in the form:
QtVsTools.Package/QML/Debugging/QmlFileSystem.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -36,6 +36,8 @@
namespace QtVsTools.Qml.Debug
{
    using QtVsTools.Core;
    struct QmlFile
    {
        public string QrcPath;
@@ -92,9 +94,8 @@
                using (var reader = XmlReader.Create(new StringReader(xmlText), settings)) {
                    rccXml = XDocument.Load(reader);
                }
            } catch (Exception e) {
                System.Diagnostics.Debug.WriteLine(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
            } catch (Exception exception) {
                exception.Log();
                return;
            }
@@ -156,8 +157,7 @@
            qrcPath = string.Format("qrc:///{0}", qrcPath);
            QmlFile file;
            if (!files.TryGetValue(qrcPath, out file))
            if (!files.TryGetValue(qrcPath, out QmlFile file))
                return default(QmlFile);
            return file;
@@ -190,8 +190,7 @@
                return default(QmlFile);
            }
            QmlFile file;
            if (files.TryGetValue(fullPath, out file))
            if (files.TryGetValue(fullPath, out QmlFile file))
                return file;
            return new QmlFile
QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Continue.cs
@@ -26,10 +26,7 @@
**
****************************************************************************/
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Threading;
namespace QtVsTools.Qml.Debug.V4
{
QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsObject.cs
@@ -26,7 +26,6 @@
**
****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Message.cs
@@ -143,14 +143,14 @@
            set { Atomic(() => tag == null, () => tag = value); }
        }
        public virtual ProtocolDriver.PendingRequest SendAsync()
        public virtual ProtocolDriver.PendingRequest SendRequest()
        {
            return Driver.SendRequest(this);
        }
        public new Response Send()
        {
            return SendAsync().WaitForResponse();
            return SendRequest().WaitForResponse();
        }
        public static new Response Send<T>(ProtocolDriver driver, Action<T> initMsg = null)
@@ -173,7 +173,7 @@
        public new virtual TResponse Send()
        {
            var pendingRequest = SendAsync();
            var pendingRequest = SendRequest();
            if (!pendingRequest.RequestSent)
                return null;
QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Client.cs
@@ -26,14 +26,9 @@
**
****************************************************************************/
using QtVsTools.Options;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -62,7 +57,7 @@
        IConnectionEventSink sink;
        IntPtr client;
        Task clientThread;
        EventWaitHandle clientCreated = new EventWaitHandle(false, EventResetMode.ManualReset);
        readonly EventWaitHandle clientCreated = new EventWaitHandle(false, EventResetMode.ManualReset);
        EventWaitHandle clientConnected;
        public uint? ThreadId { get; private set; }
@@ -82,7 +77,7 @@
                if (state != value) {
                    var oldState = state;
                    state = value;
                    Task.Run(() => sink.NotifyStateTransition(this, oldState, value));
                    _ = Task.Run(() => sink.NotifyStateTransition(this, oldState, value));
                }
            }
        }
@@ -100,14 +95,17 @@
        {
            this.sink = sink;
            Task.WaitAny(new[]
            QtVsToolsPackage.Instance.JoinableTaskFactory.Run(async () =>
            {
                // Try to start client thread
                // Unblock if thread was abruptly terminated (e.g. DLL not found)
                clientThread = Task.Run(() => ClientThread()),
                await Task.WhenAny(new[]
                {
                    // Try to start client thread
                    // Unblock if thread was abruptly terminated (e.g. DLL not found)
                    clientThread = Task.Run(() => ClientThread()),
                // Unblock if client was created (i.e. client thread is running)
                Task.Run(() => clientCreated.WaitOne())
                    // Unblock if client was created (i.e. client thread is running)
                    Task.Run(() => clientCreated.WaitOne())
                });
            });
            if (State == DebugClientState.Unavailable) {
@@ -135,7 +133,9 @@
        {
            if (State != DebugClientState.Unavailable) {
                NativeMethods.DebugClientShutdown(client);
                clientThread.Wait();
                QtVsToolsPackage.Instance.JoinableTaskFactory.Run(
                    async () => await Task.WhenAll(new[] { clientThread }));
            }
        }
@@ -180,7 +180,7 @@
            var hostNameData = Encoding.UTF8.GetBytes(hostName);
            uint timeout = (uint)QtVsToolsPackage.Instance.Options.QmlDebuggerTimeout;
            Task.Run(() =>
            _ = Task.Run(() =>
            {
                var connectTimer = new System.Diagnostics.Stopwatch();
                connectTimer.Start();
@@ -246,7 +246,7 @@
            uint timeout = (uint)QtVsToolsPackage.Instance.Options.QmlDebuggerTimeout;
            if (timeout != 0) {
                Task.Run(() =>
                _ = Task.Run(() =>
                {
                    var connectTimer = new System.Diagnostics.Stopwatch();
                    connectTimer.Start();
QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Protocol.cs
@@ -53,10 +53,10 @@
        IMessageEventSink sink;
        DebugClient client;
        int nextRequestSeq = 0;
        Dictionary<int, PendingRequest> pendingRequests = new Dictionary<int, PendingRequest>();
        readonly Dictionary<int, PendingRequest> pendingRequests = new Dictionary<int, PendingRequest>();
        Task eventHandlingThread;
        EventWaitHandle eventReceived = new EventWaitHandle(false, EventResetMode.AutoReset);
        ConcurrentQueue<Event> eventQueue = new ConcurrentQueue<Event>();
        readonly EventWaitHandle eventReceived = new EventWaitHandle(false, EventResetMode.AutoReset);
        readonly ConcurrentQueue<Event> eventQueue = new ConcurrentQueue<Event>();
        public uint? ThreadId { get { return client.ThreadId; } }
@@ -92,7 +92,8 @@
        protected override void DisposeFinally()
        {
            eventReceived.Set();
            eventHandlingThread.Wait();
            QtVsToolsPackage.Instance.JoinableTaskFactory.Run(
                async () => await Task.WhenAll(new[] { eventHandlingThread }));
            eventReceived.Dispose();
        }
@@ -100,8 +101,7 @@
        {
            while (!Disposing) {
                eventReceived.WaitOne();
                Event evt;
                while (!Disposing && eventQueue.TryDequeue(out evt))
                while (!Disposing && eventQueue.TryDequeue(out Event evt))
                    sink.NotifyEvent(evt);
            }
        }
@@ -209,8 +209,8 @@
        public class PendingRequest : Finalizable
        {
            public Request Request { get; private set; }
            EventWaitHandle responded;
            public Request Request { get; }
            readonly EventWaitHandle responded;
            public PendingRequest()
            {
QtVsTools.Package/QML/Parser/QmlParserDiagnostics.cs
@@ -37,8 +37,8 @@
    /// </summary>
    public class DiagnosticMessage
    {
        public DiagnosticMessageKind Kind { get; set; }
        public SourceLocation Location { get; set; }
        private DiagnosticMessageKind Kind { get; }
        public SourceLocation Location { get; }
        public DiagnosticMessage(DiagnosticMessageKind kind, int offset, int length)
        {
            Kind = kind;
QtVsTools.Package/QML/Parser/QmlParserInterop.cs
@@ -148,28 +148,27 @@
        IntPtr qmlTextPtr = IntPtr.Zero;
        IntPtr qmlParserPtr = IntPtr.Zero;
        List<Token> tokens;
        readonly List<Token> tokens;
        public IEnumerable<Token> Tokens
        {
            get { return tokens; }
        }
        List<DiagnosticMessage> diagnosticMessages;
        readonly List<DiagnosticMessage> diagnosticMessages;
        public IEnumerable<DiagnosticMessage> DiagnosticMessages
        {
            get { return diagnosticMessages; }
        }
        public int FirstErrorOffset { get; private set; }
        private int FirstErrorOffset { get; set; }
        List<AstNode> visitedNodes;
        readonly List<AstNode> visitedNodes;
        public IEnumerable<AstNode> AstNodes { get { return visitedNodes; } }
        public bool ParsedCorrectly { get; private set; }
        Dictionary<IntPtr, AstNode> nodesBytPtr;
        Dictionary<IntPtr, List<KeyValuePair<AstNode, PropertyInfo>>> pendingDereferences;
        readonly Dictionary<IntPtr, AstNode> nodesBytPtr;
        readonly Dictionary<IntPtr, List<KeyValuePair<AstNode, PropertyInfo>>> pendingDereferences;
        Parser()
        {
@@ -315,8 +314,7 @@
            if (ptrRef == IntPtr.Zero)
                return;
            AstNode nodeRef;
            if (nodesBytPtr.TryGetValue(ptrRef, out nodeRef)) {
            if (nodesBytPtr.TryGetValue(ptrRef, out AstNode nodeRef)) {
                nodeProperty.SetValue(node, nodeRef);
            } else {
                List<KeyValuePair<AstNode, PropertyInfo>> pendingRefList;
QtVsTools.Package/QML/Syntax/QmlAst.cs
@@ -156,7 +156,7 @@
    public class AstNode : SyntaxElement
    {
        public AstNodeKind Kind { get; private set; }
        public AstNodeKind Kind { get; }
        public AstNode(AstNodeKind kind) { Kind = kind; }
        public SourceLocation FirstSourceLocation { get; set; }
        public SourceLocation LastSourceLocation { get; set; }
QtVsTools.Package/QML/Syntax/QmlSyntax.cs
@@ -192,7 +192,7 @@
    /// </summary>
    public class Token : SyntaxElement
    {
        public TokenKind Kind { get; private set; }
        private TokenKind Kind { get; set; }
        public SourceLocation Location { get; private set; }
        protected Token() { }
        public static Token Create(TokenKind kind, int offset, int length)
QtVsTools.Package/QtMenus.vsct_TT
@@ -2,7 +2,7 @@
<!--
    *****************************************************************************
    **
    ** Copyright (C) 2016 The Qt Company Ltd.
    ** Copyright (C) 2022 The Qt Company Ltd.
    ** Contact: https://www.qt.io/licensing/
    **
    ** This file is part of the Qt VS Tools.
@@ -152,15 +152,8 @@
            <Group guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" priority="0x0600">
                <Parent guid="ProjectContextMenuGuid" id="QtProjectSubMenu"/>
            </Group>
            <Group guid="ProjectContextMenuGuid" id="ProjectContextAddNewQtClassMenuGroup" priority="0x0600">
                <Parent guid="guidSHLMainMenu" id="cmdidShellWindowNavigate7"/>
            </Group>
            <!-- Endregion Project context menu groups -->
            <Group guid="ProjectContextMenuGuid" id="ProjectContextAddNewQtClassMenuGroup" priority="0x0600">
                <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_PROJECT" />
            </Group>
            <Group guid="ItemContextMenuGuid" id="ItemContextTsMenuGroup" priority="0x0600">
                <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE" />
@@ -209,6 +202,15 @@
                    <ButtonText>qt.io</ButtonText>
                </Strings>
            </Button>
          <Button guid="MainMenuGuid" id="ViewGettingStartedId" priority="0x0100" type="Button">
            <Parent guid="MainMenuGuid" id="VersionMenuGroup" />
            <Icon guid="MenuImages" id="QtLogoBitmap" />
            <CommandFlag>DynamicVisibility</CommandFlag>
            <CommandFlag>DefaultInvisible</CommandFlag>
            <Strings>
              <ButtonText>Getting Started</ButtonText>
            </Strings>
          </Button>
            <Button guid="MainMenuGuid" id="F1QtHelpId" priority="0x0100" type="Button">
                <Parent guid="MainMenuGuid" id="VersionMenuGroup" />
@@ -285,16 +287,6 @@
                </Strings>
            </Button>
            <Button guid="MainMenuGuid" id="CreateNewTsFileId" priority="0x0100" type="Button">
                <Parent guid="MainMenuGuid" id="OthersMenuGroup" />
                <CommandFlag>DefaultDisabled</CommandFlag>
                <CommandFlag>DynamicVisibility</CommandFlag>
                <CommandFlag>DefaultInvisible</CommandFlag>
                <Strings>
                    <ButtonText>Create New Translation File</ButtonText>
                    <ToolTipText>Create a new translation file that you can open in Qt Linguist</ToolTipText>
                </Strings>
            </Button>
            <Button guid="MainMenuGuid" id="ConvertToQtMsBuild" priority="0x0100" type="Button">
                <Parent guid="MainMenuGuid" id="OthersMenuGroup" />
                <CommandFlag>DefaultDisabled</CommandFlag>
@@ -302,24 +294,6 @@
                <CommandFlag>DynamicVisibility</CommandFlag>
                <Strings>
                    <ButtonText>Convert custom build steps to Qt/MSBuild</ButtonText>
                </Strings>
            </Button>
            <Button guid="MainMenuGuid" id="ConvertToQtId" priority="0x0100" type="Button">
                <Parent guid="MainMenuGuid" id="OthersMenuGroup" />
                <CommandFlag>DefaultDisabled</CommandFlag>
                <CommandFlag>DefaultInvisible</CommandFlag>
                <CommandFlag>DynamicVisibility</CommandFlag>
                <Strings>
                    <ButtonText>Convert Project to Qt VS Tools Project</ButtonText>
                </Strings>
            </Button>
            <Button guid="MainMenuGuid" id="ConvertToQmakeId" priority="0x0100" type="Button">
                <Parent guid="MainMenuGuid" id="OthersMenuGroup" />
                <CommandFlag>DefaultDisabled</CommandFlag>
                <CommandFlag>DynamicVisibility</CommandFlag>
                <CommandFlag>DefaultInvisible</CommandFlag>
                <Strings>
                    <ButtonText>Convert Project to QMake Generated Project</ButtonText>
                </Strings>
            </Button>
            <Button guid="MainMenuGuid" id="QtProjectSettingsId" priority="0x0100" type="Button">
@@ -451,15 +425,6 @@
                </Strings>
            </Button>
            <Button guid="ProjectContextMenuGuid" id="CreateNewTsFileProjectId" priority="0x0100" type="Button">
                <Parent guid="ProjectContextMenuGuid" id="ProjectContextTsMenuGroup" />
                <CommandFlag>DefaultDisabled</CommandFlag>
                <CommandFlag>DynamicVisibility</CommandFlag>
                <Strings>
                    <ButtonText>Create New Translation File</ButtonText>
                    <ToolTipText>Create a new translation file that you can open in Qt Linguist</ToolTipText>
                </Strings>
            </Button>
            <Button guid="ProjectContextMenuGuid" id="lUpdateOnProjectId" priority="0x0100" type="Button">
                <Parent guid="ProjectContextMenuGuid" id="ProjectContextTsMenuGroup" />
                <Icon guid="MenuImages" id="LaunchLinguistBitmap" />
@@ -499,23 +464,6 @@
                    <ButtonText>Refresh IntelliSense</ButtonText>
                </Strings>
            </Button>
            <Button guid="ProjectContextMenuGuid" id="ConvertToQtProjectId" priority="0x0100" type="Button">
                <Parent guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" />
                <CommandFlag>DefaultDisabled</CommandFlag>
                <CommandFlag>DefaultInvisible</CommandFlag>
                <CommandFlag>DynamicVisibility</CommandFlag>
                <Strings>
                    <ButtonText>Convert Project to Qt VS Tools Project</ButtonText>
                </Strings>
            </Button>
            <Button guid="ProjectContextMenuGuid" id="ConvertToQmakeProjectId" priority="0x0100" type="Button">
                <Parent guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" />
                <CommandFlag>DefaultDisabled</CommandFlag>
                <CommandFlag>DynamicVisibility</CommandFlag>
                <Strings>
                    <ButtonText>Convert Project to QMake Generated Project</ButtonText>
                </Strings>
            </Button>
            <Button guid="ProjectContextMenuGuid" id="QtProjectSettingsProjectId" priority="0x0100" type="Button">
                <Parent guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" />
                <CommandFlag>DefaultDisabled</CommandFlag>
@@ -532,16 +480,6 @@
                <CommandFlag>DynamicVisibility</CommandFlag>
                <Strings>
                    <ButtonText>Change Project's Qt Version</ButtonText>
                </Strings>
            </Button>
            <Button guid="ProjectContextMenuGuid" id="ProjectAddNewQtClassProjectId" priority="0x0100" type="Button">
                <Parent guid="ProjectContextMenuGuid" id="ProjectContextAddNewQtClassMenuGroup" />
                <Icon guid="MenuImages" id="AddNewQtClassBitmap" />
                <CommandFlag>DefaultDisabled</CommandFlag>
                <CommandFlag>DefaultInvisible</CommandFlag>
                <CommandFlag>DynamicVisibility</CommandFlag>
                <Strings>
                    <ButtonText>Add Qt Class...</ButtonText>
                </Strings>
            </Button>
@@ -586,7 +524,7 @@
            -->
            <Bitmap guid="MenuImages" href="Resources\menuimages.png" usedList="LaunchDesignerBitmap,
                LaunchLinguistBitmap, OpenProFileBitmap, ImportPriFileBitmap, ExportProFileBitmap,
                CreateProFileBitmap, QtLogoBitmap, AddNewQtClassBitmap" />
                CreateProFileBitmap, QtLogoBitmap" />
        </Bitmaps>
    </Commands>
@@ -605,6 +543,7 @@
            <IDSymbol name="QtVersionId" value="0x0500" />
            <IDSymbol name="ViewQtHelpId" value="0x0501" />
            <IDSymbol name="F1QtHelpId" value="0x0502" />
            <IDSymbol name="ViewGettingStartedId" value="0x0503" />
            <IDSymbol name="LaunchMenuGroup" value="0x1021" />
            <IDSymbol name="LaunchDesignerId" value="0x0100" />
@@ -617,10 +556,7 @@
            <IDSymbol name="ExportProFileId" value="0x0105" />
            <IDSymbol name="OthersMenuGroup" value="0x1023" />
            <IDSymbol name="CreateNewTsFileId" value="0x0107" />
            <IDSymbol name="ConvertToQtMsBuild" value="0x0130" />
            <IDSymbol name="ConvertToQtId" value="0x0124" />
            <IDSymbol name="ConvertToQmakeId" value="0x0108" />
            <IDSymbol name="QtProjectSettingsId" value="0x0109" />
            <IDSymbol name="ChangeProjectQtVersionId" value="0x0126" />
@@ -665,20 +601,16 @@
            <IDSymbol name="ExportProFileProjectId" value="0x0116" />
            <IDSymbol name="ProjectContextTsMenuGroup" value="0x1028" />
            <IDSymbol name="CreateNewTsFileProjectId" value="0x0117" />
            <IDSymbol name="lUpdateOnProjectId" value="0x0118" />
            <IDSymbol name="lReleaseOnProjectId" value="0x0119" />
            <IDSymbol name="ProjectContextOthersMenuGroup" value="0x1029" />
            <IDSymbol name="ProjectConvertToQtMsBuild" value="0x0130" />
            <IDSymbol name="ProjectRefreshIntelliSense" value="0x0131" />
            <IDSymbol name="ConvertToQtProjectId" value="0x0120" />
            <IDSymbol name="ConvertToQmakeProjectId" value="0x0121" />
            <IDSymbol name="QtProjectSettingsProjectId" value="0x0122" />
            <IDSymbol name="ChangeProjectQtVersionProjectId" value="0x0123" />
            <IDSymbol name="ProjectContextAddNewQtClassMenuGroup" value="0x1031" />
            <IDSymbol name="ProjectAddNewQtClassProjectId" value="0x0200" />
            <!-- Endregion Project context menu button Ids -->
@@ -700,7 +632,6 @@
            <IDSymbol name="ExportProFileBitmap" value="5" />
            <IDSymbol name="CreateProFileBitmap" value="6" />
            <IDSymbol name="QtLogoBitmap" value="7" />
            <IDSymbol name="AddNewQtClassBitmap" value="8" />
        </GuidSymbol>
    </Symbols>
QtVsTools.Package/QtMsBuild/QtModulesEditor.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -28,15 +28,15 @@
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.Properties;
using Microsoft.Internal.VisualStudio.PlatformUI;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading.Tasks;
namespace QtVsTools.QtMsBuild
{
    using QtVsTools.Core;
    using Core;
    [Export(typeof(IPropertyPageUIValueEditor))]
    [ExportMetadata("Name", "QtModulesEditor")]
@@ -50,7 +50,15 @@
        {
            await Task.Yield();
            var modules = QtModules.Instance.GetAvailableModules()
            var qtSettings = ruleProperty.ContainingRule;
            var qtVersion = await qtSettings.GetPropertyValueAsync("QtInstall");
            var vm = QtVersionManager.The();
            var versionInfo = vm.GetVersionInfo(qtVersion);
            if (versionInfo == null)
                versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion());
            var modules = QtModules.Instance.GetAvailableModules(versionInfo.qtMajor)
                .Where(x => !string.IsNullOrEmpty(x.proVarQT))
                .Select(x => new QtModulesPopup.Module
                {
@@ -61,24 +69,28 @@
                })
                .ToList();
            var allQT = modules.SelectMany(x => x.QT).ToHashSet();
            var selectedQT = currentValue.ToString().Split(';').ToHashSet();
            var extraQT = selectedQT.Except(allQT);
            HashSet<string> selectedQt = null;
            IEnumerable<string> extraQt = null;
            if (currentValue != null) {
                var allQt = modules.SelectMany(x => x.QT).ToHashSet();
                selectedQt = currentValue.ToString().Split(';').ToHashSet();
                extraQt = selectedQt.Except(allQt);
            foreach (var module in modules)
                module.IsSelected = module.QT.Intersect(selectedQT).Count() == module.QT.Count;
                foreach (var module in modules)
                    module.IsSelected = module.QT.Intersect(selectedQt).Count() == module.QT.Count;
            }
            var popup = new QtModulesPopup();
            popup.SetModules(modules);
            popup.SetModules(modules.OrderBy(module => module.Name));
            if (popup.ShowModal().GetValueOrDefault()) {
                selectedQT = modules
                selectedQt = modules
                    .Where(x => x.IsSelected)
                    .SelectMany(x => x.QT)
                    .Union(extraQT)
                    .Union(extraQt ?? Enumerable.Empty<string>())
                    .ToHashSet();
            }
            return string.Join(";", selectedQT);
            return selectedQt == null ? "" : string.Join(";", selectedQt);
        }
    }
}
QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml
@@ -1,7 +1,7 @@
<!--
*****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -27,10 +27,10 @@
**
*****************************************************************************
-->
<local:VsToolsDialogWindow x:Class="QtVsTools.QtMsBuild.QtModulesPopup"
<vsui:DialogWindow x:Class="QtVsTools.QtMsBuild.QtModulesPopup"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:QtVsTools"
        xmlns:vsui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
        ResizeMode="CanResize"
        Width="800" MaxHeight="550"
        MouseDown="Window_MouseDown"
@@ -108,4 +108,4 @@
            <Button IsCancel="True" MinHeight="23" MinWidth="74">_Cancel</Button>
        </WrapPanel>
    </Grid>
</local:VsToolsDialogWindow>
</vsui:DialogWindow>
QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml.cs
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt VS Tools.
@@ -30,10 +30,11 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.VisualStudio.PlatformUI;
namespace QtVsTools.QtMsBuild
{
    public partial class QtModulesPopup : VsToolsDialogWindow
    public partial class QtModulesPopup : DialogWindow
    {
        public class Module
        {
@@ -81,8 +82,7 @@
        private void PopupListBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter || e.Key == Key.Space) {
                var module = PopupListBox.SelectedItem as Module;
                if (module != null && module.IsEnabled)
                if (PopupListBox.SelectedItem is Module module && module.IsEnabled)
                    module.CheckBox.IsChecked = (module.CheckBox.IsChecked != true);
            }
        }
QtVsTools.Package/QtMsBuild/QtProjectBuild.cs
@@ -40,36 +40,42 @@
using Microsoft.VisualStudio.TaskStatusCenter;
using Microsoft.VisualStudio.Threading;
using Microsoft.VisualStudio.VCProjectEngine;
using EnvDTE;
using Thread = System.Threading.Thread;
namespace QtVsTools.QtMsBuild
{
    using Common;
    using Core;
    using VisualStudio;
    using Thread = System.Threading.Thread;
    using static Common.EnumExt;
    class QtProjectBuild : Concurrent<QtProjectBuild>
    {
        static PunisherQueue<QtProjectBuild> _BuildQueue;
        static PunisherQueue<QtProjectBuild> BuildQueue =>
            StaticThreadSafeInit(() => _BuildQueue,
                () => _BuildQueue = new PunisherQueue<QtProjectBuild>(
                    getItemKey: (QtProjectBuild build) =>
                    {
                        return build.ConfiguredProject;
                    })
                );
        static LazyFactory StaticLazy { get; } = new LazyFactory();
        static ConcurrentStopwatch _RequestTimer;
        static ConcurrentStopwatch RequestTimer =>
            StaticThreadSafeInit(() => _RequestTimer, () => _RequestTimer = new ConcurrentStopwatch());
        public enum Target
        {
            // Mark project as dirty, but do not request a build
            [String("QtVsTools.QtMsBuild.QtProjectBuild.Target.SetOutdated")] SetOutdated
        }
        static IVsTaskStatusCenterService _StatusCenter;
        static IVsTaskStatusCenterService StatusCenter => StaticThreadSafeInit(() => _StatusCenter,
                () => _StatusCenter = VsServiceProvider
                    .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>());
        static PunisherQueue<QtProjectBuild> BuildQueue => StaticLazy.Get(() =>
            BuildQueue, () => new PunisherQueue<QtProjectBuild>(
                getItemKey: (QtProjectBuild build) =>
                {
                    return build.ConfiguredProject;
                }));
        static ConcurrentStopwatch RequestTimer => StaticLazy.Get(() =>
            RequestTimer, () => new ConcurrentStopwatch());
        static IVsTaskStatusCenterService StatusCenter => StaticLazy.Get(() =>
            StatusCenter, () => VsServiceProvider
                .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>());
        EnvDTE.Project Project { get; set; }
        VCProject VcProject { get; set; }
        UnconfiguredProject UnconfiguredProject { get; set; }
        ConfiguredProject ConfiguredProject { get; set; }
        Dictionary<string, string> Properties { get; set; }
@@ -80,6 +86,7 @@
        public static void StartBuild(
            EnvDTE.Project project,
            string projectPath,
            string configName,
            Dictionary<string, string> properties,
            IEnumerable<string> targets,
@@ -90,11 +97,13 @@
            if (configName == null)
                throw new ArgumentException("Configuration name cannot be null.");
            Task.Run(() => StartBuildAsync(project, configName, properties, targets, verbosity));
            _ = Task.Run(() => StartBuildAsync(
                project, projectPath, configName, properties, targets, verbosity));
        }
        public static async Task StartBuildAsync(
            EnvDTE.Project project,
            string projectPath,
            string configName,
            Dictionary<string, string> properties,
            IEnumerable<string> targets,
@@ -106,15 +115,15 @@
                throw new ArgumentException("Configuration name cannot be null.");
            RequestTimer.Restart();
            var tracker = QtProjectTracker.Get(project, projectPath);
            await tracker.Initialized;
            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                Messages.Print(string.Format(
                "{0:HH:mm:ss.FFF} QtProjectBuild({1}): Request [{2}] {3}",
                DateTime.Now, Thread.CurrentThread.ManagedThreadId,
                configName, project.FullName));
                configName, tracker.UnconfiguredProject.FullPath));
            }
            var tracker = QtProjectTracker.Get(project);
            await tracker.Initialized;
            var knownConfigs = await tracker.UnconfiguredProject.Services
                .ProjectConfigurationsService.GetKnownProjectConfigurationsAsync();
@@ -134,6 +143,7 @@
            BuildQueue.Enqueue(new QtProjectBuild()
            {
                Project = project,
                VcProject = tracker.VcProject,
                UnconfiguredProject = tracker.UnconfiguredProject,
                ConfiguredProject = configuredProject,
                Properties = properties?.ToDictionary(x => x.Key, x => x.Value),
@@ -143,6 +153,35 @@
            StaticThreadSafeInit(() => BuildDispatcher,
                () => BuildDispatcher = Task.Run(BuildDispatcherLoopAsync))
                .Forget();
        }
        public static void SetOutdated(
            EnvDTE.Project project,
            string projectPath,
            string configName,
            LoggerVerbosity verbosity = LoggerVerbosity.Quiet)
        {
            if (project == null)
                throw new ArgumentException("Project cannot be null.");
            if (configName == null)
                throw new ArgumentException("Configuration name cannot be null.");
            _ = Task.Run(() => SetOutdatedAsync(project, projectPath, configName, verbosity));
        }
        public static async Task SetOutdatedAsync(
            EnvDTE.Project project,
            string projectPath,
            string configName,
            LoggerVerbosity verbosity = LoggerVerbosity.Quiet)
        {
            await StartBuildAsync(
                project,
                projectPath,
                configName,
                null,
                new[] { Target.SetOutdated.Cast<string>() },
                verbosity);
        }
        public static void Reset()
@@ -161,8 +200,7 @@
                    }
                    await Task.Delay(100);
                }
                QtProjectBuild buildRequest;
                if (BuildQueue.TryDequeue(out buildRequest)) {
                if (BuildQueue.TryDequeue(out QtProjectBuild buildRequest)) {
                    if (dispatchStatus == null) {
                        dispatchStatus = StatusCenter.PreRegister(
                            new TaskHandlerOptions
@@ -202,6 +240,140 @@
            }
        }
        async Task<bool> BuildProjectAsync(ProjectWriteLockReleaser writeAccess)
        {
            var msBuildProject = await writeAccess.GetProjectAsync(ConfiguredProject);
            if (Targets.Any(t => t == Target.SetOutdated.Cast<string>())) {
                msBuildProject.MarkDirty();
                await writeAccess.ReleaseAsync();
                return true;
            }
            var solutionPath = QtProjectTracker.SolutionPath;
            var configProps = new Dictionary<string, string>(
                ConfiguredProject.ProjectConfiguration.Dimensions.ToImmutableDictionary())
                {
                    { "SolutionPath", solutionPath },
                    { "SolutionFileName", Path.GetFileName(solutionPath) },
                    { "SolutionName", Path.GetFileNameWithoutExtension(solutionPath) },
                    { "SolutionExt", Path.GetExtension(solutionPath) },
                    { "SolutionDir", Path.GetDirectoryName(solutionPath).TrimEnd('\\') + '\\'  }
                };
            foreach (var property in Properties)
                configProps[property.Key] = property.Value;
            var projectInstance = new ProjectInstance(msBuildProject.Xml,
                configProps, null, new ProjectCollection());
            var loggerVerbosity = LoggerVerbosity;
            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation)
                loggerVerbosity = QtVsToolsPackage.Instance.Options.BuildLoggerVerbosity;
            var buildParams = new BuildParameters()
            {
                Loggers = (loggerVerbosity != LoggerVerbosity.Quiet)
                        ? new[] { new QtProjectLogger() { Verbosity = loggerVerbosity } }
                        : null
            };
            var buildRequest = new BuildRequestData(projectInstance,
                Targets.ToArray(),
                hostServices: null,
                flags: BuildRequestDataFlags.ProvideProjectStateAfterBuild);
            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                Messages.Print(string.Format(
                    "{0:HH:mm:ss.FFF} QtProjectBuild({1}): Build [{2}] {3}",
                    DateTime.Now, Thread.CurrentThread.ManagedThreadId,
                    ConfiguredProject.ProjectConfiguration.Name,
                    UnconfiguredProject.FullPath));
                Messages.Print("=== Targets");
                foreach (var target in buildRequest.TargetNames)
                    Messages.Print(string.Format("    {0}", target));
                Messages.Print("=== Properties");
                foreach (var property in Properties) {
                    Messages.Print(string.Format("    {0}={1}",
                        property.Key, property.Value));
                }
            }
            BuildResult result = null;
            while (result == null) {
                try {
                    result = BuildManager.DefaultBuildManager.Build(
                        buildParams, buildRequest);
                } catch (InvalidOperationException) {
                    if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                        Messages.Print(string.Format(
                            "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] "
                            + "Warning: Another build is in progress; waiting...",
                            DateTime.Now,
                            Thread.CurrentThread.ManagedThreadId,
                            ConfiguredProject.ProjectConfiguration.Name));
                    }
                    await Task.Delay(3000);
                }
            }
            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                string resMsg;
                StringBuilder resInfo = new StringBuilder();
                if (result?.OverallResult == BuildResultCode.Success) {
                    resMsg = "Build ok";
                } else {
                    resMsg = "Build FAIL";
                    if (result == null) {
                        resInfo.AppendLine("####### Build returned 'null'");
                    } else {
                        resInfo.AppendLine("####### Build returned 'Failure' code");
                        if (result.ResultsByTarget != null) {
                            foreach (var tr in result.ResultsByTarget) {
                                var res = tr.Value;
                                if (res.ResultCode != TargetResultCode.Failure)
                                    continue;
                                resInfo.AppendFormat("### Target '{0}' FAIL\r\n", tr.Key);
                                if (res.Items != null && res.Items.Length > 0) {
                                    resInfo.AppendFormat(
                                        "Items: {0}\r\n", string.Join(", ", res.Items
                                            .Select(it => it.ItemSpec)));
                                }
                                var e = tr.Value?.Exception;
                                if (e != null) {
                                    resInfo.AppendFormat(
                                        "Exception: {0}\r\nStacktrace:\r\n{1}\r\n",
                                        e.Message, e.StackTrace);
                                }
                            }
                        }
                    }
                }
                Messages.Print(string.Format(
                    "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] {3}\r\n{4}",
                    DateTime.Now, Thread.CurrentThread.ManagedThreadId,
                    ConfiguredProject.ProjectConfiguration.Name,
                    resMsg, resInfo.ToString()));
            }
            bool ok = false;
            if (result == null
                || result.ResultsByTarget == null
                || result.OverallResult != BuildResultCode.Success) {
                Messages.Print(string.Format("{0}: background build FAILED!",
                        Path.GetFileName(UnconfiguredProject.FullPath)));
            } else {
                var checkResults = result.ResultsByTarget
                    .Where(x => Targets.Contains(x.Key))
                    .Select(x => x.Value);
                ok = checkResults.Any()
                    && checkResults.All(x => x.ResultCode == TargetResultCode.Success);
                if (ok)
                    msBuildProject.MarkDirty();
            }
            await writeAccess.ReleaseAsync();
            return ok;
        }
        async Task BuildAsync()
        {
            if (LoggerVerbosity != LoggerVerbosity.Quiet) {
@@ -211,7 +383,7 @@
  * Properties: {1}
  * Targets: {2}
",
                    /*{0}*/ Project.Name,
                    /*{0}*/ Path.GetFileNameWithoutExtension(UnconfiguredProject.FullPath),
                    /*{1}*/ string.Join("", Properties
                        .Select(property => string.Format(@"
        {0} = {1}",     /*{0}*/ property.Key, /*{1}*/ property.Value))),
@@ -222,151 +394,41 @@
            bool ok = false;
            try {
                ProjectWriteLockReleaser writeAccess;
                var timer = ConcurrentStopwatch.StartNew();
                while (timer.IsRunning) {
                    try {
                        writeAccess = await lockService.WriteLockAsync();
#if VS2017
                        using (var writeAccess = await lockService.WriteLockAsync())
                            ok = await BuildProjectAsync(writeAccess);
#else
                        await lockService.WriteLockAsync(
                            async (ProjectWriteLockReleaser writeAccess) =>
                            {
                                ok = await BuildProjectAsync(writeAccess);
                            });
#endif
                        timer.Stop();
                    } catch (InvalidOperationException) {
                        if (timer.ElapsedMilliseconds >= 5000)
                            throw;
#if VS2017
                        using (var readAccess = await lockService.ReadLockAsync())
                            await readAccess.ReleaseAsync();
#else
                        await lockService.ReadLockAsync(
                            async (ProjectLockReleaser readAccess) =>
                            {
                                await readAccess.ReleaseAsync();
                            });
#endif
                    }
                }
                using (writeAccess) {
                    var msBuildProject = await writeAccess.GetProjectAsync(ConfiguredProject);
                    var solutionPath = QtProjectTracker.SolutionPath;
                    var configProps = new Dictionary<string, string>(
                        ConfiguredProject.ProjectConfiguration.Dimensions.ToImmutableDictionary())
                    {
                        { "SolutionPath", solutionPath },
                        { "SolutionFileName", Path.GetFileName(solutionPath) },
                        { "SolutionName", Path.GetFileNameWithoutExtension(solutionPath) },
                        { "SolutionExt", Path.GetExtension(solutionPath) },
                        { "SolutionDir", Path.GetDirectoryName(solutionPath).TrimEnd('\\') + '\\'  }
                    };
                    foreach (var property in Properties)
                        configProps[property.Key] = property.Value;
                    var projectInstance = new ProjectInstance(msBuildProject.Xml,
                        configProps, null, new ProjectCollection());
                    var loggerVerbosity = LoggerVerbosity;
                    if (QtVsToolsPackage.Instance.Options.BuildDebugInformation)
                        loggerVerbosity = QtVsToolsPackage.Instance.Options.BuildLoggerVerbosity;
                    var buildParams = new BuildParameters()
                    {
                        Loggers = (loggerVerbosity != LoggerVerbosity.Quiet)
                                ? new[] { new QtProjectLogger() { Verbosity = loggerVerbosity } }
                                : null
                    };
                    var buildRequest = new BuildRequestData(projectInstance,
                        Targets.ToArray(),
                        hostServices: null,
                        flags: BuildRequestDataFlags.ProvideProjectStateAfterBuild);
                    if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                        Messages.Print(string.Format(
                            "{0:HH:mm:ss.FFF} QtProjectBuild({1}): Build [{2}] {3}",
                            DateTime.Now, Thread.CurrentThread.ManagedThreadId,
                            ConfiguredProject.ProjectConfiguration.Name,
                            UnconfiguredProject.FullPath));
                        Messages.Print("=== Targets");
                        foreach (var target in buildRequest.TargetNames)
                            Messages.Print(string.Format("    {0}", target));
                        Messages.Print("=== Properties");
                        foreach (var property in Properties) {
                            Messages.Print(string.Format("    {0}={1}",
                                property.Key, property.Value));
                        }
                    }
                    BuildResult result = null;
                    while (result == null) {
                        try {
                            result = BuildManager.DefaultBuildManager.Build(
                                buildParams, buildRequest);
                        } catch (InvalidOperationException) {
                            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                                Messages.Print(string.Format(
                                    "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] "
                                    + "Warning: Another build is in progress; waiting...",
                                    DateTime.Now,
                                    Thread.CurrentThread.ManagedThreadId,
                                    ConfiguredProject.ProjectConfiguration.Name));
                            }
                            await Task.Delay(3000);
                        }
                    }
                    if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                        string resMsg;
                        StringBuilder resInfo = new StringBuilder();
                        if (result?.OverallResult == BuildResultCode.Success) {
                            resMsg = "Build ok";
                        } else {
                            resMsg = "Build FAIL";
                            if (result == null) {
                                resInfo.AppendLine("####### Build returned 'null'");
                            } else {
                                resInfo.AppendLine("####### Build returned 'Failure' code");
                                if (result.ResultsByTarget != null) {
                                    foreach (var tr in result.ResultsByTarget) {
                                        var res = tr.Value;
                                        if (res.ResultCode != TargetResultCode.Failure)
                                            continue;
                                        resInfo.AppendFormat("### Target '{0}' FAIL\r\n", tr.Key);
                                        if (res.Items != null && res.Items.Length > 0) {
                                            resInfo.AppendFormat(
                                                "Items: {0}\r\n", string.Join(", ", res.Items
                                                    .Select(it => it.ItemSpec)));
                                        }
                                        var e = tr.Value?.Exception;
                                        if (e != null) {
                                            resInfo.AppendFormat(
                                                "Exception: {0}\r\nStacktrace:\r\n{1}\r\n",
                                                e.Message, e.StackTrace);
                                        }
                                    }
                                }
                            }
                        }
                        Messages.Print(string.Format(
                            "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] {3}\r\n{4}",
                            DateTime.Now, Thread.CurrentThread.ManagedThreadId,
                            ConfiguredProject.ProjectConfiguration.Name,
                            resMsg, resInfo.ToString()));
                    }
                    if (result == null
                        || result.ResultsByTarget == null
                        || result.OverallResult != BuildResultCode.Success) {
                        Messages.Print(string.Format("{0}: background build FAILED!",
                                Path.GetFileName(UnconfiguredProject.FullPath)));
                    } else {
                        var checkResults = result.ResultsByTarget
                            .Where(x => Targets.Contains(x.Key))
                            .Select(x => x.Value);
                        ok = checkResults.Any()
                            && checkResults.All(x => x.ResultCode == TargetResultCode.Success);
                        if (ok)
                            msBuildProject.MarkDirty();
                    }
                    await writeAccess.ReleaseAsync();
                }
                if (ok) {
                    var vcProject = Project.Object as VCProject;
                    var vcConfigs = vcProject.Configurations as IVCCollection;
                    var vcConfigs = VcProject.Configurations as IVCCollection;
                    var vcConfig = vcConfigs.Item(ConfiguredProject.ProjectConfiguration.Name) as VCConfiguration;
                    var props = vcConfig.Rules.Item("QtRule10_Settings") as IVCRulePropertyStorage;
                    props.SetPropertyValue("QtLastBackgroundBuild", DateTime.UtcNow.ToString("o"));
                    props?.SetPropertyValue("QtLastBackgroundBuild", DateTime.UtcNow.ToString("o"));
                }
            } catch (Exception e) {
                Messages.Print(string.Format("{0}: background build ERROR: {1}",
@@ -377,7 +439,8 @@
                Messages.Print(string.Format(
@"
== {0}: build {1}",
                    Project.Name, ok ? "successful" : "ERROR"));
                    Path.GetFileNameWithoutExtension(UnconfiguredProject.FullPath),
                    ok ? "successful" : "ERROR"));
            }
        }
    }
QtVsTools.Package/QtMsBuild/QtProjectIntelliSense.cs
@@ -31,12 +31,15 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Threading;
using Task = System.Threading.Tasks.Task;
using Thread = System.Threading.Thread;
namespace QtVsTools.QtMsBuild
{
    using Core;
    using Thread = System.Threading.Thread;
    static class QtProjectIntellisense
    {
@@ -45,25 +48,31 @@
            string configId = null,
            IEnumerable<string> selectedFiles = null)
        {
            if (project == null || !QtProjectTracker.IsTracked(project))
            ThreadHelper.ThrowIfNotOnUIThread();
            if (project == null || !QtProjectTracker.IsTracked(project.FullName))
                return;
            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                Messages.Print(string.Format(
                    "{0:HH:mm:ss.FFF} QtProjectIntellisense({1}): Refreshing: [{2}] {3}",
                    DateTime.Now, Thread.CurrentThread.ManagedThreadId,
                    (configId != null) ? configId : "(all configs)", project.FullName));
            }
            Task.Run(() => RefreshAsync(project, configId, selectedFiles));
            string projectPath = project.FullName;
            _ = Task.Run(() => RefreshAsync(project, projectPath, configId, selectedFiles, false));
        }
        public static async Task RefreshAsync(
            EnvDTE.Project project,
            string projectPath,
            string configId = null,
            IEnumerable<string> selectedFiles = null)
            IEnumerable<string> selectedFiles = null,
            bool refreshQtVars = false)
        {
            if (project == null || !QtProjectTracker.IsTracked(project))
            if (project == null || !QtProjectTracker.IsTracked(projectPath))
                return;
            var tracker = QtProjectTracker.Get(project);
            var tracker = QtProjectTracker.Get(project, projectPath);
            await tracker.Initialized;
            var properties = new Dictionary<string, string>();
@@ -84,9 +93,14 @@
            }
            foreach (var config in configs) {
                await QtProjectBuild.StartBuildAsync(
                    project, config, properties, targets,
                    LoggerVerbosity.Quiet);
                if (refreshQtVars) {
                    await QtProjectBuild.StartBuildAsync(
                        project, projectPath, config, properties, targets,
                        LoggerVerbosity.Quiet);
                } else {
                    await QtProjectBuild.SetOutdatedAsync(
                        project, projectPath, config, LoggerVerbosity.Quiet);
                }
            }
        }
    }
QtVsTools.Package/QtMsBuild/QtProjectTracker.cs
@@ -29,41 +29,41 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.Properties;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.TaskStatusCenter;
using Microsoft.VisualStudio.Threading;
using EnvDTE;
using Microsoft.VisualStudio.VCProjectEngine;
using Task = System.Threading.Tasks.Task;
namespace QtVsTools.QtMsBuild
{
    using Common;
    using Core;
    using VisualStudio;
    using Thread = System.Threading.Thread;
    using SubscriberAction = ActionBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>>;
    class QtProjectTracker : Concurrent<QtProjectTracker>
    {
        static ConcurrentDictionary<string, QtProjectTracker> _Instances;
        static ConcurrentDictionary<string, QtProjectTracker> Instances =>
            StaticThreadSafeInit(() => _Instances, () =>
                _Instances = new ConcurrentDictionary<string, QtProjectTracker>());
        static LazyFactory StaticLazy { get; } = new LazyFactory();
        static PunisherQueue<QtProjectTracker> _InitQueue;
        static PunisherQueue<QtProjectTracker> InitQueue =>
            StaticThreadSafeInit(() => _InitQueue, () =>
                _InitQueue = new PunisherQueue<QtProjectTracker>());
        static ConcurrentDictionary<string, QtProjectTracker> Instances => StaticLazy.Get(() =>
            Instances, () => new ConcurrentDictionary<string, QtProjectTracker>());
        static IVsTaskStatusCenterService _StatusCenter;
        static IVsTaskStatusCenterService StatusCenter => StaticThreadSafeInit(() => _StatusCenter,
                () => _StatusCenter = VsServiceProvider
                    .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>());
        static PunisherQueue<QtProjectTracker> InitQueue => StaticLazy.Get(() =>
            InitQueue, () => new PunisherQueue<QtProjectTracker>());
        static IVsTaskStatusCenterService StatusCenter => StaticLazy.Get(() =>
            StatusCenter, () => VsServiceProvider
                .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>());
        static Task InitDispatcher { get; set; }
        static ITaskHandler2 InitStatus { get; set; }
@@ -75,73 +75,37 @@
            Initialized = new EventWaitHandle(false, EventResetMode.ManualReset);
        }
        class Subscriber : IDisposable
        {
            public Subscriber(QtProjectTracker tracker, ConfiguredProject config)
            {
                Tracker = tracker;
                Config = config;
                Subscription = Config.Services.ProjectSubscription.JointRuleSource.SourceBlock
                    .LinkTo(new SubscriberAction(ProjectUpdateAsync),
                        ruleNames: new[]
                        {
                            "ClCompile",
                            "QtRule10_Settings",
                            "QtRule30_Moc",
                            "QtRule40_Rcc",
                            "QtRule60_Repc",
                            "QtRule50_Uic",
                            "QtRule_Translation",
                            "QtRule70_Deploy",
                        },
                        initialDataAsNew: false
                    );
            }
            QtProjectTracker Tracker { get; set; }
            ConfiguredProject Config { get; set; }
            IDisposable Subscription { get; set; }
            public void Dispose()
            {
                Subscription?.Dispose();
                Subscription = null;
            }
            async Task ProjectUpdateAsync(IProjectVersionedValue<IProjectSubscriptionUpdate> update)
            {
                await Tracker.OnProjectUpdateAsync(Config, update.Value);
            }
        }
        public EnvDTE.Project Project { get; private set; }
        public string ProjectPath { get; private set; }
        public VCProject VcProject { get; private set; }
        public UnconfiguredProject UnconfiguredProject { get; private set; }
        public EventWaitHandle Initialized { get; private set; }
        List<Subscriber> Subscribers { get; set; }
        public EventWaitHandle Initialized { get; }
        public static bool IsTracked(EnvDTE.Project project)
        public static bool IsTracked(string projectPath)
        {
            return Instances.ContainsKey(project.FullName);
            return Instances.ContainsKey(projectPath);
        }
        public static void Add(EnvDTE.Project project)
        {
            if (!QtVsToolsPackage.Instance.Options.ProjectTracking)
                return;
            Get(project);
            ThreadHelper.ThrowIfNotOnUIThread();
            Get(project, project.FullName);
        }
        public static QtProjectTracker Get(EnvDTE.Project project)
        public static QtProjectTracker Get(EnvDTE.Project project, string projectPath)
        {
            lock (StaticCriticalSection) {
                QtProjectTracker tracker = null;
                if (Instances.TryGetValue(project.FullName, out tracker))
                if (Instances.TryGetValue(projectPath, out QtProjectTracker tracker))
                    return tracker;
                tracker = new QtProjectTracker
                {
                    Project = project,
                    ProjectPath = projectPath
                };
                Instances[project.FullName] = tracker;
                Instances[projectPath] = tracker;
                InitQueue.Enqueue(tracker);
                if (InitDispatcher == null)
                    InitDispatcher = Task.Run(InitDispatcherLoopAsync);
@@ -162,14 +126,17 @@
            while (!QtVsToolsPackage.Instance.Zombied) {
                while (InitQueue.IsEmpty)
                    await Task.Delay(100);
                QtProjectTracker tracker;
                if (InitQueue.TryDequeue(out tracker)) {
                if (InitQueue.TryDequeue(out QtProjectTracker tracker)) {
                    if (InitStatus == null) {
                        await QtVsToolsPackage.Instance.JoinableTaskFactory.SwitchToMainThreadAsync();
                        await QtVsToolsPackage.Instance.JoinableTaskFactory
                            .SwitchToMainThreadAsync();
                        tracker.BeginInitStatus();
                        await TaskScheduler.Default;
                    } else {
                        await QtVsToolsPackage.Instance.JoinableTaskFactory
                            .SwitchToMainThreadAsync();
                        tracker.UpdateInitStatus(0);
                        await TaskScheduler.Default;
                    }
                    await tracker.InitializeAsync();
                }
@@ -187,9 +154,12 @@
        async Task InitializeAsync()
        {
            int p = 0;
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            UpdateInitStatus(p += 10);
            await QtVsToolsPackage.Instance.JoinableTaskFactory.SwitchToMainThreadAsync();
            VcProject = Project.Object as VCProject;
            if (VcProject == null)
                return;
            UpdateInitStatus(p += 10);
            var context = Project.Object as IVsBrowseObjectContext;
@@ -211,57 +181,23 @@
            Initialized.Set();
            Subscribers = new List<Subscriber>();
            int n = configs.Count;
            int d = (100 - p) / (n * 2);
            foreach (var config in configs) {
                var configProject = await UnconfiguredProject.LoadConfiguredProjectAsync(config);
                UpdateInitStatus(p += d);
                Subscribers.Add(new Subscriber(this, configProject));
                configProject.ProjectUnloading += OnProjectUnloading;
                configProject.ProjectUnloading += OnProjectUnloadingAsync;
                if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                    Messages.Print(string.Format(
                        "{0:HH:mm:ss.FFF} QtProjectTracker({1}): Started tracking [{2}] {3}",
                        DateTime.Now, Thread.CurrentThread.ManagedThreadId,
                        config.Name,
                        UnconfiguredProject.FullPath));
                        config.Name, ProjectPath));
                }
                UpdateInitStatus(p += d);
            }
        }
        async Task OnProjectUpdateAsync(ConfiguredProject config, IProjectSubscriptionUpdate update)
        {
            var changes = update.ProjectChanges.Values
                .Where(x => x.Difference.AnyChanges)
                .Select(x => x.Difference);
            var changesCount = changes
                .Select(x => x.AddedItems.Count
                    + x.ChangedItems.Count
                    + x.ChangedProperties.Count
                    + x.RemovedItems.Count
                    + x.RenamedItems.Count)
                .Sum();
            var changedProps = changes.SelectMany(x => x.ChangedProperties);
            if (changesCount == 0
                || (changesCount == 1
                    && changedProps.Count() == 1
                    && changedProps.First() == "QtLastBackgroundBuild")) {
                return;
            }
            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                Messages.Print(string.Format(
                    "{0:HH:mm:ss.FFF} QtProjectTracker({1}): Changed [{2}] {3}",
                    DateTime.Now, Thread.CurrentThread.ManagedThreadId,
                    config.ProjectConfiguration.Name,
                    config.UnconfiguredProject.FullPath));
            }
            await QtProjectIntellisense.RefreshAsync(Project, config.ProjectConfiguration.Name);
        }
        async Task OnProjectUnloading(object sender, EventArgs args)
        async Task OnProjectUnloadingAsync(object sender, EventArgs args)
        {
            var project = sender as ConfiguredProject;
            if (project == null || project.Services == null)
@@ -274,18 +210,16 @@
                    project.UnconfiguredProject.FullPath));
            }
            lock (CriticalSection) {
                if (Subscribers != null) {
                    Subscribers.ForEach(s => s.Dispose());
                    Subscribers.Clear();
                    Subscribers = null;
                }
                project.ProjectUnloading -= OnProjectUnloading;
                Instances.TryRemove(Project.FullName, out QtProjectTracker tracker);
                project.ProjectUnloading -= OnProjectUnloadingAsync;
                Instances.TryRemove(project.UnconfiguredProject.FullPath, out QtProjectTracker _);
            }
            await Task.Yield();
        }
        void BeginInitStatus()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            lock (StaticCriticalSection) {
                if (InitStatus != null)
                    return;
@@ -303,9 +237,8 @@
                            PercentComplete = 0
                        })
                        as ITaskHandler2;
                } catch (Exception e) {
                    Messages.Print(
                        e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                } catch (Exception exception) {
                    exception.Log();
                }
                InitStatus.RegisterTask(new Task(() => throw new InvalidOperationException()));
            }
@@ -320,13 +253,12 @@
                    InitStatus.Progress.Report(new TaskProgressData
                    {
                        ProgressText = string.Format("{0} ({1} project(s) remaining)",
                            Project.Name, InitQueue.Count),
                            Path.GetFileNameWithoutExtension(ProjectPath), InitQueue.Count),
                        CanBeCanceled = true,
                        PercentComplete = percentComplete
                    });
                } catch (Exception e) {
                    Messages.Print(
                        e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                } catch (Exception exception) {
                    exception.Log();
                }
            }
        }
QtVsTools.Package/QtMsBuild/QtVersionProvider.cs
@@ -57,7 +57,6 @@
        public async Task<ICollection<IEnumValue>> GetListedValuesAsync()
        {
            List<IEnumValue> values = new List<IEnumValue>();
            using (var qtVersions = Registry.CurrentUser.OpenSubKey(@"Software\Digia\Versions")) {
                return await Task.FromResult(
QtVsTools.Package/QtVsTools.Icons.pkgdef
New file
@@ -0,0 +1,20 @@
[$RootKey$\ShellFileAssociations\.prf]
"DefaultIconMoniker"="0d2e443f-6dbb-4001-99dc-9cd7d5c924e7:0"
[$RootKey$\ShellFileAssociations\.pri]
"DefaultIconMoniker"="0d2e443f-6dbb-4001-99dc-9cd7d5c924e7:1"
[$RootKey$\ShellFileAssociations\.pro]
"DefaultIconMoniker"="0d2e443f-6dbb-4001-99dc-9cd7d5c924e7:2"
[$RootKey$\ShellFileAssociations\.qml]
"DefaultIconMoniker"="0d2e443f-6dbb-4001-99dc-9cd7d5c924e7:3"
[$RootKey$\ShellFileAssociations\.qrc]
"DefaultIconMoniker"="0d2e443f-6dbb-4001-99dc-9cd7d5c924e7:4"
[$RootKey$\ShellFileAssociations\.ts]
"DefaultIconMoniker"="0d2e443f-6dbb-4001-99dc-9cd7d5c924e7:5"
[$RootKey$\ShellFileAssociations\.ui]
"DefaultIconMoniker"="0d2e443f-6dbb-4001-99dc-9cd7d5c924e7:6"
QtVsTools.Package/QtVsTools.Package.csproj
@@ -90,59 +90,45 @@
    <Reference Include="System.Data" />
    <Reference Include="System.Design" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.ComponentModel.Composition" />
    <Reference Include="System.Xaml" />
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
    <Reference Include="WindowsBase" />
    <Reference Include="Microsoft.VisualStudio.ProjectSystem">
      <HintPath>$(VsInstallRoot)\Common7\IDE\CommonExtensions\Microsoft\Project\Microsoft.VisualStudio.ProjectSystem.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.VisualStudio.Composition">
      <HintPath>$(VsInstallRoot)\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.Composition.dll</HintPath>
    </Reference>
  </ItemGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Version specific references
  // General package references
  // -->
  <Import Project="$(SolutionDir)\references.props" />
  <ItemGroup>
    <PackageReference Include="System.ComponentModel.Composition"
      Version="$(Version_System_ComponentModel_Composition)" />
    <PackageReference Include="Stub.System.Data.SQLite.Core.NetFramework"
      Version="$(Version_Stub_System_Data_SQLite_Core_NetFramework)"
      GeneratePathProperty="true" />
    <PackageReference Include="Newtonsoft.Json"
      Version="$(Version_Newtonsoft_Json)" />
    <PackageReference Include="Microsoft.Build"
      Version="$(Version_Microsoft_Build)" />
    <PackageReference Include="Microsoft.Build.Framework"
      Version="$(Version_Microsoft_Build_Framework)" />
    <PackageReference Include="Microsoft.Build.Tasks.Core"
      Version="$(Version_Microsoft_Build_Tasks_Core)" />
    <PackageReference Include="Microsoft.VisualStudio.SDK"
      Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" />
    <PackageReference Include="Microsoft.VSSDK.BuildTools"
      Version="$(Version_Microsoft_VSSDK_BuildTools)" />
    <PackageReference Include="Microsoft.VisualStudio.Shell.Framework"
      Version="$(Version_Microsoft_VisualStudio_Shell_Framework)" />
    <PackageReference Include="Microsoft.VisualStudio.Validation"
      Version="$(Version_Microsoft_VisualStudio_Validation)" />
    <PackageReference Include="$(Name_Microsoft_VisualStudio_Threading)"
      Version="$(Version_Microsoft_VisualStudio_Threading)" />
    <PackageReference Include="System.Collections.Immutable"
      Version="$(Version_System_Collections_Immutable)" />
    <PackageReference Include="$(Name_Microsoft_VSSDK_BuildTools)" Version="$(Version_Microsoft_VSSDK_BuildTools)" />
    <PackageReference Include="$(Name_Microsoft_VisualStudio_SDK)" Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" />
    <PackageReference Include="$(Name_Microsoft_VisualStudio_ProjectSystem)" Version="$(Version_Microsoft_VisualStudio_ProjectSystem)" />
    <PackageReference Include="$(Name_Newtonsoft_Json)" Version="$(Version_Newtonsoft_Json)" />
    <PackageReference Include="$(Name_Stub_System_Data_SQLite_Core_NetFramework)" Version="$(Version_Stub_System_Data_SQLite_Core_NetFramework)" GeneratePathProperty="true" />
    <PackageReference Include="$(Name_Microsoft_VisualStudio_Shell_15)" Version="$(Version_Microsoft_VisualStudio_Shell_15)" />
  </ItemGroup>
  <ItemGroup Condition="'$(VisualStudioVersion)'=='17.0'">
    <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)"
      Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" />
  </ItemGroup>
  <ItemGroup Condition="'$(VisualStudioVersion)'=='16.0'">
    <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)"
      Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" />
  </ItemGroup>
  <ItemGroup Condition="'$(VisualStudioVersion)'=='15.0'">
    <Reference Include="Microsoft.VisualStudio.VCProjectEngine" />
  </ItemGroup>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Version specific package references
  // -->
  <Choose>
    <When Condition="'$(VisualStudioVersion)'=='17.0'">
      <ItemGroup>
      </ItemGroup>
    </When>
    <When Condition="'$(VisualStudioVersion)'=='16.0'">
      <ItemGroup>
      </ItemGroup>
    </When>
    <When Condition="'$(VisualStudioVersion)'=='15.0'">
      <ItemGroup>
        <Reference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" />
        <PackageReference Include="$(Name_Microsoft_VisualStudio_Shell_Framework)" Version="$(Version_Microsoft_VisualStudio_Shell_Framework)" />
      </ItemGroup>
    </When>
  </Choose>
  <!--
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Solution project references
@@ -173,6 +159,27 @@
      <Project>{4cee73c9-fcfa-3a72-a0a3-036bdbb3240f}</Project>
      <Name>qrceditor</Name>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
    </ProjectReference>
    <ProjectReference Include="..\Templates\qtclass\QtTemplate.Item.QtClass.csproj">
      <Project>{4981AAE8-9AC7-4758-87EA-FB2397D6C404}</Project>
      <Name>QtTemplate.Item.QtClass</Name>
      <VSIXSubPath>ItemTemplates</VSIXSubPath>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
      <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX>
    </ProjectReference>
    <ProjectReference Include="..\Templates\translation\QtTemplate.Item.Translation.csproj">
      <Project>{202F4A6D-77CD-4992-AA53-01B585463287}</Project>
      <Name>QtTemplate.Item.Translation</Name>
      <VSIXSubPath>ItemTemplates</VSIXSubPath>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
      <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX>
    </ProjectReference>
    <ProjectReference Include="..\Templates\widgetsclass\QtTemplate.Item.WidgetsClass.csproj">
      <Project>{020422DA-33AB-4495-A439-7DAC2690795C}</Project>
      <Name>QtTemplate.Item.WidgetsClass</Name>
      <VSIXSubPath>ItemTemplates</VSIXSubPath>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
      <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX>
    </ProjectReference>
    <ProjectReference Include="..\vsqml\vsqml.vcxproj">
      <Project>{b12702ad-abfb-343a-a199-8e24837244a3}</Project>
@@ -303,6 +310,12 @@
      <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn>
      <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput>
    </T4Template>
    <Compile Include="Legacy\ProjectQtSettings.cs" />
    <Compile Include="Legacy\QtMenu.cs" />
    <Compile Include="Legacy\QtOptionsPage.cs">
      <SubType>Component</SubType>
    </Compile>
    <Compile Include="Legacy\Translation.cs" />
    <Compile Include="Properties\AssemblyInfo.tt.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
@@ -321,15 +334,11 @@
      <DesignTime>True</DesignTime>
      <DependentUpon>QtMenus.vsct_TT</DependentUpon>
    </VSCTCompile>
    <Content Include="Resources\Vsix.ico" />
    <EmbeddedResource Include="VSPackage.resx">
      <MergeWithCTO>true</MergeWithCTO>
      <ManifestResourceName>VSPackage</ManifestResourceName>
    </EmbeddedResource>
    <Compile Include="Package\AddTranslationDialog.cs">
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="Package\ChangeFor.cs" />
    <Compile Include="Legacy\ChangeFor.cs" />
    <Compile Include="Common\Concurrent.cs" />
    <Compile Include="Common\ConcurrentStopwatch.cs" />
    <Compile Include="Common\Disposable.cs" />
@@ -348,20 +357,17 @@
    <Compile Include="Editors\Editor.QtLinguist.cs" />
    <Compile Include="Editors\Editor.QtResourceEditor.cs" />
    <Compile Include="Package\ExtLoader.cs" />
    <Compile Include="Package\FormChangeQtVersion.cs">
    <Compile Include="Legacy\FormChangeQtVersion.cs">
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="Package\FormChangeQtVersion.Designer.cs">
    <Compile Include="Legacy\FormChangeQtVersion.Designer.cs">
      <DependentUpon>FormChangeQtVersion.cs</DependentUpon>
    </Compile>
    <Compile Include="Package\FormProjectQtSettings.cs">
    <Compile Include="Legacy\FormProjectQtSettings.cs">
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="Package\FormProjectQtSettings.Designer.cs">
    <Compile Include="Legacy\FormProjectQtSettings.Designer.cs">
      <DependentUpon>FormProjectQtSettings.cs</DependentUpon>
    </Compile>
    <Compile Include="Options\QtLegacyOptionsPage.cs">
      <SubType>Component</SubType>
    </Compile>
    <Compile Include="Options\QtOptionsPage.cs">
      <SubType>Component</SubType>
@@ -372,7 +378,6 @@
    <Compile Include="Options\QtVersionsPage.cs">
      <SubType>Component</SubType>
    </Compile>
    <Compile Include="Package\ProjectQtSettings.cs" />
    <Compile Include="Package\QMakeWrapper.cs" />
    <Compile Include="QML\Classification\QmlAsyncClassifier.cs" />
    <Compile Include="QML\Classification\QmlClassificationFormat.cs" />
@@ -437,9 +442,7 @@
    <Compile Include="Package\QtSolutionContextMenu.cs" />
    <Compile Include="Package\SR.cs" />
    <Compile Include="Package\Translation.cs" />
    <Compile Include="Package\TranslationItem.cs" />
    <Compile Include="VisualStudio\VsShell.cs" />
    <Compile Include="Common\VsToolsDialogWindow.cs" />
    <Compile Include="Package\Notifications.cs" />
    <Content Include="..\Changelog">
      <Link>Changelog</Link>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
@@ -456,6 +459,9 @@
      <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn>
      <LastGenOutput>source.extension.vsixmanifest</LastGenOutput>
    </T4Template>
    <Content Include="Icons\Monikers.imagemanifest">
      <IncludeInVSIX>true</IncludeInVSIX>
    </Content>
    <None Include="source.extension.vsixmanifest">
      <DependentUpon>source.extension.vsixmanifest_TT</DependentUpon>
      <SubType>Designer</SubType>
@@ -473,6 +479,16 @@
      <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn>
      <LastGenOutput>Overview.html</LastGenOutput>
    </T4Template>
    <Content Include="QtVsTools.Icons.pkgdef">
      <IncludeInVSIX>true</IncludeInVSIX>
    </Content>
    <Resource Include="Icons\prf32.png" />
    <Resource Include="Icons\pri32.png" />
    <Resource Include="Icons\pro32.png" />
    <Resource Include="Icons\qml32.png" />
    <Resource Include="Icons\qrc32.png" />
    <Resource Include="Icons\ts32.png" />
    <Resource Include="Icons\ui32.png" />
    <Content Include="Marketplace\Overview.html">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
@@ -499,6 +515,10 @@
      <IncludeInVSIX>true</IncludeInVSIX>
      <SubType>Designer</SubType>
    </Content>
    <Content Include="qt6modules.xml">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <IncludeInVSIX>true</IncludeInVSIX>
    </Content>
    <Content Include="QtVsTools.ico" />
    <Content Include="QtVsTools.Qml.Debug.pkgdef">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
@@ -509,14 +529,12 @@
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <IncludeInVSIX>true</IncludeInVSIX>
    </Content>
    <EmbeddedResource Include="Package\AddTranslationDialog.resx">
      <DependentUpon>AddTranslationDialog.cs</DependentUpon>
    </EmbeddedResource>
    <EmbeddedResource Include="Package\FormChangeQtVersion.resx">
    <EmbeddedResource Include="Legacy\FormChangeQtVersion.resx">
      <DependentUpon>FormChangeQtVersion.cs</DependentUpon>
    </EmbeddedResource>
    <EmbeddedResource Include="Package\FormProjectQtSettings.resx">
    <EmbeddedResource Include="Legacy\FormProjectQtSettings.resx">
      <DependentUpon>FormProjectQtSettings.cs</DependentUpon>
      <SubType>Designer</SubType>
    </EmbeddedResource>
    <EmbeddedResource Include="Resources.resx">
      <SubType>Designer</SubType>
@@ -546,8 +564,8 @@
    </Page>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
  <Import Project="$(SolutionDir)\transform.targets" />
  <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
  <Target Name="QtVsTools_PostBuild" AfterTargets="Build">
    <Error Condition="!Exists('$(TargetPath)')" Text="Build failed." />
    <PropertyGroup>
@@ -602,19 +620,13 @@
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // NuGet native libs
  // -->
  <PropertyGroup
    Condition="'$(PkgStub_System_Data_SQLite_Core_NetFramework)' != ''">
    <SQLitePkgDir Condition="'$(SQLitePkgDir)' == ''"
      >$(PkgStub_System_Data_SQLite_Core_NetFramework)</SQLitePkgDir>
  <PropertyGroup Condition="'$(PkgStub_System_Data_SQLite_Core_NetFramework)' != ''">
    <SQLitePkgDir Condition="'$(SQLitePkgDir)' == ''">$(PkgStub_System_Data_SQLite_Core_NetFramework)</SQLitePkgDir>
  </PropertyGroup>
  <PropertyGroup
    Condition="'$(PkgStub_System_Data_SQLite_Core_NetFramework)' == ''">
    <NuGetGlobalPkgDir Condition="'$(NuGetGlobalPkgDir)' == ''"
      >$(USERPROFILE)\.nuget\packages</NuGetGlobalPkgDir>
    <SQLitePkgRoot
      >$(NuGetGlobalPkgDir)\Stub.System.Data.SQLite.Core.NetFramework</SQLitePkgRoot>
    <SQLitePkgDir Condition="'$(SQLitePkgDir)' == ''"
      >$(SQLitePkgRoot)\$(Version_Stub_System_Data_SQLite_Core_NetFramework)</SQLitePkgDir>
  <PropertyGroup Condition="'$(PkgStub_System_Data_SQLite_Core_NetFramework)' == ''">
    <NuGetGlobalPkgDir Condition="'$(NuGetGlobalPkgDir)' == ''">$(USERPROFILE)\.nuget\packages</NuGetGlobalPkgDir>
    <SQLitePkgRoot>$(NuGetGlobalPkgDir)\Stub.System.Data.SQLite.Core.NetFramework</SQLitePkgRoot>
    <SQLitePkgDir Condition="'$(SQLitePkgDir)' == ''">$(SQLitePkgRoot)\$(Version_Stub_System_Data_SQLite_Core_NetFramework)</SQLitePkgDir>
  </PropertyGroup>
  <ItemGroup>
    <Content Include="$(SQLitePkgDir)\build\net46\x64\SQLite.Interop.dll">
QtVsTools.Package/QtVsToolsPackage.cs
@@ -27,20 +27,14 @@
****************************************************************************/
using System;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Task = System.Threading.Tasks.Task;
using System.Windows.Forms;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Settings;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
@@ -49,13 +43,15 @@
using Microsoft.Win32;
using EnvDTE;
using Task = System.Threading.Tasks.Task;
namespace QtVsTools
{
    using Core;
    using QtMsBuild;
    using SyntaxAnalysis;
    using static SyntaxAnalysis.RegExpr;
    using VisualStudio;
    using static SyntaxAnalysis.RegExpr;
    [Guid(QtVsToolsPackage.PackageGuidString)]
    [InstalledProductRegistration("#110", "#112", Version.PRODUCT_VERSION, IconResourceID = 400)]
@@ -97,7 +93,7 @@
        "Qt", "Versions", 0, 0, true, Sort = 1)]
    // Legacy options page
    [ProvideOptionPage(typeof(Options.QtLegacyOptionsPage),
    [ProvideOptionPage(typeof(Legacy.QtOptionsPage),
        "Qt", "Legacy Project Format", 0, 0, true, Sort = 2)]
    public sealed class QtVsToolsPackage : AsyncPackage, IVsServiceProvider, IProjectTracker
@@ -109,13 +105,13 @@
        public string PkgInstallPath { get; private set; }
        public Options.QtOptionsPage Options
            => GetDialogPage(typeof(Options.QtOptionsPage)) as Options.QtOptionsPage;
        public Options.QtLegacyOptionsPage LegacyOptions
            => GetDialogPage(typeof(Options.QtLegacyOptionsPage)) as Options.QtLegacyOptionsPage;
        public Legacy.QtOptionsPage LegacyOptions
            => GetDialogPage(typeof(Legacy.QtOptionsPage)) as Legacy.QtOptionsPage;
        public Editors.QtDesigner QtDesigner { get; private set; }
        public Editors.QtLinguist QtLinguist { get; private set; }
        public Editors.QtResourceEditor QtResourceEditor { get; private set; }
        private Editors.QtResourceEditor QtResourceEditor { get; set; }
        static EventWaitHandle initDone = new EventWaitHandle(false, EventResetMode.ManualReset);
        static readonly EventWaitHandle initDone = new EventWaitHandle(false, EventResetMode.ManualReset);
        static QtVsToolsPackage instance = null;
        public static QtVsToolsPackage Instance
@@ -167,7 +163,6 @@
                var timeInitBegin = initTimer.Elapsed;
                VsServiceProvider.Instance = instance = this;
                QtProject.ProjectTracker = this;
                Messages.JoinableTaskFactory = JoinableTaskFactory;
                // determine the package installation directory
                var uri = new Uri(System.Reflection.Assembly
@@ -180,7 +175,7 @@
                await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
                var timeUiThreadBegin = initTimer.Elapsed;
                if ((Dte = VsServiceProvider.GetService<DTE>()) == null)
                if ((Dte = await VsServiceProvider.GetServiceAsync<DTE>()) == null)
                    throw new Exception("Unable to get service: DTE");
                QtVSIPSettings.Options = Options;
@@ -188,14 +183,14 @@
                eventHandler = new DteEventsHandler(Dte);
                Qml.Debug.Launcher.Initialize();
                QtMainMenu.Initialize(this);
                QtSolutionContextMenu.Initialize(this);
                QtProjectContextMenu.Initialize(this);
                QtItemContextMenu.Initialize(this);
                QtMainMenu.Initialize();
                QtSolutionContextMenu.Initialize();
                QtProjectContextMenu.Initialize();
                QtItemContextMenu.Initialize();
                RegisterEditorFactory(QtDesigner = new Editors.QtDesigner());
                RegisterEditorFactory(QtLinguist = new Editors.QtLinguist());
                RegisterEditorFactory(QtResourceEditor = new Editors.QtResourceEditor());
                QtHelp.Initialize(this);
                QtHelp.Initialize();
                if (!string.IsNullOrEmpty(VsShell.InstallRootDir))
                    HelperFunctions.VCPath = Path.Combine(VsShell.InstallRootDir, "VC");
@@ -209,9 +204,11 @@
                var timeUiThreadEnd = initTimer.Elapsed;
                var vm = QtVersionManager.The(initDone);
                var error = string.Empty;
                if (vm.HasInvalidVersions(out error))
                if (vm.HasInvalidVersions(out string error, out bool defaultInvalid)) {
                    if (defaultInvalid)
                        vm.SetLatestQtVersionAsDefault();
                    Messages.Print(error);
                }
                ///////////
                // Install Qt/MSBuild files from package folder to standard location
@@ -306,9 +303,8 @@
    ================================================================",
                        urlDownloadQtIo, devRelease));
                }
            } catch (Exception e) {
                Messages.Print(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
            } catch (Exception exception) {
                exception.Log();
            } finally {
                initDone.Set();
                initTimer.Stop();
@@ -326,11 +322,35 @@
                eventHandler.SolutionEvents_Opened();
        }
        bool TestVersionInstalled()
        {
            bool newVersion = false;
            string versionFile = Path.Combine(PkgInstallPath, "lastversion.txt");
            if (File.Exists(versionFile)) {
                string lastVersion = File.ReadAllText(versionFile);
                newVersion = (lastVersion!= Version.PRODUCT_VERSION);
            } else {
                newVersion = true;
            }
            if (newVersion)
                File.WriteAllText(versionFile, Version.PRODUCT_VERSION);
            return newVersion;
        }
        public void VsMainWindowActivated()
        {
            if (QtVersionManager.The().GetVersions()?.Length == 0)
                Notifications.NoQtVersion.Show();
            if (Options.NotifyInstalled && TestVersionInstalled())
                Notifications.NotifyInstall.Show();
        }
        protected override int QueryClose(out bool canClose)
        {
            if (eventHandler != null) {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (eventHandler != null)
                eventHandler.Disconnect();
            }
            return base.QueryClose(out canClose);
        }
@@ -381,14 +401,15 @@
                File.WriteAllText(Path.Combine(visualizersPath, natvisFile),
                    natvis, System.Text.Encoding.UTF8);
            } catch (Exception e) {
                Messages.Print(
                    e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
            } catch (Exception exception) {
                exception.Log();
            }
        }
        public string GetNatvisPath()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            try {
                using (var vsRootKey = Registry.CurrentUser.OpenSubKey(Dte.RegistryRoot)) {
                    if (vsRootKey.GetValue("VisualStudioLocation") is string vsLocation)
@@ -433,6 +454,7 @@
        void IProjectTracker.AddProject(Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            QtProjectTracker.Add(project);
        }
QtVsTools.Package/Resources.resx
@@ -117,24 +117,6 @@
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="ActionDialog_Properties" xml:space="preserve">
    <value>Properties</value>
  </data>
  <data name="AddQtVersionDialog_IncorrectMakefileGenerator" xml:space="preserve">
    <value>This Qt version uses an unsupported makefile generator (used: {0}, supported: MSVC.NET, MSBUILD)</value>
  </data>
  <data name="AddTranslationDialog_FileName" xml:space="preserve">
    <value>Filename:</value>
  </data>
  <data name="AddTranslationDialog_Language" xml:space="preserve">
    <value>Language:</value>
  </data>
  <data name="AddTranslationDialog_Title" xml:space="preserve">
    <value>Add Translation</value>
  </data>
  <data name="Cancel" xml:space="preserve">
    <value>&amp;Cancel</value>
  </data>
  <data name="CannotFindQMake" xml:space="preserve">
    <value>Cannot find qmake. Make sure you have specified a Qt version.</value>
  </data>
@@ -175,32 +157,11 @@
  <data name="ImportPriFileNotResolved" xml:space="preserve">
    <value>--- (importing .pri file) file: {0} cannot be resolved. Skipping file.</value>
  </data>
  <data name="InstalledQtVersions" xml:space="preserve">
    <value>Installed Qt Versions</value>
  </data>
  <data name="NoProjectOpened" xml:space="preserve">
    <value>No Project Opened</value>
  </data>
  <data name="OK" xml:space="preserve">
    <value>&amp;OK</value>
  </data>
  <data name="IncompatibleMacros" xml:space="preserve">
    <value>The following macros are not compatible: {0}</value>
  </data>
  <data name="ProjectQtSettingsButtonText" xml:space="preserve">
    <value>Qt Project Settings</value>
  </data>
  <data name="ProjectQtVersion" xml:space="preserve">
    <value>Set Project's Qt Version</value>
  </data>
  <data name="ProjectQtVersionNotFoundError" xml:space="preserve">
    <value>There's no Qt version assigned to project {0} for configuration {1}/{2}. Please use the 'Qt Project Settings' editor to change the 'Version' to a valid Qt version for this platform.</value>
  </data>
  <data name="QtModules" xml:space="preserve">
    <value>Qt Modules</value>
  </data>
  <data name="SolutionQtVersion" xml:space="preserve">
    <value>Set Solution's Qt Version</value>
  </data>
  <data name="ExportProject_ImportPriFile" xml:space="preserve">
    <value>Import from .pri File</value>
QtVsTools.Package/qt5.natvis.xml
@@ -224,14 +224,41 @@
    </Type>
    <Type Name="##NAMESPACE##::QString">
        <DisplayString>{((reinterpret_cast&lt;unsigned short*&gt;(d)) + d->offset / 2),sub}</DisplayString>
        <StringView>((reinterpret_cast&lt;unsigned short*&gt;(d)) + d->offset / 2),sub</StringView>
        <DisplayString>{((reinterpret_cast&lt;unsigned short*&gt;(d)) + d-&gt;offset / 2),sub}</DisplayString>
        <StringView>((reinterpret_cast&lt;unsigned short*&gt;(d)) + d-&gt;offset / 2),sub</StringView>
        <Expand>
            <Item Name="[size]">d-&gt;size</Item>
            <Item Name="[referenced]">d-&gt;ref.atomic._q_value</Item>
            <ArrayItems>
                <Size>d-&gt;size</Size>
                <ValuePointer>((reinterpret_cast&lt;unsigned short*&gt;(d)) + d->offset / 2),c</ValuePointer>
                <ValuePointer>((reinterpret_cast&lt;unsigned short*&gt;(d)) + d-&gt;offset / 2),c</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QStringRef">
        <Intrinsic Name="offset" Expression="(reinterpret_cast&lt;char16_t*&gt;(m_string-&gt;d))
            + m_string-&gt;d->offset / 2" />
        <DisplayString Condition="m_string == nullptr">{m_string,[m_size]} u""</DisplayString>
        <DisplayString Condition="m_string != nullptr">{offset() + m_position,[m_size]}</DisplayString>
        <Expand>
            <Item Name="[position]" ExcludeView="simple">m_position</Item>
            <Item Name="[size]" ExcludeView="simple">m_size</Item>
            <ArrayItems Condition="m_string != nullptr">
                <Size>m_size</Size>
                <ValuePointer>offset()+m_position</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QStringView">
        <DisplayString>{m_data,[m_size]}</DisplayString>
        <StringView>m_data,[m_size]</StringView>
        <Expand>
            <Item Name="[size]" ExcludeView="simple">m_size</Item>
            <ArrayItems>
                <Size>m_size</Size>
                <ValuePointer>m_data</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
@@ -264,8 +291,8 @@
        <Intrinsic Name="query" Expression="*((QString*)(((char*)(d) + memberOffset(5))))" />
        <Intrinsic Name="fragment" Expression="*((QString*)(((char*)(d) + memberOffset(6))))" />
        <DisplayString Condition="!isEmpty(scheme().d->size)">{scheme()}://{host()}{path()}</DisplayString>
        <DisplayString Condition="isEmpty(scheme().d->size)">{path()}</DisplayString>
        <DisplayString Condition="!isEmpty(scheme().d-&gt;size)">{scheme()}://{host()}{path()}</DisplayString>
        <DisplayString Condition="isEmpty(scheme().d-&gt;size)">{path()}</DisplayString>
        <Expand>
            <Item Name="[scheme]">scheme()</Item>
            <Item Name="[username]">username()</Item>
@@ -424,8 +451,8 @@
            <IndexListItems>
                <Size>d-&gt;end - d-&gt;begin</Size>
                <ValueNode>*reinterpret_cast&lt;$T1*&gt;((sizeof($T1) &gt; sizeof(void*))
                    ? reinterpret_cast&lt;Node*&gt;(d->array + d->begin + $i)->v
                    : reinterpret_cast&lt;$T1*&gt;(d->array + d->begin + $i))
                    ? reinterpret_cast&lt;Node*&gt;(d-&gt;array + d-&gt;begin + $i)-&gt;v
                    : reinterpret_cast&lt;$T1*&gt;(d-&gt;array + d-&gt;begin + $i))
                </ValueNode>
            </IndexListItems>
        </Expand>
@@ -439,8 +466,8 @@
                <Size>d-&gt;end - d-&gt;begin</Size>
                <ValueNode>
                    *reinterpret_cast&lt;QString*&gt;((sizeof(QString) &gt; sizeof(void*))
                    ? reinterpret_cast&lt;Node*&gt;(d->array + d->begin + $i)->v
                    : reinterpret_cast&lt;QString*&gt;(d->array + d->begin + $i))
                    ? reinterpret_cast&lt;Node*&gt;(d-&gt;array + d-&gt;begin + $i)-&gt;v
                    : reinterpret_cast&lt;QString*&gt;(d-&gt;array + d-&gt;begin + $i))
                </ValueNode>
            </IndexListItems>
        </Expand>
@@ -454,8 +481,8 @@
                <Size>d-&gt;end - d-&gt;begin</Size>
                <ValueNode>
                    *reinterpret_cast&lt;QVariant*&gt;((sizeof(QVariant) &gt; sizeof(void*))
                    ? reinterpret_cast&lt;Node*&gt;(d->array + d->begin + $i)->v
                    : reinterpret_cast&lt;QVariant*&gt;(d->array + d->begin + $i))
                    ? reinterpret_cast&lt;Node*&gt;(d-&gt;array + d-&gt;begin + $i)-&gt;v
                    : reinterpret_cast&lt;QVariant*&gt;(d-&gt;array + d-&gt;begin + $i))
                </ValueNode>
            </IndexListItems>
        </Expand>
@@ -526,7 +553,7 @@
                    <Exec>node = *(bucket++)</Exec>
                    <Exec>--n</Exec>
                    <Loop>
                        <Break Condition="!node || !node->next"/>
                        <Break Condition="!node || !node-&gt;next"/>
                        <Exec>keyValuePair = reinterpret_cast&lt;Node *&gt;(node)</Exec>
                        <Item Name="[{keyValuePair-&gt;key}]">keyValuePair-&gt;value</Item>
                        <Exec>node = node-&gt;next</Exec>
QtVsTools.Package/qt6.natvis.xml
@@ -259,7 +259,34 @@
        </Expand>
    </Type>
   <Type Name="##NAMESPACE##::QByteArray">
    <Type Name="##NAMESPACE##::QStringRef">
        <DisplayString Condition="m_string == nullptr">{m_string,[m_size]} u""</DisplayString>
        <DisplayString Condition="m_string != nullptr">{m_string-&gt;d.ptr+m_position,[m_size]}</DisplayString>
        <StringView Condition="m_string == nullptr">""</StringView>
        <StringView Condition="m_string != nullptr">m_string,[m_position+m_size]</StringView>
        <Expand>
            <Item Name="[position]" ExcludeView="simple">m_position</Item>
            <Item Name="[size]" ExcludeView="simple">m_size</Item>
            <ArrayItems Condition="m_string != nullptr">
                <Size>m_size</Size>
                <ValuePointer>m_string-&gt;d.ptr+m_position</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QStringView">
        <DisplayString>{m_data,[m_size]}</DisplayString>
        <StringView>m_data,[m_size]</StringView>
        <Expand>
            <Item Name="[size]" ExcludeView="simple">m_size</Item>
            <ArrayItems>
                <Size>m_size</Size>
                <ValuePointer>m_data</ValuePointer>
            </ArrayItems>
        </Expand>
    </Type>
    <Type Name="##NAMESPACE##::QByteArray">
        <DisplayString>&quot;{((reinterpret_cast&lt;char*&gt;(d.ptr))),sb}&quot;</DisplayString>
        <StringView>((reinterpret_cast&lt;char*&gt;(d.ptr))),sb</StringView>
        <Expand>
@@ -286,8 +313,8 @@
        <Intrinsic Name="query" Expression="*((QString*)(((char*)(d) + memberOffset(5))))" />
        <Intrinsic Name="fragment" Expression="*((QString*)(((char*)(d) + memberOffset(6))))" />
        <DisplayString Condition="!isEmpty(scheme().d->size)">{scheme()}://{host()}{path()}</DisplayString>
        <DisplayString Condition="isEmpty(scheme().d->size)">{path()}</DisplayString>
        <DisplayString Condition="!isEmpty(scheme().d-&gt;size)">{scheme()}://{host()}{path()}</DisplayString>
        <DisplayString Condition="isEmpty(scheme().d-&gt;size)">{path()}</DisplayString>
        <Expand>
            <Item Name="[scheme]">scheme()</Item>
            <Item Name="[username]">username()</Item>
QtVsTools.Package/qt6modules.xml
New file
@@ -0,0 +1,510 @@
<?xml version="1.0" encoding="utf-8"?>
<QtVsTools>
  <!-- Qt Essentials -->
  <Module Id="1">
    <Name>Qt Core</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtCore</LibraryPrefix>
    <proVarQT>core</proVarQT>
    <IncludePath>$(QTDIR)\include\QtCore</IncludePath>
    <Defines>QT_CORE_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="2">
    <Name>Qt D-Bus</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtDBus</LibraryPrefix>
    <proVarQT>dbus</proVarQT>
    <IncludePath>$(QTDIR)\include\QtDBus</IncludePath>
    <Defines>QT_DBUS_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="3">
    <Name>Qt GUI</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtGui</LibraryPrefix>
    <proVarQT>gui</proVarQT>
    <IncludePath>$(QTDIR)\include\QtGui</IncludePath>
    <Defines>QT_GUI_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="4">
    <Name>Qt Network</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtNetwork</LibraryPrefix>
    <proVarQT>network</proVarQT>
    <IncludePath>$(QTDIR)\include\QtNetwork</IncludePath>
    <Defines>QT_NETWORK_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="5">
    <Name>Qt QML</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtQml</LibraryPrefix>
    <proVarQT>qml</proVarQT>
    <IncludePath>$(QTDIR)\include\QtQml</IncludePath>
    <Defines>QT_QML_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="6">
    <Name>Qt Quick</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtQuick</LibraryPrefix>
    <proVarQT>quick</proVarQT>
    <IncludePath>$(QTDIR)\include\QtQuick</IncludePath>
    <Defines>QT_QUICK_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="7">
    <Name>Qt Quick Controls</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtQuickControls2</LibraryPrefix>
    <proVarQT>quickcontrols2</proVarQT>
    <IncludePath>$(QTDIR)\include\QtQuickControls2</IncludePath>
    <Defines>QT_QUICKCONTROLS2_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="8">
    <Name>Qt Quick Dialogs</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtQuickDialogs2</LibraryPrefix>
    <proVarQT>quickdialogs2</proVarQT>
    <IncludePath>$(QTDIR)\include\QtQuickDialogs2</IncludePath>
    <Defines>QT_QUICKDIALOGS2_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="9">
    <Name>Qt Quick Layouts</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtQuickLayouts</LibraryPrefix>
    <proVarQT>quicklayouts</proVarQT>
    <IncludePath>$(QTDIR)\include\QtQuickLayouts</IncludePath>
    <Defines>QT_QUICKLAYOUTS_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="10">
    <Name>Qt Quick Test</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtQuickTest</LibraryPrefix>
    <proVarQT>qmltest</proVarQT>
    <IncludePath>$(QTDIR)\include\QtQuickTest</IncludePath>
    <Defines>QT_QMLTEST_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="11">
    <Name>Qt Test</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtTest</LibraryPrefix>
    <proVarQT>testlib</proVarQT>
    <IncludePath>$(QTDIR)\include\QtTest</IncludePath>
    <Defines>QT_TESTLIB_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <Module Id="12">
    <Name>Qt Widgets</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtWidgets</LibraryPrefix>
    <proVarQT>widgets</proVarQT>
    <IncludePath>$(QTDIR)\include\QtWidgets</IncludePath>
    <Defines>QT_WIDGETS_LIB</Defines>
    <Tag>Essential</Tag>
  </Module>
  <!-- Qt Add-Ons-->
  <Module Id="13">
    <Name>Active Qt (Server)</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtAxServer</LibraryPrefix>
    <proVarQT>axserver</proVarQT>
    <IncludePath>$(QTDIR)\include\ActiveQt</IncludePath>
    <Defines>QAXSERVER</Defines>
    <Defines>QT_AXSERVER_LIB</Defines>
    <AdditionalLibraries>Qt6AxBase.lib</AdditionalLibraries>
    <AdditionalLibrariesDebug>Qt6AxBased.lib</AdditionalLibrariesDebug>
  </Module>
  <Module Id="14">
    <Name>Active Qt (Container)</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtAxContainer</LibraryPrefix>
    <proVarQT>axcontainer</proVarQT>
    <IncludePath>$(QTDIR)\include\ActiveQt</IncludePath>
    <Defines>QT_AXCONTAINER_LIB</Defines>
    <AdditionalLibraries>Qt6AxBase.lib</AdditionalLibraries>
    <AdditionalLibrariesDebug>Qt6AxBased.lib</AdditionalLibrariesDebug>
  </Module>
  <Module Id="15">
    <Name>Qt Bluetooth</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtBluetooth</LibraryPrefix>
    <proVarQT>bluetooth</proVarQT>
    <IncludePath>$(QTDIR)\include\QtBluetooth</IncludePath>
    <Defines>QT_BLUETOOTH_LIB</Defines>
  </Module>
  <Module Id="16">
    <Name>Qt 3D</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>Qt3DCore</LibraryPrefix>
    <proVarQT>3dcore 3danimation 3dextras 3dinput 3dlogic 3drender</proVarQT>
    <IncludePath>$(QTDIR)\include\Qt3DCore</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DAnimation</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DExtras</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DInput</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DLogic</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DRender</IncludePath>
    <Defines>QT_3DCORE_LIB</Defines>
    <Defines>QT_3DANIMATION_LIB</Defines>
    <Defines>QT_3DEXTRAS_LIB</Defines>
    <Defines>QT_3DINPUT_LIB</Defines>
    <Defines>QT_3DLOGIC_LIB</Defines>
    <Defines>QT_3DRENDER_LIB</Defines>
    <AdditionalLibraries>Qt63DCore.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DAnimation.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DExtras.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DInput.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DLogic.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DRender.lib</AdditionalLibraries>
    <AdditionalLibrariesDebug>Qt63DCored.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DAnimationd.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DExtrasd.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DInputd.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DLogicd.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DRenderd.lib</AdditionalLibrariesDebug>
  </Module>
  <Module Id="17">
    <Name>Qt 5 Core Compatibility APIs</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtCore5Compat</LibraryPrefix>
    <proVarQT>core5compat</proVarQT>
    <IncludePath>$(QTDIR)\include\QtCore5Compat</IncludePath>
    <Defines>QT_CORE5COMPAT_LIB</Defines>
  </Module>
  <Module Id="18"> <!-- TODO: Split? -->
    <Name>Qt for Automation</Name>
    <proVarQT>coap mqtt opcua</proVarQT>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtCoap</LibraryPrefix>
    <AdditionalLibraries>Qt6Coap.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt6Coapd.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt6Mqtt.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt6Mqttd.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt6OpcUa.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt6OpcUad.lib</AdditionalLibraries>
  </Module>
  <Module Id="19">
    <Name>Qt Concurrent</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtConcurrent</LibraryPrefix>
    <proVarQT>concurrent</proVarQT>
    <IncludePath>$(QTDIR)\include\QtConcurrent</IncludePath>
    <Defines>QT_CONCURRENT_LIB</Defines>
  </Module>
  <Module Id="20">
    <Name>Qt Help</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtHelp</LibraryPrefix>
    <proVarQT>help</proVarQT>
    <IncludePath>$(QTDIR)\include\QtHelp</IncludePath>
    <Defines>QT_HELP_LIB</Defines>
  </Module>
  <!--
      Image formats
  -->
  <Module Id="21">
    <Name>Qt OpenGL</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtOpenGL</LibraryPrefix>
    <proVarQT>opengl</proVarQT>
    <IncludePath>$(QTDIR)\include\QtOpenGL</IncludePath>
    <Defines>QT_OPENGL_LIB</Defines>
  </Module>
  <Module Id="22">
    <Name>Qt Multimedia</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtMultimedia</LibraryPrefix>
    <proVarQT>multimedia</proVarQT>
    <IncludePath>$(QTDIR)\include\QtMultimedia</IncludePath>
    <Defines>QT_MULTIMEDIA_LIB</Defines>
  </Module>
  <Module Id="23">
    <Name>Qt Print Support</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtPrintSupport</LibraryPrefix>
    <proVarQT>printsupport</proVarQT>
    <IncludePath>$(QTDIR)\include\QtPrintSupport</IncludePath>
    <Defines>QT_PRINTSUPPORT_LIB</Defines>
  </Module>
  <Module Id="24">
    <Name>Qt Quick Widgets</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtQuickWidgets</LibraryPrefix>
    <proVarQT>quickwidgets</proVarQT>
    <IncludePath>$(QTDIR)\include\QtQuickWidgets</IncludePath>
    <Defines>QT_QUICKWIDGETS_LIB</Defines>
  </Module>
  <Module Id="25">
    <Name>Qt Remote Objects</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtRemoteObjects</LibraryPrefix>
    <proVarQT>remoteobjects</proVarQT>
    <IncludePath>$(QTDIR)\include\QtRemoteObjects</IncludePath>
    <Defines>QT_REMOTEOBJECTS_LIB</Defines>
  </Module>
  <Module Id="26">
    <Name>Qt SCXML</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtScxml</LibraryPrefix>
    <proVarQT>scxml</proVarQT>
    <IncludePath>$(QTDIR)\include\QtScxml</IncludePath>
    <Defines>QT_SCXML_LIB</Defines>
  </Module>
  <Module Id="27">
    <Name>Qt Sensors</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtSensors</LibraryPrefix>
    <proVarQT>sensors</proVarQT>
    <IncludePath>$(QTDIR)\include\QtSensors</IncludePath>
    <Defines>QT_SENSORS_LIB</Defines>
  </Module>
  <Module Id="28">
    <Name>Qt Serial Bus</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtSerialBus</LibraryPrefix>
    <proVarQT>serialbus</proVarQT>
    <IncludePath>$(QTDIR)\include\QtSerialBus</IncludePath>
    <Defines>QT_SERIALBUS_LIB</Defines>
  </Module>
  <Module Id="29">
    <Name>Qt Serial Port</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtSerialPort</LibraryPrefix>
    <proVarQT>serialport</proVarQT>
    <IncludePath>$(QTDIR)\include\QtSerialPort</IncludePath>
    <Defines>QT_SERIALPORT_LIB</Defines>
  </Module>
  <Module Id="30">
    <Name>Qt SQL</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtSql</LibraryPrefix>
    <proVarQT>sql</proVarQT>
    <IncludePath>$(QTDIR)\include\QtSql</IncludePath>
    <Defines>QT_SQL_LIB</Defines>
  </Module>
  <Module Id="31">
    <Name>Qt State Machine</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtStateMachine</LibraryPrefix>
    <proVarQT>statemachine</proVarQT>
    <IncludePath>$(QTDIR)\include\QtStateMachine</IncludePath>
    <Defines>QT_STATEMACHINE_LIB</Defines>
  </Module>
  <Module Id="32">
    <Name>Qt SVG</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtSvg</LibraryPrefix>
    <proVarQT>svg</proVarQT>
    <IncludePath>$(QTDIR)\include\QtSvg</IncludePath>
    <Defines>QT_SVG_LIB</Defines>
  </Module>
  <Module Id="34">
    <Name>Qt WebChannel</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtWebChannel</LibraryPrefix>
    <proVarQT>webchannel</proVarQT>
    <IncludePath>$(QTDIR)\include\QtWebChannel</IncludePath>
    <Defines>QT_WEBCHANNEL_LIB</Defines>
  </Module>
  <Module Id="35">
    <Name>Qt WebEngine</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtWebEngine</LibraryPrefix>
    <proVarQT>webenginecore</proVarQT>
    <IncludePath>$(QTDIR)\include\QtWebEngineCore</IncludePath>
    <Defines>QT_WEBENGINECORE_LIB</Defines>
    <AdditionalLibraries>Qt6WebEngine.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt6WebEngineCore.lib</AdditionalLibraries>
    <AdditionalLibrariesDebug>Qt6WebEngined.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt6WebEngineCored.lib</AdditionalLibrariesDebug>
  </Module>
  <Module Id="36">
    <Name>Qt WebSockets</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtWebSockets</LibraryPrefix>
    <proVarQT>websockets</proVarQT>
    <IncludePath>$(QTDIR)\include\QtWebSockets</IncludePath>
    <Defines>QT_WEBSOCKETS_LIB</Defines>
  </Module>
  <Module Id="37">
    <Name>WebView</Name>
    <proVarQT>webview</proVarQT>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtWebView</LibraryPrefix>
  </Module>
  <Module Id="38">
    <Name>Qt XML</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtXml</LibraryPrefix>
    <proVarQT>xml</proVarQT>
    <IncludePath>$(QTDIR)\include\QtXml</IncludePath>
    <Defines>QT_XML_LIB</Defines>
  </Module>
  <Module Id="39">
    <Name>Qt Positioning</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtPositioning</LibraryPrefix>
    <proVarQT>positioning</proVarQT>
    <IncludePath>$(QTDIR)\include\QtPositioning</IncludePath>
    <Defines>QT_POSITIONING_LIB</Defines>
  </Module>
  <Module Id="40">
    <Name>Qt NFC</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtNfc</LibraryPrefix>
    <proVarQT>nfc</proVarQT>
    <IncludePath>$(QTDIR)\include\QtNfc</IncludePath>
    <Defines>QT_NFC_LIB</Defines>
  </Module>
  <Module Id="41">
    <Name>Qt Charts</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtCharts</LibraryPrefix>
    <proVarQT>charts</proVarQT>
    <IncludePath>$(QTDIR)\include\QtCharts</IncludePath>
    <Defines>QT_CHARTS_LIB</Defines>
  </Module>
  <Module Id="42">
    <Name>Qt Data Visualization</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtDataVisualization</LibraryPrefix>
    <proVarQT>datavisualization</proVarQT>
    <IncludePath>$(QTDIR)\include\QtDataVisualization</IncludePath>
    <Defines>QT_DATAVISUALIZATION_LIB</Defines>
  </Module>
  <!--
      lottie
  -->
  <Module Id="44">
    <Name>Qt Network Authorization</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtNetworkAuth</LibraryPrefix>
    <proVarQT>networkauth</proVarQT>
    <IncludePath>$(QTDIR)\include\QtNetworkAuth</IncludePath>
    <Defines>QT_NETWORKAUTH_LIB</Defines>
  </Module>
  <Module Id="45">
    <Name>Qt Virtual Keyboard</Name>
    <proVarQT>virtualkeyboard</proVarQT>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtVirtualKeyboard</LibraryPrefix>
  </Module>
  <Module Id="46">
    <Name>Qt Quick 3D</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>Qt3DQuick</LibraryPrefix>
    <proVarQT>3dquick</proVarQT>
    <IncludePath>$(QTDIR)\include\Qt3DQuick</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DQuickAnimation</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DQuickExtras</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DQuickInput</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DQuickRender</IncludePath>
    <IncludePath>$(QTDIR)\include\Qt3DQuickScene2D</IncludePath>
    <Defines>QT_3DQUICK_LIB</Defines>
    <Defines>QT_3DQUICKANIMATION_LIB</Defines>
    <Defines>QT_3DQUICKEXTRAS_LIB</Defines>
    <Defines>QT_3DQUICKINPUT_LIB</Defines>
    <Defines>QT_3DQUICKRENDER_LIB</Defines>
    <Defines>QT_3DQUICKSCENE2D_LIB</Defines>
    <AdditionalLibraries>Qt63DQuick.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DQuickAnimation.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DQuickExtras.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DQuickInput.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DQuickRender.lib</AdditionalLibraries>
    <AdditionalLibraries>Qt63DQuickScene2D.lib</AdditionalLibraries>
    <AdditionalLibrariesDebug>Qt63DQuickd.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DQuickAnimationd.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DQuickExtrasd.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DQuickInputd.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DQuickRenderd.lib</AdditionalLibrariesDebug>
    <AdditionalLibrariesDebug>Qt63DQuickScene2Dd.lib</AdditionalLibrariesDebug>
  </Module>
  <Module Id="47">
    <Name>Qt Quick Timeline</Name>
    <proVarQT>quicktimeline</proVarQT>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtQuickTimeline</LibraryPrefix>
  </Module>
  <Module Id="48">
    <Name>Qt Shader Tools</Name>
    <proVarQT>shadertools</proVarQT>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtShaderTools</LibraryPrefix>
  </Module>
  <Module Id="49">
    <Name>Qt Wayland Compositor</Name>
    <proVarQT>waylandcompositor</proVarQT>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtWaylandCompositor</LibraryPrefix>
  </Module>
  <Module Id="50">
    <Name>Qt WebEngine Widgets</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtWebEngineWidgets</LibraryPrefix>
    <proVarQT>webenginewidgets</proVarQT>
    <IncludePath>$(QTDIR)\include\QtWebEngineCore</IncludePath>
    <IncludePath>$(QTDIR)\include\QtWebEngineWidgets</IncludePath>
    <Defines>QT_WEBENGINECORE_LIB</Defines>
    <Defines>QT_WEBENGINEWIDGETS_LIB</Defines>
    <AdditionalLibraries>Qt6WebEngineCore.lib</AdditionalLibraries>
    <AdditionalLibrariesDebug>Qt6WebEngineCored.lib</AdditionalLibrariesDebug>
    <AdditionalLibraries>Qt6WebEngineWidgets.lib</AdditionalLibraries>
    <AdditionalLibrariesDebug>Qt6WebEngineWidgetsd.lib</AdditionalLibrariesDebug>
  </Module>
  <Module Id="51">
    <Name>Qt WebEngine Quick</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtWebEngineQuick</LibraryPrefix>
    <proVarQT>webenginequick</proVarQT>
    <IncludePath>$(QTDIR)\include\QtWebEngineCore</IncludePath>
    <IncludePath>$(QTDIR)\include\QtWebEngineQuick</IncludePath>
    <Defines>QT_WEBENGINECORE_LIB</Defines>
    <Defines>QT_WEBENGINEQUICK_LIB</Defines>
    <AdditionalLibraries>Qt6WebEngineCore.lib</AdditionalLibraries>
    <AdditionalLibrariesDebug>Qt6WebEngineCored.lib</AdditionalLibrariesDebug>
    <AdditionalLibraries>Qt6WebEngineQuick.lib</AdditionalLibraries>
    <AdditionalLibrariesDebug>Qt6WebEngineQuickd.lib</AdditionalLibrariesDebug>
  </Module>
  <!-- Designer -->
  <Module Id="52">
    <Name>Designer</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtDesigner</LibraryPrefix>
    <HasDLL>true</HasDLL>
    <proVarQT>designer</proVarQT>
    <IncludePath>$(QTDIR)\include\QtDesigner</IncludePath>
    <Defines>QT_DESIGNER_LIB</Defines>
  </Module>
  <Module Id="53">
    <Name>Qt UI Tools</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtUiTools</LibraryPrefix>
    <HasDLL>true</HasDLL>
    <proVarQT>uitools</proVarQT>
    <IncludePath>$(QTDIR)\include\QtUiTools</IncludePath>
    <Defines>QT_UITOOLS_LIB</Defines>
  </Module>
  <Module Id="54">
    <Name>Qt UI Plugin</Name>
    <Selectable>true</Selectable>
    <LibraryPrefix>QtUiPlugin</LibraryPrefix>
    <HasDLL>false</HasDLL>
    <proVarQT>uiplugin</proVarQT>
    <IncludePath>$(QTDIR)\include\QtUiPlugin</IncludePath>
    <Defines>QT_UIPLUGIN_LIB</Defines>
    <Defines>QDESIGNER_EXPORT_WIDGETS</Defines>
  </Module>
  <!-- Designer -->
</QtVsTools>
Diff truncated after the above file
QtVsTools.Package/qtmodules.xml QtVsTools.Package/source.extension.vsixmanifest_TT QtVsTools.RegExpr/Properties/AssemblyInfo.cs QtVsTools.RegExpr/QtVsTools.RegExpr.csproj QtVsTools.RegExpr/expression/CharClassSet.cs QtVsTools.RegExpr/expression/RegExprAssert.cs QtVsTools.RegExpr/expression/RegExprRepeat.cs QtVsTools.RegExpr/expression/RegExprToken.cs QtVsTools.RegExpr/expression/Renderer.cs QtVsTools.RegExpr/parser/ParseTree.cs QtVsTools.RegExpr/parser/Parser.cs QtVsTools.RegExpr/production/Production.cs QtVsTools.RegExpr/production/ProductionRule.cs QtVsTools.RegExpr/production/ProductionRuleAction.cs QtVsTools.RegExpr/utils/Consts.cs QtVsTools.Wizards/Common/GuiPage.xaml QtVsTools.Wizards/Common/GuiPage.xaml.cs QtVsTools.Wizards/Common/UiClassInclusion.cs QtVsTools.Wizards/Common/WizardData.cs QtVsTools.Wizards/Common/WizardIntroPage.xaml QtVsTools.Wizards/Common/WizardIntroPage.xaml.cs QtVsTools.Wizards/Common/WizardPage.cs QtVsTools.Wizards/Common/WizardResult.cs QtVsTools.Wizards/Common/WizardWindow.xaml QtVsTools.Wizards/Common/WizardWindow.xaml.cs QtVsTools.Wizards/ItemWizard/QtClass/QtClassPage.xaml QtVsTools.Wizards/ItemWizard/QtClass/QtClassPage.xaml.cs QtVsTools.Wizards/ItemWizard/QtClass/QtClassWizard.cs QtVsTools.Wizards/ItemWizard/Translation/TranslationPage.xaml QtVsTools.Wizards/ItemWizard/Translation/TranslationPage.xaml.cs QtVsTools.Wizards/ItemWizard/Translation/TranslationWizard.cs QtVsTools.Wizards/ItemWizard/WidgetsClass/WidgetsClassWizard.cs QtVsTools.Wizards/ProjectWizard/ConfigPage.xaml QtVsTools.Wizards/ProjectWizard/ConfigPage.xaml.cs QtVsTools.Wizards/ProjectWizard/Console/ConsoleWizard.cs QtVsTools.Wizards/ProjectWizard/Designer/DesignerPage.xaml QtVsTools.Wizards/ProjectWizard/Designer/DesignerPage.xaml.cs QtVsTools.Wizards/ProjectWizard/Designer/DesignerWizard.cs QtVsTools.Wizards/ProjectWizard/Empty/EmptyWizard.cs QtVsTools.Wizards/ProjectWizard/Gui/GuiWizard.cs QtVsTools.Wizards/ProjectWizard/Library/LibraryClassPage.xaml QtVsTools.Wizards/ProjectWizard/Library/LibraryClassPage.xaml.cs QtVsTools.Wizards/ProjectWizard/Library/LibraryWizard.cs QtVsTools.Wizards/ProjectWizard/ProjectTemplateWizard.cs QtVsTools.Wizards/ProjectWizard/Quick/QuickWizard.cs QtVsTools.Wizards/ProjectWizard/Server/ServerPage.xaml QtVsTools.Wizards/ProjectWizard/Server/ServerPage.xaml.cs QtVsTools.Wizards/ProjectWizard/Server/ServerWizard.cs QtVsTools.Wizards/QtVsTools.Wizards.csproj QtVsTools.Wizards/Util/ClassNameValidationRule.cs QtVsTools.Wizards/Util/FileExistsInFilterValidationRule.cs QtVsTools.Wizards/Util/FileNameValidationRule.cs QtVsTools.Wizards/Util/NativeMethods.cs QtVsTools.Wizards/Util/UiClassInclusionConverter.cs QtVsTools.Wizards/Util/UnsafeNativeMethods.cs QtVsTools.Wizards/Util/VCLanguageManagerValidationRule.cs QtVsTools.Wizards/Util/VCRulePropertyStorageHelper.cs README.md Templates/console/QtTemplate.Project.Console.csproj Templates/console/console.vcxproj.filters Templates/designer/QtTemplate.Project.Designer.csproj Templates/designer/designer.vcxproj.filters Templates/designer/plugin.h Templates/designer/widget.h Templates/dialogbuttonbottom/QtTemplate.Item.DialogButtonBottom.csproj Templates/dialogbuttonright/QtTemplate.Item.DialogButtonRight.csproj Templates/empty/QtTemplate.Project.Empty.csproj Templates/gui/QtTemplate.Project.Gui.csproj Templates/gui/gui.vcxproj.filters Templates/gui/gui.vstemplate_TT Templates/gui/main.cpp Templates/gui/widget.cpp Templates/gui/widget.h Templates/gui/widget.qrc Templates/lib/QtTemplate.Project.Lib.csproj Templates/lib/lib.vcxproj.filters Templates/mainwindow/QtTemplate.Item.MainWindow.csproj Templates/qml/QtTemplate.Item.QMLFile.csproj Templates/qmldir/QtTemplate.Item.QMLDir.csproj Templates/qtclass/Properties/AssemblyInfo.cs Templates/qtclass/QtTemplate.Item.QtClass.csproj Templates/qtclass/header.h Templates/qtclass/qtclass.ico Templates/qtclass/qtclass.vstemplate_TT Templates/qtclass/source.cpp Templates/quick/QtTemplate.Project.Quick.csproj Templates/quick/quick.vcxproj.filters Templates/resource/QtTemplate.Item.Resource.csproj Templates/server/QtTemplate.Project.Server.csproj Templates/server/header.h Templates/server/server.vcxproj.filters Templates/translation/Properties/AssemblyInfo.cs Templates/translation/QtTemplate.Item.Translation.csproj Templates/translation/translation.ico Templates/translation/translation.ts Templates/translation/translation.vstemplate_TT Templates/widget/QtTemplate.Item.Widget.csproj Templates/widgetsclass/Properties/AssemblyInfo.cs Templates/widgetsclass/QtTemplate.Item.WidgetsClass.csproj Templates/widgetsclass/widget.cpp Templates/widgetsclass/widget.h Templates/widgetsclass/widget.ui Templates/widgetsclass/widgetsclass.ico Templates/widgetsclass/widgetsclass.vstemplate_TT Tests/BigSolution/generator/Program.cs Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.h Tests/BigSolution/template/BigSolution.sln Tests/BigSolution/template/QtClassLibrary/QtClass.cpp Tests/BigSolution/template/QtClassLibrary/QtClass.h Tests/BigSolution/template/QtClassLibrary/QtClassLibrary.cpp Tests/BigSolution/template/QtClassLibrary/QtClassLibrary.h Tests/BigSolution/template/QtClassLibrary/QtClassLibrary.vcxproj Tests/BigSolution/template/QtClassLibrary/QtClassLibrary.vcxproj.filters Tests/BigSolution/template/QtClassLibrary/qtclasslibrary_global.h Tests/BigSolution/template/StaticLib/Header.h Tests/BigSolution/template/StaticLib/StaticLib.cpp Tests/BigSolution/template/StaticLib/StaticLib.vcxproj Tests/BigSolution/template/StaticLib/StaticLib.vcxproj.filters Tests/BigSolution/template/loop_msbuild.bat Tests/ProjectFormats/100/QtProjectV100.cpp Tests/ProjectFormats/100/QtProjectV100.h Tests/ProjectFormats/100/QtProjectV100.pro Tests/ProjectFormats/100/QtProjectV100.qrc Tests/ProjectFormats/100/QtProjectV100.ui Tests/ProjectFormats/100/QtProjectV100.vcxproj Tests/ProjectFormats/100/QtProjectV100.vcxproj.filters Tests/ProjectFormats/100/main.cpp Tests/ProjectFormats/200/QtProjectV200.cpp Tests/ProjectFormats/200/QtProjectV200.h Tests/ProjectFormats/200/QtProjectV200.qrc Tests/ProjectFormats/200/QtProjectV200.sln Tests/ProjectFormats/200/QtProjectV200.ui Tests/ProjectFormats/200/QtProjectV200.vcxproj Tests/ProjectFormats/200/QtProjectV200.vcxproj.filters Tests/ProjectFormats/200/main.cpp Tests/ProjectFormats/300/QtProjectV300.cpp Tests/ProjectFormats/300/QtProjectV300.h Tests/ProjectFormats/300/QtProjectV300.qrc Tests/ProjectFormats/300/QtProjectV300.sln Tests/ProjectFormats/300/QtProjectV300.ui Tests/ProjectFormats/300/QtProjectV300.vcxproj Tests/ProjectFormats/300/QtProjectV300.vcxproj.filters Tests/ProjectFormats/300/main.cpp Tests/ProjectFormats/301/QtProjectV301.cpp Tests/ProjectFormats/301/QtProjectV301.h Tests/ProjectFormats/301/QtProjectV301.qrc Tests/ProjectFormats/301/QtProjectV301.sln Tests/ProjectFormats/301/QtProjectV301.ui Tests/ProjectFormats/301/QtProjectV301.vcxproj Tests/ProjectFormats/301/QtProjectV301.vcxproj.filters Tests/ProjectFormats/301/main.cpp Tests/ProjectFormats/302/QtProjectV302.cpp Tests/ProjectFormats/302/QtProjectV302.h Tests/ProjectFormats/302/QtProjectV302.qrc Tests/ProjectFormats/302/QtProjectV302.sln Tests/ProjectFormats/302/QtProjectV302.ui Tests/ProjectFormats/302/QtProjectV302.vcxproj Tests/ProjectFormats/302/QtProjectV302.vcxproj.filters Tests/ProjectFormats/302/main.cpp Tests/ProjectFormats/303/QtProjectV303.cpp Tests/ProjectFormats/303/QtProjectV303.h Tests/ProjectFormats/303/QtProjectV303.qrc Tests/ProjectFormats/303/QtProjectV303.sln Tests/ProjectFormats/303/QtProjectV303.ui Tests/ProjectFormats/303/QtProjectV303.vcxproj Tests/ProjectFormats/303/QtProjectV303.vcxproj.filters Tests/ProjectFormats/303/main.cpp Tests/ProjectFormats/304/QtProjectV304.cpp Tests/ProjectFormats/304/QtProjectV304.h Tests/ProjectFormats/304/QtProjectV304.qrc Tests/ProjectFormats/304/QtProjectV304.sln Tests/ProjectFormats/304/QtProjectV304.ui Tests/ProjectFormats/304/QtProjectV304.vcxproj Tests/ProjectFormats/304/QtProjectV304.vcxproj.filters Tests/ProjectFormats/304/main.cpp Tests/ProjectFormats/ProjectFormats.md Tests/Test_QtMsBuild.Tasks/Properties/AssemblyInfo.cs Tests/Test_QtMsBuild.Tasks/TestTaskLoggingHelper.cs Tests/Test_QtMsBuild.Tasks/Test_Join.cs Tests/Test_QtMsBuild.Tasks/Test_QtMsBuild.Tasks.csproj Tests/Test_QtMsBuild.Tasks/Test_QtRunTask.cs Tests/Test_QtVsTools.Core/Properties/AssemblyInfo.cs Tests/Test_QtVsTools.Core/Test_LazyFactory.cs Tests/Test_QtVsTools.Core/Test_QtVsTools.Core.csproj Tests/Test_QtVsTools.Package/Properties/AssemblyInfo.cs Tests/Test_QtVsTools.Package/QtVsTestClient.cs Tests/Test_QtVsTools.Package/Test_QtVersionsPage.cs Tests/Test_QtVsTools.Package/Test_QtVsTools.Package.csproj Tests/Test_QtVsTools.PriorityQueue/Properties/AssemblyInfo.cs Tests/Test_QtVsTools.PriorityQueue/Test_PriorityQueue.cs Tests/Test_QtVsTools.PriorityQueue/Test_QtVsTools.PriorityQueue.csproj Tests/Test_QtVsTools.RegExpr/Properties/AssemblyInfo.cs Tests/Test_QtVsTools.RegExpr/Test_MacroParser.cs Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj Tests/Test_QtVsTools.RegExpr/Test_SubTokens.cs Tests/Test_QtVsTools.RegExpr/Test_XmlIntParser.cs doc/config/qtvstools-project.qdocconf doc/config/style/qt5-sidebar.html doc/images/front-advanced.png doc/images/front-coding.png doc/images/front-gs.png doc/images/front-help.png doc/images/front-preview.png doc/images/front-projects.png doc/images/qtvstools-msbuild-diagram.png doc/images/qtvstools-options-qt-general.png doc/images/qtvstools-qt-project-settings.png doc/images/qtvstools-qt-translation-file-wizard.png doc/images/qtvstools-qt-widget-class-wizard.png doc/images/qtvstools-qtquick-app-modules.png doc/images/qtvstools-quick-addressbook-entries.png doc/images/qtvstools-quick-addressbook-mainwindow.png doc/images/qtvstools-quick-addressbook-popup.png doc/images/qtvstools-remote-debugging.png doc/qtvstools-online.qdocconf doc/src/qtvstools.qdoc doc/tutorial/AddressBook/adddialog.h doc/tutorial/QuickAddressBook/QuickAddressBook.sln doc/tutorial/QuickAddressBook/QuickAddressBook.vcxproj doc/tutorial/QuickAddressBook/QuickAddressBook.vcxproj.filters doc/tutorial/QuickAddressBook/QuickAddressBookTypes/AddressBookItem.qml doc/tutorial/QuickAddressBook/QuickAddressBookTypes/NewAddressPopup.qml doc/tutorial/QuickAddressBook/main.cpp doc/tutorial/QuickAddressBook/main.qml doc/tutorial/QuickAddressBook/qml.qrc doc/tutorial/QuickAddressBook/qmldir references.props version.targets vsconfig/2017.vsconfig vsconfig/2019.vsconfig vsconfig/2022.vsconfig vstools.bat vstools.sln Сборка.md