aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThe Qt Project <gerrit-noreply@qt-project.org>2024-04-23 14:09:06 +0000
committerThe Qt Project <gerrit-noreply@qt-project.org>2024-04-23 14:09:06 +0000
commita86892b8af6c6169a7143d51a5d077f24573c6d2 (patch)
treee9750e1ff09259af3ce476f39439103c2424cdfe /src
parenta7f92dd8bec1fd61c886ca039508ff6238211e5e (diff)
parentef0ee52bf92739bc5b32d70c01ff1eadd752d813 (diff)
Merge "Merge remote-tracking branch 'origin/qds/dev'"
Diffstat (limited to 'src')
m---------src/libs/3rdparty/googletest0
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.c484
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.h25
-rw-r--r--src/libs/languageutils/componentversion.cpp63
-rw-r--r--src/libs/languageutils/componentversion.h61
-rw-r--r--src/libs/nanotrace/CMakeLists.txt1
-rw-r--r--src/libs/nanotrace/nanotracehr.cpp56
-rw-r--r--src/libs/nanotrace/nanotracehr.h362
-rw-r--r--src/libs/nanotrace/staticstring.h116
-rw-r--r--src/libs/qmljs/CMakeLists.txt1
-rw-r--r--src/libs/qmljs/qmljs.qbs1
-rw-r--r--src/libs/qmljs/qmljsindenter.cpp603
-rw-r--r--src/libs/qmljs/qmljsindenter.h51
-rw-r--r--src/libs/qmljs/qmljsstaticanalysismessage.cpp5
-rw-r--r--src/libs/qmlpuppetcommunication/container/imagecontainer.cpp9
-rw-r--r--src/libs/sqlite/CMakeLists.txt4
-rw-r--r--src/libs/sqlite/sqlitebasestatement.cpp203
-rw-r--r--src/libs/sqlite/sqlitebasestatement.h82
-rw-r--r--src/libs/sqlite/sqliteexception.cpp30
-rw-r--r--src/libs/sqlite/sqliteexception.h9
-rw-r--r--src/libs/sqlite/sqliteids.h11
-rw-r--r--src/libs/sqlite/sqliteindex.h19
-rw-r--r--src/libs/sqlite/sqlitetracing.cpp38
-rw-r--r--src/libs/sqlite/sqlitetracing.h29
-rw-r--r--src/libs/sqlite/sqlitevalue.h62
-rw-r--r--src/libs/utils/smallstring.h113
-rw-r--r--src/plugins/android/androidmanager.cpp2
-rw-r--r--src/plugins/coreplugin/icore.cpp1
-rw-r--r--src/plugins/effectcomposer/compositionnode.cpp3
-rw-r--r--src/plugins/effectcomposer/compositionnode.h3
-rw-r--r--src/plugins/effectcomposer/effectcomposermodel.cpp221
-rw-r--r--src/plugins/effectcomposer/effectcomposermodel.h15
-rw-r--r--src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp12
-rw-r--r--src/plugins/effectcomposer/effectcomposeruniformsmodel.h1
-rw-r--r--src/plugins/effectcomposer/effectcomposerview.cpp82
-rw-r--r--src/plugins/effectcomposer/effectcomposerview.h5
-rw-r--r--src/plugins/effectcomposer/uniform.cpp6
-rw-r--r--src/plugins/qmldesigner/.clang-format2
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt9
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp3
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h3
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp36
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h25
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp43
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h2
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp65
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp24
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h4
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp6
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h1
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp13
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp144
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.h18
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp28
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h12
-rw-r--r--src/plugins/qmldesigner/components/componentcore/dialogutils.cpp32
-rw-r--r--src/plugins/qmldesigner/components/componentcore/dialogutils.h17
-rw-r--r--src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp52
-rw-r--r--src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/theme.h1
-rw-r--r--src/plugins/qmldesigner/components/componentcore/viewmanager.cpp23
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp38
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h2
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp11
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h7
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp23
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp49
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h26
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp10
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h2
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp34
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h4
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp423
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h132
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp258
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h10
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp149
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h19
-rw-r--r--src/plugins/qmldesigner/components/createtexture.cpp3
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp2
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp2
-rw-r--r--src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp10
-rw-r--r--src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h1
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.cpp18
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.h1
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp10
-rw-r--r--src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp10
-rw-r--r--src/plugins/qmldesigner/components/edit3d/snapconfiguration.h1
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.cpp5
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.h3
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp51
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp8
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp18
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp16
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h11
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp2
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp20
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h2
-rw-r--r--src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp7
-rw-r--r--src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp6
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp6
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp21
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h14
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp304
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h43
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp207
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h10
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp234
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h35
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp9
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp4
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp4
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp7
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp23
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbarbackend.h4
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp7
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp4
-rw-r--r--src/plugins/qmldesigner/componentsplugin/components.metainfo6
-rw-r--r--src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp139
-rw-r--r--src/plugins/qmldesigner/designercore/generatedcomponentutils.h38
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h2
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/taskqueue.h41
-rw-r--r--src/plugins/qmldesigner/designercore/include/model.h10
-rw-r--r--src/plugins/qmldesigner/designercore/include/modelfwd.h2
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodemetainfo.h8
-rw-r--r--src/plugins/qmldesigner/designercore/include/projectstorageids.h6
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlitemnode.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/rewriterview.h5
-rw-r--r--src/plugins/qmldesigner/designercore/include/subcomponentmanager.h2
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp61
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp28
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp16
-rw-r--r--src/plugins/qmldesigner/designercore/model/abstractview.cpp6
-rw-r--r--src/plugins/qmldesigner/designercore/model/bindingproperty.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/model/model.cpp39
-rw-r--r--src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/model/modelutils.cpp18
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp8
-rw-r--r--src/plugins/qmldesigner/designercore/model/rewriterview.cpp21
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp271
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.h6
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h3
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/filestatus.h12
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/filesystem.h6
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp4538
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h3695
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp114
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h27
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h1
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h176
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h12
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h43
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h442
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp516
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h39
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp20
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h2
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp94
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h7
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h6
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h2
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp14
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h4
-rw-r--r--src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp54
-rw-r--r--src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h18
-rw-r--r--src/plugins/qmldesigner/documentmanager.cpp18
-rw-r--r--src/plugins/qmldesigner/documentmanager.h6
-rw-r--r--src/plugins/qmldesigner/documentwarningwidget.cpp5
-rw-r--r--src/plugins/qmldesigner/puppetenvironmentbuilder.cpp2
-rw-r--r--src/plugins/qmldesigner/qmldesignerconstants.h12
-rw-r--r--src/plugins/qmldesigner/qmldesignerprojectmanager.cpp80
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/quick.metainfo2
-rw-r--r--src/plugins/qmldesigner/settingspage.cpp4
-rw-r--r--src/plugins/qmldesigner/utils/asset.cpp26
-rw-r--r--src/plugins/qmldesigner/utils/asset.h6
-rw-r--r--src/plugins/qmldesigner/utils/imageutils.cpp12
-rw-r--r--src/plugins/qmldesigner/utils/imageutils.h5
-rw-r--r--src/plugins/qmldesignerbase/utils/designerpaths.cpp4
-rw-r--r--src/plugins/qmlpreview/CMakeLists.txt2
-rw-r--r--src/plugins/qmlpreview/qmlpreviewplugin.cpp12
-rw-r--r--src/plugins/qmlprojectmanager/CMakeLists.txt10
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp12
-rw-r--r--src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h3
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc24
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.cpp45
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.h31
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp591
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h73
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp168
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h51
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.cpp174
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.h51
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp422
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.h77
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.cpp180
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.h47
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp258
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h101
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp180
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.h25
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp185
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.h25
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp668
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h80
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/generatecmakelistsconstants.h42
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/qmlprojectappmainqml.tpl14
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/qmlprojectenvheader.tpl11
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincppheader.tpl8
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/qmlprojectmainqml.tpl6
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodulecmakelists.tpl12
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodules.tpl16
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/cmakelists_txt_shared.tpl16
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/cmakemodule_v1.tpl (renamed from src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl)3
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v0.tpl (renamed from src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl)0
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v1.tpl (renamed from src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl)28
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/environment_h.tpl24
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/import_qml_components_h.tpl (renamed from src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl)1
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/insight.tpl19
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v0.tpl (renamed from src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincpp.tpl)0
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v1.tpl31
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl34
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/templates/qtquickcontrols2_conf.tpl7
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectgen/qmlprojectgenerator.cpp4
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectmanager.qbs10
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectplugin.cpp9
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.cpp1
-rw-r--r--src/plugins/studiowelcome/studiowelcomeplugin.cpp31
-rw-r--r--src/plugins/texteditor/texteditor.cpp5
-rw-r--r--src/tools/qml2puppet/editor3d_qt6.qrc2
-rw-r--r--src/tools/qml2puppet/mockfiles/images/crosshair.pngbin0 -> 172 bytes
-rw-r--r--src/tools/qml2puppet/mockfiles/images/crosshair@2x.pngbin0 -> 202 bytes
-rw-r--r--src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml37
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp85
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h8
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp12
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp22
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h1
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp20
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp21
-rw-r--r--src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp503
245 files changed, 12834 insertions, 8183 deletions
diff --git a/src/libs/3rdparty/googletest b/src/libs/3rdparty/googletest
-Subproject b796f7d44681514f58a683a3a71ff17c94edb0c
+Subproject f8d7d77c06936315286eb55f8de22cd23c18857
diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c
index 139ee46a6a..08c593e55c 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.c
+++ b/src/libs/3rdparty/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.45.1. By combining all the individual C code files into this
+** version 3.45.3. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -18,7 +18,7 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
-** e876e51a0ed5c5b3126f52e532044363a014.
+** 8653b758870e6ef0c98d46b3ace27849054a.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
@@ -459,9 +459,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.45.1"
-#define SQLITE_VERSION_NUMBER 3045001
-#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
+#define SQLITE_VERSION "3.45.3"
+#define SQLITE_VERSION_NUMBER 3045003
+#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -733,6 +733,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
+** <li> The application must not dereference the arrays or string pointers
+** passed as the 3rd and 4th callback parameters after it returns.
** </ul>
*/
SQLITE_API int sqlite3_exec(
@@ -2454,6 +2456,22 @@ struct sqlite3_mem_methods {
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
+**
+** [[SQLITE_CONFIG_ROWID_IN_VIEW]]
+** <dt>SQLITE_CONFIG_ROWID_IN_VIEW
+** <dd>The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability
+** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is
+** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability
+** defaults to on. This configuration option queries the current setting or
+** changes the setting to off or on. The argument is a pointer to an integer.
+** If that integer initially holds a value of 1, then the ability for VIEWs to
+** have ROWIDs is activated. If the integer initially holds zero, then the
+** ability is deactivated. Any other initial value for the integer leaves the
+** setting unchanged. After changes, if any, the integer is written with
+** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite
+** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and
+** recommended case) then the integer is always filled with zero, regardless
+** if its initial value.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2485,6 +2503,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
+#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -15097,6 +15116,7 @@ SQLITE_PRIVATE u32 sqlite3TreeTrace;
** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing
** 0x00020000 Transform DISTINCT into GROUP BY
** 0x00040000 SELECT tree dump after all code has been generated
+** 0x00080000 NOT NULL strength reduction
*/
/*
@@ -18427,6 +18447,15 @@ struct Table {
#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
#define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0)
+/* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is
+** available. By default, this macro is false
+*/
+#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
+# define ViewCanHaveRowid 0
+#else
+# define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0)
+#endif
+
/*
** Each foreign key constraint is an instance of the following structure.
**
@@ -19346,6 +19375,7 @@ struct NameContext {
#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */
#define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */
#define NC_NoSelect 0x080000 /* Do not descend into sub-selects */
+#define NC_Where 0x100000 /* Processing WHERE clause of a SELECT */
#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */
/*
@@ -19369,6 +19399,7 @@ struct Upsert {
Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */
Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */
u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */
+ u8 isDup; /* True if 2nd or later with same pUpsertIdx */
/* Above this point is the parse tree for the ON CONFLICT clauses.
** The next group of fields stores intermediate data. */
void *pToFree; /* Free memory when deleting the Upsert object */
@@ -20140,6 +20171,11 @@ struct Sqlite3Config {
#ifndef SQLITE_UNTESTABLE
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW
+ ** feature is disabled. 0 if rowids can
+ ** occur in views. */
+#endif
int bLocaltimeFault; /* True to fail localtime() calls */
int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */
int iOnceResetThreshold; /* When to reset OP_Once counters */
@@ -20595,10 +20631,13 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*);
# define EXP754 (((u64)0x7ff)<<52)
# define MAN754 ((((u64)1)<<52)-1)
# define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0)
+# define IsOvfl(X) (((X)&EXP754)==EXP754)
SQLITE_PRIVATE int sqlite3IsNaN(double);
+SQLITE_PRIVATE int sqlite3IsOverflow(double);
#else
-# define IsNaN(X) 0
-# define sqlite3IsNaN(X) 0
+# define IsNaN(X) 0
+# define sqlite3IsNaN(X) 0
+# define sqlite3IsOVerflow(X) 0
#endif
/*
@@ -21444,7 +21483,7 @@ SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8);
SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3*,Upsert*);
SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
-SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
+SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*,Upsert*);
SQLITE_PRIVATE void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert*,Index*);
SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert*);
@@ -21834,6 +21873,9 @@ static const char * const sqlite3azCompileOpt[] = {
"ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN),
# endif
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ "ALLOW_ROWID_IN_VIEW",
+#endif
#ifdef SQLITE_ALLOW_URI_AUTHORITY
"ALLOW_URI_AUTHORITY",
#endif
@@ -22854,6 +22896,9 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
#ifndef SQLITE_UNTESTABLE
0, /* xTestCallback */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */
+#endif
0, /* bLocaltimeFault */
0, /* xAltLocaltime */
0x7ffffffe, /* iOnceResetThreshold */
@@ -31309,6 +31354,7 @@ SQLITE_API void sqlite3_str_vappendf(
if( xtype==etFLOAT ){
iRound = -precision;
}else if( xtype==etGENERIC ){
+ if( precision==0 ) precision = 1;
iRound = precision;
}else{
iRound = precision+1;
@@ -34640,6 +34686,19 @@ SQLITE_PRIVATE int sqlite3IsNaN(double x){
}
#endif /* SQLITE_OMIT_FLOATING_POINT */
+#ifndef SQLITE_OMIT_FLOATING_POINT
+/*
+** Return true if the floating point value is NaN or +Inf or -Inf.
+*/
+SQLITE_PRIVATE int sqlite3IsOverflow(double x){
+ int rc; /* The value return */
+ u64 y;
+ memcpy(&y,&x,sizeof(y));
+ rc = IsOvfl(y);
+ return rc;
+}
+#endif /* SQLITE_OMIT_FLOATING_POINT */
+
/*
** Compute a string length that is limited to what can be stored in
** lower 30 bits of a 32-bit signed integer.
@@ -35199,6 +35258,9 @@ do_atof_calc:
u64 s2;
rr[0] = (double)s;
s2 = (u64)rr[0];
+#if defined(_MSC_VER) && _MSC_VER<1700
+ if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); }
+#endif
rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
if( e>0 ){
while( e>=100 ){
@@ -35641,7 +35703,7 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou
assert( p->n>0 );
assert( p->n<sizeof(p->zBuf) );
p->iDP = p->n + exp;
- if( iRound<0 ){
+ if( iRound<=0 ){
iRound = p->iDP - iRound;
if( iRound==0 && p->zBuf[i+1]>='5' ){
iRound = 1;
@@ -53262,6 +53324,14 @@ SQLITE_API unsigned char *sqlite3_serialize(
pOut = 0;
}else{
sz = sqlite3_column_int64(pStmt, 0)*szPage;
+ if( sz==0 ){
+ sqlite3_reset(pStmt);
+ sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0);
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ sz = sqlite3_column_int64(pStmt, 0)*szPage;
+ }
+ }
if( piSize ) *piSize = sz;
if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
pOut = 0;
@@ -63785,7 +63855,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
** This will be either the rollback journal or the WAL file.
*/
SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
-#if SQLITE_OMIT_WAL
+#ifdef SQLITE_OMIT_WAL
return pPager->jfd;
#else
return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd;
@@ -77088,7 +77158,10 @@ static int fillInCell(
n = nHeader + nPayload;
testcase( n==3 );
testcase( n==4 );
- if( n<4 ) n = 4;
+ if( n<4 ){
+ n = 4;
+ pPayload[nPayload] = 0;
+ }
*pnSize = n;
assert( nSrc<=nPayload );
testcase( nSrc<nPayload );
@@ -79534,7 +79607,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
if( flags & BTREE_PREFORMAT ){
rc = SQLITE_OK;
szNew = p->pBt->nPreformatSize;
- if( szNew<4 ) szNew = 4;
+ if( szNew<4 ){
+ szNew = 4;
+ newCell[3] = 0;
+ }
if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){
CellInfo info;
pPage->xParseCell(pPage, newCell, &info);
@@ -79596,7 +79672,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
}else if( loc<0 && pPage->nCell>0 ){
assert( pPage->leaf );
idx = ++pCur->ix;
- pCur->curFlags &= ~BTCF_ValidNKey;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
}else{
assert( pPage->leaf );
}
@@ -79626,7 +79702,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
*/
if( pPage->nOverflow ){
assert( rc==SQLITE_OK );
- pCur->curFlags &= ~(BTCF_ValidNKey);
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
rc = balance(pCur);
/* Must make sure nOverflow is reset to zero even if the balance()
@@ -88379,6 +88455,23 @@ static void serialGet(
pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real;
}
}
+static int serialGet7(
+ const unsigned char *buf, /* Buffer to deserialize from */
+ Mem *pMem /* Memory cell to write value into */
+){
+ u64 x = FOUR_BYTE_UINT(buf);
+ u32 y = FOUR_BYTE_UINT(buf+4);
+ x = (x<<32) + y;
+ assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 );
+ swapMixedEndianFloat(x);
+ memcpy(&pMem->u.r, &x, sizeof(x));
+ if( IsNaN(x) ){
+ pMem->flags = MEM_Null;
+ return 1;
+ }
+ pMem->flags = MEM_Real;
+ return 0;
+}
SQLITE_PRIVATE void sqlite3VdbeSerialGet(
const unsigned char *buf, /* Buffer to deserialize from */
u32 serial_type, /* Serial type to deserialize */
@@ -89058,7 +89151,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
}else if( serial_type==0 ){
rc = -1;
}else if( serial_type==7 ){
- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ serialGet7(&aKey1[d1], &mem1);
rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r);
}else{
i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]);
@@ -89083,14 +89176,18 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
}else if( serial_type==0 ){
rc = -1;
}else{
- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
if( serial_type==7 ){
- if( mem1.u.r<pRhs->u.r ){
+ if( serialGet7(&aKey1[d1], &mem1) ){
+ rc = -1; /* mem1 is a NaN */
+ }else if( mem1.u.r<pRhs->u.r ){
rc = -1;
}else if( mem1.u.r>pRhs->u.r ){
rc = +1;
+ }else{
+ assert( rc==0 );
}
}else{
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r);
}
}
@@ -89160,7 +89257,14 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* RHS is null */
else{
serial_type = aKey1[idx1];
- rc = (serial_type!=0 && serial_type!=10);
+ if( serial_type==0
+ || serial_type==10
+ || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0)
+ ){
+ assert( rc==0 );
+ }else{
+ rc = 1;
+ }
}
if( rc!=0 ){
@@ -94858,7 +94962,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
}
}
}else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
- if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+ if( (flags1 & MEM_Str)!=0 ){
+ pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+ }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn1->flags & MEM_Int );
testcase( pIn1->flags & MEM_Real );
testcase( pIn1->flags & MEM_IntReal );
@@ -94867,7 +94973,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str;
}
- if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+ if( (flags3 & MEM_Str)!=0 ){
+ pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+ }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn3->flags & MEM_Int );
testcase( pIn3->flags & MEM_Real );
testcase( pIn3->flags & MEM_IntReal );
@@ -106212,6 +106320,8 @@ static void resolveAlias(
assert( iCol>=0 && iCol<pEList->nExpr );
pOrig = pEList->a[iCol].pExpr;
assert( pOrig!=0 );
+ assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) );
+ if( pExpr->pAggInfo ) return;
db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig, 0);
if( db->mallocFailed ){
@@ -106599,8 +106709,37 @@ static int lookupName(
}
}
if( 0==cnt && VisibleRowid(pTab) ){
+ /* pTab is a potential ROWID match. Keep track of it and match
+ ** the ROWID later if that seems appropriate. (Search for "cntTab"
+ ** to find related code.) Only allow a ROWID match if there is
+ ** a single ROWID match candidate.
+ */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ /* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match
+ ** if there is a single VIEW candidate or if there is a single
+ ** non-VIEW candidate plus multiple VIEW candidates. In other
+ ** words non-VIEW candidate terms take precedence over VIEWs.
+ */
+ if( cntTab==0
+ || (cntTab==1
+ && ALWAYS(pMatch!=0)
+ && ALWAYS(pMatch->pTab!=0)
+ && (pMatch->pTab->tabFlags & TF_Ephemeral)!=0
+ && (pTab->tabFlags & TF_Ephemeral)==0)
+ ){
+ cntTab = 1;
+ pMatch = pItem;
+ }else{
+ cntTab++;
+ }
+#else
+ /* The (much more common) non-SQLITE_ALLOW_ROWID_IN_VIEW case is
+ ** simpler since we require exactly one candidate, which will
+ ** always be a non-VIEW
+ */
cntTab++;
pMatch = pItem;
+#endif
}
}
if( pMatch ){
@@ -106726,13 +106865,13 @@ static int lookupName(
** Perhaps the name is a reference to the ROWID
*/
if( cnt==0
- && cntTab==1
+ && cntTab>=1
&& pMatch
&& (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0
&& sqlite3IsRowid(zCol)
&& ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom)
){
- cnt = 1;
+ cnt = cntTab;
if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1;
pExpr->affExpr = SQLITE_AFF_INTEGER;
}
@@ -107097,6 +107236,19 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
** resolved. This prevents "column" from being counted as having been
** referenced, which might prevent a SELECT from being erroneously
** marked as correlated.
+ **
+ ** 2024-03-28: Beware of aggregates. A bare column of aggregated table
+ ** can still evaluate to NULL even though it is marked as NOT NULL.
+ ** Example:
+ **
+ ** CREATE TABLE t1(a INT NOT NULL);
+ ** SELECT a, a IS NULL, a IS NOT NULL, count(*) FROM t1;
+ **
+ ** The "a IS NULL" and "a IS NOT NULL" expressions cannot be optimized
+ ** here because at the time this case is hit, we do not yet know whether
+ ** or not t1 is being aggregated. We have to assume the worst and omit
+ ** the optimization. The only time it is safe to apply this optimization
+ ** is within the WHERE clause.
*/
case TK_NOTNULL:
case TK_ISNULL: {
@@ -107107,19 +107259,36 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
anRef[i] = p->nRef;
}
sqlite3WalkExpr(pWalker, pExpr->pLeft);
- if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
- testcase( ExprHasProperty(pExpr, EP_OuterON) );
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
- pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
- pExpr->flags |= EP_IntValue;
- pExpr->op = TK_INTEGER;
+ if( IN_RENAME_OBJECT ) return WRC_Prune;
+ if( sqlite3ExprCanBeNull(pExpr->pLeft) ){
+ /* The expression can be NULL. So the optimization does not apply */
+ return WRC_Prune;
+ }
- for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
- p->nRef = anRef[i];
+ for(i=0, p=pNC; p; p=p->pNext, i++){
+ if( (p->ncFlags & NC_Where)==0 ){
+ return WRC_Prune; /* Not in a WHERE clause. Unsafe to optimize. */
}
- sqlite3ExprDelete(pParse->db, pExpr->pLeft);
- pExpr->pLeft = 0;
}
+ testcase( ExprHasProperty(pExpr, EP_OuterON) );
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x80000 ){
+ sqlite3DebugPrintf(
+ "NOT NULL strength reduction converts the following to %d:\n",
+ pExpr->op==TK_NOTNULL
+ );
+ sqlite3ShowExpr(pExpr);
+ }
+#endif /* TREETRACE_ENABLED */
+ pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
+ pExpr->flags |= EP_IntValue;
+ pExpr->op = TK_INTEGER;
+ for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
+ p->nRef = anRef[i];
+ }
+ sqlite3ExprDelete(pParse->db, pExpr->pLeft);
+ pExpr->pLeft = 0;
return WRC_Prune;
}
@@ -108019,7 +108188,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
}
+ sNC.ncFlags |= NC_Where;
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
+ sNC.ncFlags &= ~NC_Where;
/* Resolve names in table-valued-function arguments */
for(i=0; i<p->pSrc->nSrc; i++){
@@ -108558,9 +108729,10 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
assert( pExpr->x.pList->nExpr>0 );
assert( pExpr->op==TK_FUNCTION );
pExpr = pExpr->x.pList->a[0].pExpr;
- }else{
- assert( pExpr->op==TK_COLLATE );
+ }else if( pExpr->op==TK_COLLATE ){
pExpr = pExpr->pLeft;
+ }else{
+ break;
}
}
return pExpr;
@@ -111079,9 +111251,12 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
return 0;
case TK_COLUMN:
assert( ExprUseYTab(p) );
- return ExprHasProperty(p, EP_CanBeNull) ||
- NEVER(p->y.pTab==0) || /* Reference to column of index on expr */
- (p->iColumn>=0
+ return ExprHasProperty(p, EP_CanBeNull)
+ || NEVER(p->y.pTab==0) /* Reference to column of index on expr */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ || (p->iColumn==XN_ROWID && IsView(p->y.pTab))
+#endif
+ || (p->iColumn>=0
&& p->y.pTab->aCol!=0 /* Possible due to prior error */
&& ALWAYS(p->iColumn<p->y.pTab->nCol)
&& p->y.pTab->aCol[p->iColumn].notNull==0);
@@ -123572,9 +123747,12 @@ SQLITE_PRIVATE void sqlite3CreateView(
** on a view, even though views do not have rowids. The following flag
** setting fixes this problem. But the fix can be disabled by compiling
** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that
- ** depend upon the old buggy behavior. */
-#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
- p->tabFlags |= TF_NoVisibleRowid;
+ ** depend upon the old buggy behavior. The ability can also be toggled
+ ** using sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW,...) */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ p->tabFlags |= sqlite3Config.mNoVisibleRowid; /* Optional. Allow by default */
+#else
+ p->tabFlags |= TF_NoVisibleRowid; /* Never allow rowid in view */
#endif
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
@@ -128947,13 +129125,13 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
double r1, r2;
const char *zVal;
r1 = sqlite3_value_double(pValue);
- sqlite3_str_appendf(pStr, "%!.15g", r1);
+ sqlite3_str_appendf(pStr, "%!0.15g", r1);
zVal = sqlite3_str_value(pStr);
if( zVal ){
sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8);
if( r1!=r2 ){
sqlite3_str_reset(pStr);
- sqlite3_str_appendf(pStr, "%!.20e", r1);
+ sqlite3_str_appendf(pStr, "%!0.20e", r1);
}
}
break;
@@ -129255,7 +129433,7 @@ static void replaceFunc(
}
if( zPattern[0]==0 ){
assert( sqlite3_value_type(argv[1])!=SQLITE_NULL );
- sqlite3_result_value(context, argv[0]);
+ sqlite3_result_text(context, (const char*)zStr, nStr, SQLITE_TRANSIENT);
return;
}
nPattern = sqlite3_value_bytes(argv[1]);
@@ -129738,7 +129916,7 @@ static void sumFinalize(sqlite3_context *context){
if( p->approx ){
if( p->ovrfl ){
sqlite3_result_error(context,"integer overflow",-1);
- }else if( !sqlite3IsNaN(p->rErr) ){
+ }else if( !sqlite3IsOverflow(p->rErr) ){
sqlite3_result_double(context, p->rSum+p->rErr);
}else{
sqlite3_result_double(context, p->rSum);
@@ -129755,7 +129933,7 @@ static void avgFinalize(sqlite3_context *context){
double r;
if( p->approx ){
r = p->rSum;
- if( !sqlite3IsNaN(p->rErr) ) r += p->rErr;
+ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr;
}else{
r = (double)(p->iSum);
}
@@ -129769,7 +129947,7 @@ static void totalFinalize(sqlite3_context *context){
if( p ){
if( p->approx ){
r = p->rSum;
- if( !sqlite3IsNaN(p->rErr) ) r += p->rErr;
+ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr;
}else{
r = (double)(p->iSum);
}
@@ -133175,7 +133353,7 @@ SQLITE_PRIVATE void sqlite3Insert(
pNx->iDataCur = iDataCur;
pNx->iIdxCur = iIdxCur;
if( pNx->pUpsertTarget ){
- if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){
+ if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx, pUpsert) ){
goto insert_cleanup;
}
}
@@ -135067,7 +135245,10 @@ static int xferOptimization(
}
}
#ifndef SQLITE_OMIT_CHECK
- if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
+ if( pDest->pCheck
+ && (db->mDbFlags & DBFLAG_Vacuum)==0
+ && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1)
+ ){
return 0; /* Tables have different CHECK constraints. Ticket #2252 */
}
#endif
@@ -139474,31 +139655,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
int mxCol; /* Maximum non-virtual column number */
if( pObjTab && pObjTab!=pTab ) continue;
- if( !IsOrdinaryTable(pTab) ){
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- sqlite3_vtab *pVTab;
- int a1;
- if( !IsVirtual(pTab) ) continue;
- if( pTab->nCol<=0 ){
- const char *zMod = pTab->u.vtab.azArg[0];
- if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
- }
- sqlite3ViewGetColumnNames(pParse, pTab);
- if( pTab->u.vtab.p==0 ) continue;
- pVTab = pTab->u.vtab.p->pVtab;
- if( NEVER(pVTab==0) ) continue;
- if( NEVER(pVTab->pModule==0) ) continue;
- if( pVTab->pModule->iVersion<4 ) continue;
- if( pVTab->pModule->xIntegrity==0 ) continue;
- sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
- pTab->nTabRef++;
- sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
- a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
- integrityCheckResultRow(v);
- sqlite3VdbeJumpHere(v, a1);
-#endif
- continue;
- }
+ if( !IsOrdinaryTable(pTab) ) continue;
if( isQuick || HasRowid(pTab) ){
pPk = 0;
r2 = 0;
@@ -139633,6 +139790,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** is REAL, we have to load the actual data using OP_Column
** to reliably determine if the value is a NULL. */
sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3);
+ sqlite3ColumnDefault(v, pTab, j, 3);
jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk);
VdbeCoverage(v);
}
@@ -139823,6 +139981,38 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ /* Second pass to invoke the xIntegrity method on all virtual
+ ** tables.
+ */
+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ Table *pTab = sqliteHashData(x);
+ sqlite3_vtab *pVTab;
+ int a1;
+ if( pObjTab && pObjTab!=pTab ) continue;
+ if( IsOrdinaryTable(pTab) ) continue;
+ if( !IsVirtual(pTab) ) continue;
+ if( pTab->nCol<=0 ){
+ const char *zMod = pTab->u.vtab.azArg[0];
+ if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
+ }
+ sqlite3ViewGetColumnNames(pParse, pTab);
+ if( pTab->u.vtab.p==0 ) continue;
+ pVTab = pTab->u.vtab.p->pVtab;
+ if( NEVER(pVTab==0) ) continue;
+ if( NEVER(pVTab->pModule==0) ) continue;
+ if( pVTab->pModule->iVersion<4 ) continue;
+ if( pVTab->pModule->xIntegrity==0 ) continue;
+ sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
+ pTab->nTabRef++;
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
+ a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, a1);
+ continue;
+ }
+#endif
}
{
static const int iLn = VDBE_OFFSET_LINENO(2);
@@ -140459,7 +140649,11 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
j = seen[0]-1;
pIdxInfo->aConstraintUsage[j].argvIndex = 1;
pIdxInfo->aConstraintUsage[j].omit = 1;
- if( seen[1]==0 ) return SQLITE_OK;
+ if( seen[1]==0 ){
+ pIdxInfo->estimatedCost = (double)1000;
+ pIdxInfo->estimatedRows = 1000;
+ return SQLITE_OK;
+ }
pIdxInfo->estimatedCost = (double)20;
pIdxInfo->estimatedRows = 20;
j = seen[1]-1;
@@ -143686,11 +143880,7 @@ static const char *columnTypeImpl(
** data for the result-set column of the sub-select.
*/
if( iCol<pS->pEList->nExpr
-#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
- && iCol>=0
-#else
- && ALWAYS(iCol>=0)
-#endif
+ && (!ViewCanHaveRowid || iCol>=0)
){
/* If iCol is less than zero, then the expression requests the
** rowid of the sub-select or view. This expression is legal (see
@@ -146865,6 +147055,10 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
**
** (11) The subquery is not a VALUES clause
**
+** (12) The WHERE clause is not "rowid ISNULL" or the equivalent. This
+** case only comes up if SQLite is compiled using
+** SQLITE_ALLOW_ROWID_IN_VIEW.
+**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
*/
@@ -146975,6 +147169,18 @@ static int pushDownWhereTerms(
}
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( ViewCanHaveRowid && (pWhere->op==TK_ISNULL || pWhere->op==TK_NOTNULL) ){
+ Expr *pLeft = pWhere->pLeft;
+ if( ALWAYS(pLeft)
+ && pLeft->op==TK_COLUMN
+ && pLeft->iColumn < 0
+ ){
+ return 0; /* Restriction (12) */
+ }
+ }
+#endif
+
if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){
nChng++;
pSubq->selFlags |= SF_PushDown;
@@ -147602,12 +147808,14 @@ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
while( pSel->pPrior ){ pSel = pSel->pPrior; }
sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
pTab->iPKey = -1;
+ pTab->eTabType = TABTYP_VIEW;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
/* The usual case - do not allow ROWID on a subquery */
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
#else
- pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */
+ /* Legacy compatibility mode */
+ pTab->tabFlags |= TF_Ephemeral | sqlite3Config.mNoVisibleRowid;
#endif
return pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
}
@@ -147875,7 +148083,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pNestedFrom = pFrom->pSelect->pEList;
assert( pNestedFrom!=0 );
assert( pNestedFrom->nExpr==pTab->nCol );
- assert( VisibleRowid(pTab)==0 );
+ assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid );
}else{
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
@@ -147907,7 +148115,8 @@ static int selectExpander(Walker *pWalker, Select *p){
pUsing = 0;
}
- nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom));
+ nAdd = pTab->nCol;
+ if( VisibleRowid(pTab) && (selFlags & SF_NestedFrom)!=0 ) nAdd++;
for(j=0; j<nAdd; j++){
const char *zName;
struct ExprList_item *pX; /* Newly added ExprList term */
@@ -147989,7 +148198,8 @@ static int selectExpander(Walker *pWalker, Select *p){
pX = &pNew->a[pNew->nExpr-1];
assert( pX->zEName==0 );
if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){
- if( pNestedFrom ){
+ if( pNestedFrom && (!ViewCanHaveRowid || j<pNestedFrom->nExpr) ){
+ assert( j<pNestedFrom->nExpr );
pX->zEName = sqlite3DbStrDup(db, pNestedFrom->a[j].zEName);
testcase( pX->zEName==0 );
}else{
@@ -152923,6 +153133,9 @@ SQLITE_PRIVATE void sqlite3Update(
}
}
if( chngRowid==0 && pPk==0 ){
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( isView ) sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
+#endif
sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
}
}
@@ -153460,7 +153673,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew(
SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
Parse *pParse, /* The parsing context */
SrcList *pTabList, /* Table into which we are inserting */
- Upsert *pUpsert /* The ON CONFLICT clauses */
+ Upsert *pUpsert, /* The ON CONFLICT clauses */
+ Upsert *pAll /* Complete list of all ON CONFLICT clauses */
){
Table *pTab; /* That table into which we are inserting */
int rc; /* Result code */
@@ -153563,6 +153777,14 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
continue;
}
pUpsert->pUpsertIdx = pIdx;
+ if( sqlite3UpsertOfIndex(pAll,pIdx)!=pUpsert ){
+ /* Really this should be an error. The isDup ON CONFLICT clause will
+ ** never fire. But this problem was not discovered until three years
+ ** after multi-CONFLICT upsert was added, and so we silently ignore
+ ** the problem to prevent breaking applications that might actually
+ ** have redundant ON CONFLICT clauses. */
+ pUpsert->isDup = 1;
+ }
break;
}
if( pUpsert->pUpsertIdx==0 ){
@@ -153589,9 +153811,13 @@ SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert *pUpsert){
Upsert *pNext;
if( NEVER(pUpsert==0) ) return 0;
pNext = pUpsert->pNextUpsert;
- if( pNext==0 ) return 1;
- if( pNext->pUpsertTarget==0 ) return 1;
- if( pNext->pUpsertIdx==0 ) return 1;
+ while( 1 /*exit-by-return*/ ){
+ if( pNext==0 ) return 1;
+ if( pNext->pUpsertTarget==0 ) return 1;
+ if( pNext->pUpsertIdx==0 ) return 1;
+ if( !pNext->isDup ) return 0;
+ pNext = pNext->pNextUpsert;
+ }
return 0;
}
@@ -166619,16 +166845,10 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
for(i=0; i<pIdx->nColumn; i++){
Expr *pExpr;
int j = pIdx->aiColumn[i];
- int bMaybeNullRow;
if( j==XN_EXPR ){
pExpr = pIdx->aColExpr->a[i].pExpr;
- testcase( pTabItem->fg.jointype & JT_LEFT );
- testcase( pTabItem->fg.jointype & JT_RIGHT );
- testcase( pTabItem->fg.jointype & JT_LTORJ );
- bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0;
}else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){
pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]);
- bMaybeNullRow = 0;
}else{
continue;
}
@@ -166660,7 +166880,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
p->iDataCur = pTabItem->iCursor;
p->iIdxCur = iIdxCur;
p->iIdxCol = i;
- p->bMaybeNullRow = bMaybeNullRow;
+ p->bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0;
if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){
p->aff = pIdx->zColAff[i];
}
@@ -178865,6 +179085,18 @@ SQLITE_API int sqlite3_config(int op, ...){
}
#endif /* SQLITE_OMIT_DESERIALIZE */
+ case SQLITE_CONFIG_ROWID_IN_VIEW: {
+ int *pVal = va_arg(ap,int*);
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( 0==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = TF_NoVisibleRowid;
+ if( 1==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = 0;
+ *pVal = (sqlite3GlobalConfig.mNoVisibleRowid==0);
+#else
+ *pVal = 0;
+#endif
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
break;
@@ -204785,6 +205017,7 @@ json_parse_restart:
case '[': {
/* Parse array */
iThis = pParse->nBlob;
+ assert( i<=(u32)pParse->nJson );
jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0);
iStart = pParse->nBlob;
if( pParse->oom ) return -1;
@@ -205183,6 +205416,10 @@ static void jsonReturnStringAsBlob(JsonString *pStr){
JsonParse px;
memset(&px, 0, sizeof(px));
jsonStringTerminate(pStr);
+ if( pStr->eErr ){
+ sqlite3_result_error_nomem(pStr->pCtx);
+ return;
+ }
px.zJson = pStr->zBuf;
px.nJson = pStr->nUsed;
px.db = sqlite3_context_db_handle(pStr->pCtx);
@@ -206508,8 +206745,9 @@ rebuild_from_cache:
}
p->zJson = (char*)sqlite3_value_text(pArg);
p->nJson = sqlite3_value_bytes(pArg);
+ if( db->mallocFailed ) goto json_pfa_oom;
if( p->nJson==0 ) goto json_pfa_malformed;
- if( NEVER(p->zJson==0) ) goto json_pfa_oom;
+ assert( p->zJson!=0 );
if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){
if( flgs & JSON_KEEPERROR ){
p->nErr = 1;
@@ -206675,10 +206913,10 @@ static void jsonDebugPrintBlob(
if( sz==0 && x<=JSONB_FALSE ){
sqlite3_str_append(pOut, "\n", 1);
}else{
- u32 i;
+ u32 j;
sqlite3_str_appendall(pOut, ": \"");
- for(i=iStart+n; i<iStart+n+sz; i++){
- u8 c = pParse->aBlob[i];
+ for(j=iStart+n; j<iStart+n+sz; j++){
+ u8 c = pParse->aBlob[j];
if( c<0x20 || c>=0x7f ) c = '.';
sqlite3_str_append(pOut, (char*)&c, 1);
}
@@ -208086,6 +208324,9 @@ static int jsonEachColumn(
case JEACH_VALUE: {
u32 i = jsonSkipLabel(p);
jsonReturnFromBlob(&p->sParse, i, ctx, 1);
+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
break;
}
case JEACH_TYPE: {
@@ -208132,9 +208373,9 @@ static int jsonEachColumn(
case JEACH_JSON: {
if( p->sParse.zJson==0 ){
sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob,
- SQLITE_STATIC);
+ SQLITE_TRANSIENT);
}else{
- sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_TRANSIENT);
}
break;
}
@@ -209160,11 +209401,9 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
** Clear the Rtree.pNodeBlob object
*/
static void nodeBlobReset(Rtree *pRtree){
- if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){
- sqlite3_blob *pBlob = pRtree->pNodeBlob;
- pRtree->pNodeBlob = 0;
- sqlite3_blob_close(pBlob);
- }
+ sqlite3_blob *pBlob = pRtree->pNodeBlob;
+ pRtree->pNodeBlob = 0;
+ sqlite3_blob_close(pBlob);
}
/*
@@ -209208,7 +209447,6 @@ static int nodeAcquire(
&pRtree->pNodeBlob);
}
if( rc ){
- nodeBlobReset(pRtree);
*ppNode = 0;
/* If unable to open an sqlite3_blob on the desired row, that can only
** be because the shadow tables hold erroneous data. */
@@ -209268,6 +209506,7 @@ static int nodeAcquire(
}
*ppNode = pNode;
}else{
+ nodeBlobReset(pRtree);
if( pNode ){
pRtree->nNodeRef--;
sqlite3_free(pNode);
@@ -209412,6 +209651,7 @@ static void nodeGetCoord(
int iCoord, /* Which coordinate to extract */
RtreeCoord *pCoord /* OUT: Space to write result to */
){
+ assert( iCell<NCELL(pNode) );
readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord);
}
@@ -209601,7 +209841,9 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
sqlite3_finalize(pCsr->pReadAux);
sqlite3_free(pCsr);
pRtree->nCursor--;
- nodeBlobReset(pRtree);
+ if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){
+ nodeBlobReset(pRtree);
+ }
return SQLITE_OK;
}
@@ -210186,7 +210428,11 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
int rc = SQLITE_OK;
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
if( rc==SQLITE_OK && ALWAYS(p) ){
- *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+ if( p->iCell>=NCELL(pNode) ){
+ rc = SQLITE_ABORT;
+ }else{
+ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+ }
}
return rc;
}
@@ -210204,6 +210450,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
if( rc ) return rc;
if( NEVER(p==0) ) return SQLITE_OK;
+ if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT;
if( i==0 ){
sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
}else if( i<=pRtree->nDim2 ){
@@ -211685,8 +211932,7 @@ constraint:
*/
static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
Rtree *pRtree = (Rtree *)pVtab;
- assert( pRtree->inWrTrans==0 );
- pRtree->inWrTrans++;
+ pRtree->inWrTrans = 1;
return SQLITE_OK;
}
@@ -211700,6 +211946,9 @@ static int rtreeEndTransaction(sqlite3_vtab *pVtab){
nodeBlobReset(pRtree);
return SQLITE_OK;
}
+static int rtreeRollback(sqlite3_vtab *pVtab){
+ return rtreeEndTransaction(pVtab);
+}
/*
** The xRename method for rtree module virtual tables.
@@ -211818,7 +212067,7 @@ static sqlite3_module rtreeModule = {
rtreeBeginTransaction, /* xBegin - begin transaction */
rtreeEndTransaction, /* xSync - sync transaction */
rtreeEndTransaction, /* xCommit - commit transaction */
- rtreeEndTransaction, /* xRollback - rollback transaction */
+ rtreeRollback, /* xRollback - rollback transaction */
0, /* xFindFunction - function overloading */
rtreeRename, /* xRename - rename the table */
rtreeSavepoint, /* xSavepoint */
@@ -245377,23 +245626,26 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){
int ii;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5Index *pIndex = pIter->pIndex;
for(ii=0; ii<pT->nIter; ii++){
Fts5Iter *p = pT->apIter[ii];
if( p->base.bEof==0
&& (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom))
){
- fts5MultiIterNext(p->pIndex, p, bFrom, iFrom);
+ fts5MultiIterNext(pIndex, p, bFrom, iFrom);
while( bFrom && p->base.bEof==0
&& p->base.iRowid<iFrom
- && p->pIndex->rc==SQLITE_OK
+ && pIndex->rc==SQLITE_OK
){
- fts5MultiIterNext(p->pIndex, p, 0, 0);
+ fts5MultiIterNext(pIndex, p, 0, 0);
}
}
}
- fts5IterSetOutputsTokendata(pIter);
+ if( pIndex->rc==SQLITE_OK ){
+ fts5IterSetOutputsTokendata(pIter);
+ }
}
/*
@@ -250547,7 +250799,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355", -1, SQLITE_TRANSIENT);
}
/*
diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h
index 4fdfde004e..2618b37a7b 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.h
+++ b/src/libs/3rdparty/sqlite/sqlite3.h
@@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.45.1"
-#define SQLITE_VERSION_NUMBER 3045001
-#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
+#define SQLITE_VERSION "3.45.3"
+#define SQLITE_VERSION_NUMBER 3045003
+#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -420,6 +420,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
+** <li> The application must not dereference the arrays or string pointers
+** passed as the 3rd and 4th callback parameters after it returns.
** </ul>
*/
SQLITE_API int sqlite3_exec(
@@ -2141,6 +2143,22 @@ struct sqlite3_mem_methods {
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
+**
+** [[SQLITE_CONFIG_ROWID_IN_VIEW]]
+** <dt>SQLITE_CONFIG_ROWID_IN_VIEW
+** <dd>The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability
+** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is
+** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability
+** defaults to on. This configuration option queries the current setting or
+** changes the setting to off or on. The argument is a pointer to an integer.
+** If that integer initially holds a value of 1, then the ability for VIEWs to
+** have ROWIDs is activated. If the integer initially holds zero, then the
+** ability is deactivated. Any other initial value for the integer leaves the
+** setting unchanged. After changes, if any, the integer is written with
+** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite
+** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and
+** recommended case) then the integer is always filled with zero, regardless
+** if its initial value.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2172,6 +2190,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
+#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */
/*
** CAPI3REF: Database Connection Configuration Options
diff --git a/src/libs/languageutils/componentversion.cpp b/src/libs/languageutils/componentversion.cpp
index 6c599e300b..f041740d3b 100644
--- a/src/libs/languageutils/componentversion.cpp
+++ b/src/libs/languageutils/componentversion.cpp
@@ -10,27 +10,17 @@
namespace LanguageUtils {
-const int ComponentVersion::NoVersion = -1;
-const int ComponentVersion::MaxVersion = std::numeric_limits<int>::max();
+// QTC_TEMP
-ComponentVersion::ComponentVersion()
- : _major(NoVersion), _minor(NoVersion)
+ComponentVersion::ComponentVersion(QStringView versionString)
+ : _major(NoVersion)
+ , _minor(NoVersion)
{
-}
-
-ComponentVersion::ComponentVersion(int major, int minor)
- : _major(major), _minor(minor)
-{
-}
-
-ComponentVersion::ComponentVersion(const QString &versionString)
- : _major(NoVersion), _minor(NoVersion)
-{
- int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ auto dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx == -1)
return;
bool ok = false;
- int maybeMajor = versionString.left(dotIdx).toInt(&ok);
+ auto maybeMajor = versionString.left(dotIdx).toInt(&ok);
if (!ok)
return;
int maybeMinor = versionString.mid(dotIdx + 1).toInt(&ok);
@@ -40,15 +30,6 @@ ComponentVersion::ComponentVersion(const QString &versionString)
_minor = maybeMinor;
}
-ComponentVersion::~ComponentVersion()
-{
-}
-
-bool ComponentVersion::isValid() const
-{
- return _major >= 0 && _minor >= 0;
-}
-
QString ComponentVersion::toString() const
{
QByteArray temp;
@@ -65,36 +46,4 @@ void ComponentVersion::addToHash(QCryptographicHash &hash) const
hash.addData(reinterpret_cast<const char *>(&_minor), sizeof(_minor));
}
-bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return lhs.majorVersion() < rhs.majorVersion()
- || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() < rhs.minorVersion());
-}
-
-bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return lhs.majorVersion() < rhs.majorVersion()
- || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() <= rhs.minorVersion());
-}
-
-bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return rhs < lhs;
-}
-
-bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return rhs <= lhs;
-}
-
-bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion();
-}
-
-bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return !(lhs == rhs);
-}
-
} // namespace LanguageUtils
diff --git a/src/libs/languageutils/componentversion.h b/src/libs/languageutils/componentversion.h
index 4b39e8402a..2b8311a6cb 100644
--- a/src/libs/languageutils/componentversion.h
+++ b/src/libs/languageutils/componentversion.h
@@ -9,6 +9,10 @@ QT_BEGIN_NAMESPACE
class QCryptographicHash;
QT_END_NAMESPACE
+#include <QStringView>
+
+#include <limits>
+
namespace LanguageUtils {
class LANGUAGEUTILS_EXPORT ComponentVersion
@@ -17,25 +21,56 @@ class LANGUAGEUTILS_EXPORT ComponentVersion
int _minor;
public:
- static const int NoVersion;
- static const int MaxVersion;
+ static constexpr int NoVersion = -1;
+ static constexpr int MaxVersion = std::numeric_limits<int>::max();
+
+ ComponentVersion()
+ : _major(NoVersion)
+ , _minor(NoVersion)
+ {}
- ComponentVersion();
- ComponentVersion(int major, int minor);
- explicit ComponentVersion(const QString &versionString);
- ~ComponentVersion();
+ ComponentVersion(int major, int minor)
+ : _major(major)
+ , _minor(minor)
+ {}
+
+ explicit ComponentVersion(QStringView versionString);
+ ~ComponentVersion() = default;
int majorVersion() const { return _major; }
int minorVersion() const { return _minor; }
- friend bool LANGUAGEUTILS_EXPORT operator<(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator>(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator==(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+ friend bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return std::tie(lhs._major, lhs._minor) < std::tie(rhs._major, rhs._minor);
+ }
+
+ friend bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return std::tie(lhs._major, lhs._minor) <= std::tie(rhs._major, rhs._minor);
+ }
+
+ friend bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return rhs < lhs;
+ }
+
+ friend bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return rhs <= lhs;
+ }
+
+ friend bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion();
+ }
+
+ friend bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return !(lhs == rhs);
+ }
- bool isValid() const;
+ bool isValid() const { return _major >= 0 && _minor >= 0; }
QString toString() const;
void addToHash(QCryptographicHash &hash) const;
};
diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt
index 011c26f049..50693644ea 100644
--- a/src/libs/nanotrace/CMakeLists.txt
+++ b/src/libs/nanotrace/CMakeLists.txt
@@ -4,6 +4,7 @@ add_qtc_library(Nanotrace
nanotraceglobals.h
nanotrace.cpp nanotrace.h
nanotracehr.cpp nanotracehr.h
+ staticstring.h
PUBLIC_DEPENDS Qt::Core Qt::Gui
PROPERTIES
CXX_VISIBILITY_PRESET default
diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp
index 07d2ff95ac..0006227434 100644
--- a/src/libs/nanotrace/nanotracehr.cpp
+++ b/src/libs/nanotrace/nanotracehr.cpp
@@ -45,6 +45,18 @@ unsigned int getUnsignedIntegerHash(std::thread::id id)
return static_cast<unsigned int>(std::hash<std::thread::id>{}(id) & 0xFFFFFFFF);
}
+template<std::size_t capacity>
+constexpr bool isArgumentValid(const StaticString<capacity> &string)
+{
+ return string.isValid() && string.size();
+}
+
+template<typename String>
+constexpr bool isArgumentValid(const String &string)
+{
+ return string.size();
+}
+
template<typename TraceEvent>
void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId)
{
@@ -67,23 +79,24 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st
out << R"(,"flow_in":true)";
}
- if (event.arguments.size())
+ if (isArgumentValid(event.arguments)) {
out << R"(,"args":)" << event.arguments;
+ }
out << "}";
}
-void writeMetaEvent(TraceFile<Tracing::IsEnabled> *file, std::string_view key, std::string_view value)
+void writeMetaEvent(TraceFile<Tracing::IsEnabled> &file, std::string_view key, std::string_view value)
{
- std::lock_guard lock{file->fileMutex};
- auto &out = file->out;
+ std::lock_guard lock{file.fileMutex};
+ auto &out = file.out;
if (out.is_open()) {
- file->out << R"({"name":")" << key << R"(","ph":"M", "pid":)"
- << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)"
- << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")"
- << value << R"("}})"
- << ",\n";
+ file.out << R"({"name":")" << key << R"(","ph":"M", "pid":)"
+ << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)"
+ << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")"
+ << value << R"("}})"
+ << ",\n";
}
}
@@ -103,7 +116,6 @@ std::string getThreadName()
} // namespace
-namespace Internal {
template<typename String>
void convertToString(String &string, const QImage &image)
{
@@ -132,14 +144,11 @@ void convertToString(String &string, const QImage &image)
return "alpha premultiplied"sv;
}))));
- Internal::convertToString(string, dict);
+ convertToString(string, dict);
}
-template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image);
template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image);
-} // namespace Internal
-
template<typename TraceEvent>
void flushEvents(const Utils::span<TraceEvent> events,
std::thread::id threadId,
@@ -148,8 +157,8 @@ void flushEvents(const Utils::span<TraceEvent> events,
if (events.empty())
return;
- std::lock_guard lock{eventQueue.file->fileMutex};
- auto &out = eventQueue.file->out;
+ std::lock_guard lock{eventQueue.file.fileMutex};
+ auto &out = eventQueue.file.out;
if (out.is_open()) {
auto processId = QCoreApplication::applicationPid();
@@ -200,17 +209,17 @@ void finalizeFile(EnabledTraceFile &file)
template<typename TraceEvent>
void flushInThread(EnabledEventQueue<TraceEvent> &eventQueue)
{
- if (eventQueue.file->processing.valid())
- eventQueue.file->processing.wait();
+ if (eventQueue.file.processing.valid())
+ eventQueue.file.processing.wait();
auto flush = [&](const Utils::span<TraceEvent> &events, std::thread::id threadId) {
flushEvents(events, threadId, eventQueue);
};
- eventQueue.file->processing = std::async(std::launch::async,
- flush,
- eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex),
- eventQueue.threadId);
+ eventQueue.file.processing = std::async(std::launch::async,
+ flush,
+ eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex),
+ eventQueue.threadId);
eventQueue.currentEvents = eventQueue.currentEvents.data() == eventQueue.eventsOne.data()
? eventQueue.eventsTwo
: eventQueue.eventsOne;
@@ -223,10 +232,11 @@ template NANOTRACE_EXPORT void flushInThread(
EnabledEventQueue<StringViewWithStringArgumentsTraceEvent> &eventQueue);
template<typename TraceEvent>
-EventQueue<TraceEvent, Tracing::IsEnabled>::EventQueue(EnabledTraceFile *file)
+EventQueue<TraceEvent, Tracing::IsEnabled>::EventQueue(EnabledTraceFile &file)
: file{file}
, threadId{std::this_thread::get_id()}
{
+ setEventsSpans(*eventArrayOne.get(), *eventArrayTwo.get());
Internal::EventQueueTracker<TraceEvent>::get().addQueue(this);
if (auto thread = QThread::currentThread()) {
auto name = getThreadName();
diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h
index 7856ea3582..810e4e3c5d 100644
--- a/src/libs/nanotrace/nanotracehr.h
+++ b/src/libs/nanotrace/nanotracehr.h
@@ -5,16 +5,22 @@
#include "nanotraceglobals.h"
+#include "staticstring.h"
+
#include <utils/smallstring.h>
#include <utils/span.h>
#include <QByteArrayView>
+#include <QList>
+#include <QMap>
#include <QStringView>
+#include <QVarLengthArray>
#include <QVariant>
#include <array>
#include <atomic>
#include <chrono>
+#include <exception>
#include <fstream>
#include <future>
#include <mutex>
@@ -41,7 +47,7 @@ enum class Tracing { IsDisabled, IsEnabled };
# define NO_UNIQUE_ADDRESS
#endif
-using ArgumentsString = Utils::BasicSmallString<510>;
+using ArgumentsString = StaticString<3700>;
namespace Literals {
struct TracerLiteral
@@ -80,44 +86,41 @@ struct IsDictonary
inline constexpr IsDictonary isDictonary;
-namespace Internal {
-
template<typename String>
void convertToString(String &string, std::string_view text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(text);
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, const QImage &image);
-extern template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image);
extern template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image);
template<typename String, std::size_t size>
void convertToString(String &string, const char (&text)[size])
{
- string.append(R"(")");
- string.append(text);
- string.append(R"(")");
+ string.append('\"');
+ string.append(std::string_view{text, size - 1});
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, QStringView text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(Utils::PathString{text});
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, const QByteArray &text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(std::string_view(text.data(), Utils::usize(text)));
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
@@ -129,28 +132,29 @@ void convertToString(String &string, bool isTrue)
string.append("false");
}
-template<typename String, typename Callable, typename = std::enable_if_t<std::is_invocable_v<Callable>>>
+template<typename String, typename Callable, typename std::enable_if_t<std::is_invocable_v<Callable>, bool> = true>
void convertToString(String &string, Callable &&callable)
{
convertToString(string, callable());
}
-template<typename String>
-void convertToString(String &string, int number)
+template<typename String, typename Number, typename std::enable_if_t<std::is_arithmetic_v<Number>, bool> = true>
+void convertToString(String &string, Number number)
{
- string.append(Utils::SmallString::number(number));
+ string.append(number);
}
-template<typename String>
-void convertToString(String &string, long long number)
+template<typename Enumeration>
+constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
{
- string.append(Utils::SmallString::number(number));
+ static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
+ return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
-template<typename String>
-void convertToString(String &string, double number)
+template<typename String, typename Enumeration, typename std::enable_if_t<std::is_enum_v<Enumeration>, bool> = true>
+void convertToString(String &string, Enumeration enumeration)
{
- string.append(Utils::SmallString::number(number));
+ string.append(to_underlying(enumeration));
}
template<typename String>
@@ -165,14 +169,14 @@ void convertToString(String &string, const QVariant &value)
convertToString(string, value.toString());
}
-template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsDictonary &, Arguments...> &dictonary);
-
-template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsArray &, Arguments...> &list);
-
-template<typename String, template<typename...> typename Container, typename... Arguments>
-void convertToString(String &string, const Container<Arguments...> &container);
+template<typename String, typename Type>
+void convertToString(String &string, const std::optional<Type> &value)
+{
+ if (value)
+ convertToString(string, *value);
+ else
+ convertToString(string, "empty optional");
+}
template<typename String, typename Value>
void convertArrayEntryToString(String &string, const Value &value)
@@ -184,15 +188,15 @@ void convertArrayEntryToString(String &string, const Value &value)
template<typename String, typename... Entries>
void convertArrayToString(String &string, const IsArray &, Entries &...entries)
{
- string.append(R"([)");
+ string.append('[');
(convertArrayEntryToString(string, entries), ...);
if (sizeof...(entries))
string.pop_back();
- string.append("]");
+ string.append(']');
}
template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsArray &, Arguments...> &list)
+void convertToString(String &string, const std::tuple<IsArray, Arguments...> &list)
{
std::apply([&](auto &&...entries) { convertArrayToString(string, entries...); }, list);
}
@@ -202,55 +206,107 @@ void convertDictonaryEntryToString(String &string, const std::tuple<Key, Value>
{
const auto &[key, value] = argument;
convertToString(string, key);
- string.append(":");
+ string.append(':');
convertToString(string, value);
- string.append(",");
+ string.append(',');
}
template<typename String, typename... Entries>
void convertDictonaryToString(String &string, const IsDictonary &, Entries &...entries)
{
- string.append(R"({)");
+ string.append('{');
(convertDictonaryEntryToString(string, entries), ...);
if (sizeof...(entries))
string.pop_back();
- string.append("}");
+ string.append('}');
}
template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsDictonary &, Arguments...> &dictonary)
+void convertToString(String &string, const std::tuple<IsDictonary, Arguments...> &dictonary)
{
std::apply([&](auto &&...entries) { convertDictonaryToString(string, entries...); }, dictonary);
}
-template<typename String, template<typename...> typename Container, typename... Arguments>
-void convertToString(String &string, const Container<Arguments...> &container)
+template<typename T>
+struct is_container : std::false_type
+{};
+
+template<typename... Arguments>
+struct is_container<std::vector<Arguments...>> : std::true_type
+{};
+
+template<typename... Arguments>
+struct is_container<QList<Arguments...>> : std::true_type
+{};
+
+template<typename T, qsizetype Prealloc>
+struct is_container<QVarLengthArray<T, Prealloc>> : std::true_type
+{};
+
+template<typename String, typename Container, typename std::enable_if_t<is_container<Container>::value, bool> = true>
+void convertToString(String &string, const Container &values)
+{
+ string.append('[');
+
+ for (const auto &value : values) {
+ convertToString(string, value);
+ string.append(',');
+ }
+
+ if (values.size())
+ string.pop_back();
+
+ string.append(']');
+}
+
+template<typename T>
+struct is_map : std::false_type
+{};
+
+template<typename... Arguments>
+struct is_map<QtPrivate::QKeyValueRange<Arguments...>> : std::true_type
+{};
+
+template<typename... Arguments>
+struct is_map<std::map<Arguments...>> : std::true_type
+{};
+
+template<typename String, typename Map, typename std::enable_if_t<is_map<Map>::value, bool> = true>
+void convertToString(String &string, const Map &map)
{
- string.append("[");
- for (const auto &entry : container) {
- convertToString(string, entry);
- string.append(",");
+ string.append('{');
+
+ for (const auto &[key, value] : map) {
+ convertToString(string, key);
+ string.append(':');
+ convertToString(string, value);
+ string.append(',');
}
- if (container.size())
+ if (map.begin() != map.end())
string.pop_back();
- string.append("]");
+ string.append('}');
+}
+
+template<typename String, typename Key, typename Value>
+void convertToString(String &string, const QMap<Key, Value> &dictonary)
+{
+ convertToString(string, dictonary.asKeyValueRange());
}
+namespace Internal {
+
template<typename String, typename... Arguments>
-String toArguments(Arguments &&...arguments)
+void toArguments(String &text, Arguments &&...arguments)
{
- String text;
constexpr auto argumentCount = sizeof...(Arguments);
- text.append("{");
+ text.append('{');
(convertDictonaryEntryToString(text, arguments), ...);
if (argumentCount)
text.pop_back();
- text.append("}");
-
- return text;
+ text.append('}');
}
inline std::string_view toArguments(std::string_view arguments)
@@ -259,48 +315,58 @@ inline std::string_view toArguments(std::string_view arguments)
}
template<typename String>
-void appendArguments(String &eventArguments)
+void setArguments(String &eventArguments)
{
- eventArguments = {};
+ if constexpr (std::is_same_v<String, std::string_view>)
+ eventArguments = {};
+ else
+ eventArguments.clear();
}
template<typename String>
-void appendArguments(String &eventArguments, TracerLiteral arguments)
+void setArguments(String &eventArguments, TracerLiteral arguments)
{
eventArguments = arguments;
}
template<typename String, typename... Arguments>
-[[maybe_unused]] void appendArguments(String &eventArguments, Arguments &&...arguments)
+[[maybe_unused]] void setArguments(String &eventArguments, Arguments &&...arguments)
{
static_assert(
!std::is_same_v<String, std::string_view>,
R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)");
- eventArguments = Internal::toArguments<String>(std::forward<Arguments>(arguments)...);
+ if constexpr (std::is_same_v<String, std::string_view>)
+ eventArguments = {};
+ else
+ eventArguments.clear();
+ Internal::toArguments(eventArguments, std::forward<Arguments>(arguments)...);
}
} // namespace Internal
template<typename Key, typename Value>
-auto keyValue(Key &&key, Value &&value)
+auto keyValue(const Key &key, Value &&value)
{
- return std::forward_as_tuple(std::forward<Key>(key), std::forward<Value>(value));
+ if constexpr (std::is_lvalue_reference_v<Value>)
+ return std::tuple<const Key &, const Value &>(key, value);
+ else
+ return std::tuple<const Key &, std::decay_t<Value>>(key, value);
}
template<typename... Entries>
auto dictonary(Entries &&...entries)
{
- return std::forward_as_tuple(isDictonary, std::forward<Entries>(entries)...);
+ return std::make_tuple(isDictonary, std::forward<Entries>(entries)...);
}
template<typename... Entries>
auto array(Entries &&...entries)
{
- return std::forward_as_tuple(isArray, std::forward<Entries>(entries)...);
+ return std::make_tuple(isArray, std::forward<Entries>(entries)...);
}
-enum class IsFlow : std::size_t { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out };
+enum class IsFlow : char { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out };
inline bool operator&(IsFlow first, IsFlow second)
{
@@ -309,7 +375,7 @@ inline bool operator&(IsFlow first, IsFlow second)
}
template<typename String, typename ArgumentsString>
-struct TraceEvent
+struct alignas(4096) TraceEvent
{
using StringType = String;
using ArgumentType = std::conditional_t<std::is_same_v<String, std::string_view>, TracerLiteral, String>;
@@ -324,13 +390,13 @@ struct TraceEvent
String name;
String category;
- ArgumentsString arguments;
TimePoint time;
Duration duration;
std::size_t id = 0;
- std::size_t bindId : 62;
- IsFlow flow : 2;
+ std::size_t bindId = 0;
+ IsFlow flow = IsFlow::No;
char type = ' ';
+ ArgumentsString arguments;
};
using StringViewTraceEvent = TraceEvent<std::string_view, std::string_view>;
@@ -413,6 +479,8 @@ class EventQueue
{
public:
using IsActive = std::false_type;
+
+ template<typename TraceFile> EventQueue(TraceFile &) {}
};
namespace Internal {
@@ -423,7 +491,13 @@ class EventQueueTracker
using Queue = EventQueue<TraceEvent, Tracing::IsEnabled>;
public:
- EventQueueTracker() = default;
+ EventQueueTracker()
+ {
+ terminateHandler = std::get_terminate();
+
+ std::set_terminate([]() { EventQueueTracker::get().terminate(); });
+ }
+
EventQueueTracker(const EventQueueTracker &) = delete;
EventQueueTracker(EventQueueTracker &&) = delete;
EventQueueTracker &operator=(const EventQueueTracker &) = delete;
@@ -457,8 +531,25 @@ public:
}
private:
+ void terminate()
+ {
+ flushAll();
+ if (terminateHandler)
+ terminateHandler();
+ }
+
+ void flushAll()
+ {
+ std::lock_guard lock{mutex};
+
+ for (auto queue : queues)
+ queue->flush();
+ }
+
+private:
std::mutex mutex;
std::vector<Queue *> queues;
+ std::terminate_handler terminateHandler = nullptr;
};
} // namespace Internal
@@ -466,11 +557,12 @@ template<typename TraceEvent>
class EventQueue<TraceEvent, Tracing::IsEnabled>
{
using TraceEventsSpan = Utils::span<TraceEvent>;
+ using TraceEvents = std::array<TraceEvent, 1000>;
public:
using IsActive = std::true_type;
- EventQueue(EnabledTraceFile *file);
+ EventQueue(EnabledTraceFile &file);
~EventQueue();
@@ -483,7 +575,9 @@ public:
EventQueue &operator=(const EventQueue &) = delete;
EventQueue &operator=(EventQueue &&) = delete;
- EnabledTraceFile *file = nullptr;
+ EnabledTraceFile &file;
+ std::unique_ptr<TraceEvents> eventArrayOne = std::make_unique<TraceEvents>();
+ std::unique_ptr<TraceEvents> eventArrayTwo = std::make_unique<TraceEvents>();
TraceEventsSpan eventsOne;
TraceEventsSpan eventsTwo;
TraceEventsSpan currentEvents;
@@ -505,35 +599,6 @@ extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE EventQueue<StringTraceEve
extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE
EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
-template<typename TraceEvent, std::size_t eventCount, Tracing isEnabled>
-class EventQueueData : public EventQueue<TraceEvent, isEnabled>
-{
-public:
- using IsActive = std::true_type;
-
- EventQueueData(TraceFile<Tracing::IsDisabled> &) {}
-};
-
-template<typename TraceEvent, std::size_t eventCount>
-class EventQueueData<TraceEvent, eventCount, Tracing::IsEnabled>
- : public EventQueue<TraceEvent, Tracing::IsEnabled>
-{
- using TraceEvents = std::array<TraceEvent, eventCount>;
- using Base = EventQueue<TraceEvent, Tracing::IsEnabled>;
-
-public:
- using IsActive = std::true_type;
-
- EventQueueData(EnabledTraceFile &file)
- : Base{&file}
- {
- Base::setEventsSpans(*eventsOne.get(), *eventsTwo.get());
- }
-
- std::unique_ptr<TraceEvents> eventsOne = std::make_unique<TraceEvents>();
- std::unique_ptr<TraceEvents> eventsTwo = std::make_unique<TraceEvents>();
-};
-
template<typename TraceEvent>
TraceEvent &getTraceEvent(EnabledEventQueue<TraceEvent> &eventQueue)
{
@@ -1150,6 +1215,10 @@ public:
return std::pair<TracerType, FlowTokenType>();
}
+ template<typename... Arguments>
+ void threadEvent(ArgumentType, Arguments &&...)
+ {}
+
static constexpr bool isActive() { return false; }
};
@@ -1237,6 +1306,24 @@ public:
std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)};
}
+ template<typename... Arguments>
+ void threadEvent(ArgumentType traceName, Arguments &&...arguments)
+ {
+ if (isEnabled == IsEnabled::No)
+ return;
+
+ auto &traceEvent = getTraceEvent(m_eventQueue);
+
+ traceEvent.time = Clock::now();
+ traceEvent.name = std::move(traceName);
+ traceEvent.category = traceName;
+ traceEvent.type = 'i';
+ traceEvent.id = 0;
+ traceEvent.bindId = 0;
+ traceEvent.flow = IsFlow::No;
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ }
+
EnabledEventQueue<TraceEvent> &eventQueue() const { return m_eventQueue; }
std::string_view name() const { return m_name; }
@@ -1270,7 +1357,7 @@ private:
traceEvent.id = id;
traceEvent.bindId = bindId;
traceEvent.flow = flow;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
traceEvent.time = Clock::now();
}
@@ -1296,7 +1383,7 @@ private:
traceEvent.id = id;
traceEvent.bindId = bindId;
traceEvent.flow = flow;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
}
template<typename... Arguments>
@@ -1316,9 +1403,11 @@ private:
traceEvent.id = id;
traceEvent.bindId = 0;
traceEvent.flow = IsFlow::No;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
}
+ CategoryFunctionPointer self() { return m_self; }
+
private:
StringType m_name;
EnabledEventQueue<TraceEvent> &m_eventQueue;
@@ -1403,11 +1492,8 @@ class Tracer<Category, std::true_type>
, flow{flow}
, m_category{category}
{
- if (category().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments<ArgumentsStringType>(m_arguments,
- std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
+ if (category().isEnabled == IsEnabled::Yes)
+ sendBeginTrace(std::forward<Arguments>(arguments)...);
}
public:
@@ -1426,13 +1512,15 @@ public:
: m_name{name}
, m_category{category}
{
- if (category().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments<ArgumentsStringType>(m_arguments,
- std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
+ if (category().isEnabled == IsEnabled::Yes)
+ sendBeginTrace(std::forward<Arguments>(arguments)...);
}
+ template<typename... Arguments>
+ [[nodiscard]] Tracer(ArgumentType name, Category &category, Arguments &&...arguments)
+ : Tracer(std::move(name), category.self(), std::forward<Arguments>(arguments)...)
+ {}
+
Tracer(const Tracer &) = delete;
Tracer &operator=(const Tracer &) = delete;
Tracer(Tracer &&other) noexcept = delete;
@@ -1440,7 +1528,7 @@ public:
TokenType createToken() { return {0, m_category}; }
- ~Tracer() { sendTrace(); }
+ ~Tracer() { sendEndTrace(); }
template<typename... Arguments>
Tracer beginDuration(ArgumentType name, Arguments &&...arguments)
@@ -1457,44 +1545,52 @@ public:
template<typename... Arguments>
void end(Arguments &&...arguments)
{
- sendTrace(std::forward<Arguments>(arguments)...);
+ sendEndTrace(std::forward<Arguments>(arguments)...);
m_name = {};
}
private:
template<typename... Arguments>
- void sendTrace(Arguments &&...arguments)
+ void sendBeginTrace(Arguments &&...arguments)
+ {
+ auto &category = m_category();
+ if (category.isEnabled == IsEnabled::Yes) {
+ auto &traceEvent = getTraceEvent(category.eventQueue());
+ traceEvent.name = m_name;
+ traceEvent.category = category.name();
+ traceEvent.bindId = m_bindId;
+ traceEvent.flow = flow;
+ traceEvent.type = 'B';
+ Internal::setArguments<ArgumentsStringType>(traceEvent.arguments,
+ std::forward<Arguments>(arguments)...);
+ traceEvent.time = Clock::now();
+ }
+ }
+
+ template<typename... Arguments>
+ void sendEndTrace(Arguments &&...arguments)
{
if (m_name.size()) {
- auto category = m_category();
+ auto &category = m_category();
if (category.isEnabled == IsEnabled::Yes) {
- auto duration = Clock::now() - m_start;
+ auto end = Clock::now();
auto &traceEvent = getTraceEvent(category.eventQueue());
- traceEvent.name = m_name;
+ traceEvent.name = std::move(m_name);
traceEvent.category = category.name();
- traceEvent.time = m_start;
- traceEvent.duration = duration;
+ traceEvent.time = end;
traceEvent.bindId = m_bindId;
traceEvent.flow = flow;
- traceEvent.type = 'X';
- if (sizeof...(arguments)) {
- m_arguments.clear();
- Internal::appendArguments<ArgumentsStringType>(traceEvent.arguments,
- std::forward<Arguments>(
- arguments)...);
- } else {
- traceEvent.arguments = m_arguments;
- }
+ traceEvent.type = 'E';
+ Internal::setArguments<ArgumentsStringType>(traceEvent.arguments,
+ std::forward<Arguments>(arguments)...);
}
}
}
private:
- TimePoint m_start;
StringType m_name;
- ArgumentsStringType m_arguments;
- std::size_t m_bindId;
- IsFlow flow;
+ std::size_t m_bindId = 0;
+ IsFlow flow = IsFlow::No;
CategoryFunctionPointer m_category;
};
diff --git a/src/libs/nanotrace/staticstring.h b/src/libs/nanotrace/staticstring.h
new file mode 100644
index 0000000000..d787bd2fe3
--- /dev/null
+++ b/src/libs/nanotrace/staticstring.h
@@ -0,0 +1,116 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <utils/smallstringview.h>
+
+#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L))
+# include <QLocale>
+#endif
+
+#include <array>
+#include <charconv>
+#include <limits>
+
+namespace NanotraceHR {
+
+template<std::size_t Capacity>
+class StaticString
+{
+public:
+ StaticString() = default;
+ StaticString(const StaticString &) = delete;
+ StaticString &operator=(const StaticString &) = delete;
+
+ char *data() { return m_data.data(); }
+
+ const char *data() const { return m_data.data(); }
+
+ void append(Utils::SmallStringView string) noexcept
+ {
+ auto newSize = m_size + string.size();
+
+ if (newSize <= Capacity) {
+ std::char_traits<char>::copy(std::next(data(), static_cast<std::ptrdiff_t>(m_size)),
+ string.data(),
+ string.size());
+ m_size = newSize;
+ } else {
+ m_size = Capacity + 1;
+ }
+ }
+
+ void append(char character) noexcept
+ {
+ auto newSize = m_size + 1;
+
+ if (newSize <= Capacity) {
+ auto current = std::next(data(), static_cast<std::ptrdiff_t>(m_size));
+ *current = character;
+
+ m_size = newSize;
+ } else {
+ m_size = Capacity + 1;
+ }
+ }
+
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ void append(Type number)
+ {
+#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L))
+ if constexpr (std::is_floating_point_v<Type>) {
+ QLocale locale{QLocale::Language::C};
+ append(locale.toString(number).toStdString());
+ return;
+ }
+#endif
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+ }
+
+ void pop_back() { --m_size; }
+
+ StaticString &operator+=(Utils::SmallStringView string) noexcept
+ {
+ append(string);
+
+ return *this;
+ }
+
+ StaticString &operator+=(char character) noexcept
+ {
+ append(character);
+
+ return *this;
+ }
+
+ template<typename Type, typename = std::enable_if_t<std::is_arithmetic_v<Type>>>
+ StaticString &operator+=(Type number) noexcept
+ {
+ append(number);
+
+ return *this;
+ }
+
+ bool isValid() const { return m_size <= Capacity; }
+
+ std::size_t size() const { return m_size; }
+
+ friend std::ostream &operator<<(std::ostream &out, const StaticString &text)
+ {
+ return out << std::string_view{text.data(), text.size()};
+ }
+
+ void clear() { m_size = 0; }
+
+private:
+ std::array<char, Capacity> m_data;
+ std::size_t m_size = 0;
+};
+
+} // namespace NanotraceHR
diff --git a/src/libs/qmljs/CMakeLists.txt b/src/libs/qmljs/CMakeLists.txt
index 4ca15cf7cc..a4966a8b4a 100644
--- a/src/libs/qmljs/CMakeLists.txt
+++ b/src/libs/qmljs/CMakeLists.txt
@@ -33,7 +33,6 @@ add_qtc_library(QmlJS
qmljsfindexportedcpptypes.cpp qmljsfindexportedcpptypes.h
qmljsicons.cpp qmljsicons.h
qmljsimportdependencies.cpp qmljsimportdependencies.h
- qmljsindenter.cpp qmljsindenter.h
qmljsinterpreter.cpp qmljsinterpreter.h
qmljslineinfo.cpp qmljslineinfo.h
qmljslink.cpp qmljslink.h
diff --git a/src/libs/qmljs/qmljs.qbs b/src/libs/qmljs/qmljs.qbs
index 18434c83ba..d5cc21faf1 100644
--- a/src/libs/qmljs/qmljs.qbs
+++ b/src/libs/qmljs/qmljs.qbs
@@ -28,7 +28,6 @@ QtcLibrary {
"qmljsfindexportedcpptypes.cpp", "qmljsfindexportedcpptypes.h",
"qmljsicons.cpp", "qmljsicons.h",
"qmljsimportdependencies.cpp", "qmljsimportdependencies.h",
- "qmljsindenter.cpp", "qmljsindenter.h",
"qmljsinterpreter.cpp", "qmljsinterpreter.h",
"qmljsdialect.cpp", "qmljsdialect.h",
"qmljslineinfo.cpp", "qmljslineinfo.h",
diff --git a/src/libs/qmljs/qmljsindenter.cpp b/src/libs/qmljs/qmljsindenter.cpp
deleted file mode 100644
index 8055847d90..0000000000
--- a/src/libs/qmljs/qmljsindenter.cpp
+++ /dev/null
@@ -1,603 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-/*
- This file is a self-contained interactive indenter for Qt Script.
-
- The general problem of indenting a program is ill posed. On
- the one hand, an indenter has to analyze programs written in a
- free-form formal language that is best described in terms of
- tokens, not characters, not lines. On the other hand, indentation
- applies to lines and white space characters matter, and otherwise
- the programs to indent are formally invalid in general, as they
- are begin edited.
-
- The approach taken here works line by line. We receive a program
- consisting of N lines or more, and we want to compute the
- indentation appropriate for the Nth line. Lines beyond the Nth
- lines are of no concern to us, so for simplicity we pretend the
- program has exactly N lines and we call the Nth line the "bottom
- line". Typically, we have to indent the bottom line when it's
- still empty, so we concentrate our analysis on the N - 1 lines
- that precede.
-
- By inspecting the (N - 1)-th line, the (N - 2)-th line, ...
- backwards, we determine the kind of the bottom line and indent it
- accordingly.
-
- * The bottom line is a comment line. See
- bottomLineStartsInCComment() and
- indentWhenBottomLineStartsInCComment().
- * The bottom line is a continuation line. See isContinuationLine()
- and indentForContinuationLine().
- * The bottom line is a standalone line. See
- indentForStandaloneLine().
-
- Certain tokens that influence the indentation, notably braces,
- are looked for in the lines. This is done by simple string
- comparison, without a real tokenizer. Confusing constructs such
- as comments and string literals are removed beforehand.
-*/
-
-#include <qmljs/qmljsindenter.h>
-#include <qmljs/qmljsscanner.h>
-
-#include <QTextBlock>
-
-using namespace QmlJS;
-
-/*
- Saves and restores the state of the global linizer. This enables
- backtracking.
-
- Identical to the defines in qmljslineinfo.cpp
-*/
-#define YY_SAVE() LinizerState savedState = yyLinizerState
-#define YY_RESTORE() yyLinizerState = savedState
-
-
-QmlJSIndenter::QmlJSIndenter()
- : caseOrDefault(QRegularExpression(QLatin1String(
- "^\\s*(?:"
- "case\\b[^:]+|"
- "default)"
- "\\s*:.*$")))
-
-{
-
- /*
- The indenter supports a few parameters:
-
- * ppHardwareTabSize is the size of a '\t' in your favorite editor.
- * ppIndentSize is the size of an indentation, or software tab
- size.
- * ppContinuationIndentSize is the extra indent for a continuation
- line, when there is nothing to align against on the previous
- line.
- * ppCommentOffset is the indentation within a C-style comment,
- when it cannot be picked up.
- */
-
- ppHardwareTabSize = 8;
- ppIndentSize = 4;
- ppContinuationIndentSize = 8;
- ppCommentOffset = 2;
-}
-
-QmlJSIndenter::~QmlJSIndenter()
-{
-}
-
-void QmlJSIndenter::setTabSize(int size)
-{
- ppHardwareTabSize = size;
-}
-
-void QmlJSIndenter::setIndentSize(int size)
-{
- ppIndentSize = size;
- ppContinuationIndentSize = 2 * size;
-}
-
-/*
- Returns true if string t is made only of white space; otherwise
- returns false.
-*/
-bool QmlJSIndenter::isOnlyWhiteSpace(const QString &t) const
-{
- return firstNonWhiteSpace(t).isNull();
-}
-
-/*
- Assuming string t is a line, returns the column number of a given
- index. Column numbers and index are identical for strings that don't
- contain '\t's.
-*/
-int QmlJSIndenter::columnForIndex(const QString &t, int index) const
-{
- int col = 0;
- if (index > t.length())
- index = t.length();
-
- for (int i = 0; i < index; i++) {
- if (t.at(i) == QLatin1Char('\t'))
- col = ((col / ppHardwareTabSize) + 1) * ppHardwareTabSize;
- else
- col++;
- }
- return col;
-}
-
-/*
- Returns the indentation size of string t.
-*/
-int QmlJSIndenter::indentOfLine(const QString &t) const
-{
- return columnForIndex(t, t.indexOf(firstNonWhiteSpace(t)));
-}
-
-/*
- Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better
- left alone since they break the "index equals column" rule. No
- provisions are taken against '\n' or '\r', which shouldn't occur in
- t anyway.
-*/
-void QmlJSIndenter::eraseChar(QString &t, int k, QChar ch) const
-{
- if (t.at(k) != QLatin1Char('\t'))
- t[k] = ch;
-}
-
-/*
- Returns '(' if the last parenthesis is opening, ')' if it is
- closing, and QChar() if there are no parentheses in t.
-*/
-QChar QmlJSIndenter::lastParen() const
-{
- for (int index = yyLinizerState.tokens.size() - 1; index != -1; --index) {
- const Token &token = yyLinizerState.tokens.at(index);
-
- if (token.is(Token::LeftParenthesis))
- return QLatin1Char('(');
-
- else if (token.is(Token::RightParenthesis))
- return QLatin1Char(')');
- }
-
- return QChar();
-}
-
-/*
- Returns true if typedIn the same as okayCh or is null; otherwise
- returns false.
-*/
-bool QmlJSIndenter::okay(QChar typedIn, QChar okayCh) const
-{
- return typedIn == QChar() || typedIn == okayCh;
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram
- assuming that it starts in a C-style comment, a condition that is
- tested elsewhere.
-
- Essentially, we're trying to align against some text on the
- previous line.
-*/
-int QmlJSIndenter::indentWhenBottomLineStartsInMultiLineComment()
-{
- QTextBlock block = yyProgram.lastBlock().previous();
- QString blockText;
-
- for (; block.isValid(); block = block.previous()) {
- blockText = block.text();
-
- if (! isOnlyWhiteSpace(blockText))
- break;
- }
-
- return indentOfLine(blockText);
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram,
- assuming it's a continuation line.
-
- We're trying to align the continuation line against some parenthesis
- or other bracked left opened on a previous line, or some interesting
- operator such as '='.
-*/
-int QmlJSIndenter::indentForContinuationLine()
-{
- int braceDepth = 0;
- int delimDepth = 0;
-
- bool leftBraceFollowed = *yyLeftBraceFollows;
-
- for (int i = 0; i < SmallRoof; i++) {
- int hook = -1;
-
- int j = yyLine->length();
- while (j > 0 && hook < 0) {
- j--;
- QChar ch = yyLine->at(j);
-
- switch (ch.unicode()) {
- case ')':
- delimDepth++;
- break;
- case ']':
- braceDepth++;
- break;
- case '}':
- braceDepth++;
- break;
- case '(':
- delimDepth--;
- /*
- An unclosed delimiter is a good place to align at,
- at least for some styles (including Qt's).
- */
- if (delimDepth == -1)
- hook = j;
- break;
-
- case '[':
- braceDepth--;
- /*
- An unclosed delimiter is a good place to align at,
- at least for some styles (including Qt's).
- */
- if (braceDepth == -1)
- hook = j;
- break;
- case '{':
- braceDepth--;
- /*
- A left brace followed by other stuff on the same
- line is typically for an enum or an initializer.
- Such a brace must be treated just like the other
- delimiters.
- */
- if (braceDepth == -1) {
- if (j < yyLine->length() - 1)
- hook = j;
- else
- return 0; // shouldn't happen
- }
- break;
- case '=':
- /*
- An equal sign is a very natural alignment hook
- because it's usually the operator with the lowest
- precedence in statements it appears in. Case in
- point:
-
- int x = 1 +
- 2;
-
- However, we have to beware of constructs such as
- default arguments and explicit enum constant
- values:
-
- void foo(int x = 0,
- int y = 0);
-
- And not
-
- void foo(int x = 0,
- int y = 0);
-
- These constructs are caracterized by a ',' at the
- end of the unfinished lines or by unbalanced
- parentheses.
- */
- Q_ASSERT(j - 1 >= 0);
-
- if (QString::fromLatin1("!=<>").indexOf(yyLine->at(j - 1)) == -1 &&
- j + 1 < yyLine->length() && yyLine->at(j + 1) != QLatin1Char('=')) {
- if (braceDepth == 0 && delimDepth == 0 &&
- j < yyLine->length() - 1 &&
- !yyLine->endsWith(QLatin1Char(',')) &&
- (yyLine->contains(QLatin1Char('(')) == yyLine->contains(QLatin1Char(')'))))
- hook = j;
- }
- }
- }
-
- if (hook >= 0) {
- /*
- Yes, we have a delimiter or an operator to align
- against! We don't really align against it, but rather
- against the following token, if any. In this example,
- the following token is "11":
-
- int x = (11 +
- 2);
-
- If there is no such token, we use a continuation indent:
-
- static QRegExp foo(QString(
- "foo foo foo foo foo foo foo foo foo"));
- */
- hook++;
- while (hook < yyLine->length()) {
- if (!yyLine->at(hook).isSpace())
- return columnForIndex(*yyLine, hook);
- hook++;
- }
- return indentOfLine(*yyLine) + ppContinuationIndentSize;
- }
-
- if (braceDepth != 0)
- break;
-
- /*
- The line's delimiters are balanced. It looks like a
- continuation line or something.
- */
- if (delimDepth == 0) {
- if (leftBraceFollowed) {
- /*
- We have
-
- int main()
- {
-
- or
-
- Bar::Bar()
- : Foo(x)
- {
-
- The "{" should be flush left.
- */
- if (!isContinuationLine())
- return indentOfLine(*yyLine);
- } else if (isContinuationLine() || yyLine->endsWith(QLatin1Char(','))) {
- /*
- We have
-
- x = a +
- b +
- c;
-
- or
-
- int t[] = {
- 1, 2, 3,
- 4, 5, 6
-
- The "c;" should fall right under the "b +", and the
- "4, 5, 6" right under the "1, 2, 3,".
- */
- return indentOfLine(*yyLine);
- } else {
- /*
- We have
-
- stream << 1 +
- 2;
-
- We could, but we don't, try to analyze which
- operator has precedence over which and so on, to
- obtain the excellent result
-
- stream << 1 +
- 2;
-
- We do have a special trick above for the assignment
- operator above, though.
- */
- return indentOfLine(*yyLine) + ppContinuationIndentSize;
- }
- }
-
- if (!readLine())
- break;
- }
- return 0;
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram if
- that line is standalone (or should be indented likewise).
-
- Indenting a standalone line is tricky, mostly because of braceless
- control statements. Grossly, we are looking backwards for a special
- line, a "hook line", that we can use as a starting point to indent,
- and then modify the indentation level according to the braces met
- along the way to that hook.
-
- Let's consider a few examples. In all cases, we want to indent the
- bottom line.
-
- Example 1:
-
- x = 1;
- y = 2;
-
- The hook line is "x = 1;". We met 0 opening braces and 0 closing
- braces. Therefore, "y = 2;" inherits the indent of "x = 1;".
-
- Example 2:
-
- if (x) {
- y;
-
- The hook line is "if (x) {". No matter what precedes it, "y;" has
- to be indented one level deeper than the hook line, since we met one
- opening brace along the way.
-
- Example 3:
-
- if (a)
- while (b) {
- c;
- }
- d;
-
- To indent "d;" correctly, we have to go as far as the "if (a)".
- Compare with
-
- if (a) {
- while (b) {
- c;
- }
- d;
-
- Still, we're striving to go back as little as possible to
- accommodate people with irregular indentation schemes. A hook line
- near at hand is much more reliable than a remote one.
-*/
-int QmlJSIndenter::indentForStandaloneLine()
-{
- for (int i = 0; i < SmallRoof; i++) {
- if (!*yyLeftBraceFollows) {
- YY_SAVE();
-
- if (matchBracelessControlStatement()) {
- /*
- The situation is this, and we want to indent "z;":
-
- if (x &&
- y)
- z;
-
- yyLine is "if (x &&".
- */
- return indentOfLine(*yyLine) + ppIndentSize;
- }
- YY_RESTORE();
- }
-
- if (yyLine->endsWith(QLatin1Char(';')) || yyLine->contains(QLatin1Char('{'))) {
- /*
- The situation is possibly this, and we want to indent
- "z;":
-
- while (x)
- y;
- z;
-
- We return the indent of "while (x)". In place of "y;",
- any arbitrarily complex compound statement can appear.
- */
-
- if (*yyBraceDepth > 0) {
- do {
- if (!readLine())
- break;
- } while (*yyBraceDepth > 0);
- }
-
- LinizerState hookState;
-
- while (isContinuationLine())
- readLine();
- hookState = yyLinizerState;
-
- readLine();
- if (*yyBraceDepth <= 0) {
- do {
- if (!matchBracelessControlStatement())
- break;
- hookState = yyLinizerState;
- } while (readLine());
- }
-
- yyLinizerState = hookState;
-
- while (isContinuationLine())
- readLine();
-
- int indentChange = - *yyBraceDepth;
- if (caseOrDefault.match(*yyLine).hasMatch())
- ++indentChange;
-
- /*
- Never trust lines containing only '{' or '}', as some
- people (Richard M. Stallman) format them weirdly.
- */
- if (yyLine->trimmed().length() > 1)
- return indentOfLine(*yyLine) + indentChange * ppIndentSize;
- }
-
- if (!readLine())
- return -*yyBraceDepth * ppIndentSize;
- }
- return 0;
-}
-
-/*
- Returns the recommended indent for the bottom line of program.
- Unless null, typedIn stores the character of yyProgram that
- triggered reindentation.
-
- This function works better if typedIn is set properly; it is
- slightly more conservative if typedIn is completely wild, and
- slighly more liberal if typedIn is always null. The user might be
- annoyed by the liberal behavior.
-*/
-int QmlJSIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn)
-{
- if (begin == end)
- return 0;
-
- const QTextBlock last = end.previous();
-
- initialize(begin, last);
-
- QString bottomLine = last.text();
- QChar firstCh = firstNonWhiteSpace(bottomLine);
- int indent = 0;
-
- if (bottomLineStartsInMultilineComment()) {
- /*
- The bottom line starts in a C-style comment. Indent it
- smartly, unless the user has already played around with it,
- in which case it's better to leave her stuff alone.
- */
- if (isOnlyWhiteSpace(bottomLine))
- indent = indentWhenBottomLineStartsInMultiLineComment();
- else
- indent = indentOfLine(bottomLine);
- } else {
- if (isUnfinishedLine())
- indent = indentForContinuationLine();
- else
- indent = indentForStandaloneLine();
-
- if ((okay(typedIn, QLatin1Char('}')) && firstCh == QLatin1Char('}'))
- || (okay(typedIn, QLatin1Char(']')) && firstCh == QLatin1Char(']'))) {
- /*
- A closing brace is one level more to the left than the
- code it follows.
- */
- indent -= ppIndentSize;
- } else if (okay(typedIn, QLatin1Char(':'))) {
- if (caseOrDefault.match(bottomLine).hasMatch()) {
- /*
- Move a case label (or the ':' in front of a
- constructor initialization list) one level to the
- left, but only if the user did not play around with
- it yet. Some users have exotic tastes in the
- matter, and most users probably are not patient
- enough to wait for the final ':' to format their
- code properly.
-
- We don't attempt the same for goto labels, as the
- user is probably the middle of "foo::bar". (Who
- uses goto, anyway?)
- */
- if (indentOfLine(bottomLine) <= indent)
- indent -= ppIndentSize;
- else
- indent = indentOfLine(bottomLine);
- }
- }
- }
-
- return qMax(0, indent);
-}
-
diff --git a/src/libs/qmljs/qmljsindenter.h b/src/libs/qmljs/qmljsindenter.h
deleted file mode 100644
index 55141c1b3b..0000000000
--- a/src/libs/qmljs/qmljsindenter.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <qmljs/qmljs_global.h>
-#include <qmljs/qmljslineinfo.h>
-
-#include <QRegularExpression>
-
-QT_FORWARD_DECLARE_CLASS(QTextBlock)
-
-namespace QmlJS {
-
-class QMLJS_EXPORT QmlJSIndenter : public LineInfo
-{
- Q_DISABLE_COPY(QmlJSIndenter)
-
-public:
- QmlJSIndenter();
- ~QmlJSIndenter();
-
- void setTabSize(int size);
- void setIndentSize(int size);
-
- int indentForBottomLine(QTextBlock firstBlock, QTextBlock lastBlock, QChar typedIn);
-
-private:
- bool isOnlyWhiteSpace(const QString &t) const;
- int columnForIndex(const QString &t, int index) const;
- int indentOfLine(const QString &t) const;
-
- void eraseChar(QString &t, int k, QChar ch) const;
- QChar lastParen() const;
- bool okay(QChar typedIn, QChar okayCh) const;
-
- int indentWhenBottomLineStartsInMultiLineComment();
- int indentForContinuationLine();
- int indentForStandaloneLine();
-
-private:
- int ppHardwareTabSize;
- int ppIndentSize;
- int ppContinuationIndentSize;
- int ppCommentOffset;
-
-private:
- QRegularExpression caseOrDefault;
-};
-
-} // namespace QmlJS
diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.cpp b/src/libs/qmljs/qmljsstaticanalysismessage.cpp
index 5b15e69428..432b97aba7 100644
--- a/src/libs/qmljs/qmljsstaticanalysismessage.cpp
+++ b/src/libs/qmljs/qmljsstaticanalysismessage.cpp
@@ -189,8 +189,9 @@ StaticAnalysisMessages::StaticAnalysisMessages()
Tr::tr("Maximum string value length is %1."), 1);
newMsg(ErrInvalidArrayValueLength, Error,
Tr::tr("%1 elements expected in array value."), 1);
- newMsg(WarnImperativeCodeNotEditableInVisualDesigner, Warning,
- Tr::tr("Imperative code is not supported in Qt Design Studio."));
+ newMsg(WarnImperativeCodeNotEditableInVisualDesigner,
+ Warning,
+ Tr::tr("JavaScript can break the visual tooling in Qt Design Studio."));
newMsg(WarnUnsupportedTypeInVisualDesigner, Warning,
Tr::tr("This type (%1) is not supported in Qt Design Studio."), 1);
newMsg(WarnReferenceToParentItemNotSupportedByVisualDesigner, Warning,
diff --git a/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp b/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
index 545d4d927d..7747a9d118 100644
--- a/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
+++ b/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
@@ -15,7 +15,7 @@
#define QTC_ASSERT_STRING(cond) qDebug("SOFT ASSERT: \"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
#define QTC_ASSERT(cond, action) if (cond) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
-static Q_LOGGING_CATEGORY(imageContainerDebug, "qtc.imagecontainer.debug", QtDebugMsg)
+static Q_LOGGING_CATEGORY(imageContainerDebug, "qtc.imagecontainer")
namespace QmlDesigner {
@@ -194,9 +194,10 @@ static void readSharedMemory(qint32 key, ImageContainer &container)
QImage image = QImage(imageWidth, imageHeight, QImage::Format(imageFormat));
image.setDevicePixelRatio(pixelRatio);
- if (image.isNull())
- qCInfo(imageContainerDebug()) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat;
- else
+ if (image.isNull()) {
+ if (imageWidth || imageHeight || imageFormat)
+ qCWarning(imageContainerDebug) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat;
+ } else
std::memcpy(image.bits(), reinterpret_cast<const qint32*>(sharedMemory.constData()) + 6, byteCount);
container.setImage(image);
diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt
index 2c7e1ebbf3..f86f31871a 100644
--- a/src/libs/sqlite/CMakeLists.txt
+++ b/src/libs/sqlite/CMakeLists.txt
@@ -31,7 +31,7 @@ endif()
add_qtc_library(Sqlite
PROPERTIES AUTOMOC OFF AUTOUIC OFF
- DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal
+ DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal Nanotrace
INCLUDES
../3rdparty/sqlite
PUBLIC_INCLUDES
@@ -58,7 +58,7 @@ add_qtc_library(Sqlite
sqlitesessionchangeset.cpp sqlitesessionchangeset.h
sqlitesessions.cpp sqlitesessions.h
sqlitetable.h
- sqlitetransaction.h
+ sqlitetracing.cpp sqlitetracing.h
sqlitetransaction.h
sqlitevalue.h
sqlitewritestatement.h
diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp
index 23466c0cea..8557bf6ad2 100644
--- a/src/libs/sqlite/sqlitebasestatement.cpp
+++ b/src/libs/sqlite/sqlitebasestatement.cpp
@@ -26,32 +26,7 @@ extern "C" int sqlite3_carray_bind(
namespace Sqlite {
-namespace {
-using TraceFile = NanotraceHR::TraceFile<sqliteTracingStatus()>;
-
-TraceFile traceFile{"sqlite.json"};
-
-thread_local NanotraceHR::EventQueueData<NanotraceHR::StringViewTraceEvent, 10000, sqliteTracingStatus()>
- eventQueueData(traceFile);
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteLowLevelCategory();
-
-thread_local NanotraceHR::StringViewCategory<sqliteTracingStatus()> sqliteLowLevelCategory_{
- "sqlite low level"_t, eventQueueData, sqliteLowLevelCategory};
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteLowLevelCategory()
-{
- return sqliteLowLevelCategory_;
-}
-
-thread_local NanotraceHR::StringViewCategory<sqliteTracingStatus()> sqliteHighLevelCategory_{
- "sqlite high level"_t, eventQueueData, sqliteHighLevelCategory};
-} // namespace
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteHighLevelCategory()
-{
- return sqliteHighLevelCategory_;
-}
+using NanotraceHR::keyValue;
BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database)
: m_database(database)
@@ -107,14 +82,18 @@ void BaseStatement::waitForUnlockNotify() const
void BaseStatement::reset() const noexcept
{
- NanotraceHR::Tracer tracer{"reset"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"reset"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle())};
sqlite3_reset(m_compiledStatement.get());
}
bool BaseStatement::next() const
{
- NanotraceHR::Tracer tracer{"next"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"next"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle())};
int resultCode;
do {
@@ -141,7 +120,10 @@ void BaseStatement::step() const
void BaseStatement::bindNull(int index)
{
- NanotraceHR::Tracer tracer{"bind null"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind null"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index)};
int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index);
if (resultCode != SQLITE_OK)
@@ -155,7 +137,11 @@ void BaseStatement::bind(int index, NullValue)
void BaseStatement::bind(int index, int value)
{
- NanotraceHR::Tracer tracer{"bind int"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind int"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -164,7 +150,11 @@ void BaseStatement::bind(int index, int value)
void BaseStatement::bind(int index, long long value)
{
- NanotraceHR::Tracer tracer{"bind long long"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind long long"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -173,7 +163,11 @@ void BaseStatement::bind(int index, long long value)
void BaseStatement::bind(int index, double value)
{
- NanotraceHR::Tracer tracer{"bind double"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind double"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -182,7 +176,11 @@ void BaseStatement::bind(int index, double value)
void BaseStatement::bind(int index, void *pointer)
{
- NanotraceHR::Tracer tracer{"bind pointer"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind pointer"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(pointer))};
int resultCode = sqlite3_bind_pointer(m_compiledStatement.get(), index, pointer, "carray", nullptr);
if (resultCode != SQLITE_OK)
@@ -191,7 +189,12 @@ void BaseStatement::bind(int index, void *pointer)
void BaseStatement::bind(int index, Utils::span<const int> values)
{
- NanotraceHR::Tracer tracer{"bind int span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind int span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -205,7 +208,12 @@ void BaseStatement::bind(int index, Utils::span<const int> values)
void BaseStatement::bind(int index, Utils::span<const long long> values)
{
- NanotraceHR::Tracer tracer{"bind long long span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind long long span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -219,7 +227,12 @@ void BaseStatement::bind(int index, Utils::span<const long long> values)
void BaseStatement::bind(int index, Utils::span<const double> values)
{
- NanotraceHR::Tracer tracer{"bind double span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind double span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -233,7 +246,12 @@ void BaseStatement::bind(int index, Utils::span<const double> values)
void BaseStatement::bind(int index, Utils::span<const char *> values)
{
- NanotraceHR::Tracer tracer{"bind const char* span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind const char* span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -247,7 +265,11 @@ void BaseStatement::bind(int index, Utils::span<const char *> values)
void BaseStatement::bind(int index, Utils::SmallStringView text)
{
- NanotraceHR::Tracer tracer{"bind string"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind string"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("text", text)};
int resultCode = sqlite3_bind_text(m_compiledStatement.get(),
index,
@@ -260,7 +282,12 @@ void BaseStatement::bind(int index, Utils::SmallStringView text)
void BaseStatement::bind(int index, BlobView blobView)
{
- NanotraceHR::Tracer tracer{"bind blob"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind blob"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(blobView.data())),
+ keyValue("size", blobView.size())};
int resultCode = SQLITE_OK;
@@ -280,7 +307,11 @@ void BaseStatement::bind(int index, BlobView blobView)
void BaseStatement::bind(int index, const Value &value)
{
- NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "bind value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ };
switch (value.type()) {
case ValueType::Integer:
@@ -303,7 +334,11 @@ void BaseStatement::bind(int index, const Value &value)
void BaseStatement::bind(int index, ValueView value)
{
- NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "bind value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ };
switch (value.type()) {
case ValueType::Integer:
@@ -326,7 +361,9 @@ void BaseStatement::bind(int index, ValueView value)
void BaseStatement::prepare(Utils::SmallStringView sqlStatement)
{
- NanotraceHR::Tracer tracer{"prepare"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"prepare"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sql statement", sqlStatement)};
if (!m_database.isLocked())
throw DatabaseIsNotLocked{};
@@ -342,14 +379,18 @@ void BaseStatement::prepare(Utils::SmallStringView sqlStatement)
nullptr);
m_compiledStatement.reset(sqliteStatement);
- if (resultCode == SQLITE_LOCKED)
+ if (resultCode == SQLITE_LOCKED) {
+ tracer.tick("wait for unlock"_t);
waitForUnlockNotify();
+ }
} while (resultCode == SQLITE_LOCKED);
if (resultCode != SQLITE_OK)
Sqlite::throwError(resultCode, sqliteDatabaseHandle());
+
+ tracer.end(keyValue("sqlite statement", handle()));
}
sqlite3 *BaseStatement::sqliteDatabaseHandle() const
@@ -427,7 +468,10 @@ StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column)
Type BaseStatement::fetchType(int column) const
{
- NanotraceHR::Tracer tracer{"fetch type"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch type"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
auto dataType = sqlite3_column_type(m_compiledStatement.get(), column);
@@ -449,9 +493,16 @@ Type BaseStatement::fetchType(int column) const
int BaseStatement::fetchIntValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch int"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch int"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_int(m_compiledStatement.get(), column);
- return sqlite3_column_int(m_compiledStatement.get(), column);
+ tracer.end(keyValue("value", value));
+
+ return value;
}
template<>
@@ -473,9 +524,16 @@ long BaseStatement::fetchValue<long>(int column) const
long long BaseStatement::fetchLongLongValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch long long"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch long long"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_int64(m_compiledStatement.get(), column);
- return sqlite3_column_int64(m_compiledStatement.get(), column);
+ tracer.end(keyValue("value", value));
+
+ return value;
}
template<>
@@ -486,14 +544,24 @@ long long BaseStatement::fetchValue<long long>(int column) const
double BaseStatement::fetchDoubleValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch double"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch double"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_double(m_compiledStatement.get(), column);
+
+ tracer.end(keyValue("value", value));
- return sqlite3_column_double(m_compiledStatement.get(), column);
+ return value;
}
BlobView BaseStatement::fetchBlobValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch blob"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch blob"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
return convertToBlobForColumn(m_compiledStatement.get(), column);
}
@@ -507,7 +575,16 @@ double BaseStatement::fetchValue<double>(int column) const
template<typename StringType>
StringType BaseStatement::fetchValue(int column) const
{
- return convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
+ NanotraceHR::Tracer tracer{"fetch string value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto text = convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
+
+ tracer.end(keyValue("text", text));
+
+ return text;
}
template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<Utils::SmallStringView>(
@@ -519,11 +596,25 @@ template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathSt
Utils::SmallStringView BaseStatement::fetchSmallStringViewValue(int column) const
{
- return fetchValue<Utils::SmallStringView>(column);
+ NanotraceHR::Tracer tracer{"fetch string view"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto text = fetchValue<Utils::SmallStringView>(column);
+
+ tracer.end(keyValue("text", text));
+
+ return text;
}
ValueView BaseStatement::fetchValueView(int column) const
{
+ NanotraceHR::Tracer tracer{"fetch value view"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
int dataType = sqlite3_column_type(m_compiledStatement.get(), column);
switch (dataType) {
case SQLITE_NULL:
@@ -543,7 +634,11 @@ ValueView BaseStatement::fetchValueView(int column) const
void BaseStatement::Deleter::operator()(sqlite3_stmt *statement)
{
- NanotraceHR::Tracer tracer{"finalize"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "finalize"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", reinterpret_cast<std::uintptr_t>(statement)),
+ };
sqlite3_finalize(statement);
}
diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h
index d2306c67a5..3710021ff5 100644
--- a/src/libs/sqlite/sqlitebasestatement.h
+++ b/src/libs/sqlite/sqlitebasestatement.h
@@ -9,6 +9,7 @@
#include "sqliteblob.h"
#include "sqliteexception.h"
#include "sqliteids.h"
+#include "sqlitetracing.h"
#include "sqlitetransaction.h"
#include "sqlitevalue.h"
@@ -30,8 +31,6 @@ using std::int64_t;
namespace Sqlite {
-using namespace NanotraceHR::Literals;
-
class Database;
class DatabaseBackend;
@@ -44,17 +43,6 @@ constexpr static std::underlying_type_t<Enumeration> to_underlying(Enumeration e
return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
-constexpr NanotraceHR::Tracing sqliteTracingStatus()
-{
-#ifdef ENABLE_SQLITE_TRACING
- return NanotraceHR::tracingStatus();
-#else
- return NanotraceHR::Tracing::IsDisabled;
-#endif
-}
-
-SQLITE_EXPORT NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteHighLevelCategory();
-
class SQLITE_EXPORT BaseStatement
{
public:
@@ -136,6 +124,11 @@ public:
protected:
~BaseStatement() = default;
+ std::uintptr_t handle() const
+ {
+ return reinterpret_cast<std::uintptr_t>(m_compiledStatement.get());
+ }
+
private:
struct Deleter
{
@@ -166,7 +159,12 @@ public:
void execute()
{
- NanotraceHR::Tracer tracer{"execute"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{
+ "execute"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle()),
+ };
Resetter resetter{this};
BaseStatement::next();
@@ -175,7 +173,10 @@ public:
template<typename... ValueType>
void bindValues(const ValueType &...values)
{
- NanotraceHR::Tracer tracer{"bind"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"bind"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
static_assert(BindParameterCount == sizeof...(values), "Wrong binding parameter count!");
@@ -186,7 +187,10 @@ public:
template<typename... ValueType>
void write(const ValueType&... values)
{
- NanotraceHR::Tracer tracer{"write"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"write"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
bindValues(values...);
@@ -206,24 +210,37 @@ public:
struct is_container<QVarLengthArray<T, Prealloc>> : std::true_type
{};
+ template<typename T>
+ struct is_small_container : std::false_type
+ {};
+
+ template<typename T, qsizetype Prealloc>
+ struct is_small_container<QVarLengthArray<T, Prealloc>> : std::true_type
+ {};
+
template<typename Container,
std::size_t capacity = 32,
typename = std::enable_if_t<is_container<Container>::value>,
typename... QueryTypes>
auto values(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"values"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
Container resultValues;
- resultValues.reserve(std::max(capacity, m_maximumResultCount));
+ using size_tupe = typename Container::size_type;
+ if constexpr (!is_small_container<Container>::value)
+ resultValues.reserve(static_cast<size_tupe>(std::max(capacity, m_maximumResultCount)));
bindValues(queryValues...);
while (BaseStatement::next())
emplaceBackValues(resultValues);
- setMaximumResultCount(resultValues.size());
+ setMaximumResultCount(static_cast<std::size_t>(resultValues.size()));
return resultValues;
}
@@ -241,7 +258,10 @@ public:
template<typename ResultType, typename... QueryTypes>
auto value(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"value"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"value"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
ResultType resultValue{};
@@ -257,7 +277,10 @@ public:
template<typename ResultType, typename... QueryTypes>
auto optionalValue(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"optionalValue"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"optionalValue"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
std::optional<ResultType> resultValue;
@@ -273,6 +296,7 @@ public:
template<typename Type>
static auto toValue(Utils::SmallStringView sqlStatement, Database &database)
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"toValue"_t, sqliteHighLevelCategory()};
StatementImplementation statement(sqlStatement, database);
@@ -287,7 +311,10 @@ public:
template<typename Callable, typename... QueryTypes>
void readCallback(Callable &&callable, const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"readCallback"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"readCallback"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
@@ -304,7 +331,10 @@ public:
template<typename Container, typename... QueryTypes>
void readTo(Container &container, const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"readTo"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"readTo"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
@@ -402,9 +432,11 @@ public:
private:
using TracerCategory = std::decay_t<decltype(sqliteHighLevelCategory())>;
- NanotraceHR::Tracer<TracerCategory, typename TracerCategory::IsActive> tracer{
- "range"_t, sqliteHighLevelCategory()};
StatementImplementation &m_statement;
+ NanotraceHR::Tracer<TracerCategory, typename TracerCategory::IsActive> tracer{
+ "range"_t,
+ sqliteHighLevelCategory(),
+ NanotraceHR::keyValue("sqlite statement", m_statement.handle())};
};
template<typename ResultType>
diff --git a/src/libs/sqlite/sqliteexception.cpp b/src/libs/sqlite/sqliteexception.cpp
index b5f581ad68..bb4a474adb 100644
--- a/src/libs/sqlite/sqliteexception.cpp
+++ b/src/libs/sqlite/sqliteexception.cpp
@@ -3,14 +3,20 @@
#include "sqliteexception.h"
+#include "sqlitetracing.h"
+
#include <utils/smallstringio.h>
+#include <nanotrace/nanotracehr.h>
+
#include <sqlite.h>
#include <QDebug>
namespace Sqlite {
+using NanotraceHR::keyValue;
+
const char *Exception::what() const noexcept
{
return "Sqlite::Exception";
@@ -18,7 +24,10 @@ const char *Exception::what() const noexcept
const char *ExceptionWithMessage::what() const noexcept
{
- return "Sqlite::ExceptionWithMessage";
+ static Utils::SmallString text = Utils::SmallString::join(
+ {"Sqlite::ExceptionWithMessage", m_sqliteErrorMessage});
+
+ return text.data();
}
void ExceptionWithMessage::printWarning() const
@@ -26,6 +35,13 @@ void ExceptionWithMessage::printWarning() const
qWarning() << what() << m_sqliteErrorMessage;
}
+StatementIsBusy::StatementIsBusy(Utils::SmallString &&sqliteErrorMessage)
+ : ExceptionWithMessage{std::move(sqliteErrorMessage)}
+{
+ sqliteHighLevelCategory().threadEvent("StatementIsBusy"_t,
+ keyValue("error message", std::string_view{what()}));
+}
+
const char *StatementIsBusy::what() const noexcept
{
return "Sqlite::StatementIsBusy";
@@ -36,9 +52,19 @@ const char *DatabaseIsBusy::what() const noexcept
return "Sqlite::DatabaseIsBusy";
}
+StatementHasError::StatementHasError(Utils::SmallString &&sqliteErrorMessage)
+ : ExceptionWithMessage{std::move(sqliteErrorMessage)}
+{
+ sqliteHighLevelCategory().threadEvent("StatementHasError"_t,
+ keyValue("error message", std::string_view{what()}));
+}
+
const char *StatementHasError::what() const noexcept
{
- return "Sqlite::StatementHasError";
+ static Utils::SmallString text = Utils::SmallString::join(
+ {"Sqlite::StatementHasError: ", message()});
+
+ return text.data();
}
const char *StatementIsMisused::what() const noexcept
diff --git a/src/libs/sqlite/sqliteexception.h b/src/libs/sqlite/sqliteexception.h
index f0cadfc748..17a0639e19 100644
--- a/src/libs/sqlite/sqliteexception.h
+++ b/src/libs/sqlite/sqliteexception.h
@@ -23,13 +23,15 @@ public:
class SQLITE_EXPORT ExceptionWithMessage : public Exception
{
public:
- ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = Utils::SmallString{})
+ ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = {})
: m_sqliteErrorMessage(std::move(sqliteErrorMessage))
{}
const char *what() const noexcept override;
void printWarning() const;
+ std::string_view message() const noexcept { return m_sqliteErrorMessage; }
+
private:
Utils::SmallString m_sqliteErrorMessage;
};
@@ -37,7 +39,7 @@ private:
class SQLITE_EXPORT StatementIsBusy : public ExceptionWithMessage
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ StatementIsBusy(Utils::SmallString &&sqliteErrorMessage);
const char *what() const noexcept override;
};
@@ -90,7 +92,8 @@ public:
class SQLITE_EXPORT StatementHasError : public ExceptionWithMessage
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ StatementHasError(Utils::SmallString &&sqliteErrorMessage);
+
const char *what() const noexcept override;
};
diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h
index d64e4d9645..1ffd546d9f 100644
--- a/src/libs/sqlite/sqliteids.h
+++ b/src/libs/sqlite/sqliteids.h
@@ -5,6 +5,7 @@
#include <utils/span.h>
+#include <nanotrace/nanotracehr.h>
#include <type_traits>
#include <vector>
@@ -64,6 +65,15 @@ public:
[[noreturn, deprecated]] InternalIntegerType operator&() const { throw std::exception{}; }
+ template<typename String>
+ friend void convertToString(String &string, BasicId id)
+ {
+ if (id.isValid())
+ NanotraceHR::convertToString(string, id.internalId());
+ else
+ NanotraceHR::convertToString(string, "invalid");
+ }
+
private:
InternalIntegerType id = 0;
};
@@ -88,4 +98,5 @@ struct hash<Sqlite::BasicId<Type, InternalIntegerType>>
return std::hash<InternalIntegerType>{}(id.internalId());
}
};
+
} // namespace std
diff --git a/src/libs/sqlite/sqliteindex.h b/src/libs/sqlite/sqliteindex.h
index f320fcd599..7c7a8dbb2b 100644
--- a/src/libs/sqlite/sqliteindex.h
+++ b/src/libs/sqlite/sqliteindex.h
@@ -40,6 +40,8 @@ public:
return Utils::SmallString::join({"CREATE ",
m_indexType == IndexType::Unique ? "UNIQUE " : "",
"INDEX IF NOT EXISTS index_",
+ kindName(),
+ "_",
m_tableName,
"_",
m_columnNames.join("_"),
@@ -65,6 +67,23 @@ public:
}
private:
+ std::string_view kindName() const
+ {
+ using namespace std::string_view_literals;
+
+ if (m_indexType == IndexType::Unique && m_condition.hasContent())
+ return "unique_partial"sv;
+
+ if (m_indexType == IndexType::Unique)
+ return "unique"sv;
+
+ if (m_condition.hasContent())
+ return "partial"sv;
+
+ return "normal"sv;
+ }
+
+private:
Utils::SmallString m_tableName;
Utils::SmallStringVector m_columnNames;
IndexType m_indexType;
diff --git a/src/libs/sqlite/sqlitetracing.cpp b/src/libs/sqlite/sqlitetracing.cpp
new file mode 100644
index 0000000000..700546f146
--- /dev/null
+++ b/src/libs/sqlite/sqlitetracing.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sqlitetracing.h"
+
+namespace Sqlite {
+
+TraceFile &traceFile()
+{
+ static TraceFile traceFile{"tracing.json"};
+
+ return traceFile;
+}
+
+namespace {
+
+thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewWithStringArgumentsTraceEvent,
+ sqliteTracingStatus()>
+ eventQueue(traceFile());
+
+} // namespace
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteLowLevelCategory()
+{
+ thread_local NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()>
+ sqliteLowLevelCategory_{"sqlite low level"_t, eventQueue, sqliteLowLevelCategory};
+ return sqliteLowLevelCategory_;
+}
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteHighLevelCategory()
+{
+ thread_local NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()>
+ sqliteHighLevelCategory_{"sqlite high level"_t, eventQueue, sqliteHighLevelCategory};
+
+ return sqliteHighLevelCategory_;
+}
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitetracing.h b/src/libs/sqlite/sqlitetracing.h
new file mode 100644
index 0000000000..8dadc6de0d
--- /dev/null
+++ b/src/libs/sqlite/sqlitetracing.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sqliteglobal.h"
+
+#include <nanotrace/nanotracehr.h>
+
+namespace Sqlite {
+using namespace NanotraceHR::Literals;
+
+constexpr NanotraceHR::Tracing sqliteTracingStatus()
+{
+#ifdef ENABLE_SQLITE_TRACING
+ return NanotraceHR::Tracing::IsEnabled;
+#else
+ return NanotraceHR::Tracing::IsDisabled;
+#endif
+}
+
+using TraceFile = NanotraceHR::TraceFile<sqliteTracingStatus()>;
+
+SQLITE_EXPORT TraceFile &traceFile();
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteLowLevelCategory();
+
+SQLITE_EXPORT NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &
+sqliteHighLevelCategory();
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h
index fe576f3fec..725682494b 100644
--- a/src/libs/sqlite/sqlitevalue.h
+++ b/src/libs/sqlite/sqlitevalue.h
@@ -6,6 +6,7 @@
#include "sqliteblob.h"
#include "sqliteexception.h"
+#include <nanotrace/nanotracehr.h>
#include <utils/smallstring.h>
#include <QVariant>
@@ -33,7 +34,7 @@ public:
explicit ValueBase(NullValue) {}
explicit ValueBase(VariantType &&value)
- : value(value)
+ : value(std::move(value))
{}
explicit ValueBase(const char *value)
@@ -43,6 +44,7 @@ public:
explicit ValueBase(long long value)
: value(value)
{}
+
explicit ValueBase(int value)
: value(static_cast<long long>(value))
{}
@@ -60,11 +62,6 @@ public:
{}
- explicit ValueBase(StringType &&value)
- : value(std::move(value))
-
- {}
-
explicit ValueBase(BlobView value)
: value(value)
@@ -229,14 +226,42 @@ public:
class ValueView : public ValueBase<Utils::SmallStringView, BlobView>
{
public:
+ ValueView() = default;
+
+ explicit ValueView(NullValue) {}
+
explicit ValueView(ValueBase &&base)
: ValueBase(std::move(base))
{}
+ explicit ValueView(Utils::SmallStringView value)
+ : ValueBase(value)
+ {}
+
+ explicit ValueView(BlobView value)
+ : ValueBase(value)
+ {}
+
+ explicit ValueView(long long value)
+ : ValueBase(value)
+ {}
+
+ explicit ValueView(int value)
+ : ValueBase(static_cast<long long>(value))
+ {}
+
+ explicit ValueView(uint value)
+ : ValueBase(static_cast<long long>(value))
+ {}
+
+ explicit ValueView(double value)
+ : ValueBase(value)
+ {}
+
template<typename Type>
static ValueView create(Type &&value_)
{
- return ValueView{ValueBase{value_}};
+ return ValueView(std::forward<Type>(value_));
}
};
@@ -386,4 +411,27 @@ private:
};
using Values = std::vector<Value>;
+
+template<typename String>
+void convertToString(String &string, const Value &value)
+{
+ switch (value.type()) {
+ case ValueType::Null:
+ convertToString(string, "null");
+ break;
+ case ValueType::Integer:
+ convertToString(string, value.toInteger());
+ break;
+ case ValueType::Float:
+ convertToString(string, value.toFloat());
+ break;
+ case ValueType::String:
+ convertToString(string, value.toStringView());
+ break;
+ case ValueType::Blob:
+ convertToString(string, "blob");
+ break;
+ }
+}
+
} // namespace Sqlite
diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h
index c522a6cae9..e8c3d74b92 100644
--- a/src/libs/utils/smallstring.h
+++ b/src/libs/utils/smallstring.h
@@ -93,7 +93,7 @@ public:
static_cast<std::size_t>(std::distance(begin, end))}
{}
- template<typename Type, typename = std::enable_if_t<std::is_pointer<Type>::value>>
+ template<typename Type, typename std::enable_if_t<std::is_pointer<Type>::value, bool> = true>
BasicSmallString(Type characterPointer) noexcept
: BasicSmallString(characterPointer, std::char_traits<char>::length(characterPointer))
{
@@ -118,7 +118,7 @@ public:
template<typename BeginIterator,
typename EndIterator,
- typename = std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value>>
+ typename std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value, bool> = true>
BasicSmallString(BeginIterator begin, EndIterator end) noexcept
: BasicSmallString(&(*begin), size_type(end - begin))
{}
@@ -354,6 +354,14 @@ public:
return false;
}
+ bool startsWith(QStringView subStringToSearch) const noexcept
+ {
+ if (size() >= Utils::usize(subStringToSearch))
+ return subStringToSearch == QLatin1StringView{data(), subStringToSearch.size()};
+
+ return false;
+ }
+
bool startsWith(char characterToSearch) const noexcept
{
return data()[0] == characterToSearch;
@@ -423,13 +431,55 @@ public:
size_type oldSize = size();
size_type newSize = oldSize + string.size();
- reserve(optimalCapacity(newSize));
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
std::char_traits<char>::copy(std::next(data(), static_cast<std::ptrdiff_t>(oldSize)),
string.data(),
string.size());
setSize(newSize);
}
+ void append(char character) noexcept
+ {
+ size_type oldSize = size();
+ size_type newSize = oldSize + 1;
+
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
+ auto current = std::next(data(), static_cast<std::ptrdiff_t>(oldSize));
+ *current = character;
+ setSize(newSize);
+ }
+
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ void append(Type number)
+ {
+#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+#else
+ if constexpr (std::is_floating_point_v<Type>) {
+ QLocale locale{QLocale::Language::C};
+ append(locale.toString(number));
+ return;
+ } else {
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+ }
+
+#endif
+ }
+
void append(QStringView string) noexcept
{
QStringEncoder encoder{QStringEncoder::Utf8};
@@ -469,6 +519,13 @@ public:
return *this;
}
+ BasicSmallString &operator+=(char character) noexcept
+ {
+ append(character);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(QStringView string) noexcept
{
append(string);
@@ -476,6 +533,14 @@ public:
return *this;
}
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ BasicSmallString &operator+=(Type number) noexcept
+ {
+ append(number);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(std::initializer_list<SmallStringView> list) noexcept
{
appendInitializerList(list, size());
@@ -580,37 +645,12 @@ public:
return joinedString;
}
- static
- BasicSmallString number(int number)
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ static BasicSmallString number(Type number)
{
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
- }
-
- static BasicSmallString number(long long int number) noexcept
- {
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<long long int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
- }
-
- static BasicSmallString number(double number) noexcept
- {
-#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<double>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
-#else
- QLocale locale{QLocale::Language::C};
- return BasicSmallString{locale.toString(number)};
-#endif
+ BasicSmallString string;
+ string.append(number);
+ return string;
}
char &operator[](std::size_t index) noexcept { return *(data() + index); }
@@ -655,7 +695,6 @@ public:
friend BasicSmallString operator+(const BasicSmallString &first,
const char (&second)[ArraySize]) noexcept
{
-
return operator+(first, SmallStringView(second));
}
@@ -687,8 +726,10 @@ unittest_public:
bool fitsNotInCapacity(size_type capacity) const noexcept
{
- return (isShortString() && capacity > shortStringCapacity())
- || (!isShortString() && capacity > m_data.reference.capacity);
+ if (isShortString())
+ return capacity > shortStringCapacity();
+
+ return capacity > m_data.reference.capacity;
}
static size_type optimalHeapCapacity(const size_type size) noexcept
diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp
index 3666762be6..5128f761a0 100644
--- a/src/plugins/android/androidmanager.cpp
+++ b/src/plugins/android/androidmanager.cpp
@@ -532,7 +532,7 @@ QString androidNameForApiLevel(int x)
case 33:
return QLatin1String("Android 13.0 (\"Tiramisu\")");
case 34:
- return QLatin1String("Android API 34");
+ return QLatin1String("Android 14.0 (\"UpsideDownCake\")");
default:
return Tr::tr("Unknown Android version. API Level: %1").arg(x);
}
diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp
index 85401aa8bc..a625895b68 100644
--- a/src/plugins/coreplugin/icore.cpp
+++ b/src/plugins/coreplugin/icore.cpp
@@ -2575,6 +2575,7 @@ void ICorePrivate::changeLog()
});
auto versionCombo = new QComboBox;
+ versionCombo->setMinimumWidth(80);
for (const VersionFilePair &f : versionedFiles)
versionCombo->addItem(f.first.toString());
dialog = new LogDialog(ICore::dialogParent());
diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp
index 108eb5801d..d939e2283a 100644
--- a/src/plugins/effectcomposer/compositionnode.cpp
+++ b/src/plugins/effectcomposer/compositionnode.cpp
@@ -113,6 +113,9 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray());
m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray());
+ if (json.contains("extraMargin"))
+ m_extraMargin = json.value("extraMargin").toInt();
+
if (json.contains("enabled"))
m_isEnabled = json["enabled"].toBool();
diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h
index b3348bb38f..433468688a 100644
--- a/src/plugins/effectcomposer/compositionnode.h
+++ b/src/plugins/effectcomposer/compositionnode.h
@@ -52,6 +52,8 @@ public:
int decRefCount();
void setRefCount(int count);
+ int extraMargin() const { return m_extraMargin; }
+
signals:
void uniformsModelChanged();
void isEnabledChanged();
@@ -70,6 +72,7 @@ private:
QString m_id;
bool m_isEnabled = true;
int m_refCount = 0;
+ int m_extraMargin = 0;
QList<Uniform *> m_uniforms;
diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp
index 5a5ad5718c..a983072334 100644
--- a/src/plugins/effectcomposer/effectcomposermodel.cpp
+++ b/src/plugins/effectcomposer/effectcomposermodel.cpp
@@ -94,6 +94,11 @@ bool EffectComposerModel::setData(const QModelIndex &index, const QVariant &valu
return true;
}
+void EffectComposerModel::setEffectsTypePrefix(const QString &prefix)
+{
+ m_effectTypePrefix = prefix;
+}
+
void EffectComposerModel::setIsEmpty(bool val)
{
if (m_isEmpty != val) {
@@ -207,14 +212,14 @@ void EffectComposerModel::clear(bool clearName)
void EffectComposerModel::assignToSelected()
{
const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
- const QString path = effectsAssetsDir + QDir::separator() + m_currentComposition + ".qep";
+ const QString path = effectsAssetsDir + '/' + m_currentComposition + ".qep";
emit assignToSelectedTriggered(path);
}
QString EffectComposerModel::getUniqueEffectName() const
{
const QString effectsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
- const QString path = effectsDir + QDir::separator() + "Effect%1.qep";
+ const QString path = effectsDir + '/' + "Effect%1.qep";
int num = 0;
@@ -224,6 +229,14 @@ QString EffectComposerModel::getUniqueEffectName() const
return QString("Effect%1").arg(num, 2, 10, QChar('0'));
}
+bool EffectComposerModel::nameExists(const QString &name) const
+{
+ const QString effectsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
+ const QString path = effectsDir + '/' + "%1" + ".qep";
+
+ return QFile::exists(path.arg(name));
+}
+
QString EffectComposerModel::fragmentShader() const
{
return m_fragmentShader;
@@ -490,6 +503,8 @@ QJsonObject nodeToJson(const CompositionNode &node)
nodeObject.insert("enabled", node.isEnabled());
nodeObject.insert("version", 1);
nodeObject.insert("id", node.id());
+ if (node.extraMargin())
+ nodeObject.insert("extraMargin", node.extraMargin());
// Add properties
QJsonArray propertiesArray;
@@ -676,10 +691,44 @@ R"(
)";
s += frameProp.arg(tr("Frame"), tr("This property allows explicit control of current animation frame."));
}
+
s += " }\n";
s += " }\n";
}
+ if (m_shaderFeatures.enabled(ShaderFeatures::Source) && m_extraMargin) {
+ QString generalSection =
+ R"(
+ Section {
+ caption: "%1"
+ width: parent.width
+
+ SectionLayout {
+ PropertyLabel {
+ text: "%2"
+ tooltip: "%3"
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 1000
+ decimals: 0
+ stepSize: 1
+ sliderIndicatorVisible: true
+ backendValue: backendValues.extraMargin
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+ ExpandingSpacer {}
+ }
+ }
+ }
+)";
+ s += generalSection.arg(tr("General"), tr("Extra Margin"),
+ tr("This property specifies how much of extra space is reserved for the effect outside the parent geometry."));
+ }
+
for (const auto &node : std::as_const(m_nodes)) {
const QList<Uniform *> uniforms = static_cast<EffectComposerUniformsModel *>(
node->uniformsModel())->uniforms();
@@ -739,8 +788,46 @@ Item {
s += header;
if (m_shaderFeatures.enabled(ShaderFeatures::Source)) {
- s += " // This is the main source for the effect. Set internally to the current parent item. Do not modify.\n";
- s += " property Item source: null\n";
+ QString sourceStr{
+R"(
+ // This is the main source for the effect. Set internally to the current parent item. Do not modify.
+ property Item source: null
+)"
+ };
+
+ QString extraMarginStr{
+R"(
+ // This property specifies how much of extra space is reserved for the effect outside the parent geometry.
+ // It should be sufficient for most use cases but if the application uses extreme values it may be necessary to
+ // increase this value.
+ property int extraMargin: %1
+
+ onExtraMarginChanged: setupSourceRect()
+
+ function setupSourceRect() {
+ if (source) {
+ var w = source.width + extraMargin * 2
+ var h = source.height + extraMargin * 2
+ source.layer.sourceRect = Qt.rect(-extraMargin, -extraMargin, w, h)
+ }
+ }
+
+ function connectSource(enable) {
+ if (source) {
+ if (enable) {
+ source.widthChanged.connect(setupSourceRect)
+ source.heightChanged.connect(setupSourceRect)
+ } else {
+ source.widthChanged.disconnect(setupSourceRect)
+ source.heightChanged.disconnect(setupSourceRect)
+ }
+ }
+ }
+)"
+ };
+ s += sourceStr;
+ if (m_extraMargin)
+ s += extraMarginStr.arg(m_extraMargin);
}
if (m_shaderFeatures.enabled(ShaderFeatures::Time)
|| m_shaderFeatures.enabled(ShaderFeatures::Frame)) {
@@ -762,7 +849,8 @@ R"(
if (_oldParent && _oldParent !== parent) {
_oldParent.layer.enabled = false
_oldParent.layer.effect = null
- %2
+ %7
+ %4%2
_oldParent.update()
_oldParent = null
}
@@ -772,7 +860,8 @@ R"(
parent.layer.enabled = true
parent.layer.effect = effectComponent
}
- %1
+ %6
+ %4%1%5%3
}
}
@@ -780,35 +869,50 @@ R"(
if (visible) {
parent.layer.enabled = true
parent.layer.effect = effectComponent
- source = parent
+ %6
+ %4%1%5%3
} else {
parent.layer.enabled = false
parent.layer.effect = null
- source = null
+ %8
+ %4%2
}
parent.update()
}
+
)"
};
+ QString mipmap1;
+ QString mipmap2;
+ QString mipmap3;
if (m_shaderFeatures.enabled(ShaderFeatures::Mipmap)) {
- QString mipmap1{
+ mipmap1 = QString {
R"(parent.layer.smooth = true
- parent.layer.mipmap = true
- %1)"
+ parent.layer.mipmap = true)"
};
- QString mipmap2{
+ mipmap2 = QString {
R"(_oldParent.layer.smooth = false
- _oldParent.layer.mipmap = false
- %2)"
+ _oldParent.layer.mipmap = false)"
+ };
+ mipmap3 = QString {
+ R"(parent.layer.smooth = false
+ parent.layer.mipmap = false)"
};
- parentChanged = parentChanged.arg(mipmap1, mipmap2);
}
- parentChanged = parentChanged.arg(m_shaderFeatures.enabled(ShaderFeatures::Source)
- ? QString("source = parent") : QString(),
- m_shaderFeatures.enabled(ShaderFeatures::Source)
- ? QString("source = null") : QString());
+ if (m_shaderFeatures.enabled(ShaderFeatures::Source)) {
+ parentChanged = parentChanged.arg(QString("source = parent"),
+ QString("source = null"),
+ m_extraMargin ? QString(" setupSourceRect()") : QString(),
+ m_extraMargin ? QString("connectSource(false)\n ") : QString(),
+ m_extraMargin ? QString("\n connectSource(true)\n") : QString(),
+ mipmap1,
+ mipmap2,
+ mipmap3);
+ } else {
+ parentChanged = parentChanged.arg(QString(), QString(), QString());
+ }
s += parentChanged;
// Custom properties
@@ -846,7 +950,7 @@ void EffectComposerModel::saveComposition(const QString &name)
}
const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
- const QString path = effectsAssetsDir + QDir::separator() + name + ".qep";
+ const QString path = effectsAssetsDir + '/' + name + ".qep";
auto saveFile = QFile(path);
if (!saveFile.open(QIODevice::WriteOnly)) {
QString error = QString("Error: Couldn't save composition file: '%1'").arg(path);
@@ -854,6 +958,8 @@ void EffectComposerModel::saveComposition(const QString &name)
return;
}
+ updateExtraMargin();
+
QJsonObject json;
// File format version
json.insert("version", 1);
@@ -974,7 +1080,7 @@ void EffectComposerModel::saveResources(const QString &name)
// Get effects dir
const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory();
- const QString effectsResPath = effectsResDir.pathAppended(name).toString() + QDir::separator();
+ const QString effectsResPath = effectsResDir.pathAppended(name).toString() + '/';
Utils::FilePath effectPath = Utils::FilePath::fromString(effectsResPath);
// Create the qmldir for effects
@@ -982,7 +1088,7 @@ void EffectComposerModel::saveResources(const QString &name)
Utils::FilePath qmldirPath = effectsResDir.resolvePath(qmldirFileName);
QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
- qmldirContent.append("module Effects\n");
+ qmldirContent.append(QString("module %1\n").arg(m_effectTypePrefix));
qmldirPath.writeFileContents(qmldirContent.toUtf8());
}
@@ -1000,7 +1106,7 @@ void EffectComposerModel::saveResources(const QString &name)
qmldirPath = effectPath.resolvePath(qmldirFileName);
qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
- qmldirContent.append("module Effects.");
+ qmldirContent.append(QString("module %1.").arg(m_effectTypePrefix));
qmldirContent.append(name);
qmldirContent.append('\n');
qmldirContent.append(name);
@@ -1041,7 +1147,27 @@ void EffectComposerModel::saveResources(const QString &name)
const QString qmlString = qmlStringList.join('\n');
QString qmlFilePath = effectsResPath + qmlFilename;
- writeToFile(qmlString.toUtf8(), qmlFilePath, FileType::Text);
+
+ // Get exposed properties from the old qml file if it exists
+ QSet<QByteArray> oldExposedProps;
+ Utils::FilePath oldQmlFile = Utils::FilePath::fromString(qmlFilePath);
+ if (oldQmlFile.exists()) {
+ const QByteArray oldQmlContent = oldQmlFile.fileContents().value();
+ oldExposedProps = getExposedProperties(oldQmlContent);
+ }
+
+ const QByteArray qmlUtf8 = qmlString.toUtf8();
+ if (!oldExposedProps.isEmpty()) {
+ const QSet<QByteArray> newExposedProps = getExposedProperties(qmlUtf8);
+ oldExposedProps.subtract(newExposedProps);
+ if (!oldExposedProps.isEmpty()) {
+ // If there were exposed properties that are no longer exposed, those
+ // need to be removed from any instances of the effect in the scene
+ emit removePropertiesFromScene(oldExposedProps, name);
+ }
+ }
+
+ writeToFile(qmlUtf8, qmlFilePath, FileType::Text);
newFileNames.append(qmlFilename);
// Save shaders and images
@@ -1110,7 +1236,7 @@ void EffectComposerModel::saveResources(const QString &name)
endResetModel();
}
- emit resourcesSaved(QString("Effects.%1.%1").arg(name).toUtf8(), effectPath);
+ emit resourcesSaved(QString("%1.%2.%2").arg(m_effectTypePrefix, name).toUtf8(), effectPath);
}
void EffectComposerModel::resetEffectError(int type)
@@ -1729,6 +1855,19 @@ void EffectComposerModel::setIsEnabled(bool enabled)
emit isEnabledChanged();
}
+bool EffectComposerModel::hasValidTarget() const
+{
+ return m_hasValidTarget;
+}
+
+void EffectComposerModel::setHasValidTarget(bool validTarget)
+{
+ if (m_hasValidTarget == validTarget)
+ return;
+ m_hasValidTarget = validTarget;
+ emit hasValidTargetChanged();
+}
+
QString EffectComposerModel::getQmlImagesString(bool localFiles)
{
QString imagesString;
@@ -1831,6 +1970,12 @@ QString EffectComposerModel::getQmlComponentString(bool localFiles)
s += l2 + "vertexShader: 'file:///" + vertFile + "'\n";
s += l2 + "fragmentShader: 'file:///" + fragFile + "'\n";
s += l2 + "anchors.fill: " + (localFiles ? "rootItem.source" : "parent") + "\n";
+ if (localFiles) {
+ if (m_extraMargin)
+ s += l2 + "anchors.margins: -rootItem.extraMargin\n";
+ } else {
+ s += l2 + "anchors.margins: -root.extraMargin\n";
+ }
if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) {
QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth())
.arg(m_shaderFeatures.gridMeshHeight());
@@ -1866,6 +2011,32 @@ void EffectComposerModel::connectCompositionNode(CompositionNode *node)
});
}
+void EffectComposerModel::updateExtraMargin()
+{
+ m_extraMargin = 0;
+ for (CompositionNode *node : std::as_const(m_nodes))
+ m_extraMargin = qMax(node->extraMargin(), m_extraMargin);
+}
+
+QSet<QByteArray> EffectComposerModel::getExposedProperties(const QByteArray &qmlContent)
+{
+ QSet<QByteArray> returnSet;
+ const QByteArrayList lines = qmlContent.split('\n');
+ const QByteArray propertyTag {" property"}; // Match only toplevel exposed properties
+ for (const QByteArray &line : lines) {
+ if (line.startsWith(propertyTag)) {
+ QByteArrayList words = line.trimmed().split(' ');
+ if (words.size() >= 3) {
+ QByteArray propName = words[2];
+ if (propName.endsWith(':'))
+ propName.chop(1);
+ returnSet.insert(propName);
+ }
+ }
+ }
+ return returnSet;
+}
+
QString EffectComposerModel::currentComposition() const
{
return m_currentComposition;
diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h
index bd4040efc2..14ef09e8a9 100644
--- a/src/plugins/effectcomposer/effectcomposermodel.h
+++ b/src/plugins/effectcomposer/effectcomposermodel.h
@@ -11,6 +11,7 @@
#include <QFileSystemWatcher>
#include <QMap>
#include <QRegularExpression>
+#include <QSet>
#include <QTemporaryFile>
#include <QTimer>
@@ -48,6 +49,7 @@ class EffectComposerModel : public QAbstractListModel
Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged)
Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged)
Q_PROPERTY(bool isEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
+ Q_PROPERTY(bool hasValidTarget READ hasValidTarget WRITE setHasValidTarget NOTIFY hasValidTargetChanged)
Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged)
public:
@@ -58,6 +60,8 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+ void setEffectsTypePrefix(const QString &prefix);
+
bool isEmpty() const { return m_isEmpty; }
void setIsEmpty(bool val);
@@ -70,6 +74,7 @@ public:
Q_INVOKABLE void clear(bool clearName = false);
Q_INVOKABLE void assignToSelected();
Q_INVOKABLE QString getUniqueEffectName() const;
+ Q_INVOKABLE bool nameExists(const QString &name) const;
bool shadersUpToDate() const;
void setShadersUpToDate(bool newShadersUpToDate);
@@ -77,6 +82,9 @@ public:
bool isEnabled() const;
void setIsEnabled(bool enabled);
+ bool hasValidTarget() const;
+ void setHasValidTarget(bool validTarget);
+
QString fragmentShader() const;
void setFragmentShader(const QString &newFragmentShader);
@@ -110,12 +118,14 @@ signals:
void effectErrorChanged();
void shadersUpToDateChanged();
void isEnabledChanged();
+ void hasValidTargetChanged();
void shadersBaked();
void currentCompositionChanged();
void nodesChanged();
void resourcesSaved(const QByteArray &type, const Utils::FilePath &path);
void hasUnsavedChangesChanged();
void assignToSelectedTriggered(const QString &effectPath);
+ void removePropertiesFromScene(QSet<QByteArray> props, const QString &typeName);
private:
enum Roles {
@@ -176,6 +186,8 @@ private:
QString getDesignerSpecifics() const;
void connectCompositionNode(CompositionNode *node);
+ void updateExtraMargin();
+ QSet<QByteArray> getExposedProperties(const QByteArray &qmlContent);
QList<CompositionNode *> m_nodes;
@@ -210,8 +222,11 @@ private:
QString m_qmlComponentString;
bool m_loadComponentImages = true;
bool m_isEnabled = true;
+ bool m_hasValidTarget = false;
QString m_currentComposition;
QTimer m_rebakeTimer;
+ int m_extraMargin = 0;
+ QString m_effectTypePrefix;
const QRegularExpression m_spaceReg = QRegularExpression("\\s+");
};
diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp
index 492d5a9e80..c2c162a87e 100644
--- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp
+++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp
@@ -59,7 +59,9 @@ bool EffectComposerUniformsModel::setData(const QModelIndex &index, const QVaria
int idx = value.toString().indexOf("file:");
QString path = idx > 0 ? updatedValue.right(updatedValue.size() - idx - 5) : updatedValue;
- updatedValue = QUrl::fromLocalFile(path).toString();
+
+ if (idx == -1)
+ updatedValue = QUrl::fromLocalFile(path).toString();
uniform->setValue(updatedValue);
g_propertyData.insert(uniform->name(), updatedValue);
@@ -73,6 +75,14 @@ bool EffectComposerUniformsModel::setData(const QModelIndex &index, const QVaria
return true;
}
+bool EffectComposerUniformsModel::resetData(int row)
+{
+ QModelIndex idx = index(row, 0);
+ QTC_ASSERT(idx.isValid(), return false);
+
+ return setData(idx, idx.data(DefaultValueRole), ValueRole);
+}
+
void EffectComposerUniformsModel::resetModel()
{
beginResetModel();
diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h
index 65b2d7b2f0..fc82194fdf 100644
--- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h
+++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h
@@ -20,6 +20,7 @@ public:
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+ Q_INVOKABLE bool resetData(int row);
void resetModel();
diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp
index e7a879bd8b..48c6a33c4b 100644
--- a/src/plugins/effectcomposer/effectcomposerview.cpp
+++ b/src/plugins/effectcomposer/effectcomposerview.cpp
@@ -10,7 +10,9 @@
#include <designermcumanager.h>
#include <documentmanager.h>
#include <modelnodeoperations.h>
+#include <qmlchangeset.h>
#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
#include <coreplugin/icore.h>
@@ -31,6 +33,7 @@ void EffectComposerContext::contextHelp(const HelpCallback &callback) const
EffectComposerView::EffectComposerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies)
: AbstractView{externalDependencies}
+ , m_componentUtils(externalDependencies)
{
}
@@ -48,14 +51,73 @@ QmlDesigner::WidgetInfo EffectComposerView::widgetInfo()
m_widget = new EffectComposerWidget{this};
connect(m_widget->effectComposerModel(), &EffectComposerModel::assignToSelectedTriggered, this,
- [&] (const QString &effectPath) {
- executeInTransaction("EffectComposerView::widgetInfo", [&] {
+ [this] (const QString &effectPath) {
+ executeInTransaction("EffectComposerView assignToSelectedTriggered", [&] {
const QList<QmlDesigner::ModelNode> selectedNodes = selectedModelNodes();
for (const QmlDesigner::ModelNode &node : selectedNodes)
QmlDesigner::ModelNodeOperations::handleItemLibraryEffectDrop(effectPath, node);
});
});
+ connect(m_widget->effectComposerModel(), &EffectComposerModel::removePropertiesFromScene, this,
+ [this] (QSet<QByteArray> props, const QString &typeName) {
+ // Remove specified properties from all instances of specified type
+
+ QmlDesigner::DesignDocument *document
+ = QmlDesigner::QmlDesignerPlugin::instance()->currentDesignDocument();
+ if (!document)
+ return;
+
+ const QByteArray fullType = QString("%1.%2.%2").arg(m_componentUtils.composedEffectsTypePrefix(),
+ typeName).toUtf8();
+ const QList<QmlDesigner::ModelNode> allNodes = allModelNodes();
+ QList<QmlDesigner::ModelNode> typeNodes;
+ QList<QmlDesigner::ModelNode> propertyChangeNodes;
+ for (const QmlDesigner::ModelNode &node : allNodes) {
+ if (QmlDesigner::QmlPropertyChanges::isValidQmlPropertyChanges(node))
+ propertyChangeNodes.append(node);
+#ifdef QDS_USE_PROJECTSTORAGE
+// TODO: typeName() shouldn't be used with projectstorage. Needs alternative solution (using modules?)
+#else
+ else if (node.metaInfo().typeName() == fullType)
+ typeNodes.append(node);
+#endif
+ }
+ if (!typeNodes.isEmpty()) {
+ bool clearStacks = false;
+
+ executeInTransaction("EffectComposerView removePropertiesFromScene", [&] {
+ for (QmlDesigner::ModelNode node : std::as_const(propertyChangeNodes)) {
+ QmlDesigner::ModelNode targetNode = QmlDesigner::QmlPropertyChanges(node).target();
+ if (typeNodes.contains(targetNode)) {
+ for (const QByteArray &prop : props) {
+ if (node.hasProperty(prop)) {
+ node.removeProperty(prop);
+ clearStacks = true;
+ }
+ }
+ QList<QmlDesigner::AbstractProperty> remainingProps = node.properties();
+ if (remainingProps.size() == 1 && remainingProps[0].name() == "target")
+ node.destroy(); // Remove empty changes node
+ }
+ }
+ for (const QmlDesigner::ModelNode &node : std::as_const(typeNodes)) {
+ for (const QByteArray &prop : props) {
+ if (node.hasProperty(prop)) {
+ node.removeProperty(prop);
+ clearStacks = true;
+ }
+ }
+ }
+ });
+
+ // Reset undo stack as changing of the actual effect cannot be undone, and thus the
+ // stack will contain only unworkable states
+ if (clearStacks)
+ document->clearUndoRedoStacks();
+ }
+ });
+
auto context = new EffectComposerContext(m_widget.data());
Core::ICore::addContextObject(context);
}
@@ -91,6 +153,7 @@ void EffectComposerView::modelAttached(QmlDesigner::Model *model)
if (m_currProjectPath != currProjectPath) { // starting a new project
m_widget->effectComposerNodesModel()->loadModel();
m_widget->effectComposerModel()->clear(true);
+ m_widget->effectComposerModel()->setEffectsTypePrefix(m_componentUtils.composedEffectsTypePrefix());
m_widget->effectComposerModel()->setIsEnabled(
!QmlDesigner::DesignerMcuManager::instance().isMCUProject());
m_widget->initView();
@@ -105,4 +168,19 @@ void EffectComposerView::modelAboutToBeDetached(QmlDesigner::Model *model)
AbstractView::modelAboutToBeDetached(model);
}
+void EffectComposerView::selectedNodesChanged(const QList<QmlDesigner::ModelNode> & selectedNodeList,
+ const QList<QmlDesigner::ModelNode> & /*lastSelectedNodeList*/)
+{
+ bool hasValidTarget = false;
+
+ for (const QmlDesigner::ModelNode &node : selectedNodeList) {
+ if (node.metaInfo().isQtQuickItem()) {
+ hasValidTarget = true;
+ break;
+ }
+ }
+
+ m_widget->effectComposerModel()->setHasValidTarget(hasValidTarget);
+}
+
} // namespace EffectComposer
diff --git a/src/plugins/effectcomposer/effectcomposerview.h b/src/plugins/effectcomposer/effectcomposerview.h
index c7a381cb7d..b264fe0fd9 100644
--- a/src/plugins/effectcomposer/effectcomposerview.h
+++ b/src/plugins/effectcomposer/effectcomposerview.h
@@ -4,7 +4,9 @@
#pragma once
#include "abstractview.h"
+#include "modelnode.h"
+#include <generatedcomponentutils.h>
#include <coreplugin/icontext.h>
#include <QPointer>
@@ -35,6 +37,8 @@ public:
// AbstractView
void modelAttached(QmlDesigner::Model *model) override;
void modelAboutToBeDetached(QmlDesigner::Model *model) override;
+ void selectedNodesChanged(const QList<QmlDesigner::ModelNode> &selectedNodeList,
+ const QList<QmlDesigner::ModelNode> &lastSelectedNodeList) override;
private:
void customNotification(const AbstractView *view, const QString &identifier,
@@ -42,6 +46,7 @@ private:
QPointer<EffectComposerWidget> m_widget;
QString m_currProjectPath;
+ QmlDesigner::GeneratedComponentUtils m_componentUtils;
};
} // namespace EffectComposer
diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp
index 98d5ffd336..590b38b423 100644
--- a/src/plugins/effectcomposer/uniform.cpp
+++ b/src/plugins/effectcomposer/uniform.cpp
@@ -94,7 +94,13 @@ void Uniform::setValue(const QVariant &newValue)
{
if (m_value != newValue) {
m_value = newValue;
+
emit uniformValueChanged();
+
+ if (m_type == Type::Sampler) {
+ m_backendValue->setValue(newValue);
+ emit uniformBackendValueChanged();
+ }
}
}
diff --git a/src/plugins/qmldesigner/.clang-format b/src/plugins/qmldesigner/.clang-format
index d3695ac298..366f82f76f 100644
--- a/src/plugins/qmldesigner/.clang-format
+++ b/src/plugins/qmldesigner/.clang-format
@@ -2,6 +2,7 @@ Language: Cpp
AccessModifierOffset: -4
AlignEscapedNewlines: DontAlign
AllowShortFunctionsOnASingleLine: Inline
+AlwaysBreakTemplateDeclarations: true # use with clang 19
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
@@ -15,6 +16,7 @@ BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: AfterComma
+# BreakTemplateDeclarations: Yes # use with clang 19
ColumnLimit: 100
IncludeCategories:
- Regex: 'Q.*'
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index b5b64bebbc..520c4ebc79 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -8,7 +8,6 @@ if (APPLE)
set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner")
endif()
-add_compile_options("$<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>")
set(BUILD_NOT_DESIGNSTUDIO NOT ${BUILD_NOT_DESIGNSTUDIO})
option(QTC_USE_QML_DESIGNER_LITE "Use Qml Designer Lite" ${BUILD_NOT_DESIGNSTUDIO})
@@ -46,6 +45,10 @@ add_qtc_library(QmlDesignerUtils STATIC
qmldesignerutils_global.h
)
+
+target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>)
+target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang>:-Wno-unneeded-internal-declaration>)
+
extend_qtc_library(QmlDesignerUtils
CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON
@@ -91,6 +94,8 @@ add_qtc_library(QmlDesignerCore STATIC
SOURCES
rewritertransaction.cpp
rewritertransaction.h
+ generatedcomponentutils.cpp
+ generatedcomponentutils.h
)
extend_qtc_library(QmlDesignerCore
@@ -631,6 +636,7 @@ extend_qtc_plugin(QmlDesigner
svgpasteaction.cpp svgpasteaction.h
viewmanager.cpp viewmanager.h
utils3d.cpp utils3d.h
+ dialogutils.cpp dialogutils.h
)
extend_qtc_plugin(QmlDesigner
@@ -822,6 +828,7 @@ extend_qtc_plugin(QmlDesigner
contentlibraryeffect.cpp contentlibraryeffect.h
contentlibraryeffectscategory.cpp contentlibraryeffectscategory.h
contentlibraryeffectsmodel.cpp contentlibraryeffectsmodel.h
+ contentlibraryusermodel.cpp contentlibraryusermodel.h
)
extend_qtc_plugin(QmlDesigner
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp
index dc5a1c9741..b821cc6595 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp
@@ -2,9 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "assetslibraryiconprovider.h"
-#include "asset.h"
-#include "modelnodeoperations.h"
+#include <modelnodeoperations.h>
#include <theme.h>
#include <utils/hdrimage.h>
#include <utils/ktximage.h>
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h
index fb38605ea6..d52779232f 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h
@@ -3,12 +3,11 @@
#pragma once
+#include <asset.h>
#include <synchronousimagecache.h>
#include <QQuickImageProvider>
-#include "asset.h"
-
namespace QmlDesigner {
struct Thumbnail
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
index c2359409eb..9d09f52d8f 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
@@ -1,21 +1,19 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include <QCheckBox>
-#include <QFileInfo>
-#include <QFileSystemModel>
-#include <QMessageBox>
-#include <QSortFilterProxyModel>
-
-#include "asset.h"
#include "assetslibrarymodel.h"
+#include <asset.h>
#include <modelnodeoperations.h>
#include <qmldesignerplugin.h>
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
-#include <utils/qtcassert.h>
+#include <utils/filesystemwatcher.h>
+
+#include <QFileInfo>
+#include <QFileSystemModel>
+#include <QMessageBox>
namespace QmlDesigner {
@@ -38,7 +36,7 @@ void AssetsLibraryModel::createBackendModel()
QObject::connect(m_sourceFsModel, &QFileSystemModel::directoryLoaded, this,
[this]([[maybe_unused]] const QString &dir) {
- syncHaveFiles();
+ syncHasFiles();
});
m_fileWatcher = new Utils::FileSystemWatcher(parent());
@@ -207,7 +205,7 @@ bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
}
}
-bool AssetsLibraryModel::checkHaveFiles(const QModelIndex &parentIdx) const
+bool AssetsLibraryModel::checkHasFiles(const QModelIndex &parentIdx) const
{
if (!parentIdx.isValid())
return false;
@@ -218,30 +216,30 @@ bool AssetsLibraryModel::checkHaveFiles(const QModelIndex &parentIdx) const
if (!isDirectory(newIdx))
return true;
- if (checkHaveFiles(newIdx))
+ if (checkHasFiles(newIdx))
return true;
}
return false;
}
-void AssetsLibraryModel::setHaveFiles(bool value)
+void AssetsLibraryModel::setHasFiles(bool value)
{
- if (m_haveFiles != value) {
- m_haveFiles = value;
- emit haveFilesChanged();
+ if (m_hasFiles != value) {
+ m_hasFiles = value;
+ emit hasFilesChanged();
}
}
-bool AssetsLibraryModel::checkHaveFiles() const
+bool AssetsLibraryModel::checkHasFiles() const
{
auto rootIdx = indexForPath(m_rootPath);
- return checkHaveFiles(rootIdx);
+ return checkHasFiles(rootIdx);
}
-void AssetsLibraryModel::syncHaveFiles()
+void AssetsLibraryModel::syncHasFiles()
{
- setHaveFiles(checkHaveFiles());
+ setHasFiles(checkHasFiles());
}
QString AssetsLibraryModel::getUniqueName(const QString &oldName) {
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
index 9334e86e9b..2516be787f 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
@@ -3,12 +3,13 @@
#pragma once
-#include <QFileInfo>
-#include <QFileSystemModel>
#include <QSortFilterProxyModel>
-#include <utils/filesystemwatcher.h>
-#include <utils/qtcassert.h>
+namespace Utils {
+class FileSystemWatcher;
+}
+
+QT_FORWARD_DECLARE_CLASS(QFileSystemModel)
namespace QmlDesigner {
@@ -22,7 +23,7 @@ public:
void setRootPath(const QString &newPath);
void setSearchText(const QString &searchText);
- Q_PROPERTY(bool haveFiles READ haveFiles NOTIFY haveFilesChanged);
+ Q_PROPERTY(bool hasFiles READ hasFiles NOTIFY hasFilesChanged)
Q_INVOKABLE QString rootPath() const;
Q_INVOKABLE QString filePath(const QModelIndex &index) const;
@@ -35,7 +36,7 @@ public:
Q_INVOKABLE QModelIndex parentDirIndex(const QString &path) const;
Q_INVOKABLE QModelIndex parentDirIndex(const QModelIndex &index) const;
Q_INVOKABLE QString parentDirPath(const QString &path) const;
- Q_INVOKABLE void syncHaveFiles();
+ Q_INVOKABLE void syncHasFiles();
Q_INVOKABLE QList<QModelIndex> parentIndices(const QModelIndex &index) const;
Q_INVOKABLE bool indexIsValid(const QModelIndex &index) const;
@@ -55,30 +56,30 @@ public:
return std::min(result, 1);
}
- bool haveFiles() const { return m_haveFiles; }
+ bool hasFiles() const { return m_hasFiles; }
QString getUniqueName(const QString &oldName);
signals:
void directoryLoaded(const QString &path);
void rootPathChanged();
- void haveFilesChanged();
+ void hasFilesChanged();
void fileChanged(const QString &path);
void effectsDeleted(const QStringList &effectNames);
private:
- void setHaveFiles(bool value);
+ void setHasFiles(bool value);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
void resetModel();
void createBackendModel();
void destroyBackendModel();
- bool checkHaveFiles(const QModelIndex &parentIdx) const;
- bool checkHaveFiles() const;
+ bool checkHasFiles(const QModelIndex &parentIdx) const;
+ bool checkHasFiles() const;
QString m_searchText;
QString m_rootPath;
QFileSystemModel *m_sourceFsModel = nullptr;
- bool m_haveFiles = false;
+ bool m_hasFiles = false;
Utils::FileSystemWatcher *m_fileWatcher = nullptr;
};
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
index 3b98eb6baf..4b270c8902 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
@@ -3,20 +3,22 @@
#include "assetslibrarywidget.h"
-#include "asset.h"
#include "assetslibraryiconprovider.h"
#include "assetslibrarymodel.h"
#include "assetslibraryview.h"
-#include "designeractionmanager.h"
-#include "import.h"
-#include "modelnodeoperations.h"
-#include "nodemetainfo.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
-#include "theme.h"
-#include <utils3d.h>
+#include <asset.h>
+#include <designeractionmanager.h>
+#include <designerpaths.h>
+#include <hdrimage.h>
+#include <import.h>
+#include <modelnodeoperations.h>
+#include <nodemetainfo.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
#include <studioquickwidget.h>
+#include <theme.h>
+#include <utils3d.h>
#include <coreplugin/fileutils.h>
#include <coreplugin/icore.h>
@@ -287,14 +289,16 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList
// Remove usages of deleted effects from the current document
m_assetsView->executeInTransaction(__FUNCTION__, [&]() {
QList<ModelNode> allNodes = m_assetsView->allModelNodes();
- const QString typeTemplate = "Effects.%1.%1";
- const QString importUrlTemplate = "Effects.%1";
+ const QString typeTemplate = "%1.%2.%2";
+ const QString importUrlTemplate = "%1.%2";
const Imports imports = m_assetsView->model()->imports();
Imports removedImports;
+ const QString typePrefix = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().composedEffectsTypePrefix();
for (const QString &effectName : effectNames) {
if (effectName.isEmpty())
continue;
- const TypeName type = typeTemplate.arg(effectName).toUtf8();
+ const TypeName type = typeTemplate.arg(typePrefix, effectName).toUtf8();
for (ModelNode &node : allNodes) {
if (node.metaInfo().typeName() == type) {
clearStacks = true;
@@ -302,7 +306,7 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList
}
}
- const QString importPath = importUrlTemplate.arg(effectName);
+ const QString importPath = importUrlTemplate.arg(typePrefix, effectName);
Import removedImport = Utils::findOrDefault(imports, [&importPath](const Import &import) {
return import.url() == importPath;
});
@@ -374,7 +378,7 @@ QList<QToolButton *> AssetsLibraryWidget::createToolBarWidgets()
void AssetsLibraryWidget::handleSearchFilterChanged(const QString &filterText)
{
- if (filterText == m_filterText || (!m_assetsModel->haveFiles()
+ if (filterText == m_filterText || (!m_assetsModel->hasFiles()
&& filterText.contains(m_filterText, Qt::CaseInsensitive)))
return;
@@ -643,4 +647,15 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog
}
}
+bool AssetsLibraryWidget::userBundleEnabled() const
+{
+ // TODO: this method is to be removed after user bundle implementation is complete
+ return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool();
+}
+
+void AssetsLibraryWidget::addAssetsToContentLibrary(const QStringList &assetPaths)
+{
+ m_assetsView->emitCustomNotification("add_assets_to_content_lib", {}, {assetPaths});
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
index ed987d14de..8b59ae0785 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
@@ -98,6 +98,8 @@ public:
Q_INVOKABLE void showInGraphicalShell(const QString &path);
Q_INVOKABLE QString showInGraphicalShellMsg() const;
+ Q_INVOKABLE bool userBundleEnabled() const;
+ Q_INVOKABLE void addAssetsToContentLibrary(const QStringList &assetPaths);
signals:
void itemActivated(const QString &itemName);
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
index ddfb82746c..8b506affc4 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
@@ -60,28 +60,21 @@ inline static bool isValidColorName(const QString &colorName)
/**
* @brief getCustomUrl
- * MimeType = <MainType/SubType>
* Address = <Url|LocalFile>
*
* @param value The input value to be evaluated
- * @param dataType if the value is a valid url or image, the data type
+ * @param dataType if the value is a valid url, the data type
* will be stored to this parameter, otherwise, it will be Unknown
- * @param urlResult if the value is a valid url or image, the address
+ * @param urlResult if the value is a valid url, the address
* will be stored in this parameter, otherwise it will be empty.
- * @param subType if the value is a valid image, the image subtype
- * will be stored in this parameter, otherwise it will be empty.
- * @return true if the result is either url or image
+ * @return true if the result is url
*/
static bool getCustomUrl(const QString &value,
CollectionDetails::DataType &dataType,
- QUrl *urlResult = nullptr,
- QString *subType = nullptr)
+ QUrl *urlResult = nullptr)
{
static const QRegularExpression urlRegex{
- "^(?<MimeType>"
- "(?<MainType>image)\\/"
- "(?<SubType>apng|avif|gif|jpeg|png|(?:svg\\+xml)|webp|xyz)\\:)?" // end of MimeType
- "(?<Address>"
+ "^(?<Address>"
"(?<Url>https?:\\/\\/"
"(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+"
"[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/"
@@ -92,29 +85,18 @@ static bool getCustomUrl(const QString &value,
};
const QRegularExpressionMatch match = urlRegex.match(value.trimmed());
- if (match.hasMatch()) {
- if (match.hasCaptured("Address")) {
- if (match.hasCaptured("MimeType") && match.captured("MainType") == "image")
- dataType = CollectionDetails::DataType::Image;
- else
- dataType = CollectionDetails::DataType::Url;
+ if (match.hasCaptured("Address")) {
+ dataType = CollectionDetails::DataType::Url;
- if (urlResult)
- urlResult->setUrl(match.captured("Address"));
+ if (urlResult)
+ urlResult->setUrl(match.captured("Address"));
- if (subType)
- *subType = match.captured("SubType");
-
- return true;
- }
+ return true;
}
if (urlResult)
urlResult->clear();
- if (subType)
- subType->clear();
-
dataType = CollectionDetails::DataType::Unknown;
return false;
}
@@ -248,14 +230,8 @@ static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataT
return variantValue.toBool();
case DataType::Color:
return variantValue.value<QColor>();
- case DataType::Image: {
- DataType type;
- QUrl url;
- if (getCustomUrl(variantValue.toString(), type, &url))
- return url;
- return variantValue.toString();
- }
case DataType::Url:
+ case DataType::Image:
return variantValue.value<QUrl>();
default:
return variantValue;
@@ -285,12 +261,7 @@ static QJsonValue variantToJsonValue(
return variant.toDouble();
case DataType::Integer:
return variant.toInt();
- case DataType::Image: {
- const QUrl url(variant.toUrl());
- if (url.isValid())
- return QString("image/xyz:%1").arg(url.toString());
- return {};
- }
+ case DataType::Image:
case DataType::String:
case DataType::Color:
case DataType::Url:
@@ -569,13 +540,6 @@ QVariant CollectionDetails::data(int row, int column) const
const QJsonValue cellValue = d->dataRecords.at(row).at(column);
- if (typeAt(column) == DataType::Image) {
- const QUrl imageUrl = valueToVariant(cellValue, DataType::Image).toUrl();
-
- if (imageUrl.isValid())
- return imageUrl;
- }
-
return cellValue.toVariant();
}
@@ -614,7 +578,10 @@ DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column
if (columnType == DataType::Unknown || isEmptyJsonValue(cellValue))
return DataTypeWarning::Warning::None;
- if (columnType == DataType::Real && cellType == DataType::Integer)
+ if ((columnType == DataType::String || columnType == DataType::Real) && cellType == DataType::Integer)
+ return DataTypeWarning::Warning::None;
+
+ if ((columnType == DataType::Url || columnType == DataType::Image) && cellType == DataType::String)
return DataTypeWarning::Warning::None;
if (columnType != cellType)
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
index b26b1a845e..d2917ec302 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
@@ -93,6 +93,7 @@ bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &v
if (prevWarning != m_currentCollection.cellWarningCheck(index.row(), index.column()))
roles << DataTypeWarningRole;
+ setHasUnsavedChanges(true);
emit dataChanged(index, index, roles);
}
@@ -128,11 +129,11 @@ bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] con
row = qBound(0, row, rowCount());
- beginResetModel();
+ beginInsertRows({}, row, row + count - 1);
m_currentCollection.insertEmptyRows(row, count);
- endResetModel();
+ endInsertRows();
+ setHasUnsavedChanges(true);
- selectRow(row);
return true;
}
@@ -151,12 +152,6 @@ bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIn
if (!columnCount(parent))
removeRows(0, rowCount(parent), parent);
- int nextColumn = column - 1;
- if (nextColumn < 0 && columnCount(parent) > 0)
- nextColumn = 0;
-
- selectColumn(nextColumn);
-
ensureSingleCell();
return columnsRemoved;
}
@@ -254,6 +249,7 @@ bool CollectionDetailsModel::addColumn(int column, const QString &name, const QS
{},
CollectionDataTypeModel::dataTypeFromString(propertyType));
endInsertColumns();
+ setHasUnsavedChanges(true);
return m_currentCollection.containsPropertyName(name);
}
@@ -309,6 +305,7 @@ bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue
{Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole});
}
+ setHasUnsavedChanges(true);
return changed;
}
@@ -441,6 +438,7 @@ bool CollectionDetailsModel::saveDataStoreCollections()
if (reference != currentReference)
closeCollectionIfSaved(reference);
}
+ setHasUnsavedChanges(false);
return true;
}
}
@@ -618,4 +616,12 @@ QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning
return DataTypeWarning::getDataTypeWarningString(warning);
}
+void CollectionDetailsModel::setHasUnsavedChanges(bool val)
+{
+ if (m_hasUnsavedChanges == val)
+ return;
+ m_hasUnsavedChanges = val;
+ emit hasUnsavedChangesChanged();
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
index 24a040cce6..8844ff4a3e 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
@@ -20,6 +20,7 @@ class CollectionDetailsModel : public QAbstractTableModel
Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged)
Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged)
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
+ Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged)
public:
enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole, DataTypeWarningRole };
@@ -70,12 +71,14 @@ public:
const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const;
bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const;
QString getFirstColumnName(const CollectionReference &reference) const;
+ void setHasUnsavedChanges(bool val);
signals:
void collectionNameChanged(const QString &collectionName);
void selectedColumnChanged(int);
void selectedRowChanged(int);
void isEmptyChanged(bool);
+ void hasUnsavedChangesChanged();
void warning(const QString &title, const QString &body);
private slots:
@@ -93,6 +96,7 @@ private:
QHash<CollectionReference, CollectionDetails> m_openedCollections;
CollectionDetails m_currentCollection;
bool m_isEmpty = true;
+ bool m_hasUnsavedChanges = false;
int m_selectedColumn = -1;
int m_selectedRow = -1;
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp
index f56bb36e88..2cc6ac05a6 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp
@@ -62,6 +62,12 @@ bool CollectionDetailsSortFilterModel::selectColumn(int column)
return m_source->selectColumn(mapToSource(index(0, column)).column());
}
+void CollectionDetailsSortFilterModel::deselectAll()
+{
+ QTC_ASSERT(m_source, return);
+ m_source->deselectAll();
+}
+
CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default;
bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow,
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h
index 93305f3ca2..10f6e09b05 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h
@@ -31,6 +31,7 @@ public:
Q_INVOKABLE bool selectRow(int row);
Q_INVOKABLE bool selectColumn(int column);
+ Q_INVOKABLE void deselectAll();
signals:
void selectedColumnChanged(int);
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
index 4725987f12..29b833cc2c 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
@@ -3,6 +3,7 @@
#include "collectioneditorutils.h"
+#include "collectiondatatypemodel.h"
#include "model.h"
#include "nodemetainfo.h"
#include "propertymetainfo.h"
@@ -95,13 +96,17 @@ Utils::FilePath dataStoreDir()
if (!currentProject)
return {};
- return currentProject->projectDirectory().pathAppended("/imports/"
- + currentProject->displayName());
+ FilePath oldImportDirectory = currentProject->projectDirectory().pathAppended(
+ "imports/" + currentProject->displayName());
+ if (oldImportDirectory.exists())
+ return oldImportDirectory;
+
+ return currentProject->projectDirectory().pathAppended(currentProject->displayName());
}
inline Utils::FilePath collectionPath(const QString &filePath)
{
- return dataStoreDir().pathAppended("/" + filePath);
+ return dataStoreDir().pathAppended(filePath);
}
inline Utils::FilePath qmlDirFilePath()
@@ -288,7 +293,7 @@ QJsonObject defaultCollection()
QJsonArray columns;
QJsonObject defaultColumn;
defaultColumn.insert("name", "Column 1");
- defaultColumn.insert("type", "string");
+ defaultColumn.insert("type", CollectionDataTypeModel::dataTypeToString(DataType::String));
columns.append(defaultColumn);
QJsonArray collectionData;
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
index f6ec821fde..0c9a2eed94 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
@@ -35,6 +35,12 @@ bool isStudioCollectionModel(const QmlDesigner::ModelNode &node)
return node.metaInfo().isQtQuickStudioUtilsJsonListModel();
}
+inline bool isProjectImport(const QmlDesigner::Import &import)
+{
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject();
+ return currentProject && import.toString() == currentProject->displayName();
+}
+
inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node,
const QmlDesigner::PropertyName &propertyName,
const QVariant &value)
@@ -60,14 +66,10 @@ CollectionView::CollectionView(ExternalDependenciesInterface &externalDependenci
, m_dataStore(std::make_unique<DataStoreModelNode>())
{
- connect(ProjectExplorer::ProjectManager::instance(),
- &ProjectExplorer::ProjectManager::startupProjectChanged, this, [this] {
- resetDataStoreNode();
- if (m_widget.get())
- m_widget->collectionDetailsModel()->removeAllCollections();
- });
}
+CollectionView::~CollectionView() = default;
+
bool CollectionView::hasWidget() const
{
return true;
@@ -75,11 +77,16 @@ bool CollectionView::hasWidget() const
QmlDesigner::WidgetInfo CollectionView::widgetInfo()
{
- if (m_widget.isNull()) {
- m_widget = new CollectionWidget(this);
+ if (!m_widget) {
+ m_widget = Utils::makeUniqueObjectPtr<CollectionWidget>(this);
m_widget->setMinimumSize(m_widget->minimumSizeHint());
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, m_widget.get(), [&] {
+ resetDataStoreNode();
+ m_widget->collectionDetailsModel()->removeAllCollections();
+ });
- auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data());
+ auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.get());
Core::ICore::addContextObject(collectionEditorContext);
CollectionListModel *listModel = m_widget->listModel().data();
@@ -97,7 +104,7 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo()
connect(listModel, &CollectionListModel::modelReset, this, [this] {
CollectionListModel *listModel = m_widget->listModel().data();
- if (listModel->sourceNode() == m_dataStore->modelNode())
+ if (listModel->sourceNode() == dataStoreNode())
m_dataStore->setCollectionNames(listModel->collections());
});
@@ -128,7 +135,7 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo()
});
}
- return createWidgetInfo(m_widget.data(),
+ return createWidgetInfo(m_widget.get(),
"CollectionEditor",
WidgetInfo::LeftPane,
0,
@@ -139,23 +146,22 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo()
void CollectionView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
+ m_widget->setProjectImportExists(Utils::anyOf(model->imports(), isProjectImport));
resetDataStoreNode();
}
void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model)
{
- m_libraryInfoIsUpdated = false;
- m_reloadCounter = 0;
- m_rewriterAmended = false;
- m_dataStoreTypeFound = false;
- disconnect(m_documentUpdateConnection);
- QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear());
- m_widget->listModel()->setDataStoreNode();
+ unloadDataStore();
+ m_widget->setProjectImportExists(false);
}
void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
[[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList)
{
+ if (!m_widget)
+ return;
+
QList<ModelNode> selectedCollectionNodes = Utils::filtered(selectedNodeList,
&isStudioCollectionModel);
@@ -170,10 +176,17 @@ void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeLi
}
m_widget->setTargetNodeSelected(singleSelectedHasModelProperty);
+}
- // More than one model is selected. So ignore them
- if (selectedCollectionNodes.size() > 1)
- return;
+void CollectionView::importsChanged(const Imports &addedImports, const Imports &removedImports)
+{
+ if (Utils::anyOf(addedImports, isProjectImport)) {
+ m_widget->setProjectImportExists(true);
+ resetDataStoreNode();
+ } else if (Utils::anyOf(removedImports, isProjectImport)) {
+ m_widget->setProjectImportExists(false);
+ unloadDataStore();
+ }
}
void CollectionView::customNotification(const AbstractView *,
@@ -181,6 +194,9 @@ void CollectionView::customNotification(const AbstractView *,
const QList<ModelNode> &nodeList,
const QList<QVariant> &data)
{
+ if (!m_widget)
+ return;
+
if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty())
onItemLibraryNodeCreated(nodeList.first());
else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty())
@@ -219,8 +235,27 @@ void CollectionView::addResource(const QUrl &url, const QString &name)
});
}
+void CollectionView::addProjectImport()
+{
+ if (!m_widget)
+ return;
+
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject();
+ if (!currentProject)
+ return;
+
+ executeInTransaction(__FUNCTION__, [&] {
+ Import import = Import::createLibraryImport(currentProject->displayName());
+ if (!model()->hasImport(import, true, true))
+ model()->changeImports({import}, {});
+ });
+}
+
void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node)
{
+ if (!m_widget)
+ return;
+
using DataType = CollectionDetails::DataType;
executeInTransaction("CollectionView::assignCollectionToNode", [&]() {
m_dataStore->assignCollectionToNode(
@@ -279,12 +314,18 @@ void CollectionView::assignCollectionToSelectedNode(const QString &collectionNam
void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection)
{
+ if (!m_widget)
+ return;
+
addTask(QSharedPointer<CollectionTask>(
new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName)));
}
void CollectionView::openCollection(const QString &collectionName)
{
+ if (!m_widget)
+ return;
+
m_widget->openCollection(collectionName);
}
@@ -296,9 +337,13 @@ void CollectionView::registerDeclarativeType()
void CollectionView::resetDataStoreNode()
{
+ if (!m_widget)
+ return;
+
m_dataStore->reloadModel();
- ModelNode dataStore = m_dataStore->modelNode();
+ ModelNode dataStore = dataStoreNode();
+ m_widget->setDataStoreExists(dataStore.isValid());
if (!dataStore || m_widget->listModel()->sourceNode() == dataStore)
return;
@@ -339,28 +384,11 @@ void CollectionView::ensureDataStoreExists()
{
bool filesJustCreated = false;
bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated);
- if (filesExist) {
- if (filesJustCreated) {
- // Force code model reset to notice changes to existing module
- auto modelManager = QmlJS::ModelManagerInterface::instance();
- if (modelManager) {
- m_libraryInfoIsUpdated = false;
-
- m_expectedDocumentUpdates.clear();
- m_expectedDocumentUpdates << CollectionEditorUtils::dataStoreQmlFilePath()
- << CollectionEditorUtils::dataStoreJsonFilePath();
-
- m_documentUpdateConnection = connect(modelManager,
- &QmlJS::ModelManagerInterface::documentUpdated,
- this,
- &CollectionView::onDocumentUpdated);
-
- modelManager->resetCodeModel();
- }
- resetDataStoreNode();
- } else {
- m_libraryInfoIsUpdated = true;
- }
+ if (filesExist && filesJustCreated) {
+ // Force code model reset to notice changes to existing module
+ if (auto modelManager = QmlJS::ModelManagerInterface::instance())
+ modelManager->resetCodeModel();
+ resetDataStoreNode();
}
}
@@ -380,6 +408,18 @@ NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const
return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME);
}
+void CollectionView::unloadDataStore()
+{
+ m_reloadCounter = 0;
+ m_rewriterAmended = false;
+ m_dataStoreTypeFound = false;
+ QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear());
+ if (m_widget) {
+ m_widget->setDataStoreExists(dataStoreNode().isValid());
+ m_widget->listModel()->setDataStoreNode();
+ }
+}
+
void CollectionView::ensureStudioModelImport()
{
executeInTransaction(__FUNCTION__, [&] {
@@ -395,29 +435,21 @@ void CollectionView::ensureStudioModelImport()
void CollectionView::onItemLibraryNodeCreated(const ModelNode &node)
{
+ if (!m_widget)
+ return;
+
if (node.metaInfo().isQtQuickListView()) {
addTask(QSharedPointer<CollectionTask>(
new DropListViewTask(this, m_widget->listModel(), node)));
}
}
-void CollectionView::onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc)
-{
- if (m_expectedDocumentUpdates.contains(doc->fileName()))
- m_expectedDocumentUpdates.remove(doc->fileName());
-
- if (m_expectedDocumentUpdates.isEmpty()) {
- disconnect(m_documentUpdateConnection);
- m_libraryInfoIsUpdated = true;
- }
-}
-
void CollectionView::addTask(QSharedPointer<CollectionTask> task)
{
ensureDataStoreExists();
if (m_dataStoreTypeFound)
task->process();
- else if (m_dataStore->modelNode())
+ else if (dataStoreNode())
m_delayedTasks << task;
}
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
index a4b16c4c27..3de3bd7ae6 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
@@ -3,9 +3,12 @@
#pragma once
-#include "abstractview.h"
#include "datastoremodelnode.h"
-#include "modelnode.h"
+
+#include <abstractview.h>
+#include <modelnode.h>
+
+#include <utils/uniqueobjectptr.h>
#include <QJsonObject>
@@ -27,6 +30,7 @@ class CollectionView : public AbstractView
public:
explicit CollectionView(ExternalDependenciesInterface &externalDependencies);
+ ~CollectionView();
bool hasWidget() const override;
WidgetInfo widgetInfo() override;
@@ -37,6 +41,8 @@ public:
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override;
+ void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
+
void customNotification(const AbstractView *view,
const QString &identifier,
const QList<ModelNode> &nodeList,
@@ -44,6 +50,7 @@ public:
void addResource(const QUrl &url, const QString &name);
+ void addProjectImport();
void assignCollectionToNode(const QString &collectionName, const ModelNode &node);
void assignCollectionToSelectedNode(const QString &collectionName);
void addNewCollection(const QString &collectionName, const QJsonObject &localCollection);
@@ -61,17 +68,14 @@ private:
friend class CollectionTask;
NodeMetaInfo jsonCollectionMetaInfo() const;
+ void unloadDataStore();
void ensureStudioModelImport();
void onItemLibraryNodeCreated(const ModelNode &node);
- void onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc);
void addTask(QSharedPointer<CollectionTask> task);
- QPointer<CollectionWidget> m_widget;
std::unique_ptr<DataStoreModelNode> m_dataStore;
- QSet<Utils::FilePath> m_expectedDocumentUpdates;
+ Utils::UniqueObjectPtr<CollectionWidget> m_widget;
QList<QSharedPointer<CollectionTask>> m_delayedTasks;
- QMetaObject::Connection m_documentUpdateConnection;
- bool m_libraryInfoIsUpdated = false;
bool m_dataStoreTypeFound = false;
bool m_rewriterAmended = false;
int m_reloadCounter = 0;
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
index 093729dc67..dd706145cf 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
@@ -54,8 +54,7 @@ QString getPreferredCollectionName(const QUrl &url, const QString &collectionNam
namespace QmlDesigner {
CollectionWidget::CollectionWidget(CollectionView *view)
- : QFrame()
- , m_view(view)
+ : m_view(view)
, m_listModel(new CollectionListModel)
, m_collectionDetailsModel(new CollectionDetailsModel)
, m_collectionDetailsSortFilterModel(std::make_unique<CollectionDetailsSortFilterModel>())
@@ -104,6 +103,8 @@ CollectionWidget::CollectionWidget(CollectionView *view)
QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME);
}
+CollectionWidget::~CollectionWidget() = default;
+
void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
{
if (m_view)
@@ -250,6 +251,11 @@ bool CollectionWidget::importFile(const QString &collectionName,
return false;
}
+void CollectionWidget::addProjectImport()
+{
+ m_view->addProjectImport();
+}
+
void CollectionWidget::addCollectionToDataStore(const QString &collectionName)
{
m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection());
@@ -288,6 +294,24 @@ void CollectionWidget::setTargetNodeSelected(bool selected)
emit targetNodeSelectedChanged(m_targetNodeSelected);
}
+void CollectionWidget::setProjectImportExists(bool exists)
+{
+ if (m_projectImportExists == exists)
+ return;
+
+ m_projectImportExists = exists;
+ emit projectImportExistsChanged(m_projectImportExists);
+}
+
+void CollectionWidget::setDataStoreExists(bool exists)
+{
+ if (m_dataStoreExists == exists)
+ return;
+
+ m_dataStoreExists = exists;
+ emit dataStoreExistsChanged(m_dataStoreExists);
+}
+
void CollectionWidget::deleteSelectedCollection()
{
QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection");
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
index 0957bd81e0..13c3566c78 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
@@ -22,9 +22,12 @@ class CollectionWidget : public QFrame
Q_OBJECT
Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged)
+ Q_PROPERTY(bool projectImportExists MEMBER m_projectImportExists NOTIFY projectImportExistsChanged)
+ Q_PROPERTY(bool dataStoreExists MEMBER m_dataStoreExists NOTIFY dataStoreExistsChanged)
public:
CollectionWidget(CollectionView *view);
+ ~CollectionWidget();
void contextHelp(const Core::IContext::HelpCallback &callback) const;
QPointer<CollectionListModel> listModel() const;
@@ -32,7 +35,7 @@ public:
void reloadQmlSource();
- virtual QSize minimumSizeHint() const;
+ QSize minimumSizeHint() const override;
Q_INVOKABLE bool loadJsonFile(const QUrl &url, const QString &collectionName = {});
Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {});
@@ -44,6 +47,7 @@ public:
const QUrl &url,
const bool &firstRowIsHeader = true);
+ Q_INVOKABLE void addProjectImport();
Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName);
Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName);
Q_INVOKABLE void openCollection(const QString &collectionName);
@@ -51,11 +55,15 @@ public:
void warn(const QString &title, const QString &body);
void setTargetNodeSelected(bool selected);
+ void setProjectImportExists(bool exists);
+ void setDataStoreExists(bool exists);
void deleteSelectedCollection();
signals:
void targetNodeSelectedChanged(bool);
+ void projectImportExistsChanged(bool);
+ void dataStoreExistsChanged(bool);
private:
QString generateUniqueCollectionName(const ModelNode &node, const QString &name);
@@ -66,6 +74,8 @@ private:
std::unique_ptr<CollectionDetailsSortFilterModel> m_collectionDetailsSortFilterModel;
QScopedPointer<StudioQuickWidget> m_quickWidget;
bool m_targetNodeSelected = false;
+ bool m_projectImportExists = false;
+ bool m_dataStoreExists = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp
new file mode 100644
index 0000000000..f882ae528d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <model.h>
+
+#include <coreplugin/messagebox.h>
+
+namespace QmlDesigner {
+
+namespace DialogUtils {
+
+void showWarningForInvalidId(const QString &id)
+{
+ constexpr char text[] = R"(
+The ID <b>'%1'</b> is invalid.
+
+Make sure the ID is:
+<ul>
+<li>Unique within the QML file.</li>
+<li>Beginning with a lowercase letter.</li>
+<li>Without any blank space or symbol.</li>
+<li>Not a reserved QML keyword. </li>
+</ul>
+)";
+
+ Core::AsynchronousMessageBox::warning(Model::tr("Invalid Id"),
+ Model::tr(text).arg(id));
+}
+
+} // namespace DialogUtils
+
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.h b/src/plugins/qmldesigner/components/componentcore/dialogutils.h
new file mode 100644
index 0000000000..3ca98016dd
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <qmldesignercomponents_global.h>
+
+#include <QString>
+
+namespace QmlDesigner {
+
+namespace DialogUtils {
+
+QMLDESIGNERCOMPONENTS_EXPORT void showWarningForInvalidId(const QString &id);
+
+} // namespace DialogUtils
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
index 8d3412e0e8..89b50c4d1a 100644
--- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
@@ -452,7 +452,9 @@ void LayoutInGridLayout::removeSpacersBySpanning(QList<ModelNode> &nodes)
{
for (const ModelNode &node : std::as_const(m_spacerNodes)) {
if (int index = nodes.indexOf(node)) {
- ModelNode before = nodes.at(index -1);
+ ModelNode before;
+ if (index > 0)
+ before = nodes.at(index - 1);
if (m_spacerNodes.contains(before)) {
m_spacerNodes.removeAll(node);
m_layoutedNodes.removeAll(node);
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp
index f6e18458b2..4cbebd738d 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp
@@ -105,8 +105,10 @@ bool selectionIsImported3DAsset(const SelectionContext &selectionState)
// Node is not a file component, so we have to check if the current doc itself is
fileName = node.model()->fileUrl().toLocalFile();
}
- if (fileName.contains(Constants::QUICK_3D_ASSETS_FOLDER))
+ if (QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().isImport3dPath(fileName)) {
return true;
+ }
}
return false;
}
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index cebe7d7c53..a5274c70e2 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -1138,18 +1138,12 @@ static QString getAssetDefaultDirectory(const QString &assetDir, const QString &
{
QString adjustedDefaultDirectory = defaultDirectory;
- Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
-
- if (contentPath.pathAppended("content").exists())
- contentPath = contentPath.pathAppended("content");
+ Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentResourcePath();
Utils::FilePath assetPath = contentPath.pathAppended(assetDir);
- if (!assetPath.exists()) {
- // Create the default asset type directory if it doesn't exist
- QDir dir(contentPath.toString());
- dir.mkpath(assetDir);
- }
+ if (!assetPath.exists())
+ assetPath.createDir();
if (assetPath.exists() && assetPath.isDir())
adjustedDefaultDirectory = assetPath.toString();
@@ -1694,7 +1688,14 @@ void editIn3dView(const SelectionContext &selectionContext)
if (selectionContext.view() && selectionContext.hasSingleSelectedModelNode()
&& selectionContext.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Editor3D", true);
- selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true);
+ const QPointF scenePos = selectionContext.scenePosition();
+ if (scenePos.isNull()) {
+ selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true);
+ } else {
+ selectionContext.view()->emitCustomNotification("pick_3d_node_from_2d_scene",
+ {selectionContext.currentSingleSelectedNode()},
+ {scenePos});
+ }
}
}
@@ -1727,13 +1728,12 @@ void openOldEffectMaker(const QString &filePath)
return;
}
- Utils::FilePath projectPath = target->project()->projectDirectory();
- QString effectName = QFileInfo(filePath).baseName();
- QString effectResDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER)
- + "/" + effectName;
- Utils::FilePath effectResPath = projectPath.pathAppended(effectResDir);
+ Utils::FilePath effectResPath = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().composedEffectsBasePath()
+ .pathAppended(QFileInfo(filePath).baseName());
+
if (!effectResPath.exists())
- QDir().mkpath(effectResPath.toString());
+ effectResPath.createDir();
const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
if (baseQtVersion) {
@@ -1769,14 +1769,11 @@ void openOldEffectMaker(const QString &filePath)
Utils::FilePath getEffectsImportDirectory()
{
- QString defaultDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER);
- Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
- Utils::FilePath effectsPath = projectPath.pathAppended(defaultDir);
+ Utils::FilePath effectsPath = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().composedEffectsBasePath();
- if (!effectsPath.exists()) {
- QDir dir(projectPath.toString());
- dir.mkpath(effectsPath.toString());
- }
+ if (!effectsPath.exists())
+ effectsPath.createDir();
return effectsPath;
}
@@ -1794,12 +1791,9 @@ QString getEffectsDefaultDirectory(const QString &defaultDir)
QString getEffectIcon(const QString &effectPath)
{
- Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
- QString effectName = QFileInfo(effectPath).baseName();
- QString effectResDir = "asset_imports/Effects/" + effectName;
- Utils::FilePath effectResPath = projectPath.resolvePath(effectResDir + "/" + effectName + ".qml");
-
- return effectResPath.exists() ? QString("effectExported") : QString("effectClass");
+ Utils::FilePath effectFile = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().composedEffectPath(effectPath);
+ return effectFile.exists() ? QString("effectExported") : QString("effectClass");
}
bool useLayerEffect()
diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp
index 4a229564c6..24047f650f 100644
--- a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp
@@ -222,6 +222,10 @@ bool createQmlrcFile(const FilePath &qmlrcFilePath)
rccProcess.setWorkingDirectory(project->projectDirectory());
const QStringList arguments = {"--binary",
+ "--compress",
+ "9",
+ "--threshold",
+ "30",
"--output",
qmlrcFilePath.toString(),
tempQrcFile.toString()};
diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h
index 73184d391c..392f6c94f6 100644
--- a/src/plugins/qmldesigner/components/componentcore/theme.h
+++ b/src/plugins/qmldesigner/components/componentcore/theme.h
@@ -83,6 +83,7 @@ public:
binding_medium,
bounds_small,
branch_medium,
+ cameraSpeed_medium,
camera_medium,
camera_small,
centerHorizontal,
diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
index 05d6f5fdf0..b011d9fbbf 100644
--- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
@@ -67,16 +67,20 @@ public:
, collectionView{externalDependencies}
, contentLibraryView{externalDependencies}
, componentView{externalDependencies}
+#ifndef QTC_USE_QML_DESIGNER_LITE
, edit3DView{externalDependencies}
+#endif
, formEditorView{externalDependencies}
, textEditorView{externalDependencies}
, assetsLibraryView{externalDependencies}
, itemLibraryView(imageCache, externalDependencies)
, navigatorView{externalDependencies}
, propertyEditorView(imageCache, externalDependencies)
+#ifndef QTC_USE_QML_DESIGNER_LITE
, materialEditorView{externalDependencies}
, materialBrowserView{imageCache, externalDependencies}
, textureEditorView{imageCache, externalDependencies}
+#endif
, statesEditorView{externalDependencies}
{}
@@ -89,16 +93,20 @@ public:
CollectionView collectionView;
ContentLibraryView contentLibraryView;
ComponentView componentView;
+#ifndef QTC_USE_QML_DESIGNER_LITE
Edit3DView edit3DView;
+#endif
FormEditorView formEditorView;
TextEditorView textEditorView;
AssetsLibraryView assetsLibraryView;
ItemLibraryView itemLibraryView;
NavigatorView navigatorView;
PropertyEditorView propertyEditorView;
+#ifndef QTC_USE_QML_DESIGNER_LITE
MaterialEditorView materialEditorView;
MaterialBrowserView materialBrowserView;
TextureEditorView textureEditorView;
+#endif
StatesEditorView statesEditorView;
std::vector<std::unique_ptr<AbstractView>> additionalViews;
@@ -203,6 +211,7 @@ QList<AbstractView *> ViewManager::views() const
QList<AbstractView *> ViewManager::standardViews() const
{
+#ifndef QTC_USE_QML_DESIGNER_LITE
QList<AbstractView *> list = {&d->edit3DView,
&d->formEditorView,
&d->textEditorView,
@@ -215,6 +224,16 @@ QList<AbstractView *> ViewManager::standardViews() const
&d->textureEditorView,
&d->statesEditorView,
&d->designerActionManagerView};
+#else
+ QList<AbstractView *> list = {&d->formEditorView,
+ &d->textEditorView,
+ &d->assetsLibraryView,
+ &d->itemLibraryView,
+ &d->navigatorView,
+ &d->propertyEditorView,
+ &d->statesEditorView,
+ &d->designerActionManagerView};
+#endif
if (enableModelEditor())
list.append(&d->collectionView);
@@ -384,16 +403,20 @@ QList<WidgetInfo> ViewManager::widgetInfos() const
{
QList<WidgetInfo> widgetInfoList;
+#ifndef QTC_USE_QML_DESIGNER_LITE
widgetInfoList.append(d->edit3DView.widgetInfo());
+#endif
widgetInfoList.append(d->formEditorView.widgetInfo());
widgetInfoList.append(d->textEditorView.widgetInfo());
widgetInfoList.append(d->assetsLibraryView.widgetInfo());
widgetInfoList.append(d->itemLibraryView.widgetInfo());
widgetInfoList.append(d->navigatorView.widgetInfo());
widgetInfoList.append(d->propertyEditorView.widgetInfo());
+#ifndef QTC_USE_QML_DESIGNER_LITE
widgetInfoList.append(d->materialEditorView.widgetInfo());
widgetInfoList.append(d->materialBrowserView.widgetInfo());
widgetInfoList.append(d->textureEditorView.widgetInfo());
+#endif
widgetInfoList.append(d->statesEditorView.widgetInfo());
if (enableModelEditor())
widgetInfoList.append(d->collectionView.widgetInfo());
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp
index 9e6bdd03b9..5c8d42a306 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp
@@ -32,9 +32,6 @@ ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundle
{
m_importTimer.setInterval(200);
connect(&m_importTimer, &QTimer::timeout, this, &ContentLibraryBundleImporter::handleImportTimer);
- m_moduleName = QStringLiteral("%1.%2").arg(
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER),
- m_bundleId).mid(1); // Chop leading slash
}
// Returns empty string on success or an error message on failure.
@@ -69,7 +66,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
qmldirContent.append("module ");
- qmldirContent.append(m_moduleName);
+ qmldirContent.append(moduleName());
qmldirContent.append('\n');
}
@@ -77,7 +74,9 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
const bool qmlFileExists = qmlSourceFile.exists();
const QString qmlType = qmlSourceFile.baseName();
const QString fullTypeName = QStringLiteral("%1.%2.%3")
- .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType);
+ .arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().componentBundlesTypePrefix(),
+ m_bundleId, qmlType);
if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName])
return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName);
if (!qmldirContent.contains(qmlFile)) {
@@ -126,7 +125,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
if (!model)
return "Model not available, cannot add import statement or update code model";
- Import import = Import::createLibraryImport(m_moduleName, "1.0");
+ Import import = Import::createLibraryImport(moduleName(), "1.0");
if (!model->hasImport(import)) {
if (model->possibleImports().contains(import)) {
m_importAddPending = false;
@@ -134,7 +133,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
model->changeImports({import}, {});
} catch (const RewritingException &) {
// No point in trying to add import asynchronously either, so just fail out
- return QStringLiteral("Failed to add import statement for: '%1'").arg(m_moduleName);
+ return QStringLiteral("Failed to add import statement for: '%1'").arg(moduleName());
}
} else {
// If import is not yet possible, import statement needs to be added asynchronously to
@@ -188,7 +187,7 @@ void ContentLibraryBundleImporter::handleImportTimer()
if (m_importAddPending) {
try {
- Import import = Import::createLibraryImport(m_moduleName, "1.0");
+ Import import = Import::createLibraryImport(moduleName(), "1.0");
if (model->possibleImports().contains(import)) {
model->changeImports({import}, {});
m_importAddPending = false;
@@ -253,6 +252,13 @@ void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundl
}
}
+QString ContentLibraryBundleImporter::moduleName()
+{
+ return QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().componentBundlesTypePrefix(),
+ m_bundleId);
+}
+
QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
{
FilePath bundleImportPath = resolveBundleImportPath();
@@ -275,7 +281,9 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
QString qmlType = qmlFilePath.baseName();
const QString fullTypeName = QStringLiteral("%1.%2.%3")
- .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType);
+ .arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().componentBundlesTypePrefix(),
+ m_bundleId, qmlType);
if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName])
return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName);
@@ -327,7 +335,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (model) {
- Import import = Import::createLibraryImport(m_moduleName, "1.0");
+ Import import = Import::createLibraryImport(moduleName(), "1.0");
if (model->imports().contains(import))
model->changeImports({}, {import});
}
@@ -342,16 +350,12 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
FilePath ContentLibraryBundleImporter::resolveBundleImportPath()
{
- FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
+ FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().componentBundlesBasePath();
if (bundleImportPath.isEmpty())
return bundleImportPath;
- const QString projectBundlePath = QStringLiteral("%1%2/%3").arg(
- QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER),
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER),
- m_bundleId).mid(1); // Chop leading slash
-
- return bundleImportPath.resolvePath(projectBundlePath);
+ return bundleImportPath.resolvePath(m_bundleId);
}
} // namespace QmlDesigner::Internal
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h
index 3aff09fe34..7fb2a48886 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h
@@ -46,10 +46,10 @@ private:
void handleImportTimer();
QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath);
void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap);
+ QString moduleName();
Utils::FilePath m_bundleDir;
QString m_bundleId;
- QString m_moduleName;
QStringList m_sharedFiles;
QTimer m_importTimer;
int m_importTimerCount = 0;
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp
index 6b1de2d2a7..334c017116 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp
@@ -7,8 +7,8 @@
#include "contentlibraryeffect.h"
#include "contentlibraryeffectscategory.h"
#include "contentlibrarywidget.h"
-#include "qmldesignerconstants.h"
+#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <utils/hostosinfo.h>
@@ -187,10 +187,11 @@ void ContentLibraryEffectsModel::loadBundle()
QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(itemObj.value("icon").toString()));
QString qml = itemObj.value("qml").toString();
- TypeName type = QLatin1String("%1.%2.%3").arg(
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1),
- bundleId,
- qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
+ TypeName type = QLatin1String("%1.%2.%3")
+ .arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().componentBundlesTypePrefix(),
+ bundleId,
+ qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
auto bundleItem = new ContentLibraryEffect(category, item, qml, type, icon, files);
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
index f546ea98cd..55af2accbd 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
@@ -3,9 +3,8 @@
#pragma once
-#include "qmldesignercorelib_global.h"
+#include "nodeinstanceglobal.h"
-#include <QDataStream>
#include <QObject>
#include <QUrl>
@@ -22,6 +21,7 @@ class ContentLibraryMaterial : public QObject
Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT)
Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT)
Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT)
+ Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
public:
ContentLibraryMaterial(QObject *parent,
@@ -31,7 +31,7 @@ public:
const QUrl &icon,
const QStringList &files,
const QString &downloadPath,
- const QString &baseWebUrl);
+ const QString &baseWebUrl = {});
bool filter(const QString &searchText);
@@ -66,6 +66,7 @@ private:
QString m_downloadPath;
QString m_baseWebUrl;
QStringList m_allFiles;
+ const QString m_itemType = "material";
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
index 7594c691b5..26747d359c 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
@@ -8,12 +8,12 @@
#include "contentlibrarymaterialscategory.h"
#include "contentlibrarywidget.h"
-#include <designerpaths.h>
+#include "designerpaths.h"
#include "filedownloader.h"
#include "fileextractor.h"
#include "multifiledownloader.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
+
+#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
@@ -275,9 +275,9 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir)
auto category = new ContentLibraryMaterialsCategory(this, cat);
const QJsonObject matsObj = catsObj.value(cat).toObject();
- const QStringList mats = matsObj.keys();
- for (const QString &mat : mats) {
- const QJsonObject matObj = matsObj.value(mat).toObject();
+ const QStringList matsNames = matsObj.keys();
+ for (const QString &matName : matsNames) {
+ const QJsonObject matObj = matsObj.value(matName).toObject();
QStringList files;
const QJsonArray assetsArr = matObj.value("files").toArray();
@@ -286,12 +286,13 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir)
QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString()));
QString qml = matObj.value("qml").toString();
- TypeName type = QLatin1String("%1.%2.%3").arg(
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1),
- bundleId,
- qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
+ TypeName type = QLatin1String("%1.%2.%3")
+ .arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().componentBundlesTypePrefix(),
+ bundleId,
+ qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
- auto bundleMat = new ContentLibraryMaterial(category, mat, qml, type, icon, files,
+ auto bundleMat = new ContentLibraryMaterial(category, matName, qml, type, icon, files,
m_downloadPath, m_baseUrl);
category->addBundleMaterial(bundleMat);
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
index 7ab239aab4..80dd7e816f 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
@@ -12,20 +12,19 @@
namespace QmlDesigner {
ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo,
- const QString &downloadPath, const QUrl &icon,
- const QString &key, const QString &webTextureUrl,
- const QString &webIconUrl, const QString &fileExt,
+ const QString &dirPath, const QString &suffix,
const QSize &dimensions, const qint64 sizeInBytes,
- bool hasUpdate, bool isNew)
+ const QString &key, const QString &textureUrl,
+ const QString &iconUrl, bool hasUpdate, bool isNew)
: QObject(parent)
, m_iconPath(iconFileInfo.filePath())
- , m_downloadPath(downloadPath)
- , m_webTextureUrl(webTextureUrl)
- , m_webIconUrl(webIconUrl)
+ , m_dirPath(dirPath)
+ , m_textureUrl(textureUrl)
+ , m_iconUrl(iconUrl)
, m_baseName{iconFileInfo.baseName()}
- , m_fileExt(fileExt)
+ , m_suffix(suffix)
, m_textureKey(key)
- , m_icon(icon)
+ , m_icon(QUrl::fromLocalFile(iconFileInfo.absoluteFilePath()))
, m_dimensions(dimensions)
, m_sizeInBytes(sizeInBytes)
, m_hasUpdate(hasUpdate)
@@ -54,9 +53,9 @@ QString ContentLibraryTexture::iconPath() const
return m_iconPath;
}
-QString ContentLibraryTexture::resolveFileExt()
+QString ContentLibraryTexture::resolveSuffix()
{
- const QFileInfoList files = QDir(m_downloadPath).entryInfoList(QDir::Files);
+ const QFileInfoList files = QDir(m_dirPath).entryInfoList(QDir::Files);
const QFileInfoList textureFiles = Utils::filtered(files, [this](const QFileInfo &fi) {
return fi.baseName() == m_baseName;
});
@@ -76,22 +75,20 @@ QString ContentLibraryTexture::resolveFileExt()
QString ContentLibraryTexture::resolveToolTipText()
{
- if (m_fileExt.isEmpty()) {
- // No supplied or resolved extension means we have just the icon and no other data
- return m_baseName;
- }
+ if (m_suffix.isEmpty())
+ return m_baseName; // empty suffix means we have just the icon and no other data
- QString fileName = m_baseName + m_fileExt;
+ QString fileName = m_baseName + m_suffix;
QString imageInfo;
if (!m_isDownloaded && m_sizeInBytes > 0 && !m_dimensions.isNull()) {
- imageInfo = ImageUtils::imageInfo(m_dimensions, m_sizeInBytes);
+ imageInfo = ImageUtils::imageInfoString(m_dimensions, m_sizeInBytes);
} else {
- QString fullDownloadPath = m_downloadPath + '/' + fileName;
- imageInfo = ImageUtils::imageInfo(fullDownloadPath);
+ QString fullDownloadPath = m_dirPath + '/' + fileName;
+ imageInfo = ImageUtils::imageInfoString(fullDownloadPath);
}
- return QStringLiteral("%1\n%2").arg(fileName, imageInfo);
+ return QString("%1\n%2").arg(fileName, imageInfo);
}
bool ContentLibraryTexture::isDownloaded() const
@@ -99,9 +96,9 @@ bool ContentLibraryTexture::isDownloaded() const
return m_isDownloaded;
}
-QString ContentLibraryTexture::downloadedTexturePath() const
+QString ContentLibraryTexture::texturePath() const
{
- return m_downloadPath + '/' + m_baseName + m_fileExt;
+ return m_dirPath + '/' + m_baseName + m_suffix;
}
void ContentLibraryTexture::setDownloaded()
@@ -116,16 +113,16 @@ void ContentLibraryTexture::setDownloaded()
void ContentLibraryTexture::doSetDownloaded()
{
- if (m_fileExt.isEmpty())
- m_fileExt = resolveFileExt();
+ if (m_suffix.isEmpty())
+ m_suffix = resolveSuffix();
- m_isDownloaded = QFileInfo::exists(downloadedTexturePath());
+ m_isDownloaded = QFileInfo::exists(texturePath());
m_toolTip = resolveToolTipText();
}
QString ContentLibraryTexture::parentDirPath() const
{
- return m_downloadPath;
+ return m_dirPath;
}
QString ContentLibraryTexture::textureKey() const
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
index 9f5b46630f..8f7197bc72 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
@@ -19,17 +19,18 @@ class ContentLibraryTexture : public QObject
Q_PROPERTY(QString textureToolTip MEMBER m_toolTip NOTIFY textureToolTipChanged)
Q_PROPERTY(QUrl textureIcon MEMBER m_icon CONSTANT)
Q_PROPERTY(bool textureVisible MEMBER m_visible NOTIFY textureVisibleChanged)
- Q_PROPERTY(QString textureWebUrl MEMBER m_webTextureUrl CONSTANT)
- Q_PROPERTY(QString textureWebIconUrl MEMBER m_webIconUrl CONSTANT)
+ Q_PROPERTY(QString textureUrl MEMBER m_textureUrl CONSTANT)
+ Q_PROPERTY(QString textureIconUrl MEMBER m_iconUrl CONSTANT)
Q_PROPERTY(bool textureHasUpdate WRITE setHasUpdate READ hasUpdate NOTIFY hasUpdateChanged)
Q_PROPERTY(bool textureIsNew MEMBER m_isNew CONSTANT)
Q_PROPERTY(QString textureKey MEMBER m_textureKey CONSTANT)
+ Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
public:
- ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &downloadPath,
- const QUrl &icon, const QString &key, const QString &webTextureUrl,
- const QString &webIconUrl, const QString &fileExt, const QSize &dimensions,
- const qint64 sizeInBytes, bool hasUpdate, bool isNew);
+ ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &dirPath,
+ const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes,
+ const QString &key = {}, const QString &textureUrl = {},
+ const QString &iconUrl = {}, bool hasUpdate = false, bool isNew = false);
Q_INVOKABLE bool isDownloaded() const;
Q_INVOKABLE void setDownloaded();
@@ -38,7 +39,7 @@ public:
QUrl icon() const;
QString iconPath() const;
- QString downloadedTexturePath() const;
+ QString texturePath() const;
QString parentDirPath() const;
QString textureKey() const;
@@ -51,17 +52,17 @@ signals:
void hasUpdateChanged();
private:
- QString resolveFileExt();
+ QString resolveSuffix();
QString resolveToolTipText();
void doSetDownloaded();
QString m_iconPath;
- QString m_downloadPath;
- QString m_webTextureUrl;
- QString m_webIconUrl;
+ QString m_dirPath;
+ QString m_textureUrl;
+ QString m_iconUrl;
QString m_toolTip;
QString m_baseName;
- QString m_fileExt;
+ QString m_suffix;
QString m_textureKey;
QUrl m_icon;
QSize m_dimensions;
@@ -71,6 +72,7 @@ private:
bool m_visible = true;
bool m_hasUpdate = false;
bool m_isNew = false;
+ const QString m_itemType = "texture";
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp
index 77519ad88f..0cafe8d138 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp
@@ -14,17 +14,15 @@ namespace QmlDesigner {
ContentLibraryTexturesCategory::ContentLibraryTexturesCategory(QObject *parent, const QString &name)
: QObject(parent), m_name(name) {}
-void ContentLibraryTexturesCategory::addTexture(const QFileInfo &tex, const QString &downloadPath,
+void ContentLibraryTexturesCategory::addTexture(const QFileInfo &texIcon, const QString &downloadPath,
const QString &key, const QString &webTextureUrl,
- const QString &webIconUrl, const QString &fileExt,
+ const QString &iconUrl, const QString &suffix,
const QSize &dimensions, const qint64 sizeInBytes,
bool hasUpdate, bool isNew)
{
- QUrl icon = QUrl::fromLocalFile(tex.absoluteFilePath());
-
m_categoryTextures.append(new ContentLibraryTexture(
- this, tex, downloadPath, icon, key, webTextureUrl, webIconUrl,
- fileExt, dimensions, sizeInBytes, hasUpdate, isNew));
+ this, texIcon, downloadPath, suffix, dimensions, sizeInBytes,
+ key, webTextureUrl, iconUrl, hasUpdate, isNew));
}
bool ContentLibraryTexturesCategory::filter(const QString &searchText)
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h
index 166528f05a..857346df06 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h
@@ -28,7 +28,7 @@ public:
ContentLibraryTexturesCategory(QObject *parent, const QString &name);
void addTexture(const QFileInfo &tex, const QString &subPath, const QString &key,
- const QString &webTextureUrl, const QString &webIconUrl, const QString &fileExt,
+ const QString &webTextureUrl, const QString &iconUrl, const QString &suffix,
const QSize &dimensions, const qint64 sizeInBytes, bool hasUpdate, bool isNew);
bool filter(const QString &searchText);
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp
index 319ca2686f..b575b6b9b2 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp
@@ -95,37 +95,37 @@ QHash<int, QByteArray> ContentLibraryTexturesModel::roleNames() const
/**
* @brief Load the bundle categorized icons. Actual textures are downloaded on demand
*
- * @param bundlePath local path to the bundle folder and icons
- * @param metaData bundle textures metadata
+ * @param textureBundleUrl remote url to the texture bundle
+ * @param bundleIconPath local path to the texture bundle icons folder
+ * @param jsonData bundle textures information from the bundle json
*/
-void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, const QString &iconsUrl,
+void ContentLibraryTexturesModel::loadTextureBundle(const QString &textureBundleUrl,
const QString &bundleIconPath,
- const QVariantMap &metaData)
+ const QVariantMap &jsonData)
{
if (!m_bundleCategories.isEmpty())
return;
QDir bundleDir = QString("%1/%2").arg(bundleIconPath, m_category);
- if (!bundleDir.exists()) {
- qWarning() << __FUNCTION__ << "textures bundle folder doesn't exist." << bundleDir.absolutePath();
- return;
- }
+ QTC_ASSERT(bundleDir.exists(), return);
- const QVariantMap imageItems = metaData.value("image_items").toMap();
+ const QVariantMap imageItems = jsonData.value("image_items").toMap();
const QFileInfoList dirs = bundleDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &dir : dirs) {
auto category = new ContentLibraryTexturesCategory(this, dir.fileName());
- const QFileInfoList texFiles = QDir(dir.filePath()).entryInfoList(QDir::Files);
- for (const QFileInfo &tex : texFiles) {
- QString textureUrl = QString("%1/%2/%3.zip").arg(remoteUrl, dir.fileName(), tex.baseName());
- QString iconUrl = QString("%1/%2/%3.png").arg(iconsUrl, dir.fileName(), tex.baseName());
-
- QString localDownloadPath = QString("%1/%2/%3")
+ const QFileInfoList texIconFiles = QDir(dir.filePath()).entryInfoList(QDir::Files);
+ for (const QFileInfo &texIcon : texIconFiles) {
+ QString textureUrl = QString("%1/%2/%3/%4.zip").arg(textureBundleUrl, m_category,
+ dir.fileName(), texIcon.baseName());
+ QString iconUrl = QString("%1/icons/%2/%3/%4.png").arg(textureBundleUrl, m_category,
+ dir.fileName(), texIcon.baseName());
+
+ QString texturePath = QString("%1/%2/%3")
.arg(Paths::bundlesPathSetting(),
m_category,
dir.fileName());
- QString key = QString("%1/%2/%3").arg(m_category, dir.fileName(), tex.baseName());
+ QString key = QString("%1/%2/%3").arg(m_category, dir.fileName(), texIcon.baseName());
QString fileExt;
QSize dimensions;
qint64 sizeInBytes = -1;
@@ -141,7 +141,7 @@ void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, co
isNew = m_newFiles.contains(key);
}
- category->addTexture(tex, localDownloadPath, key, textureUrl, iconUrl, fileExt,
+ category->addTexture(texIcon, texturePath, key, textureUrl, iconUrl, fileExt,
dimensions, sizeInBytes, hasUpdate, isNew);
}
m_bundleCategories.append(category);
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h
index 92db4151a8..94e223a251 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h
@@ -37,8 +37,8 @@ public:
void setHasSceneEnv(bool b);
void resetModel();
- void loadTextureBundle(const QString &remoteUrl, const QString &iconsUrl,
- const QString &bundlePath, const QVariantMap &metaData);
+ void loadTextureBundle(const QString &textureBundleUrl, const QString &bundlePath,
+ const QVariantMap &metaData);
signals:
void isEmptyChanged();
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp
new file mode 100644
index 0000000000..18d6e45fa8
--- /dev/null
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp
@@ -0,0 +1,423 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "contentlibraryusermodel.h"
+
+#include "contentlibrarybundleimporter.h"
+#include "contentlibrarymaterial.h"
+#include "contentlibrarymaterialscategory.h"
+#include "contentlibrarytexture.h"
+#include "contentlibrarywidget.h"
+
+#include <designerpaths.h>
+#include <imageutils.h>
+#include <qmldesignerplugin.h>
+
+#include <utils/algorithm.h>
+#include <utils/hostosinfo.h>
+#include <utils/qtcassert.h>
+
+#include <QCoreApplication>
+#include <QFileInfo>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QQmlEngine>
+#include <QStandardPaths>
+#include <QUrl>
+
+namespace QmlDesigner {
+
+ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent)
+ : QAbstractListModel(parent)
+ , m_widget(parent)
+{
+ m_userCategories = {tr("Materials"), tr("Textures")/*, tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO
+
+ loadMaterialBundle();
+ loadTextureBundle();
+}
+
+int ContentLibraryUserModel::rowCount(const QModelIndex &) const
+{
+ return m_userCategories.size();
+}
+
+QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const
+{
+ QTC_ASSERT(index.isValid() && index.row() < m_userCategories.size(), return {});
+ QTC_ASSERT(roleNames().contains(role), return {});
+
+ if (role == NameRole)
+ return m_userCategories.at(index.row());
+
+ if (role == ItemsRole) {
+ if (index.row() == 0)
+ return QVariant::fromValue(m_userMaterials);
+ if (index.row() == 1)
+ return QVariant::fromValue(m_userTextures);
+ if (index.row() == 2)
+ return QVariant::fromValue(m_user3DItems);
+ if (index.row() == 3)
+ return QVariant::fromValue(m_userEffects);
+ }
+
+ if (role == VisibleRole)
+ return true; // TODO
+
+ return {};
+}
+
+bool ContentLibraryUserModel::isValidIndex(int idx) const
+{
+ return idx > -1 && idx < rowCount();
+}
+
+void ContentLibraryUserModel::updateIsEmpty()
+{
+ bool anyMatVisible = Utils::anyOf(m_userMaterials, [&](ContentLibraryMaterial *mat) {
+ return mat->visible();
+ });
+
+ bool newEmpty = !anyMatVisible || !m_widget->hasMaterialLibrary() || !hasRequiredQuick3DImport();
+
+ if (newEmpty != m_isEmpty) {
+ m_isEmpty = newEmpty;
+ emit isEmptyChanged();
+ }
+}
+
+void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml,
+ const QUrl &icon, const QStringList &files)
+{
+ auto libMat = new ContentLibraryMaterial(this, name, qml, qmlToModule(qml), icon, files,
+ Paths::bundlesPathSetting().append("/User/materials"));
+
+ m_userMaterials.append(libMat);
+ int matSectionIdx = 0;
+ emit dataChanged(index(matSectionIdx), index(matSectionIdx));
+}
+
+void ContentLibraryUserModel::addTextures(const QStringList &paths)
+{
+ QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"};
+ bundleDir.mkpath(".");
+ bundleDir.mkdir("icons");
+
+ for (const QString &path : paths) {
+ QFileInfo fileInfo(path);
+ QString suffix = '.' + fileInfo.suffix();
+ auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png"));
+ QPair<QSize, qint64> info = ImageUtils::imageInfo(path);
+ QString dirPath = fileInfo.path();
+ QSize imgDims = info.first;
+ qint64 imgFileSize = info.second;
+
+ auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize);
+ m_userTextures.append(tex);
+ }
+
+ int texSectionIdx = 1;
+ emit dataChanged(index(texSectionIdx), index(texSectionIdx));
+}
+
+// returns unique library material's name and qml component
+QPair<QString, QString> ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const
+{
+ QTC_ASSERT(!m_bundleObj.isEmpty(), return {});
+
+ const QJsonObject matsObj = m_bundleObj.value("materials").toObject();
+ const QStringList matNames = matsObj.keys();
+
+ QStringList matQmls;
+ for (const QString &matName : matNames)
+ matQmls.append(matsObj.value(matName).toObject().value("qml").toString().chopped(4)); // remove .qml
+
+ QString retName = matName.isEmpty() ? "Material" : matName;
+ retName = retName.trimmed();
+
+ QString retQml = retName;
+ retQml.remove(' ');
+ if (retQml.at(0).isLower())
+ retQml[0] = retQml.at(0).toUpper();
+ retQml.prepend("My");
+
+ int num = 1;
+ if (matNames.contains(retName) || matQmls.contains(retQml)) {
+ while (matNames.contains(retName + QString::number(num))
+ || matQmls.contains(retQml + QString::number(num))) {
+ ++num;
+ }
+
+ retName += QString::number(num);
+ retQml += QString::number(num);
+ }
+
+ return {retName, retQml + ".qml"};
+}
+
+TypeName ContentLibraryUserModel::qmlToModule(const QString &qmlName) const
+{
+ return QLatin1String("%1.%2.%3").arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().componentBundlesTypePrefix(),
+ m_bundleId,
+ qmlName.chopped(4)).toLatin1(); // chopped(4): remove .qml
+}
+
+QHash<int, QByteArray> ContentLibraryUserModel::roleNames() const
+{
+ static const QHash<int, QByteArray> roles {
+ {NameRole, "categoryName"},
+ {VisibleRole, "categoryVisible"},
+ {ItemsRole, "categoryItems"}
+ };
+ return roles;
+}
+
+void ContentLibraryUserModel::createImporter(const QString &bundlePath, const QString &bundleId,
+ const QStringList &sharedFiles)
+{
+ m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles);
+#ifdef QDS_USE_PROJECTSTORAGE
+ connect(m_importer,
+ &Internal::ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::TypeName &typeName) {
+ m_importerRunning = false;
+ emit importerRunningChanged();
+ if (typeName.size())
+ emit bundleMaterialImported(typeName);
+ });
+#else
+ connect(m_importer,
+ &Internal::ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
+ m_importerRunning = false;
+ emit importerRunningChanged();
+ if (metaInfo.isValid())
+ emit bundleMaterialImported(metaInfo);
+ });
+#endif
+
+ connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
+ Q_UNUSED(metaInfo)
+ m_importerRunning = false;
+ emit importerRunningChanged();
+ emit bundleMaterialUnimported(metaInfo);
+ });
+
+ resetModel();
+ updateIsEmpty();
+}
+
+QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef()
+{
+ return m_bundleObj;
+}
+
+void ContentLibraryUserModel::loadMaterialBundle()
+{
+ if (m_matBundleExists)
+ return;
+
+ QDir bundleDir{Paths::bundlesPathSetting() + "/User/materials"};
+ bundleDir.mkpath(".");
+
+ if (m_bundleObj.isEmpty()) {
+ auto jsonFilePath = Utils::FilePath::fromString(bundleDir.filePath("user_materials_bundle.json"));
+ if (!jsonFilePath.exists()) {
+ QString jsonContent = "{\n";
+ jsonContent += " \"id\": \"UserMaterialBundle\",\n";
+ jsonContent += " \"materials\": {\n";
+ jsonContent += " }\n";
+ jsonContent += "}";
+ jsonFilePath.writeFileContents(jsonContent.toLatin1());
+ }
+
+ QFile jsonFile(jsonFilePath.path());
+ if (!jsonFile.open(QIODevice::ReadOnly)) {
+ qWarning("Couldn't open user_materials_bundle.json");
+ return;
+ }
+
+ QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(jsonFile.readAll());
+ if (matBundleJsonDoc.isNull()) {
+ qWarning("Invalid user_materials_bundle.json file");
+ return;
+ } else {
+ m_bundleObj = matBundleJsonDoc.object();
+ }
+ }
+
+ m_bundleId = m_bundleObj.value("id").toString();
+
+ // parse materials
+ const QJsonObject matsObj = m_bundleObj.value("materials").toObject();
+ const QStringList materialNames = matsObj.keys();
+ for (const QString &matName : materialNames) {
+ const QJsonObject matObj = matsObj.value(matName).toObject();
+
+ QStringList files;
+ const QJsonArray assetsArr = matObj.value("files").toArray();
+ for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr)
+ files.append(asset.toString());
+
+ QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString()));
+ QString qml = matObj.value("qml").toString();
+
+ TypeName type = qmlToModule(qml);
+
+ auto userMat = new ContentLibraryMaterial(this, matName, qml, type, icon, files,
+ bundleDir.path(), "");
+
+ m_userMaterials.append(userMat);
+ }
+
+ QStringList sharedFiles;
+ const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray();
+ for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
+ sharedFiles.append(file.toString());
+
+ createImporter(bundleDir.path(), m_bundleId, sharedFiles);
+
+ m_matBundleExists = true;
+ emit matBundleExistsChanged();
+}
+
+void ContentLibraryUserModel::loadTextureBundle()
+{
+ if (!m_userTextures.isEmpty())
+ return;
+
+ QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"};
+ bundleDir.mkpath(".");
+ bundleDir.mkdir("icons");
+
+ const QFileInfoList fileInfos = bundleDir.entryInfoList(QDir::Files);
+ for (const QFileInfo &fileInfo : fileInfos) {
+ QString suffix = '.' + fileInfo.suffix();
+ auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png"));
+ QPair<QSize, qint64> info = ImageUtils::imageInfo(fileInfo.path());
+ QString dirPath = fileInfo.path();
+ QSize imgDims = info.first;
+ qint64 imgFileSize = info.second;
+
+ auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize);
+ m_userTextures.append(tex);
+ }
+
+ int texSectionIdx = 1;
+ emit dataChanged(index(texSectionIdx), index(texSectionIdx));
+}
+
+bool ContentLibraryUserModel::hasRequiredQuick3DImport() const
+{
+ return m_widget->hasQuick3DImport() && m_quick3dMajorVersion == 6 && m_quick3dMinorVersion >= 3;
+}
+
+bool ContentLibraryUserModel::matBundleExists() const
+{
+ return m_matBundleExists;
+}
+
+Internal::ContentLibraryBundleImporter *ContentLibraryUserModel::bundleImporter() const
+{
+ return m_importer;
+}
+
+void ContentLibraryUserModel::setSearchText(const QString &searchText)
+{
+ QString lowerSearchText = searchText.toLower();
+
+ if (m_searchText == lowerSearchText)
+ return;
+
+ m_searchText = lowerSearchText;
+
+ for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials))
+ mat->filter(m_searchText);
+
+ updateIsEmpty();
+}
+
+void ContentLibraryUserModel::updateImportedState(const QStringList &importedMats)
+{
+ bool changed = false;
+
+ for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials))
+ changed |= mat->setImported(importedMats.contains(mat->qml().chopped(4)));
+
+ if (changed)
+ resetModel();
+}
+
+void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor)
+{
+ bool oldRequiredImport = hasRequiredQuick3DImport();
+
+ m_quick3dMajorVersion = major;
+ m_quick3dMinorVersion = minor;
+
+ bool newRequiredImport = hasRequiredQuick3DImport();
+
+ if (oldRequiredImport == newRequiredImport)
+ return;
+
+ emit hasRequiredQuick3DImportChanged();
+
+ updateIsEmpty();
+}
+
+void ContentLibraryUserModel::resetModel()
+{
+ beginResetModel();
+ endResetModel();
+}
+
+void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool add)
+{
+ emit applyToSelectedTriggered(mat, add);
+}
+
+void ContentLibraryUserModel::addToProject(ContentLibraryMaterial *mat)
+{
+ QString err = m_importer->importComponent(mat->qml(), mat->files());
+
+ if (err.isEmpty()) {
+ m_importerRunning = true;
+ emit importerRunningChanged();
+ } else {
+ qWarning() << __FUNCTION__ << err;
+ }
+}
+
+void ContentLibraryUserModel::removeFromProject(ContentLibraryMaterial *mat)
+{
+ emit bundleMaterialAboutToUnimport(mat->type());
+
+ QString err = m_importer->unimportComponent(mat->qml());
+
+ if (err.isEmpty()) {
+ m_importerRunning = true;
+ emit importerRunningChanged();
+ } else {
+ qWarning() << __FUNCTION__ << err;
+ }
+}
+
+bool ContentLibraryUserModel::hasModelSelection() const
+{
+ return m_hasModelSelection;
+}
+
+void ContentLibraryUserModel::setHasModelSelection(bool b)
+{
+ if (b == m_hasModelSelection)
+ return;
+
+ m_hasModelSelection = b;
+ emit hasModelSelectionChanged();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h
new file mode 100644
index 0000000000..3e9a96fd9d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h
@@ -0,0 +1,132 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "modelfwd.h"
+
+#include <QAbstractListModel>
+#include <QJsonObject>
+
+QT_FORWARD_DECLARE_CLASS(QUrl)
+
+namespace QmlDesigner {
+
+class ContentLibraryEffect;
+class ContentLibraryMaterial;
+class ContentLibraryTexture;
+class ContentLibraryWidget;
+class NodeMetaInfo;
+
+namespace Internal {
+class ContentLibraryBundleImporter;
+}
+
+class ContentLibraryUserModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged)
+ Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
+ Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged)
+ Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged)
+ Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged)
+ Q_PROPERTY(QList<ContentLibraryMaterial *> userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged)
+ Q_PROPERTY(QList<ContentLibraryTexture *> userTextures MEMBER m_userTextures NOTIFY userTexturesChanged)
+ Q_PROPERTY(QList<ContentLibraryEffect *> user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged)
+ Q_PROPERTY(QList<ContentLibraryEffect *> userEffects MEMBER m_userEffects NOTIFY userEffectsChanged)
+
+public:
+ ContentLibraryUserModel(ContentLibraryWidget *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QHash<int, QByteArray> roleNames() const override;
+
+ void setSearchText(const QString &searchText);
+ void updateImportedState(const QStringList &importedMats);
+
+ QPair<QString, QString> getUniqueLibMaterialNameAndQml(const QString &matName) const;
+ TypeName qmlToModule(const QString &qmlName) const;
+
+ void setQuick3DImportVersion(int major, int minor);
+
+ bool hasRequiredQuick3DImport() const;
+
+ bool matBundleExists() const;
+
+ bool hasModelSelection() const;
+ void setHasModelSelection(bool b);
+
+ void resetModel();
+ void updateIsEmpty();
+
+ void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files);
+ void addTextures(const QStringList &paths);
+
+ void setBundleObj(const QJsonObject &newBundleObj);
+ QJsonObject &bundleJsonObjectRef();
+
+ Internal::ContentLibraryBundleImporter *bundleImporter() const;
+
+ Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
+ Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat);
+ Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat);
+
+signals:
+ void isEmptyChanged();
+ void hasRequiredQuick3DImportChanged();
+ void hasModelSelectionChanged();
+ void userMaterialsChanged();
+ void userTexturesChanged();
+ void user3DItemsChanged();
+ void userEffectsChanged();
+
+ void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ void bundleMaterialImported(const QmlDesigner::TypeName &typeName);
+#else
+ void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo);
+#endif
+ void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type);
+ void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo);
+ void importerRunningChanged();
+ void matBundleExistsChanged();
+
+private:
+ void loadMaterialBundle();
+ void loadTextureBundle();
+ bool isValidIndex(int idx) const;
+ void createImporter(const QString &bundlePath, const QString &bundleId,
+ const QStringList &sharedFiles);
+
+ ContentLibraryWidget *m_widget = nullptr;
+ QString m_searchText;
+ QString m_bundleId;
+
+ QList<ContentLibraryMaterial *> m_userMaterials;
+ QList<ContentLibraryTexture *> m_userTextures;
+ QList<ContentLibraryEffect *> m_userEffects;
+ QList<ContentLibraryEffect *> m_user3DItems;
+ QStringList m_userCategories;
+
+ QJsonObject m_bundleObj;
+ Internal::ContentLibraryBundleImporter *m_importer = nullptr;
+
+ bool m_isEmpty = true;
+ bool m_matBundleExists = false;
+ bool m_hasModelSelection = false;
+ bool m_importerRunning = false;
+
+ int m_quick3dMajorVersion = -1;
+ int m_quick3dMinorVersion = -1;
+
+ QString m_importerBundlePath;
+ QString m_importerBundleId;
+ QStringList m_importerSharedFiles;
+
+ enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole };
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
index 61ae078ea8..dd8a4d9919 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
@@ -3,6 +3,8 @@
#include "contentlibraryview.h"
+#include "asset.h"
+#include "bindingproperty.h"
#include "contentlibrarybundleimporter.h"
#include "contentlibraryeffect.h"
#include "contentlibraryeffectsmodel.h"
@@ -10,13 +12,17 @@
#include "contentlibrarymaterialsmodel.h"
#include "contentlibrarytexture.h"
#include "contentlibrarytexturesmodel.h"
+#include "contentlibraryusermodel.h"
#include "contentlibrarywidget.h"
+#include "documentmanager.h"
#include "externaldependenciesinterface.h"
#include "nodelistproperty.h"
#include "qmldesignerconstants.h"
#include "qmlobjectnode.h"
#include "variantproperty.h"
-#include <utils3d.h>
+#include "utils3d.h"
+
+#include <designerpaths.h>
#include <coreplugin/messagebox.h>
#include <enumeration.h>
@@ -30,6 +36,10 @@
#include <qtsupport/qtkitaspect.h>
#endif
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QPixmap>
#include <QVector3D>
namespace QmlDesigner {
@@ -204,6 +214,8 @@ WidgetInfo ContentLibraryView::widgetInfo()
connect(effectsModel, &ContentLibraryEffectsModel::bundleItemUnimported, this,
&ContentLibraryView::updateBundleEffectsImportedState);
+
+ connectUserBundle();
}
return createWidgetInfo(m_widget.data(),
@@ -213,6 +225,64 @@ WidgetInfo ContentLibraryView::widgetInfo()
tr("Content Library"));
}
+void ContentLibraryView::connectUserBundle()
+{
+ ContentLibraryUserModel *userModel = m_widget->userModel().data();
+
+ connect(userModel,
+ &ContentLibraryUserModel::applyToSelectedTriggered,
+ this,
+ [&](ContentLibraryMaterial *bundleMat, bool add) {
+ if (m_selectedModels.isEmpty())
+ return;
+
+ m_bundleMaterialTargets = m_selectedModels;
+ m_bundleMaterialAddToSelected = add;
+
+ ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
+ if (defaultMat.isValid())
+ applyBundleMaterialToDropTarget(defaultMat);
+ else
+ m_widget->userModel()->addToProject(bundleMat);
+ });
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ connect(userModel,
+ &ContentLibraryUserModel::bundleMaterialImported,
+ this,
+ [&](const QmlDesigner::TypeName &typeName) {
+ applyBundleMaterialToDropTarget({}, typeName);
+ updateBundleUserMaterialsImportedState();
+ });
+#else
+ connect(userModel,
+ &ContentLibraryUserModel::bundleMaterialImported,
+ this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
+ applyBundleMaterialToDropTarget({}, metaInfo);
+ updateBundleUserMaterialsImportedState();
+ });
+#endif
+
+ connect(userModel, &ContentLibraryUserModel::bundleMaterialAboutToUnimport, this,
+ [&] (const QmlDesigner::TypeName &type) {
+ // delete instances of the bundle material that is about to be unimported
+ executeInTransaction("ContentLibraryView::connectUserModel", [&] {
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
+ if (!matLib.isValid())
+ return;
+
+ Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) {
+ if (mat.isValid() && mat.type() == type)
+ QmlObjectNode(mat).destroy();
+ });
+ });
+ });
+
+ connect(userModel, &ContentLibraryUserModel::bundleMaterialUnimported, this,
+ &ContentLibraryView::updateBundleUserMaterialsImportedState);
+}
+
void ContentLibraryView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
@@ -276,6 +346,7 @@ void ContentLibraryView::selectedNodesChanged(const QList<ModelNode> &selectedNo
});
m_widget->materialsModel()->setHasModelSelection(!m_selectedModels.isEmpty());
+ m_widget->userModel()->setHasModelSelection(!m_selectedModels.isEmpty());
}
void ContentLibraryView::customNotification(const AbstractView *view,
@@ -283,8 +354,6 @@ void ContentLibraryView::customNotification(const AbstractView *view,
const QList<ModelNode> &nodeList,
const QList<QVariant> &data)
{
- Q_UNUSED(data)
-
if (view == this)
return;
@@ -324,6 +393,12 @@ void ContentLibraryView::customNotification(const AbstractView *view,
m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant();
m_widget->effectsModel()->addInstance(m_draggedBundleEffect);
m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this);
+ } else if (identifier == "add_material_to_content_lib") {
+ QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return);
+
+ addLibMaterial(nodeList.first(), data.first().value<QPixmap>());
+ } else if (identifier == "add_assets_to_content_lib") {
+ addLibAssets(data.first().toStringList());
}
}
@@ -452,6 +527,163 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle
}
#endif
+// Add a project material to Content Library's user tab
+void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &icon)
+{
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/");
+
+ auto [name, qml] = m_widget->userModel()->getUniqueLibMaterialNameAndQml(
+ mat.variantProperty("objectName").value().toString());
+
+ bundlePath.pathAppended("icons").createDir();
+ bundlePath.pathAppended("images").createDir();
+ bundlePath.pathAppended("shaders").createDir();
+
+ QString iconPath = QLatin1String("icons/%1.png").arg(mat.id());
+ QString fullIconPath = bundlePath.pathAppended(iconPath).toString();
+
+ // save icon
+ bool iconSaved = icon.save(fullIconPath);
+ if (!iconSaved)
+ qWarning() << __FUNCTION__ << "icon save failed";
+
+ // generate and save material Qml file
+ const QStringList depAssets = writeLibMaterialQml(mat, qml);
+
+ // add the material to the bundle json
+ QJsonObject &jsonRef = m_widget->userModel()->bundleJsonObjectRef();
+ QJsonObject matsObj = jsonRef.value("materials").toObject();
+ QJsonObject matObj;
+ matObj.insert("qml", qml);
+ matObj.insert("icon", iconPath);
+ QJsonArray filesArr;
+ for (const QString &assetPath : depAssets)
+ filesArr.append(assetPath);
+ matObj.insert("files", filesArr);
+
+ matsObj.insert(name, matObj);
+ jsonRef.insert("materials", matsObj);
+ auto result = bundlePath.pathAppended("user_materials_bundle.json")
+ .writeFileContents(QJsonDocument(jsonRef).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ // copy material assets to bundle folder
+ for (const QString &assetPath : depAssets) {
+ Asset asset(assetPath);
+ QString subDir;
+ if (asset.isImage())
+ subDir = "images";
+ else if (asset.isShader())
+ subDir = "shaders";
+
+ Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath);
+ Utils::FilePath assetPathTarget = bundlePath.pathAppended(QString("%1/%2")
+ .arg(subDir, "/" + asset.fileName()));
+
+ auto result = assetPathSource.copyFile(assetPathTarget);
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+ }
+
+ m_widget->userModel()->addMaterial(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets);
+}
+
+QStringList ContentLibraryView::writeLibMaterialQml(const ModelNode &mat, const QString &qml)
+{
+ QStringList depListIds;
+ auto [qmlString, assets] = modelNodeToQmlString(mat, depListIds);
+
+ qmlString.prepend("import QtQuick\nimport QtQuick3D\n\n");
+
+ auto qmlPath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/" + qml);
+ auto result = qmlPath.writeFileContents(qmlString.toUtf8());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ return assets.values();
+}
+
+QPair<QString, QSet<QString>> ContentLibraryView::modelNodeToQmlString(const ModelNode &node,
+ QStringList &depListIds,
+ int depth)
+{
+ QString qml;
+ QSet<QString> assets;
+
+ QString indent = QString(" ").repeated(depth * 4);
+
+ qml += indent + node.simplifiedTypeName() + " {\n";
+
+ indent = QString(" ").repeated((depth + 1) * 4);
+
+ qml += indent + "id: " + (depth == 0 ? "root" : node.id()) + " \n\n";
+
+ const QList<AbstractProperty> matProps = node.properties();
+ for (const AbstractProperty &p : matProps) {
+ if (p.isVariantProperty()) {
+ QVariant pValue = p.toVariantProperty().value();
+ QString val;
+ if (strcmp(pValue.typeName(), "QString") == 0 || strcmp(pValue.typeName(), "QColor") == 0) {
+ val = QLatin1String("\"%1\"").arg(pValue.toString());
+ } else if (strcmp(pValue.typeName(), "QUrl") == 0) {
+ val = QLatin1String("\"%1\"").arg(pValue.toString());
+ assets.insert(pValue.toString());
+ } else if (strcmp(pValue.typeName(), "QmlDesigner::Enumeration") == 0) {
+ val = pValue.value<QmlDesigner::Enumeration>().toString();
+ } else {
+ val = pValue.toString();
+ }
+
+ qml += indent + p.name() + ": " + val + "\n";
+ } else if (p.isBindingProperty()) {
+ qml += indent + p.name() + ": " + p.toBindingProperty().expression() + "\n";
+
+ ModelNode depNode = modelNodeForId(p.toBindingProperty().expression());
+
+ if (depNode && !depListIds.contains(depNode.id())) {
+ depListIds.append(depNode.id());
+ auto [depQml, depAssets] = modelNodeToQmlString(depNode, depListIds, depth + 1);
+ qml += "\n" + depQml + "\n";
+ assets.unite(depAssets);
+ }
+ }
+ }
+
+ indent = QString(" ").repeated(depth * 4);
+
+ qml += indent + "}\n";
+
+ return {qml, assets};
+}
+
+void ContentLibraryView::addLibAssets(const QStringList &paths)
+{
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/textures");
+ QStringList pathsInBundle;
+
+ for (const QString &path : paths) {
+ Asset asset(path);
+ auto assetPath = Utils::FilePath::fromString(path);
+
+ // save icon
+ QString iconSavePath = bundlePath.pathAppended("icons/" + assetPath.baseName() + ".png").toString();
+ QPixmap icon = asset.pixmap({120, 120});
+ bool iconSaved = icon.save(iconSavePath);
+ if (!iconSaved)
+ qWarning() << __FUNCTION__ << "icon save failed";
+
+ // save asset
+ auto result = assetPath.copyFile(bundlePath.pathAppended(asset.fileName()));
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ pathsInBundle.append(bundlePath.pathAppended(asset.fileName()).toString());
+ }
+
+ m_widget->userModel()->addTextures(pathsInBundle);
+}
+
ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type)
{
ModelNode matLib = Utils3D::materialLibraryNode(this);
@@ -477,6 +709,7 @@ ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &t
return {};
}
+
#ifdef QDS_USE_PROJECTSTORAGE
ModelNode ContentLibraryView::createMaterial(const TypeName &typeName)
{
@@ -548,6 +781,25 @@ void ContentLibraryView::updateBundleMaterialsImportedState()
m_widget->materialsModel()->updateImportedState(importedBundleMats);
}
+void ContentLibraryView::updateBundleUserMaterialsImportedState()
+{
+ using namespace Utils;
+
+ if (!m_widget->userModel()->bundleImporter())
+ return;
+
+ QStringList importedBundleMats;
+
+ FilePath bundlePath = m_widget->userModel()->bundleImporter()->resolveBundleImportPath();
+
+ if (bundlePath.exists()) {
+ importedBundleMats = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}),
+ [](const FilePath &f) { return f.fileName().chopped(4); });
+ }
+
+ m_widget->userModel()->updateImportedState(importedBundleMats);
+}
+
void ContentLibraryView::updateBundleEffectsImportedState()
{
using namespace Utils;
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
index 3b57b7a4ab..03d42fa8bc 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
@@ -10,6 +10,8 @@
#include <QObject>
#include <QPointer>
+QT_FORWARD_DECLARE_CLASS(QPixmap)
+
namespace QmlDesigner {
class ContentLibraryEffect;
@@ -46,10 +48,18 @@ public:
const QVariant &data) override;
private:
+ void connectUserBundle();
void active3DSceneChanged(qint32 sceneId);
void updateBundleMaterialsImportedState();
+ void updateBundleUserMaterialsImportedState();
void updateBundleEffectsImportedState();
void updateBundlesQuick3DVersion();
+ void addLibMaterial(const ModelNode &mat, const QPixmap &icon);
+ void addLibAssets(const QStringList &paths);
+ QStringList writeLibMaterialQml(const ModelNode &mat, const QString &qml);
+ QPair<QString, QSet<QString>> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds,
+ int depth = 0);
+
#ifdef QDS_USE_PROJECTSTORAGE
void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const TypeName &typeName = {});
#else
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
index c885a76ba7..9375d43fd4 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
@@ -10,6 +10,7 @@
#include "contentlibrarytexture.h"
#include "contentlibrarytexturesmodel.h"
#include "contentlibraryiconprovider.h"
+#include "contentlibraryusermodel.h"
#include "utils/filedownloader.h"
#include "utils/fileextractor.h"
@@ -100,10 +101,10 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
&& m_textureToDrag->isDownloaded()) {
QMimeData *mimeData = new QMimeData;
mimeData->setData(Constants::MIME_TYPE_BUNDLE_TEXTURE,
- {m_textureToDrag->downloadedTexturePath().toUtf8()});
+ {m_textureToDrag->texturePath().toUtf8()});
// Allows standard file drag-n-drop. As of now needed to drop on Assets view
- mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->downloadedTexturePath())});
+ mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->texturePath())});
emit bundleTextureDragStarted(m_textureToDrag);
model->startDrag(mimeData, m_textureToDrag->icon().toLocalFile());
@@ -126,6 +127,7 @@ ContentLibraryWidget::ContentLibraryWidget()
, m_texturesModel(new ContentLibraryTexturesModel("Textures", this))
, m_environmentsModel(new ContentLibraryTexturesModel("Environments", this))
, m_effectsModel(new ContentLibraryEffectsModel(this))
+ , m_userModel(new ContentLibraryUserModel(this))
{
qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader");
qmlRegisterType<QmlDesigner::FileExtractor>("WebFetcher", 1, 0, "FileExtractor");
@@ -140,18 +142,12 @@ ContentLibraryWidget::ContentLibraryWidget()
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground));
- m_baseUrl = QmlDesignerPlugin::settings()
- .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString()
- + "/textures";
+ m_textureBundleUrl = QmlDesignerPlugin::settings()
+ .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() + "/textures";
- m_texturesUrl = m_baseUrl + "/Textures";
- m_textureIconsUrl = m_baseUrl + "/icons/Textures";
- m_environmentIconsUrl = m_baseUrl + "/icons/Environments";
- m_environmentsUrl = m_baseUrl + "/Environments";
+ m_bundlePath = Paths::bundlesPathSetting();
- m_downloadPath = Paths::bundlesPathSetting();
-
- loadTextureBundle();
+ loadTextureBundles();
Theme::setupTheme(m_quickWidget->engine());
m_quickWidget->quickWidget()->installEventFilter(this);
@@ -177,38 +173,34 @@ ContentLibraryWidget::ContentLibraryWidget()
{"materialsModel", QVariant::fromValue(m_materialsModel.data())},
{"texturesModel", QVariant::fromValue(m_texturesModel.data())},
{"environmentsModel", QVariant::fromValue(m_environmentsModel.data())},
- {"effectsModel", QVariant::fromValue(m_effectsModel.data())}});
+ {"effectsModel", QVariant::fromValue(m_effectsModel.data())},
+ {"userModel", QVariant::fromValue(m_userModel.data())}});
reloadQmlSource();
}
-QVariantMap ContentLibraryWidget::readBundleMetadata()
+QVariantMap ContentLibraryWidget::readTextureBundleJson()
{
- QVariantMap metaData;
- QFile jsonFile(m_downloadPath + "/texture_bundle.json");
+ QVariantMap jsonData;
+ QFile jsonFile(m_bundlePath + "/texture_bundle.json");
if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text))
- metaData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap();
+ jsonData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap();
- int version = metaData["version"].toInt();
+ int version = jsonData["version"].toInt();
if (version > TextureBundleMetadataVersion) {
qWarning() << "Unrecognized texture metadata file version: " << version;
return {};
}
- return metaData;
+ return jsonData;
}
-void ContentLibraryWidget::loadTextureBundle()
+void ContentLibraryWidget::loadTextureBundles()
{
- QDir bundleDir{m_downloadPath};
+ QDir bundleDir{m_bundlePath};
- if (fetchTextureBundleMetadata(bundleDir) && fetchTextureBundleIcons(bundleDir)) {
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
- }
+ if (fetchTextureBundleJson(bundleDir) && fetchTextureBundleIcons(bundleDir))
+ populateTextureBundleModels();
}
std::tuple<QVariantMap, QVariantMap, QVariantMap> ContentLibraryWidget::compareTextureMetaFiles(
@@ -272,9 +264,9 @@ void ContentLibraryWidget::fetchNewTextureIcons(const QVariantMap &existingFiles
});
auto multidownloader = new MultiFileDownloader(this);
- multidownloader->setBaseUrl(QString(m_baseUrl + "/icons"));
+ multidownloader->setBaseUrl(QString(m_textureBundleUrl + "/icons"));
multidownloader->setFiles(fileList);
- multidownloader->setTargetDirPath(m_downloadPath + "/TextureBundleIcons");
+ multidownloader->setTargetDirPath(m_bundlePath + "/TextureBundleIcons");
auto downloader = new FileDownloader(this);
downloader->setDownloadEnabled(true);
@@ -314,15 +306,8 @@ void ContentLibraryWidget::fetchNewTextureIcons(const QVariantMap &existingFiles
existingFile.flush();
}
- if (fetchTextureBundleIcons(bundleDir)) {
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath,
- metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
- }
-
+ if (fetchTextureBundleIcons(bundleDir))
+ populateTextureBundleModels();
});
multidownloader->start();
@@ -433,50 +418,45 @@ QStringList ContentLibraryWidget::saveNewTextures(const QDir &bundleDir, const Q
}
}
-bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir)
+bool ContentLibraryWidget::fetchTextureBundleJson(const QDir &bundleDir)
{
QString filePath = bundleDir.filePath("texture_bundle.json");
QFileInfo fi(filePath);
- bool metaFileExists = fi.exists() && fi.size() > 0;
+ bool jsonFileExists = fi.exists() && fi.size() > 0;
- QString metaFileUrl = m_baseUrl + "/texture_bundle.zip";
+ QString bundleZipUrl = m_textureBundleUrl + "/texture_bundle.zip";
FileDownloader *downloader = new FileDownloader(this);
- downloader->setUrl(metaFileUrl);
+ downloader->setUrl(bundleZipUrl);
downloader->setProbeUrl(false);
downloader->setDownloadEnabled(true);
+ downloader->start();
QObject::connect(downloader, &FileDownloader::downloadFailed, this,
- [this, metaFileExists, bundleDir] {
- if (metaFileExists) {
- if (fetchTextureBundleIcons(bundleDir)) {
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath,
- metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
- }
+ [this, jsonFileExists, bundleDir] {
+ if (jsonFileExists) {
+ if (fetchTextureBundleIcons(bundleDir))
+ populateTextureBundleModels();
}
});
QObject::connect(downloader, &FileDownloader::finishedChanged, this,
- [this, downloader, bundleDir, metaFileExists, filePath] {
+ [this, downloader, bundleDir, jsonFileExists, filePath] {
FileExtractor *extractor = new FileExtractor(this);
extractor->setArchiveName(downloader->completeBaseName());
extractor->setSourceFile(downloader->outputFile());
- if (!metaFileExists)
+ if (!jsonFileExists)
extractor->setTargetPath(bundleDir.absolutePath());
extractor->setAlwaysCreateDir(false);
extractor->setClearTargetPathContents(false);
QObject::connect(extractor, &FileExtractor::finishedChanged, this,
- [this, downloader, bundleDir, extractor, metaFileExists, filePath] {
+ [this, downloader, bundleDir, extractor, jsonFileExists, filePath] {
downloader->deleteLater();
extractor->deleteLater();
- if (metaFileExists) {
+ if (jsonFileExists) {
QVariantMap newFiles, existing;
QVariantMap modifiedFilesEntries;
@@ -498,32 +478,35 @@ bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir)
}
}
- if (fetchTextureBundleIcons(bundleDir)) {
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath,
- metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
- }
+ if (fetchTextureBundleIcons(bundleDir))
+ populateTextureBundleModels();
});
extractor->extract();
});
- downloader->start();
return false;
}
+void ContentLibraryWidget::populateTextureBundleModels()
+{
+ QVariantMap jsonData = readTextureBundleJson();
+
+ QString bundleIconPath = m_bundlePath + "/TextureBundleIcons";
+
+ m_texturesModel->loadTextureBundle(m_textureBundleUrl, bundleIconPath, jsonData);
+ m_environmentsModel->loadTextureBundle(m_textureBundleUrl, bundleIconPath, jsonData);
+}
+
bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir)
{
QString iconsPath = bundleDir.filePath("TextureBundleIcons");
QDir iconsDir(iconsPath);
- if (iconsDir.exists() && iconsDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).length() > 0)
+ if (iconsDir.exists() && !iconsDir.isEmpty())
return true;
- QString zipFileUrl = m_baseUrl + "/icons.zip";
+ QString zipFileUrl = m_textureBundleUrl + "/icons.zip";
FileDownloader *downloader = new FileDownloader(this);
downloader->setUrl(zipFileUrl);
@@ -543,13 +526,7 @@ bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir)
[this, downloader, extractor] {
downloader->deleteLater();
extractor->deleteLater();
-
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath,
- metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
+ populateTextureBundleModels();
});
extractor->extract();
@@ -572,7 +549,7 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey)
checksumOnServer = m_environmentsModel->removeModifiedFileEntry(textureKey);
QJsonObject metaDataObj;
- QFile jsonFile(m_downloadPath + "/texture_bundle.json");
+ QFile jsonFile(m_bundlePath + "/texture_bundle.json");
if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
metaDataObj = QJsonDocument::fromJson(jsonFile.readAll()).object();
jsonFile.close();
@@ -589,7 +566,7 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey)
QJsonDocument outDoc(metaDataObj);
QByteArray data = outDoc.toJson();
- QFile outFile(m_downloadPath + "/texture_bundle.json");
+ QFile outFile(m_bundlePath + "/texture_bundle.json");
if (outFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text)) {
outFile.write(data);
outFile.flush();
@@ -601,6 +578,12 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey)
m_environmentsModel->markTextureHasNoUpdates(subcategory, textureKey);
}
+bool ContentLibraryWidget::userBundleEnabled() const
+{
+ // TODO: this method is to be removed after user bundle implementation is complete
+ return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool();
+}
+
QSize ContentLibraryWidget::sizeHint() const
{
return {420, 420};
@@ -715,6 +698,7 @@ void ContentLibraryWidget::updateSearch()
m_effectsModel->setSearchText(m_filterText);
m_texturesModel->setSearchText(m_filterText);
m_environmentsModel->setSearchText(m_filterText);
+ m_userModel->setSearchText(m_filterText);
m_quickWidget->update();
}
@@ -777,7 +761,7 @@ void ContentLibraryWidget::addImage(ContentLibraryTexture *tex)
if (!tex->isDownloaded())
return;
- emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Image);
+ emit addTextureRequested(tex->texturePath(), AddTextureMode::Image);
}
void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex)
@@ -785,7 +769,7 @@ void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex)
if (!tex->isDownloaded())
return;
- emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Texture);
+ emit addTextureRequested(tex->texturePath(), AddTextureMode::Texture);
}
void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex)
@@ -793,7 +777,7 @@ void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex)
if (!tex->isDownloaded())
return;
- emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::LightProbe);
+ emit addTextureRequested(tex->texturePath(), AddTextureMode::LightProbe);
}
void ContentLibraryWidget::updateSceneEnvState()
@@ -821,4 +805,9 @@ QPointer<ContentLibraryEffectsModel> ContentLibraryWidget::effectsModel() const
return m_effectsModel;
}
+QPointer<ContentLibraryUserModel> ContentLibraryWidget::userModel() const
+{
+ return m_userModel;
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
index ab71a3dc79..c4d51d0362 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
@@ -24,6 +24,7 @@ class ContentLibraryMaterial;
class ContentLibraryMaterialsModel;
class ContentLibraryTexture;
class ContentLibraryTexturesModel;
+class ContentLibraryUserModel;
class ContentLibraryWidget : public QFrame
{
@@ -65,6 +66,7 @@ public:
QPointer<ContentLibraryTexturesModel> texturesModel() const;
QPointer<ContentLibraryTexturesModel> environmentsModel() const;
QPointer<ContentLibraryEffectsModel> effectsModel() const;
+ QPointer<ContentLibraryUserModel> userModel() const;
Q_INVOKABLE void startDragEffect(QmlDesigner::ContentLibraryEffect *eff, const QPointF &mousePos);
Q_INVOKABLE void startDragMaterial(QmlDesigner::ContentLibraryMaterial *mat, const QPointF &mousePos);
@@ -74,6 +76,7 @@ public:
Q_INVOKABLE void addLightProbe(QmlDesigner::ContentLibraryTexture *tex);
Q_INVOKABLE void updateSceneEnvState();
Q_INVOKABLE void markTextureUpdated(const QString &textureKey);
+ Q_INVOKABLE bool userBundleEnabled() const;
QSize sizeHint() const override;
@@ -97,21 +100,23 @@ private:
void updateSearch();
void setIsDragging(bool val);
QString findTextureBundlePath();
- void loadTextureBundle();
- QVariantMap readBundleMetadata();
- bool fetchTextureBundleMetadata(const QDir &bundleDir);
+ void loadTextureBundles();
+ QVariantMap readTextureBundleJson();
+ bool fetchTextureBundleJson(const QDir &bundleDir);
bool fetchTextureBundleIcons(const QDir &bundleDir);
void fetchNewTextureIcons(const QVariantMap &existingFiles, const QVariantMap &newFiles,
const QString &existingMetaFilePath, const QDir &bundleDir);
std::tuple<QVariantMap, QVariantMap, QVariantMap> compareTextureMetaFiles(
const QString &existingMetaFile, const QString downloadedMetaFile);
QStringList saveNewTextures(const QDir &bundleDir, const QStringList &newFiles);
+ void populateTextureBundleModels();
QScopedPointer<StudioQuickWidget> m_quickWidget;
QPointer<ContentLibraryMaterialsModel> m_materialsModel;
QPointer<ContentLibraryTexturesModel> m_texturesModel;
QPointer<ContentLibraryTexturesModel> m_environmentsModel;
QPointer<ContentLibraryEffectsModel> m_effectsModel;
+ QPointer<ContentLibraryUserModel> m_userModel;
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
@@ -127,12 +132,8 @@ private:
bool m_hasQuick3DImport = false;
bool m_isDragging = false;
bool m_isQt6Project = false;
- QString m_baseUrl;
- QString m_texturesUrl;
- QString m_textureIconsUrl;
- QString m_environmentIconsUrl;
- QString m_environmentsUrl;
- QString m_downloadPath;
+ QString m_textureBundleUrl;
+ QString m_bundlePath;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp
index b6e99ae972..56056e3fd2 100644
--- a/src/plugins/qmldesigner/components/createtexture.cpp
+++ b/src/plugins/qmldesigner/components/createtexture.cpp
@@ -17,6 +17,7 @@
#include <coreplugin/messagebox.h>
#include <QTimer>
+#include <QUrl>
namespace QmlDesigner {
@@ -94,7 +95,7 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat
newTexNode.setIdWithoutRefactoring(m_view->model()->generateNewId(assetPath.baseName()));
VariantProperty sourceProp = newTexNode.variantProperty("source");
- sourceProp.setValue(textureSource);
+ sourceProp.setValue(QUrl(textureSource));
matLib.defaultNodeListProperty().reparentHere(newTexNode);
}
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
index e84623e26c..36ce3373e5 100644
--- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
@@ -77,8 +77,8 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
connect(m_toolbar, &CurveEditorToolBar::currentFrameChanged, [this, model](int frame) {
model->setCurrentFrame(frame);
+ m_view->setCurrentFrame(frame, false);
updateStatusLine();
- m_view->viewport()->update();
});
connect(m_toolbar, &CurveEditorToolBar::zoomChanged, [this](double zoom) {
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
index 72410ffb07..8da87ce119 100644
--- a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
@@ -129,6 +129,8 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent)
m_currentSpin->setFrame(false);
connect(m_currentSpin, &QSpinBox::valueChanged, this, &CurveEditorToolBar::currentFrameChanged);
+ connect(model, &CurveEditorModel::commitCurrentFrame,
+ this, [this](int frame) { m_currentSpin->setValue(frame); });
addSpacer();
diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp
index 95a260c26f..a1dfcc8f98 100644
--- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp
@@ -15,6 +15,8 @@
#include "qmlobjectnode.h"
#include "variantproperty.h"
+#include <model/modelutils.h>
+
#include <utils3d.h>
#include <utils/expected.h>
@@ -292,7 +294,7 @@ bool BakeLightsDataModel::reset()
if (!hasExposedProps && node.metaInfo().isFileComponent()
&& node.metaInfo().isQtQuick3DNode()) {
- const QString compFile = node.metaInfo().componentFileName();
+ const QString compFile = ModelUtils::componentFilePath(node);
const QString projPath = m_view->externalDependencies().currentProjectDirPath();
if (compFile.startsWith(projPath)) {
// Quick and dirty scan of the component source to check if it potentially has
diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp
index 76560ac192..f5a74ee864 100644
--- a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp
@@ -66,9 +66,11 @@ void CameraSpeedConfiguration::resetDefaults()
void CameraSpeedConfiguration::hideCursor()
{
- if (QGuiApplication::overrideCursor())
+ if (m_cursorHidden)
return;
+ m_cursorHidden = true;
+
QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
if (QWindow *w = QGuiApplication::focusWindow())
@@ -77,9 +79,11 @@ void CameraSpeedConfiguration::hideCursor()
void CameraSpeedConfiguration::restoreCursor()
{
- if (!QGuiApplication::overrideCursor())
+ if (!m_cursorHidden)
return;
+ m_cursorHidden = false;
+
QGuiApplication::restoreOverrideCursor();
if (QWindow *w = QGuiApplication::focusWindow())
@@ -88,7 +92,7 @@ void CameraSpeedConfiguration::restoreCursor()
void CameraSpeedConfiguration::holdCursorInPlace()
{
- if (!QGuiApplication::overrideCursor())
+ if (!m_cursorHidden)
return;
if (QWindow *w = QGuiApplication::focusWindow())
diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h
index 55256890cb..fef06efc49 100644
--- a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h
+++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h
@@ -71,6 +71,7 @@ private:
double m_multiplier = 0.;
bool m_changes = false;
QPoint m_lastPos;
+ bool m_cursorHidden = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
index 911ee1fb15..a20904b2e5 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
@@ -279,6 +279,14 @@ void Edit3DView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
+ QString currProjectPath = QmlDesigner::DocumentManager::currentProjectDirPath().toString();
+ if (m_currProjectPath != currProjectPath) {
+ // Opening a new project -> reset camera speeds
+ m_currProjectPath = currProjectPath;
+ m_previousCameraSpeed = -1.;
+ m_previousCameraMultiplier = -1.;
+ }
+
syncSnapAuxPropsToSettings();
rootModelNode().setAuxiliaryData(edit3dGridColorProperty,
@@ -356,7 +364,12 @@ void Edit3DView::handleEntriesChanged()
append(model()->qtQuick3DOrthographicCameraMetaInfo(), EK_cameras);
append(model()->qtQuick3DPerspectiveCameraMetaInfo(), EK_cameras);
- auto assetsModule = model()->module("Quick3DAssets");
+ Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance()
+ ->documentManager()
+ .generatedComponentUtils()
+ .import3dTypePrefix();
+
+ auto assetsModule = model()->module(import3dTypePrefix);
for (const auto &metaInfo : model()->metaInfosForModule(assetsModule))
append(metaInfo, EK_importedModels);
@@ -373,7 +386,8 @@ void Edit3DView::handleEntriesChanged()
} else if (entry.typeName() == "QtQuick3D.OrthographicCamera"
|| entry.typeName() == "QtQuick3D.PerspectiveCamera") {
entryKey = EK_cameras;
- } else if (entry.typeName().startsWith("Quick3DAssets.")
+ } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().import3dTypePrefix().toUtf8())
&& NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) {
entryKey = EK_importedModels;
} else {
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
index 781b26d8d8..fad87aae1f 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
@@ -192,6 +192,7 @@ private:
double m_previousCameraSpeed = -1.;
double m_previousCameraMultiplier = -1.;
+ QString m_currProjectPath;
friend class Edit3DAction;
};
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
index 07102ae893..6f1cf2e183 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
@@ -766,7 +766,11 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
QString fileName = QFileInfo(assetPath).baseName();
fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter
auto model = m_view->model();
- auto metaInfo = model->metaInfo(model->module("Quick3DAssets"), fileName.toUtf8());
+ Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance()
+ ->documentManager()
+ .generatedComponentUtils()
+ .import3dTypePrefix();
+ auto metaInfo = model->metaInfo(model->module(import3dTypePrefix), fileName.toUtf8());
if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) {
auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()};
QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false);
@@ -780,7 +784,9 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
for (const QString &assetPath : added3DAssets) {
QString fileName = QFileInfo(assetPath).baseName();
fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter
- QString type = QString("Quick3DAssets.%1.%1").arg(fileName);
+ QString type = QString("%1.%2.%2").arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().import3dTypePrefix(),
+ fileName);
QList<ItemLibraryEntry> entriesForType = itemLibInfo->entriesForType(type.toUtf8());
if (!entriesForType.isEmpty()) { // should always be true, but just in case
QmlVisualNode::createQml3DNode(view(),
diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp
index 26c5fe0eb2..8890ea8964 100644
--- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp
@@ -87,9 +87,11 @@ void SnapConfiguration::resetDefaults()
void SnapConfiguration::hideCursor()
{
- if (QGuiApplication::overrideCursor())
+ if (m_cursorHidden)
return;
+ m_cursorHidden = true;
+
QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
if (QWindow *w = QGuiApplication::focusWindow())
@@ -98,9 +100,11 @@ void SnapConfiguration::hideCursor()
void SnapConfiguration::restoreCursor()
{
- if (!QGuiApplication::overrideCursor())
+ if (!m_cursorHidden)
return;
+ m_cursorHidden = false;
+
QGuiApplication::restoreOverrideCursor();
if (QWindow *w = QGuiApplication::focusWindow())
@@ -109,7 +113,7 @@ void SnapConfiguration::restoreCursor()
void SnapConfiguration::holdCursorInPlace()
{
- if (!QGuiApplication::overrideCursor())
+ if (!m_cursorHidden)
return;
if (QWindow *w = QGuiApplication::focusWindow())
diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h
index 729e6ce7d1..ae401f60f9 100644
--- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h
+++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h
@@ -103,6 +103,7 @@ private:
double m_scaleInterval = 0.;
bool m_changes = false;
QPoint m_lastPos;
+ bool m_cursorHidden = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp
index c0bebbb82b..804ac076e6 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp
@@ -64,13 +64,14 @@ namespace QmlDesigner {
DesignDocument acts as a facade to a model representing a qml document,
and the different views/widgets accessing it.
*/
-DesignDocument::DesignDocument(ProjectStorageDependencies projectStorageDependencies,
+DesignDocument::DesignDocument([[maybe_unused]] const QUrl &filePath,
+ ProjectStorageDependencies projectStorageDependencies,
ExternalDependenciesInterface &externalDependencies)
#ifdef QDS_USE_PROJECTSTORAGE
: m_documentModel(Model::create(projectStorageDependencies,
"Item",
{Import::createLibraryImport("QtQuick")},
- {},
+ filePath,
std::make_unique<ModelResourceManagement>()))
#else
: m_documentModel(
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h
index 0d75141205..52089d67c1 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.h
+++ b/src/plugins/qmldesigner/components/integration/designdocument.h
@@ -41,7 +41,8 @@ class QMLDESIGNERCOMPONENTS_EXPORT DesignDocument : public QObject
Q_OBJECT
public:
- DesignDocument(ProjectStorageDependencies projectStorageDependencies,
+ DesignDocument(const QUrl &filePath,
+ ProjectStorageDependencies projectStorageDependencies,
ExternalDependenciesInterface &externalDependencies);
~DesignDocument() override;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
index 29fff4b359..2c69072602 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
@@ -12,6 +12,7 @@
#include <variantproperty.h>
#include <theme.h>
+#include <utils/filepath.h>
#include <utils/outputformatter.h>
#include <projectexplorer/project.h>
@@ -130,43 +131,8 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
if (targetDir.isEmpty())
targetDir = defaulTargetDirectory;
- // Import is always done under known folder. The order of preference for folder is:
- // 1) An existing QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path
- // 2) An existing QUICK_3D_ASSETS_FOLDER under any project import path
- // 3) New QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path
- // 4) New QUICK_3D_ASSETS_FOLDER under any project import path
- // 5) New QUICK_3D_ASSETS_FOLDER under new DEFAULT_ASSET_IMPORT_FOLDER under project
- const QString defaultAssetFolder = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER);
- const QString quick3DFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
- QString candidatePath = targetDir + defaultAssetFolder + quick3DFolder;
- int candidatePriority = 5;
-
- for (const auto &importPath : std::as_const(importPaths)) {
- if (importPath.startsWith(targetDir)) {
- const bool isDefaultFolder = importPath.endsWith(defaultAssetFolder);
- const QString assetFolder = importPath + quick3DFolder;
- const bool exists = QFileInfo::exists(assetFolder);
- if (exists) {
- if (isDefaultFolder) {
- // Priority one location, stop looking
- candidatePath = assetFolder;
- break;
- } else if (candidatePriority > 2) {
- candidatePriority = 2;
- candidatePath = assetFolder;
- }
- } else {
- if (candidatePriority > 3 && isDefaultFolder) {
- candidatePriority = 3;
- candidatePath = assetFolder;
- } else if (candidatePriority > 4) {
- candidatePriority = 4;
- candidatePath = assetFolder;
- }
- }
- }
- }
- m_quick3DImportPath = candidatePath;
+ m_quick3DImportPath = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().import3dBasePath().toString();
if (!m_quick3DFiles.isEmpty()) {
QVector<QJsonObject> groups;
@@ -294,11 +260,14 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
QFileInfo compFileInfo{compFileName};
// Find to top asset folder
- const QString assetFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER).mid(1);
+ const QString oldAssetFolder = QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER);
+ QString assetFolder = QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER);
const QStringList parts = compFileName.split('/');
int i = parts.size() - 1;
int previousSize = 0;
for (; i >= 0; --i) {
+ if (parts[i] == oldAssetFolder)
+ assetFolder = oldAssetFolder;
if (parts[i] == assetFolder)
break;
previousSize = parts[i].size();
@@ -733,8 +702,10 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
// and move the remaining member to ungrouped options
// Note: <= 2 instead of < 2 because each group has group label member
if (i != 0 && groupWidgets.size() <= 2) {
- widgets[0].prepend(groupWidgets[1]);
- groupWidgets[0].first->hide(); // hide group label
+ if (groupWidgets.size() == 2)
+ widgets[0].prepend(groupWidgets[1]);
+ if (groupWidgets.size() >= 1)
+ groupWidgets[0].first->hide(); // hide group label
groupWidgets.clear();
}
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
index 48958ceec9..ed1f8041e9 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
@@ -1,6 +1,7 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "itemlibraryassetimporter.h"
+
#include "assetimportupdatedialog.h"
#include "qmldesignerplugin.h"
#include "qmldesignerconstants.h"
@@ -329,12 +330,15 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd)
if (qmldirFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
QString qmlInfo;
qmlInfo.append("module ");
- qmlInfo.append(m_importPath.split('/').last());
+ qmlInfo.append(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().import3dTypePrefix());
qmlInfo.append(".");
qmlInfo.append(pd.assetName);
qmlInfo.append('\n');
m_requiredImports.append(
- QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), pd.assetName));
+ QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().import3dTypePrefix(),
+ pd.assetName));
while (qmlIt.hasNext()) {
qmlIt.next();
QFileInfo fi = QFileInfo(qmlIt.filePath());
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index dbeacc7595..3bff210520 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -296,15 +296,16 @@ void ItemLibraryModel::setSearchText(const QString &searchText)
Import ItemLibraryModel::entryToImport(const ItemLibraryEntry &entry)
{
+#ifndef QDS_USE_PROJECTSTORAGE
if (entry.majorVersion() == -1 && entry.minorVersion() == -1)
return Import::createFileImport(entry.requiredImport());
-
+#endif
return Import::createLibraryImport(entry.requiredImport(), QString::number(entry.majorVersion()) + QLatin1Char('.') +
QString::number(entry.minorVersion()));
}
-void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, Model *model)
+void ItemLibraryModel::update(Model *model)
{
if (!model)
return;
@@ -312,9 +313,12 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo,
beginResetModel();
clearSections();
+ GeneratedComponentUtils compUtils = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils();
+
QStringList excludedImports {
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".MaterialBundle",
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".EffectBundle"
+ compUtils.componentBundlesTypePrefix() + ".MaterialBundle",
+ compUtils.componentBundlesTypePrefix() + ".EffectBundle"
};
// create import sections
@@ -323,10 +327,12 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo,
QHash<QString, ItemLibraryImport *> importHash;
for (const Import &import : model->imports()) {
if (import.url() != projectName) {
- if (excludedImports.contains(import.url()) || import.url().startsWith("Effects."))
+ if (excludedImports.contains(import.url())
+ || import.url().startsWith(compUtils.composedEffectsTypePrefix())) {
continue;
+ }
bool addNew = true;
- bool isQuick3DAsset = import.url().startsWith("Quick3DAssets.");
+ bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix());
QString importUrl = import.url();
if (isQuick3DAsset)
importUrl = ItemLibraryImport::quick3DAssetsTitle();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
index 212ddf8e04..b04ea492d2 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
@@ -37,7 +37,7 @@ public:
QString searchText() const;
ItemLibraryImport *importByUrl(const QString &importName) const;
- void update(ItemLibraryInfo *itemLibraryInfo, Model *model);
+ void update(Model *model);
void updateUsedImports(const Imports &usedImports);
QMimeData *getMimeData(const ItemLibraryEntry &itemLibraryEntry);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
index b710a8226f..ffefc43178 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
@@ -118,9 +118,9 @@ void ItemLibraryWidget::resizeEvent(QResizeEvent *event)
ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache)
: m_itemIconSize(24, 24)
- , m_itemLibraryModel(new ItemLibraryModel(this))
- , m_addModuleModel(new ItemLibraryAddImportModel(this))
- , m_itemsWidget(new StudioQuickWidget(this))
+ , m_itemLibraryModel(std::make_unique<ItemLibraryModel>())
+ , m_addModuleModel(std::make_unique<ItemLibraryAddImportModel>())
+ , m_itemsWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>())
, m_imageCache{imageCache}
{
m_compressionTimer.setInterval(1000);
@@ -146,7 +146,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache)
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
layout->setSpacing(0);
- layout->addWidget(m_itemsWidget.data());
+ layout->addWidget(m_itemsWidget.get());
updateSearch();
@@ -167,8 +167,8 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache)
auto map = m_itemsWidget->registerPropertyMap("ItemLibraryBackend");
- map->setProperties({{"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.data())},
- {"addModuleModel", QVariant::fromValue(m_addModuleModel.data())},
+ map->setProperties({{"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.get())},
+ {"addModuleModel", QVariant::fromValue(m_addModuleModel.get())},
{"itemLibraryIconWidth", m_itemIconSize.width()},
{"itemLibraryIconHeight", m_itemIconSize.height()},
{"rootView", QVariant::fromValue(this)},
@@ -333,9 +333,7 @@ void ItemLibraryWidget::updateModel()
m_compressionTimer.stop();
}
-#ifndef QDS_USE_PROJECTSTORAGE
- m_itemLibraryModel->update(m_itemLibraryInfo.data(), m_model.data());
-#endif
+ m_itemLibraryModel->update(m_model.data());
if (m_itemLibraryModel->rowCount() == 0 && !m_updateRetry) {
m_updateRetry = true; // Only retry once to avoid endless loops
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
index b56532b218..2940db7a73 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
@@ -10,9 +10,10 @@
#include <studioquickwidget.h>
-#include <utils/fancylineedit.h>
-#include <utils/dropsupport.h>
#include <previewtooltip/previewtooltipbackend.h>
+#include <utils/dropsupport.h>
+#include <utils/fancylineedit.h>
+#include <utils/uniqueobjectptr.h>
#include <QFileIconProvider>
#include <QFrame>
@@ -104,10 +105,10 @@ private:
#ifndef QDS_USE_PROJECTSTORAGE
QPointer<ItemLibraryInfo> m_itemLibraryInfo;
#endif
- QPointer<ItemLibraryModel> m_itemLibraryModel;
- QPointer<ItemLibraryAddImportModel> m_addModuleModel;
+ std::unique_ptr<ItemLibraryModel> m_itemLibraryModel;
+ std::unique_ptr<ItemLibraryAddImportModel> m_addModuleModel;
- QScopedPointer<StudioQuickWidget> m_itemsWidget;
+ Utils::UniqueObjectPtr<StudioQuickWidget> m_itemsWidget;
std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut;
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp
index ec95f3e5d3..918956a04c 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp
@@ -63,7 +63,7 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role)
return tr("Texture has no source image.");
ModelNode texNode = m_textureList.at(index.row());
- QString info = ImageUtils::imageInfo(source);
+ QString info = ImageUtils::imageInfoString(source);
if (info.isEmpty())
return tr("Texture has no data.");
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
index 47a1e8d293..7cf0a875bc 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
@@ -56,6 +56,13 @@ public:
m_pixmaps.insert(node.internalId(), pixmap);
}
+ QPixmap getPixmap(const ModelNode &node)
+ {
+ QTC_ASSERT(node, return {});
+
+ return m_pixmaps.value(node.internalId());
+ }
+
void clearPixmapCache()
{
m_pixmaps.clear();
@@ -357,6 +364,13 @@ void MaterialBrowserWidget::focusMaterialSection(bool focusMatSec)
}
}
+void MaterialBrowserWidget::addMaterialToContentLibrary()
+{
+ ModelNode mat = m_materialBrowserModel->selectedMaterial();
+ m_materialBrowserView->emitCustomNotification("add_material_to_content_lib", {mat},
+ {m_previewImageProvider->getPixmap(mat)});
+}
+
QString MaterialBrowserWidget::qmlSourcesPath()
{
#ifdef SHARE_QML_PATH
@@ -420,4 +434,10 @@ QPointer<MaterialBrowserTexturesModel> MaterialBrowserWidget::materialBrowserTex
return m_materialBrowserTexturesModel;
}
+bool MaterialBrowserWidget::userBundleEnabled() const
+{
+ // TODO: this method is to be removed after user bundle implementation is complete
+ return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool();
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
index bfe7ace34d..97c994e565 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
@@ -60,6 +60,8 @@ public:
Q_INVOKABLE void acceptAssetsDropOnMaterial(int matIndex, const QList<QUrl> &urls);
Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId);
Q_INVOKABLE void focusMaterialSection(bool focusMatSec);
+ Q_INVOKABLE void addMaterialToContentLibrary();
+ Q_INVOKABLE bool userBundleEnabled() const;
StudioQuickWidget *quickWidget() const;
diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
index 58b4c42749..a3ab5f2cd7 100644
--- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
+++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
@@ -5,6 +5,8 @@
#include "nodemetainfo.h"
#include "ui_choosefrompropertylistdialog.h"
+#include <qmldesignerplugin.h>
+
namespace QmlDesigner {
// This will filter and return possible properties that the given type can be bound to
@@ -100,7 +102,10 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
#ifdef QDS_USE_PROJECTSTORAGE
// TODO add the types here or use the module
#else
- } else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) {
+ } else if (insertInfo.typeName().startsWith(
+ QString("%1.MaterialBundle").arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().componentBundlesTypePrefix())
+ .toUtf8())) {
if (parentInfo.isQtQuick3DModel())
propertyList.append("materials");
#endif
diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
index aacaf6dc0d..223e04fd96 100644
--- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
+++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
@@ -9,9 +9,8 @@
#include "navigatortreeview.h"
#include "navigatorwidget.h"
#include "choosefrompropertylistdialog.h"
-#include "qproxystyle.h"
-
#include <model/modelutils.h>
+#include <dialogutils.h>
#include <modelnodecontextmenu.h>
#include <theme.h>
#include <qmldesignerconstants.h>
@@ -169,8 +168,7 @@ static void setId(const QModelIndex &index, const QString &newId)
return;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"),
- NavigatorTreeView::tr("%1 is an invalid id.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
} else if (modelNode.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"),
NavigatorTreeView::tr("%1 already exists.").arg(newId));
diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp
index 45f89ae339..fdd58c77a3 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp
@@ -143,7 +143,7 @@ QString DynamicPropertiesProxyModel::newPropertyName() const
{
DynamicPropertiesModel *propsModel = dynamicPropertiesModel();
- return QString::fromUtf8(uniquePropertyName("property", propsModel->singleSelectedNode()));
+ return QString::fromUtf8(uniquePropertyName("newName", propsModel->singleSelectedNode()));
}
void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type)
@@ -167,6 +167,10 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr
QVariant value = defaultValueForType(typeName);
VariantProperty variantProp = modelNode.variantProperty(name.toUtf8());
variantProp.setDynamicTypeNameAndValue(typeName, value);
+ } else if (type == "signal") {
+ SignalDeclarationProperty signalDeclarationProperty
+ = modelNode.signalDeclarationProperty(name.toUtf8());
+ signalDeclarationProperty.setSignature("()");
} else {
QString expression = defaultExpressionForType(typeName);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
index 7f1ab00bb9..6f3242a18e 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
@@ -284,6 +284,27 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml
}
}
+void PropertyEditorQmlBackend::handleInstancePropertyChangedInModelNodeProxy(
+ const ModelNode &modelNode, const PropertyName &propertyName)
+{
+ m_backendModelNode.handleInstancePropertyChanged(modelNode, propertyName);
+}
+
+void PropertyEditorQmlBackend::handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property)
+{
+ m_backendModelNode.handleVariantPropertyChanged(property);
+}
+
+void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property)
+{
+ m_backendModelNode.handleBindingPropertyChanged(property);
+}
+
+void PropertyEditorQmlBackend::handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property)
+{
+ m_backendModelNode.handlePropertiesRemoved(property);
+}
+
void PropertyEditorQmlBackend::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode,
const PropertyName &name,
const QVariant &value,
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
index b677258488..d4e158fca4 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
@@ -3,10 +3,10 @@
#pragma once
-#include "qmlanchorbindingproxy.h"
#include "designerpropertymap.h"
-#include "propertyeditorvalue.h"
#include "propertyeditorcontextobject.h"
+#include "propertyeditorvalue.h"
+#include "qmlanchorbindingproxy.h"
#include "qmlmodelnodeproxy.h"
#include "quick2propertyeditorview.h"
@@ -71,7 +71,15 @@ public:
PropertyEditorView *propertyEditor);
void setupInsightAttachedProperties(const QmlObjectNode &qmlObjectNode,
PropertyEditorView *propertyEditor);
- void setupAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, PropertyEditorView *propertyEditor);
+ void setupAuxiliaryProperties(const QmlObjectNode &qmlObjectNode,
+ PropertyEditorView *propertyEditor);
+
+ void handleInstancePropertyChangedInModelNodeProxy(const ModelNode &modelNode,
+ const PropertyName &propertyName);
+
+ void handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property);
+ void handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property);
+ void handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property);
static NodeMetaInfo findCommonAncestor(const ModelNode &node);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
index 663ebafb65..70686f31ae 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
@@ -10,9 +10,12 @@
#include "designmodewidget.h"
#include "nodemetainfo.h"
#include "nodeproperty.h"
+#include "propertyeditorview.h"
+#include "qmldesignerplugin.h"
#include "qmlitemnode.h"
#include "qmlobjectnode.h"
-#include "qmldesignerplugin.h"
+#include "rewritertransaction.h"
+#include "rewritingexception.h"
#include <enumeration.h>
@@ -153,7 +156,8 @@ void PropertyEditorValue::setExpressionWithEmit(const QString &expression)
if (m_expression != expression) {
setExpression(expression);
m_value.clear();
- emit expressionChanged(nameAsQString()); // Note that we set the name in this case
+ emit expressionChanged(nameAsQString());
+ emit expressionChangedQml();// Note that we set the name in this case
}
}
@@ -180,7 +184,7 @@ bool PropertyEditorValue::isInSubState() const
bool PropertyEditorValue::isBound() const
{
const QmlObjectNode objectNode(modelNode());
- return objectNode.isValid() && objectNode.hasBindingProperty(name());
+ return m_forceBound || (objectNode.isValid() && objectNode.hasBindingProperty(name()));
}
bool PropertyEditorValue::isInModel() const
@@ -334,6 +338,7 @@ void PropertyEditorValue::resetValue()
m_expression = QString();
emit valueChanged(nameAsQString(), QVariant());
emit expressionChanged({});
+ emit expressionChangedQml();
}
}
@@ -425,6 +430,34 @@ QStringList PropertyEditorValue::getExpressionAsList() const
return generateStringList(expression());
}
+QVector<double> PropertyEditorValue::getExpressionAsVector() const
+{
+ const QRegularExpression rx(
+ QRegularExpression::anchoredPattern("Qt.vector(2|3|4)d\\((.*?)\\)"));
+ const QRegularExpressionMatch match = rx.match(expression());
+ if (!match.hasMatch())
+ return {};
+
+ const QStringList floats = match.captured(2).split(',', Qt::SkipEmptyParts);
+
+ bool ok;
+
+ const int num = match.captured(1).toInt();
+
+ if (num != floats.count())
+ return {};
+
+ QVector<double> ret;
+ for (const QString &number : floats) {
+ ret.append(number.toDouble(&ok));
+
+ if (!ok)
+ return {};
+ }
+
+ return ret;
+}
+
bool PropertyEditorValue::idListAdd(const QString &value)
{
const QmlObjectNode objectNode(modelNode());
@@ -507,6 +540,15 @@ void PropertyEditorValue::openMaterialEditor(int idx)
m_modelNode.view()->emitCustomNotification("select_material", {}, {idx});
}
+void PropertyEditorValue::setForceBound(bool b)
+{
+ if (m_forceBound == b)
+ return;
+ m_forceBound = b;
+
+ emit isBoundChanged();
+}
+
QStringList PropertyEditorValue::generateStringList(const QString &string) const
{
QString copy = string;
@@ -687,4 +729,260 @@ void PropertyEditorNodeWrapper::update()
emit typeChanged();
}
+QQmlPropertyMap *PropertyEditorSubSelectionWrapper::properties()
+{
+ return &m_valuesPropertyMap;
+}
+
+static QObject *variantToQObject(const QVariant &value)
+{
+ if (value.typeId() == QMetaType::QObjectStar || value.typeId() > QMetaType::User)
+ return *(QObject **)value.constData();
+
+ return nullptr;
+}
+
+void PropertyEditorSubSelectionWrapper::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode,
+ const PropertyName &name,
+ const QVariant &value)
+{
+ PropertyName propertyName(name);
+ propertyName.replace('.', '_');
+ auto valueObject = qobject_cast<PropertyEditorValue*>(variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(propertyName))));
+ if (!valueObject) {
+ valueObject = new PropertyEditorValue(&m_valuesPropertyMap);
+ QObject::connect(valueObject, &PropertyEditorValue::valueChanged, this, &PropertyEditorSubSelectionWrapper::changeValue);
+ QObject::connect(valueObject, &PropertyEditorValue::expressionChanged, this, &PropertyEditorSubSelectionWrapper::changeExpression);
+ QObject::connect(valueObject, &PropertyEditorValue::exportPropertyAsAliasRequested, this, &PropertyEditorSubSelectionWrapper::exportPropertyAsAlias);
+ QObject::connect(valueObject, &PropertyEditorValue::removeAliasExportRequested, this, &PropertyEditorSubSelectionWrapper::removeAliasExport);
+ m_valuesPropertyMap.insert(QString::fromUtf8(propertyName), QVariant::fromValue(valueObject));
+ }
+ valueObject->setName(name);
+ valueObject->setModelNode(qmlObjectNode);
+
+ if (qmlObjectNode.propertyAffectedByCurrentState(name) && !(qmlObjectNode.modelNode().property(name).isBindingProperty()))
+ valueObject->setValue(qmlObjectNode.modelValue(name));
+
+ else
+ valueObject->setValue(value);
+
+ if (propertyName != "id" &&
+ qmlObjectNode.currentState().isBaseState() &&
+ qmlObjectNode.modelNode().property(propertyName).isBindingProperty()) {
+ valueObject->setExpression(qmlObjectNode.modelNode().bindingProperty(propertyName).expression());
+ } else {
+ if (qmlObjectNode.hasBindingProperty(name))
+ valueObject->setExpression(qmlObjectNode.expression(name));
+ else
+ valueObject->setExpression(qmlObjectNode.instanceValue(name).toString());
+ }
+}
+
+void PropertyEditorSubSelectionWrapper::exportPropertyAsAlias(const QString &name)
+{
+ if (name.isNull())
+ return;
+
+ if (locked())
+ return;
+
+ QTC_ASSERT(m_modelNode.isValid(), return);
+
+ view()->executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){
+ PropertyEditorView::generateAliasForProperty(m_modelNode, name);
+ });
+}
+
+void PropertyEditorSubSelectionWrapper::removeAliasExport(const QString &name)
+{
+ if (name.isNull())
+ return;
+
+ if (locked())
+ return;
+
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ view()->executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name]() {
+ PropertyEditorView::removeAliasForProperty(m_modelNode, name);
+ });
+}
+
+bool PropertyEditorSubSelectionWrapper::locked() const
+{
+ return m_locked;
+}
+
+PropertyEditorSubSelectionWrapper::PropertyEditorSubSelectionWrapper(const ModelNode &modelNode)
+ : m_modelNode(modelNode)
+{
+ QmlObjectNode qmlObjectNode(modelNode);
+
+ QTC_ASSERT(qmlObjectNode.isValid(), return );
+
+ for (const auto &property : qmlObjectNode.modelNode().metaInfo().properties()) {
+ auto propertyName = property.name();
+ createPropertyEditorValue(qmlObjectNode,
+ propertyName,
+ qmlObjectNode.instanceValue(propertyName));
+ }
+}
+
+ModelNode PropertyEditorSubSelectionWrapper::modelNode() const
+{
+ return m_modelNode;
+}
+
+void PropertyEditorSubSelectionWrapper::deleteModelNode()
+{
+ QmlObjectNode objectNode(m_modelNode);
+
+ view()->executeInTransaction("PropertyEditorView::changeExpression", [&] {
+ if (objectNode.isValid())
+ objectNode.destroy();
+ });
+}
+
+void PropertyEditorSubSelectionWrapper::changeValue(const QString &name)
+{
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ if (name.isNull())
+ return;
+
+ if (locked())
+ return;
+
+ const QScopeGuard cleanup([&] { m_locked = false; });
+ m_locked = true;
+
+ const NodeMetaInfo metaInfo = m_modelNode.metaInfo();
+ QVariant castedValue;
+ PropertyEditorValue *value = qobject_cast<PropertyEditorValue *>(
+ variantToQObject(m_valuesPropertyMap.value(name)));
+
+ if (auto property = metaInfo.property(name.toUtf8())) {
+ castedValue = property.castedValue(value->value());
+
+ if (castedValue.typeId() == QVariant::Color) {
+ QColor color = castedValue.value<QColor>();
+ QColor newColor = QColor(color.name());
+ newColor.setAlpha(color.alpha());
+ castedValue = QVariant(newColor);
+ }
+
+ if (!value->value().isValid()) { // reset
+ removePropertyFromModel(name.toUtf8());
+ } else {
+ if (castedValue.isValid())
+ commitVariantValueToModel(name.toUtf8(), castedValue);
+ }
+ }
+}
+
+void PropertyEditorSubSelectionWrapper::setValueFromModel(const PropertyName &name,
+ const QVariant &value)
+{
+ m_locked = true;
+
+ QmlObjectNode qmlObjectNode(m_modelNode);
+
+ PropertyName propertyName = name;
+ propertyName.replace('.', '_');
+ auto propertyValue = qobject_cast<PropertyEditorValue *>(
+ variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(propertyName))));
+ if (propertyValue)
+ propertyValue->setValue(value);
+ m_locked = false;
+}
+
+void PropertyEditorSubSelectionWrapper::resetValue(const PropertyName &name)
+{
+ auto propertyValue = qobject_cast<PropertyEditorValue *>(
+ variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(name))));
+ if (propertyValue)
+ propertyValue->resetValue();
+}
+
+bool PropertyEditorSubSelectionWrapper::isRelevantModelNode(const ModelNode &modelNode) const
+{
+ QmlObjectNode objectNode(m_modelNode);
+ return modelNode == m_modelNode || objectNode.propertyChangeForCurrentState() == modelNode;
+}
+
+void PropertyEditorSubSelectionWrapper::changeExpression(const QString &propertyName)
+{
+ PropertyName name = propertyName.toUtf8();
+
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ if (name.isNull())
+ return;
+
+ if (locked())
+ return;
+
+ const QScopeGuard cleanup([&] { m_locked = false; });
+ m_locked = true;
+
+ view()->executeInTransaction("PropertyEditorView::changeExpression", [this, name, propertyName] {
+ QmlObjectNode qmlObjectNode{m_modelNode};
+ PropertyEditorValue *value = qobject_cast<PropertyEditorValue *>(
+ variantToQObject(m_valuesPropertyMap.value(propertyName)));
+
+ if (!value) {
+ qWarning() << "PropertyEditor::changeExpression no value for " << propertyName;
+ return;
+ }
+
+ if (value->expression().isEmpty()) {
+ value->resetValue();
+ return;
+ }
+ PropertyEditorView::setExpressionOnObjectNode(qmlObjectNode, name, value->expression());
+ }); /* end of transaction */
+}
+
+void PropertyEditorSubSelectionWrapper::removePropertyFromModel(const PropertyName &propertyName)
+{
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ m_locked = true;
+ try {
+ RewriterTransaction transaction = view()->beginRewriterTransaction(
+ "PropertyEditorView::removePropertyFromModel");
+
+ QmlObjectNode(m_modelNode).removeProperty(propertyName);
+
+ transaction.commit();
+ } catch (const RewritingException &e) {
+ e.showException();
+ }
+ m_locked = false;
+}
+
+void PropertyEditorSubSelectionWrapper::commitVariantValueToModel(const PropertyName &propertyName,
+ const QVariant &value)
+{
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ try {
+ RewriterTransaction transaction = view()->beginRewriterTransaction(
+ "PropertyEditorView::commitVariantValueToMode");
+
+ QmlObjectNode(m_modelNode).setVariantProperty(propertyName, value);
+
+ transaction.commit();
+ } catch (const RewritingException &e) {
+ e.showException();
+ }
+}
+
+AbstractView *PropertyEditorSubSelectionWrapper::view() const
+{
+ QTC_CHECK(m_modelNode.isValid());
+
+ return m_modelNode.view();
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
index 59236c4fe2..70a51fffc2 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
@@ -14,6 +14,43 @@ namespace QmlDesigner {
class PropertyEditorValue;
+class PropertyEditorSubSelectionWrapper : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QQmlPropertyMap *properties READ properties NOTIFY propertiesChanged)
+
+signals:
+ void propertiesChanged();
+
+public:
+ QQmlPropertyMap *properties();
+ PropertyEditorSubSelectionWrapper(const ModelNode &modelNode);
+ ModelNode modelNode() const;
+
+ Q_INVOKABLE void deleteModelNode();
+
+ void setValueFromModel(const PropertyName &name, const QVariant &value);
+ void resetValue(const PropertyName &name);
+
+ bool isRelevantModelNode(const ModelNode &modelNode) const;
+
+private:
+ void changeValue(const QString &name);
+ void changeExpression(const QString &propertyName);
+ void createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value);
+ void exportPropertyAsAlias(const QString &name);
+ void removeAliasExport(const QString &name);
+ bool locked() const;
+
+ ModelNode m_modelNode;
+ QQmlPropertyMap m_valuesPropertyMap;
+ bool m_locked = false;
+ void removePropertyFromModel(const PropertyName &propertyName);
+ void commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value);
+ AbstractView *view() const;
+};
+
class PropertyEditorNodeWrapper : public QObject
{
Q_OBJECT
@@ -126,12 +163,15 @@ public:
bool isIdList() const;
Q_INVOKABLE QStringList getExpressionAsList() const;
+ Q_INVOKABLE QVector<double> getExpressionAsVector() const;
Q_INVOKABLE bool idListAdd(const QString &value);
Q_INVOKABLE bool idListRemove(int idx);
Q_INVOKABLE bool idListReplace(int idx, const QString &value);
Q_INVOKABLE void commitDrop(const QString &dropData);
Q_INVOKABLE void openMaterialEditor(int idx);
+ Q_INVOKABLE void setForceBound(bool b);
+
public slots:
void resetValue();
void setEnumeration(const QString &scope, const QString &name);
@@ -143,6 +183,8 @@ signals:
void expressionChanged(const QString &name); // HACK - We use the same notifer for the backend and frontend.
// If name is empty the signal is used for QML.
+ void expressionChangedQml();
+
void exportPropertyAsAliasRequested(const QString &name);
void removeAliasExportRequested(const QString &name);
@@ -168,6 +210,7 @@ private:
bool m_hasActiveDrag = false;
bool m_isValid = false; // if the property value belongs to a non-existing complexProperty it is invalid
PropertyEditorNodeWrapper *m_complexNode;
+ bool m_forceBound = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
index 1ff098f4ea..b22b39e238 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
@@ -256,66 +256,19 @@ void PropertyEditorView::changeExpression(const QString &propertyName)
underscoreName.replace('.', '_');
QmlObjectNode qmlObjectNode{m_selectedNode};
- PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(QString::fromLatin1(underscoreName));
+ PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(
+ QString::fromUtf8(underscoreName));
if (!value) {
qWarning() << "PropertyEditor::changeExpression no value for " << underscoreName;
return;
}
- if (auto property = qmlObjectNode.modelNode().metaInfo().property(name)) {
- const auto &propertType = property.propertyType();
- if (propertType.isColor()) {
- if (QColor(value->expression().remove('"')).isValid()) {
- qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"')));
- return;
- }
- } else if (propertType.isBool()) {
- if (isTrueFalseLiteral(value->expression())) {
- if (value->expression().compare("true", Qt::CaseInsensitive) == 0)
- qmlObjectNode.setVariantProperty(name, true);
- else
- qmlObjectNode.setVariantProperty(name, false);
- return;
- }
- } else if (propertType.isInteger()) {
- bool ok;
- int intValue = value->expression().toInt(&ok);
- if (ok) {
- qmlObjectNode.setVariantProperty(name, intValue);
- return;
- }
- } else if (propertType.isFloat()) {
- bool ok;
- qreal realValue = value->expression().toDouble(&ok);
- if (ok) {
- qmlObjectNode.setVariantProperty(name, realValue);
- return;
- }
- } else if (propertType.isVariant()) {
- bool ok;
- qreal realValue = value->expression().toDouble(&ok);
- if (ok) {
- qmlObjectNode.setVariantProperty(name, realValue);
- return;
- } else if (isTrueFalseLiteral(value->expression())) {
- if (value->expression().compare("true", Qt::CaseInsensitive) == 0)
- qmlObjectNode.setVariantProperty(name, true);
- else
- qmlObjectNode.setVariantProperty(name, false);
- return;
- }
- }
- }
-
if (value->expression().isEmpty()) {
value->resetValue();
return;
}
-
- if (qmlObjectNode.expression(name) != value->expression()
- || !qmlObjectNode.propertyAffectedByCurrentState(name))
- qmlObjectNode.setBindingProperty(name, value->expression());
+ setExpressionOnObjectNode(qmlObjectNode, name, value->expression());
}); /* end of transaction */
}
@@ -330,21 +283,8 @@ void PropertyEditorView::exportPropertyAsAlias(const QString &name)
if (noValidSelection())
return;
- executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){
- const QString id = m_selectedNode.validId();
- QString upperCasePropertyName = name;
- upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper());
- QString aliasName = id + upperCasePropertyName;
- aliasName.replace(".", ""); //remove all dots
-
- PropertyName propertyName = aliasName.toUtf8();
- if (rootModelNode().hasProperty(propertyName)) {
- Core::AsynchronousMessageBox::warning(tr("Cannot Export Property as Alias"),
- tr("Property %1 does already exist for root component.").arg(aliasName));
- return;
- }
- rootModelNode().bindingProperty(propertyName).setDynamicTypeNameAndExpression("alias", id + "." + name);
- });
+ executeInTransaction("PropertyEditorView::exportPropertyAsAlias",
+ [this, name]() { generateAliasForProperty(m_selectedNode, name); });
}
void PropertyEditorView::removeAliasExport(const QString &name)
@@ -358,15 +298,8 @@ void PropertyEditorView::removeAliasExport(const QString &name)
if (noValidSelection())
return;
- executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){
- const QString id = m_selectedNode.validId();
-
- for (const BindingProperty &property : rootModelNode().bindingProperties())
- if (property.expression() == (id + "." + name)) {
- rootModelNode().removeProperty(property.name());
- break;
- }
- });
+ executeInTransaction("PropertyEditorView::exportPropertyAsAlias",
+ [this, name]() { removeAliasForProperty(m_selectedNode, name); });
}
bool PropertyEditorView::locked() const
@@ -384,11 +317,113 @@ void PropertyEditorView::refreshMetaInfos(const TypeIds &deletedTypeIds)
m_propertyComponentGenerator.refreshMetaInfos(deletedTypeIds);
}
+void PropertyEditorView::setExpressionOnObjectNode(const QmlObjectNode &constObjectNode,
+ const PropertyName &name,
+ const QString &newExpression)
+{
+ auto qmlObjectNode = constObjectNode;
+ auto expression = newExpression;
+ if (auto property = qmlObjectNode.modelNode().metaInfo().property(name)) {
+ const auto &propertType = property.propertyType();
+ if (propertType.isColor()) {
+ if (QColor(expression.remove('"')).isValid()) {
+ qmlObjectNode.setVariantProperty(name, QColor(expression.remove('"')));
+ return;
+ }
+ } else if (propertType.isBool()) {
+ if (isTrueFalseLiteral(expression)) {
+ if (expression.compare("true", Qt::CaseInsensitive) == 0)
+ qmlObjectNode.setVariantProperty(name, true);
+ else
+ qmlObjectNode.setVariantProperty(name, false);
+ return;
+ }
+ } else if (propertType.isInteger()) {
+ bool ok;
+ int intValue = expression.toInt(&ok);
+ if (ok) {
+ qmlObjectNode.setVariantProperty(name, intValue);
+ return;
+ }
+ } else if (propertType.isFloat()) {
+ bool ok;
+ qreal realValue = expression.toDouble(&ok);
+ if (ok) {
+ qmlObjectNode.setVariantProperty(name, realValue);
+ return;
+ }
+ } else if (propertType.isVariant()) {
+ bool ok;
+ qreal realValue = expression.toDouble(&ok);
+ if (ok) {
+ qmlObjectNode.setVariantProperty(name, realValue);
+ return;
+ } else if (isTrueFalseLiteral(expression)) {
+ if (expression.compare("true", Qt::CaseInsensitive) == 0)
+ qmlObjectNode.setVariantProperty(name, true);
+ else
+ qmlObjectNode.setVariantProperty(name, false);
+ return;
+ }
+ }
+ }
+
+ if (qmlObjectNode.expression(name) != expression
+ || !qmlObjectNode.propertyAffectedByCurrentState(name))
+ qmlObjectNode.setBindingProperty(name, expression);
+}
+
+void PropertyEditorView::generateAliasForProperty(const ModelNode &modelNode, const QString &name)
+{
+ QTC_ASSERT(modelNode.isValid(), return );
+
+ auto view = modelNode.view();
+
+ auto rootNode = view->rootModelNode();
+
+ auto nonConstModelNode = modelNode;
+ const QString id = nonConstModelNode.validId();
+
+ QString upperCasePropertyName = name;
+ upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper());
+ QString aliasName = id + upperCasePropertyName;
+ aliasName.replace(".", ""); //remove all dots
+
+ PropertyName propertyName = aliasName.toUtf8();
+ if (rootNode.hasProperty(propertyName)) {
+ Core::AsynchronousMessageBox::warning(
+ tr("Cannot Export Property as Alias"),
+ tr("Property %1 does already exist for root component.").arg(aliasName));
+ return;
+ }
+ rootNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression("alias", id + "." + name);
+}
+
+void PropertyEditorView::removeAliasForProperty(const ModelNode &modelNode, const QString &propertyName)
+{
+ QTC_ASSERT(modelNode.isValid(), return );
+
+ auto view = modelNode.view();
+
+ auto rootNode = view->rootModelNode();
+
+ auto nonConstModelNode = modelNode;
+
+ const QString id = nonConstModelNode.validId();
+
+ for (const BindingProperty &property : rootNode.bindingProperties()) {
+ if (property.expression() == (id + "." + propertyName)) {
+ rootNode.removeProperty(property.name());
+ break;
+ }
+ }
+}
+
void PropertyEditorView::updateSize()
{
if (!m_qmlBackEndForCurrentType)
return;
- auto frame = m_qmlBackEndForCurrentType->widget()->findChild<QWidget*>("propertyEditorFrame");
+ auto frame = m_qmlBackEndForCurrentType->widget()->findChild<QWidget *>("propertyEditorFrame");
if (frame)
frame->resize(m_stackedWidget->size());
}
@@ -747,7 +782,11 @@ void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty> &proper
if (noValidSelection())
return;
+ QTC_ASSERT(m_qmlBackEndForCurrentType, return );
+
for (const AbstractProperty &property : propertyList) {
+ m_qmlBackEndForCurrentType->handlePropertiesRemovedInModelNodeProxy(property);
+
ModelNode node(property.parentModelNode());
if (node.isRootNode() && !m_selectedNode.isRootNode())
@@ -805,7 +844,11 @@ void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>&
if (noValidSelection())
return;
+ QTC_ASSERT(m_qmlBackEndForCurrentType, return );
+
for (const VariantProperty &property : propertyList) {
+ m_qmlBackEndForCurrentType->handleVariantPropertyChangedInModelNodeProxy(property);
+
ModelNode node(property.parentModelNode());
if (propertyIsAttachedLayoutProperty(property.name()))
@@ -830,7 +873,11 @@ void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty> &
if (locked() || noValidSelection())
return;
+ QTC_ASSERT(m_qmlBackEndForCurrentType, return );
+
for (const BindingProperty &property : propertyList) {
+ m_qmlBackEndForCurrentType->handleBindingPropertyChangedInModelNodeProxy(property);
+
ModelNode node(property.parentModelNode());
if (property.isAliasExport())
@@ -952,6 +999,9 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
{
if (!m_selectedNode.isValid())
return;
+
+ QTC_ASSERT(m_qmlBackEndForCurrentType, return );
+
m_locked = true;
using ModelNodePropertyPair = QPair<ModelNode, PropertyName>;
@@ -960,7 +1010,11 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
const QmlObjectNode qmlObjectNode(modelNode);
const PropertyName propertyName = propertyPair.second;
- if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode && qmlObjectNode.currentState().isValid()) {
+ m_qmlBackEndForCurrentType->handleInstancePropertyChangedInModelNodeProxy(modelNode,
+ propertyName);
+
+ if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode
+ && qmlObjectNode.currentState().isValid()) {
const AbstractProperty property = modelNode.property(propertyName);
if (modelNode == m_selectedNode || qmlObjectNode.propertyChangeForCurrentState() == qmlObjectNode) {
if ( !modelNode.hasProperty(propertyName) || modelNode.property(property.name()).isBindingProperty() )
@@ -969,7 +1023,6 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name()));
}
}
-
}
m_locked = false;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h
index cc9b522839..ef2b71f558 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h
@@ -84,6 +84,16 @@ public:
void refreshMetaInfos(const TypeIds &deletedTypeIds) override;
+ static void setExpressionOnObjectNode(const QmlObjectNode &objectNode,
+ const PropertyName &name,
+ const QString &expression);
+
+ static void generateAliasForProperty(const ModelNode &modelNode,
+ const QString &propertyName);
+
+ static void removeAliasForProperty(const ModelNode &modelNode,
+ const QString &propertyName);
+
protected:
void timerEvent(QTimerEvent *event) override;
void setupPane(const TypeName &typeName);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp
index 96ec5f92e7..fc39b37137 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp
@@ -4,6 +4,15 @@
#include "abstractview.h"
#include "qmlmodelnodeproxy.h"
+#include <nodemetainfo.h>
+
+#include <nodeabstractproperty.h>
+#include <nodelistproperty.h>
+#include <variantproperty.h>
+
+#include <utils/qtcassert.h>
+#include <utils/algorithm.h>
+
#include <QtQml>
namespace QmlDesigner {
@@ -17,6 +26,8 @@ void QmlModelNodeProxy::setup(const QmlObjectNode &objectNode)
{
m_qmlObjectNode = objectNode;
+ m_subselection.clear();
+
emit modelNodeChanged();
}
@@ -75,4 +86,227 @@ QString QmlModelNodeProxy::simplifiedTypeName() const
return m_qmlObjectNode.simplifiedTypeName();
}
+static QList<int> toInternalIdList(const QList<ModelNode> &nodes)
+{
+ return Utils::transform(nodes, [](const ModelNode &node) { return node.internalId(); });
+}
+
+QList<int> QmlModelNodeProxy::allChildren(int internalId) const
+{
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ if (internalId >= 0)
+ modelNode = modelNode.view()->modelNodeForInternalId(internalId);
+
+ return allChildren(modelNode);
+}
+
+QList<int> QmlModelNodeProxy::allChildrenOfType(const QString &typeName, int internalId) const
+{
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ if (internalId >= 0)
+ modelNode = modelNode.view()->modelNodeForInternalId(internalId);
+
+ return allChildrenOfType(modelNode, typeName);
+}
+
+QString QmlModelNodeProxy::simplifiedTypeName(int internalId) const
+{
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ return modelNode.view()->modelNodeForInternalId(internalId).simplifiedTypeName();
+}
+
+PropertyEditorSubSelectionWrapper *QmlModelNodeProxy::findWrapper(int internalId) const
+{
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item->modelNode().internalId() == internalId)
+ return item.data();
+ }
+
+ return nullptr;
+}
+
+PropertyEditorSubSelectionWrapper *QmlModelNodeProxy::registerSubSelectionWrapper(int internalId)
+{
+ auto result = findWrapper(internalId);
+
+ if (result)
+ return result;
+
+ QTC_ASSERT(m_qmlObjectNode.isValid(), return nullptr);
+
+ ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalId);
+
+ QTC_ASSERT(node.isValid(), return nullptr);
+
+ QSharedPointer<PropertyEditorSubSelectionWrapper> wrapper(
+ new PropertyEditorSubSelectionWrapper(node));
+ m_subselection.append(wrapper);
+
+ QJSEngine::setObjectOwnership(wrapper.data(), QJSEngine::CppOwnership);
+
+ return wrapper.data();
+}
+
+void QmlModelNodeProxy::createModelNode(int internalIdParent,
+ const QString &property,
+ const QString &typeName,
+ const QString &requiredImport)
+{
+ QTC_ASSERT(m_qmlObjectNode.isValid(), return );
+
+ auto modelNode = m_qmlObjectNode.modelNode();
+
+ AbstractView *view = modelNode.view();
+
+ auto parentModelNode = m_qmlObjectNode.modelNode();
+ if (internalIdParent >= 0)
+ parentModelNode = view->modelNodeForInternalId(internalIdParent);
+
+ QTC_ASSERT(parentModelNode.isValid(), return );
+
+ Import import;
+ Q_ASSERT(import.isEmpty());
+
+ if (!requiredImport.isEmpty() && !view->model()->hasImport(requiredImport))
+ import = Import::createLibraryImport(requiredImport);
+
+ view->executeInTransaction("QmlModelNodeProxy::createModelNode", [&] {
+ if (!import.isEmpty())
+ view->model()->changeImports({import}, {});
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newNode = view->createModelNode(typeName.toUtf8());
+#else
+ NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8());
+ ModelNode newNode = view->createModelNode(metaInfo.typeName(),
+ metaInfo.majorVersion(),
+ metaInfo.minorVersion());
+#endif
+ parentModelNode.nodeAbstractProperty(property.toUtf8()).reparentHere(newNode);
+ });
+}
+
+void QmlModelNodeProxy::moveNode(int internalIdParent,
+ const QString &propertyName,
+ int fromIndex,
+ int toIndex)
+{
+ QTC_ASSERT(m_qmlObjectNode.isValid(), return );
+
+ ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent);
+
+ QTC_ASSERT(node.isValid(), return );
+ AbstractView *view = m_qmlObjectNode.view();
+ view->executeInTransaction("QmlModelNodeProxy::swapNode", [&] {
+ node.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex);
+ });
+}
+
+bool QmlModelNodeProxy::isInstanceOf(const QString &typeName, int internalId) const
+{
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ if (internalId >= 0)
+ modelNode = modelNode.view()->modelNodeForInternalId(internalId);
+
+ NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8());
+
+ return modelNode.metaInfo().isBasedOn(metaInfo);
+}
+
+void QmlModelNodeProxy::changeType(int internalId, const QString &typeName)
+{
+ QTC_ASSERT(m_qmlObjectNode.isValid(), return );
+
+ ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalId);
+
+ QTC_ASSERT(node.isValid(), return );
+
+ QTC_ASSERT(!node.isRootNode(), return );
+#ifdef QDS_USE_PROJECTSTORAGE
+ node.changeType(typeName.toUtf8(), -1, -1);
+#else
+ NodeMetaInfo metaInfo = node.model()->metaInfo(typeName.toUtf8());
+ node.changeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion());
+#endif
+}
+
+void QmlModelNodeProxy::handleInstancePropertyChanged(const ModelNode &modelNode,
+ const PropertyName &propertyName)
+{
+ const QmlObjectNode qmlObjectNode(modelNode);
+
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item && item->isRelevantModelNode(modelNode)) {
+ if (!modelNode.hasProperty(propertyName)
+ || modelNode.property(propertyName).isBindingProperty()) {
+ item->setValueFromModel(propertyName, qmlObjectNode.instanceValue(propertyName));
+ } else {
+ item->setValueFromModel(propertyName, qmlObjectNode.modelValue(propertyName));
+ }
+ }
+ }
+}
+
+void QmlModelNodeProxy::handleBindingPropertyChanged(const BindingProperty &property)
+{
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item && item->isRelevantModelNode(property.parentModelNode())) {
+ QmlObjectNode objectNode(item->modelNode());
+ if (objectNode.modelNode().property(property.name()).isBindingProperty())
+ item->setValueFromModel(property.name(), objectNode.instanceValue(property.name()));
+ else
+ item->setValueFromModel(property.name(), objectNode.modelValue(property.name()));
+ }
+ }
+}
+
+void QmlModelNodeProxy::handleVariantPropertyChanged(const VariantProperty &property)
+{
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item && item->isRelevantModelNode(property.parentModelNode())) {
+ QmlObjectNode objectNode(item->modelNode());
+ if (objectNode.modelNode().property(property.name()).isBindingProperty())
+ item->setValueFromModel(property.name(), objectNode.instanceValue(property.name()));
+ else
+ item->setValueFromModel(property.name(), objectNode.modelValue(property.name()));
+ }
+ }
+}
+
+void QmlModelNodeProxy::handlePropertiesRemoved(const AbstractProperty &property)
+{
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item && item->isRelevantModelNode(property.parentModelNode())) {
+ QmlObjectNode objectNode(item->modelNode());
+ item->resetValue(property.name());
+ item->setValueFromModel(property.name(), objectNode.instanceValue(property.name()));
+ }
+ }
+}
+
+QList<int> QmlModelNodeProxy::allChildren(const ModelNode &modelNode) const
+{
+ return toInternalIdList(modelNode.directSubModelNodes());
+}
+
+QList<int> QmlModelNodeProxy::allChildrenOfType(const ModelNode &modelNode, const QString &typeName) const
+{
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8());
+
+ return toInternalIdList(modelNode.directSubModelNodesOfType(metaInfo));
}
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h
index 4740b01fbd..d8a49d7e10 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h
@@ -3,6 +3,8 @@
#pragma once
+#include "propertyeditorvalue.h"
+
#include <qmlitemnode.h>
#include <QObject>
@@ -16,6 +18,7 @@ class QMLDESIGNERCORE_EXPORT QmlModelNodeProxy : public QObject
Q_PROPERTY(QmlDesigner::ModelNode modelNode READ modelNode NOTIFY modelNodeChanged)
Q_PROPERTY(bool multiSelection READ multiSelection NOTIFY modelNodeChanged)
+
public:
explicit QmlModelNodeProxy(QObject *parent = nullptr);
@@ -36,13 +39,45 @@ public:
QString simplifiedTypeName() const;
+ Q_INVOKABLE QList<int> allChildren(int internalId = -1) const;
+ Q_INVOKABLE QList<int> allChildrenOfType(const QString &typeName, int internalId = -1) const;
+
+ Q_INVOKABLE QString simplifiedTypeName(int internalId) const;
+
+ Q_INVOKABLE PropertyEditorSubSelectionWrapper *registerSubSelectionWrapper(int internalId);
+
+ Q_INVOKABLE void createModelNode(int internalIdParent,
+ const QString &property,
+ const QString &typeName,
+ const QString &requiredImport = {});
+
+ Q_INVOKABLE void moveNode(int internalIdParent,
+ const QString &propertyName,
+ int fromIndex,
+ int toIndex);
+
+ Q_INVOKABLE bool isInstanceOf(const QString &typeName, int internalId = -1) const;
+
+ Q_INVOKABLE void changeType(int internalId, const QString &typeName);
+
+ void handleInstancePropertyChanged(const ModelNode &modelNode, const PropertyName &propertyName);
+
+ void handleBindingPropertyChanged(const BindingProperty &property);
+ void handleVariantPropertyChanged(const VariantProperty &property);
+ void handlePropertiesRemoved(const AbstractProperty &property);
+
signals:
void modelNodeChanged();
void selectionToBeChanged();
void selectionChanged();
private:
+ QList<int> allChildren(const ModelNode &modelNode) const;
+ QList<int> allChildrenOfType(const ModelNode &modelNode, const QString &typeName) const;
+ PropertyEditorSubSelectionWrapper *findWrapper(int internalId) const;
+
QmlObjectNode m_qmlObjectNode;
+ QList<QSharedPointer<PropertyEditorSubSelectionWrapper>> m_subselection;
};
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp
index 2d8998404a..f289583cc3 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp
@@ -40,7 +40,7 @@ SetFrameValueDialog::SetFrameValueDialog(qreal frame, const QVariant &value,
valueLabel->setAlignment(Qt::AlignRight);
valueLabel->setFixedWidth(labelWidth);
- m_frameControl->setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
+ m_frameControl->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max());
m_frameControl->setValue(static_cast<int>(frame));
m_frameControl->setAlignment(Qt::AlignRight);
@@ -86,7 +86,6 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value)
switch (value.metaType().id())
{
-
case QMetaType::QColor: {
auto* widget = new ColorControl(value.value<QColor>());
m_valueGetter = [widget]() { return widget->value(); };
@@ -102,7 +101,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value)
case QMetaType::Int: {
auto* widget = new QSpinBox;
- widget->setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
+ widget->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max());
widget->setAlignment(Qt::AlignRight);
widget->setValue(value.toInt());
m_valueGetter = [widget]() { return widget->value(); };
@@ -120,7 +119,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value)
case QMetaType::Float: {
auto* widget = new QDoubleSpinBox;
- widget->setRange(std::numeric_limits<float>::min(), std::numeric_limits<float>::max());
+ widget->setRange(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
widget->setAlignment(Qt::AlignRight);
widget->setValue(value.toFloat());
m_valueGetter = [widget]() { return static_cast<float>(widget->value()); };
@@ -132,7 +131,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value)
default: {
auto* widget = new QDoubleSpinBox;
- widget->setRange(std::numeric_limits<double>::min(), std::numeric_limits<double>::max());
+ widget->setRange(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max());
widget->setAlignment(Qt::AlignRight);
widget->setValue(value.toDouble());
m_valueGetter = [widget]() { return widget->value(); };
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
index 83551378ee..e65c05c39f 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
@@ -13,6 +13,7 @@
#include <variantproperty.h>
#include <qmlitemnode.h>
#include <qmlobjectnode.h>
+#include <dialogutils.h>
#include <coreplugin/messagebox.h>
@@ -141,8 +142,7 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent)
bool error = false;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
- tr("%1 is an invalid id.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
error = true;
} else if (animation().view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
index b3d408dc0d..08915b6577 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
@@ -10,6 +10,7 @@
#include <nodemetainfo.h>
#include <rewritertransaction.h>
#include <variantproperty.h>
+#include <dialogutils.h>
#include <coreplugin/messagebox.h>
@@ -125,8 +126,7 @@ TimelineForm::TimelineForm(QWidget *parent)
bool error = false;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
- tr("%1 is an invalid id.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
error = true;
} else if (m_timeline.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
index 7905df68e9..8288e69316 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
@@ -644,11 +644,12 @@ void TimelineView::registerActions()
TimelineWidget *TimelineView::createWidget()
{
- if (!m_timelineWidget)
+ if (!m_timelineWidget) {
m_timelineWidget = new TimelineWidget(this);
- auto *timelineContext = new TimelineContext(m_timelineWidget);
- Core::ICore::addContextObject(timelineContext);
+ auto *timelineContext = new TimelineContext(m_timelineWidget);
+ Core::ICore::addContextObject(timelineContext);
+ }
return m_timelineWidget;
}
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
index e9df928c96..cb84183f92 100644
--- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
+++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
@@ -21,6 +21,9 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/modemanager.h>
+
+#include <texteditor/textdocument.h>
+
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
@@ -302,6 +305,20 @@ ToolBarBackend::ToolBarBackend(QObject *parent)
this,
&ToolBarBackend::documentIndexChanged);
+ connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, this, [this]() {
+ static QMetaObject::Connection *lastConnection = nullptr;
+ delete lastConnection;
+
+ if (auto textDocument = qobject_cast<TextEditor::TextDocument *>(
+ Core::EditorManager::currentDocument())) {
+ connect(textDocument->document(),
+ &QTextDocument::modificationChanged,
+ this,
+ &ToolBarBackend::isDocumentDirtyChanged);
+ emit isDocumentDirtyChanged();
+ }
+ });
+
connect(Core::EditorManager::instance(),
&Core::EditorManager::currentEditorChanged,
this,
@@ -740,6 +757,12 @@ bool ToolBarBackend::isSharingEnabled()
return QmlDesigner::checkEnterpriseLicense();
}
+bool ToolBarBackend::isDocumentDirty() const
+{
+ return Core::EditorManager::currentDocument()
+ && Core::EditorManager::currentDocument()->isModified();
+}
+
void ToolBarBackend::launchGlobalAnnotations()
{
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION);
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
index 5d0b0e712a..02bdae1717 100644
--- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
+++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
@@ -97,6 +97,7 @@ class ToolBarBackend : public QObject
Q_PROPERTY(bool isMCUs READ isMCUs NOTIFY isMCUsChanged)
Q_PROPERTY(bool projectOpened READ projectOpened NOTIFY projectOpenedChanged)
Q_PROPERTY(bool isSharingEnabled READ isSharingEnabled NOTIFY isSharingEnabledChanged)
+ Q_PROPERTY(bool isDocumentDirty READ isDocumentDirty NOTIFY isDocumentDirtyChanged)
public:
ToolBarBackend(QObject *parent = nullptr);
@@ -147,6 +148,8 @@ public:
bool isSharingEnabled();
+ bool isDocumentDirty() const;
+
static void launchGlobalAnnotations();
signals:
@@ -167,6 +170,7 @@ signals:
void isMCUsChanged();
void projectOpenedChanged();
void isSharingEnabledChanged();
+ void isDocumentDirtyChanged();
private:
void setupWorkspaces();
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
index 104127bd49..9f9e888823 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
@@ -318,11 +318,12 @@ ModelNode TransitionEditorView::addNewTransition()
TransitionEditorWidget *TransitionEditorView::createWidget()
{
- if (!m_transitionEditorWidget)
+ if (!m_transitionEditorWidget) {
m_transitionEditorWidget = new TransitionEditorWidget(this);
- auto *transitionContext = new TransitionContext(m_transitionEditorWidget);
- Core::ICore::addContextObject(transitionContext);
+ auto *transitionContext = new TransitionContext(m_transitionEditorWidget);
+ Core::ICore::addContextObject(transitionContext);
+ }
return m_transitionEditorWidget;
}
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp
index 1770ba63fc..dcbb0b23b3 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp
@@ -13,6 +13,7 @@
#include <rewritertransaction.h>
#include <variantproperty.h>
#include <qmlitemnode.h>
+#include <dialogutils.h>
#include <coreplugin/messagebox.h>
@@ -45,8 +46,7 @@ TransitionForm::TransitionForm(QWidget *parent)
bool error = false;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(tr("Invalid ID"),
- tr("%1 is an invalid ID.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
error = true;
} else if (m_transition.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid ID"),
diff --git a/src/plugins/qmldesigner/componentsplugin/components.metainfo b/src/plugins/qmldesigner/componentsplugin/components.metainfo
index 8a1e365266..2e070aa322 100644
--- a/src/plugins/qmldesigner/componentsplugin/components.metainfo
+++ b/src/plugins/qmldesigner/componentsplugin/components.metainfo
@@ -12,6 +12,7 @@ MetaInfo {
Property { name: "width"; type: "int"; value: 100; }
Property { name: "height"; type: "int"; value: 100; }
+ toolTip: qsTr("Organizes items in a row.")
}
}
@@ -28,6 +29,7 @@ MetaInfo {
Property { name: "width"; type: "int"; value: 100; }
Property { name: "height"; type: "int"; value: 100; }
+ toolTip: qsTr("Organizes items in a column.")
}
}
@@ -44,6 +46,7 @@ MetaInfo {
Property { name: "width"; type: "int"; value: 100; }
Property { name: "height"; type: "int"; value: 100; }
+ toolTip: qsTr("Organizes items in a grid.")
}
}
@@ -57,7 +60,7 @@ MetaInfo {
}
ItemLibraryEntry {
- name: "StackLayout"
+ name: "Stack Layout"
category: "Qt Quick - Layouts"
libraryIcon: ":/componentsplugin/images/stack-layouts-icon.png"
version: "1.0"
@@ -65,6 +68,7 @@ MetaInfo {
Property { name: "width"; type: "int"; value: 100; }
Property { name: "height"; type: "int"; value: 100; }
+ toolTip: qsTr("Organizes items in a grid. Only the top item is visible.")
}
}
}
diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp
new file mode 100644
index 0000000000..5ee5790b53
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp
@@ -0,0 +1,139 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "generatedcomponentutils.h"
+
+#include <qmldesignerconstants.h>
+
+namespace QmlDesigner {
+
+GeneratedComponentUtils::GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies)
+ : m_externalDependencies(externalDependencies)
+{
+}
+
+Utils::FilePath GeneratedComponentUtils::generatedComponentsPath() const
+{
+ Utils::FilePath projectPath = Utils::FilePath::fromString(m_externalDependencies.currentProjectDirPath());
+ if (projectPath.isEmpty())
+ return {};
+
+ Utils::FilePath assetImportsPath = projectPath.resolvePath(QLatin1String(Constants::OLD_ASSET_IMPORT_FOLDER));
+ if (assetImportsPath.exists())
+ return assetImportsPath;
+
+ Utils::FilePath componentsPath = projectPath.resolvePath(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER));
+ if (!componentsPath.exists())
+ componentsPath.createDir();
+
+ return componentsPath;
+}
+
+Utils::FilePath GeneratedComponentUtils::composedEffectsBasePath() const
+{
+ Utils::FilePath basePath = generatedComponentsPath();
+ if (basePath.isEmpty())
+ return {};
+
+ QString effectsImportPath;
+ if (basePath.endsWith(Constants::OLD_ASSET_IMPORT_FOLDER))
+ effectsImportPath = Constants::OLD_EFFECTS_FOLDER;
+ else
+ effectsImportPath = Constants::COMPOSED_EFFECTS_TYPE;
+
+ return basePath.resolvePath(effectsImportPath);
+}
+
+Utils::FilePath GeneratedComponentUtils::composedEffectPath(const QString &effectPath) const
+{
+ Utils::FilePath effectsBasePath = composedEffectsBasePath();
+
+ QString effectName = Utils::FilePath::fromString(effectPath).baseName();
+
+ return effectsBasePath.resolvePath(effectName + "/" + effectName + ".qml");
+}
+
+Utils::FilePath GeneratedComponentUtils::componentBundlesBasePath() const
+{
+ Utils::FilePath basePath = generatedComponentsPath();
+
+ if (basePath.isEmpty())
+ return {};
+
+ return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE));
+}
+
+Utils::FilePath GeneratedComponentUtils::import3dBasePath() const
+{
+ Utils::FilePath basePath = generatedComponentsPath();
+
+ if (basePath.isEmpty())
+ return {};
+
+ Utils::FilePath import3dPath;
+ if (basePath.endsWith(Constants::OLD_ASSET_IMPORT_FOLDER))
+ return basePath.resolvePath(QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER));
+
+ return basePath.resolvePath(QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER));
+}
+
+bool GeneratedComponentUtils::isImport3dPath(const QString &path) const
+{
+ return path.contains('/' + QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER))
+ || path.contains(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER) + '/'
+ + QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER));
+}
+
+bool GeneratedComponentUtils::isComposedEffectPath(const QString &path) const
+{
+ return path.contains(Constants::OLD_EFFECTS_IMPORT_FOLDER)
+ || path.contains('/' + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE));
+}
+
+QString GeneratedComponentUtils::generatedComponentTypePrefix() const
+{
+ Utils::FilePath basePath = generatedComponentsPath();
+ if (basePath.isEmpty() || basePath.endsWith(Constants::OLD_ASSET_IMPORT_FOLDER))
+ return {};
+
+ return Constants::GENERATED_COMPONENTS_FOLDER;
+}
+
+QString GeneratedComponentUtils::import3dTypePrefix() const
+{
+ QString basePrefix = generatedComponentTypePrefix();
+
+ if (basePrefix == Constants::GENERATED_COMPONENTS_FOLDER)
+ return basePrefix + '.' + QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER);
+
+ return Constants::OLD_QUICK_3D_ASSETS_FOLDER;
+}
+
+QString GeneratedComponentUtils::import3dTypePath() const
+{
+ QString prefix = import3dTypePrefix();
+ prefix.replace('.', '/');
+ return prefix;
+}
+
+QString GeneratedComponentUtils::componentBundlesTypePrefix() const
+{
+ QString basePrefix = generatedComponentTypePrefix();
+
+ if (basePrefix.endsWith(Constants::GENERATED_COMPONENTS_FOLDER))
+ return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_TYPE);
+
+ return Constants::COMPONENT_BUNDLES_TYPE;
+}
+
+QString GeneratedComponentUtils::composedEffectsTypePrefix() const
+{
+ QString basePrefix = generatedComponentTypePrefix();
+
+ if (basePrefix == Constants::GENERATED_COMPONENTS_FOLDER)
+ return basePrefix + '.' + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE);
+
+ return Constants::OLD_EFFECTS_FOLDER;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h
new file mode 100644
index 0000000000..e2e9baf3e7
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <externaldependenciesinterface.h>
+#include <qmldesignercorelib_exports.h>
+
+#include <utils/filepath.h>
+
+#include <QString>
+
+namespace QmlDesigner {
+
+class QMLDESIGNERCORE_EXPORT GeneratedComponentUtils {
+public:
+ GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies);
+
+ Utils::FilePath generatedComponentsPath() const;
+ Utils::FilePath composedEffectsBasePath() const;
+ Utils::FilePath composedEffectPath(const QString &effectPath) const;
+ Utils::FilePath componentBundlesBasePath() const;
+ Utils::FilePath import3dBasePath() const;
+
+ bool isImport3dPath(const QString &path) const;
+ bool isComposedEffectPath(const QString &path) const;
+
+ QString generatedComponentTypePrefix() const;
+ QString import3dTypePrefix() const;
+ QString import3dTypePath() const;
+ QString componentBundlesTypePrefix() const;
+ QString composedEffectsTypePrefix() const;
+
+private:
+ ExternalDependenciesInterface &m_externalDependencies;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
index 955e676d3b..97148e664f 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
@@ -76,8 +76,10 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
AbortCallback abortCallback,
ImageCache::TraceToken traceToken)
{
+#ifdef QDS_USE_PROJECTSTORAGE
if (!m_projectStorage || !m_pathCache)
return;
+#endif
using namespace NanotraceHR::Literals;
auto [collectorTraceToken, flowtoken] = traceToken.beginDurationWithFlow(
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
index c2a912cc3a..fec68c2894 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
@@ -64,8 +64,10 @@ private:
QSize captureImageMaximumSize;
ExternalDependenciesInterface &m_externalDependencies;
ImageCacheCollectorNullImageHandling nullImageHandling{};
+#ifdef QDS_USE_PROJECTSTORAGE
ProjectStorageType *m_projectStorage = nullptr;
PathCacheType *m_pathCache = nullptr;
+#endif
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h
index fac7e7d9bf..dc5ed3de23 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h
@@ -31,7 +31,7 @@ public:
{
std::unique_lock lock{m_mutex};
- ensureThreadIsRunning(std::move(traceToken));
+ ensureThreadIsRunning(lock, std::move(traceToken));
m_tasks.emplace_back(std::forward<Arguments>(arguments)...);
}
@@ -54,6 +54,15 @@ public:
clearTasks(oldTasks);
}
+ void putThreadToSleep()
+ {
+ {
+ std::unique_lock lock{m_mutex};
+ m_sleeping = true;
+ }
+ m_condition.notify_all();
+ }
+
private:
void destroy()
{
@@ -66,19 +75,20 @@ private:
{
using namespace std::literals::chrono_literals;
std::unique_lock lock{m_mutex};
- if (m_finishing)
+
+ if (m_finishing || m_sleeping)
return {std::move(lock), true};
+
if (m_tasks.empty()) {
auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] {
- return m_tasks.size() || m_finishing;
+ return m_tasks.size() || m_finishing || m_sleeping;
});
- if (timedOutWithoutEntriesOrFinishing || m_finishing) {
+ if (timedOutWithoutEntriesOrFinishing)
m_sleeping = true;
- return {std::move(lock), true};
- }
}
- return {std::move(lock), false};
+
+ return {std::move(lock), m_finishing || m_sleeping};
}
[[nodiscard]] std::optional<Task> getTask(std::unique_lock<std::mutex> lock)
@@ -94,29 +104,38 @@ private:
return {std::move(task)};
}
- template<typename TraceToken>
- void ensureThreadIsRunning(TraceToken traceToken)
+ template<typename Lock, typename TraceToken>
+ void ensureThreadIsRunning(Lock &lock, TraceToken traceToken)
{
using namespace NanotraceHR::Literals;
if (m_finishing || !m_sleeping)
return;
+ if (m_sleeping) {
+ lock.unlock();
+ joinThread();
+ lock.lock();
+
+ m_sleeping = false;
+ }
+
if (m_backgroundThread.joinable())
return;
- m_sleeping = false;
-
auto [threadCreateToken, flowToken] = traceToken.beginDurationWithFlow(
"thread is created in the task queue"_t);
m_backgroundThread = std::thread{[this](auto traceToken) {
auto duration = traceToken.beginDuration(
"thread is ready"_t);
+
while (true) {
auto [lock, abort] = waitForTasks();
duration.end();
+
if (abort)
return;
+
auto getTaskToken = duration.beginDuration(
"get task from queue"_t);
if (auto task = getTask(std::move(lock)); task) {
diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h
index b907e6c5d8..85f129cdbc 100644
--- a/src/plugins/qmldesigner/designercore/include/model.h
+++ b/src/plugins/qmldesigner/designercore/include/model.h
@@ -166,6 +166,7 @@ public:
NodeMetaInfo qtQmlConnectionsMetaInfo() const;
NodeMetaInfo qtQmlModelsListModelMetaInfo() const;
NodeMetaInfo qtQmlModelsListElementMetaInfo() const;
+ NodeMetaInfo qtQmlXmlListModelXmlListModelRoleMetaInfo() const;
NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const;
NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const;
NodeMetaInfo qtQuick3DDirectionalLightMetaInfo() const;
@@ -218,14 +219,17 @@ public:
// Imports:
const Imports &imports() const;
- const Imports &possibleImports() const;
- const Imports &usedImports() const;
+ Imports possibleImports() const;
+ Imports usedImports() const;
void changeImports(Imports importsToBeAdded, Imports importsToBeRemoved);
+#ifndef QDS_USE_PROJECTSTORAGE
void setPossibleImports(Imports possibleImports);
+#endif
+#ifndef QDS_USE_PROJECTSTORAGE
void setUsedImports(Imports usedImports);
+#endif
bool hasImport(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const;
bool isImportPossible(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const;
- QString pathForImport(const Import &import);
QStringList importPaths() const;
Import highestPossibleImport(const QString &importPath);
diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h
index 0a062289fd..91c533fe7b 100644
--- a/src/plugins/qmldesigner/designercore/include/modelfwd.h
+++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h
@@ -77,7 +77,7 @@ constexpr bool useProjectStorage()
using ProjectStorageType = ProjectStorageInterface;
using PathCacheType = SourcePathCacheInterface;
#else
-using ProjectStorageType = ProjectStorage<Sqlite::Database>;
+using ProjectStorageType = ProjectStorage;
using PathCacheType = SourcePathCache<ProjectStorageType, NonLockingMutex>;
#endif
diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
index 53c755ddc8..ba2e2cda65 100644
--- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
+++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
@@ -27,9 +27,11 @@ QT_END_NAMESPACE
# define DEPRECATED_VERSION_NUMBER \
[[deprecated( \
"In most cases you don't need them anymore because the import is setting them!")]]
+# define DEPRECATED_COMPONENT_FILE_NAME [[deprecated("Use sourceId() instead.")]]
#else
# define DEPRECATED_TYPENAME
# define DEPRECATED_VERSION_NUMBER
+# define DEPRECATED_COMPONENT_FILE_NAME
#endif
namespace QmlDesigner {
@@ -116,7 +118,7 @@ public:
Storage::Info::ItemLibraryEntries itemLibrariesEntries() const;
SourceId sourceId() const;
- QString componentFileName() const;
+ DEPRECATED_COMPONENT_FILE_NAME QString componentFileName() const;
bool isBasedOn(const NodeMetaInfo &metaInfo) const;
bool isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo2) const;
@@ -167,6 +169,7 @@ public:
bool isQtMultimediaSoundEffect() const;
bool isQtObject() const;
bool isQtQmlConnections() const;
+ bool isQtQmlModelsListElement() const;
bool isQtQuick3DBakedLightmap() const;
bool isQtQuick3DBuffer() const;
bool isQtQuick3DCamera() const;
@@ -176,7 +179,6 @@ public:
bool isQtQuick3DInstanceList() const;
bool isQtQuick3DInstanceListEntry() const;
bool isQtQuick3DLight() const;
- bool isQtQuickListElement() const;
bool isQtQuickListModel() const;
bool isQtQuickListView() const;
bool isQtQuick3DMaterial() const;
@@ -265,12 +267,14 @@ public:
private:
const Storage::Info::Type &typeData() const;
+ PropertyDeclarationId defaultPropertyDeclarationId() const;
bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const;
private:
TypeId m_typeId;
NotNullPointer<const ProjectStorageType> m_projectStorage = {};
mutable std::optional<Storage::Info::Type> m_typeData;
+ mutable std::optional<PropertyDeclarationId> m_defaultPropertyId;
std::shared_ptr<NodeMetaInfoPrivate> m_privateData;
};
diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h
index bc66e0d2b2..0b157e55e7 100644
--- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h
+++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h
@@ -7,6 +7,8 @@
#include <utils/span.h>
+#include <QVarLengthArray>
+
namespace QmlDesigner {
enum class BasicIdType {
@@ -29,6 +31,8 @@ enum class BasicIdType {
using TypeId = Sqlite::BasicId<BasicIdType::Type>;
using TypeIds = std::vector<TypeId>;
+template<std::size_t size>
+using SmallTypeIds = QVarLengthArray<TypeId, size>;
using PropertyDeclarationId = Sqlite::BasicId<BasicIdType::PropertyDeclaration>;
using PropertyDeclarationIds = std::vector<PropertyDeclarationId>;
@@ -47,6 +51,8 @@ using SourceContextIds = std::vector<SourceContextId>;
using SourceId = Sqlite::BasicId<BasicIdType::Source, int>;
using SourceIds = std::vector<SourceId>;
+template<std::size_t size>
+using SmallSourceIds = QVarLengthArray<SourceId, size>;
using ModuleId = Sqlite::BasicId<BasicIdType::Module, int>;
using ModuleIds = std::vector<ModuleId>;
diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
index dde5515a5a..e11f201cdb 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
@@ -174,6 +174,7 @@ public:
ModelNode targetTransition() const;
void assignTargetFlowItem(const QmlFlowTargetNode &flowItem);
QmlFlowItemNode flowItemParent() const;
+private:
void destroyTarget();
};
diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h
index 23841accda..0134349682 100644
--- a/src/plugins/qmldesigner/designercore/include/rewriterview.h
+++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h
@@ -123,7 +123,10 @@ public:
bool renameId(const QString& oldId, const QString& newId);
const QmlJS::Document *document() const;
+
+#ifndef QDS_USE_PROJECTSTORAGE
const QmlJS::ScopeChain *scopeChain() const;
+#endif
QString convertTypeToImportAlias(const QString &type) const;
@@ -135,8 +138,6 @@ public:
void setCheckLinkErrors(bool b) { m_checkLinkErrors = b; }
- QString pathForImport(const Import &import);
-
QStringList importDirectories() const;
QSet<QPair<QString, QString> > qrcMapping() const;
diff --git a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h
index 7fa2348854..a42164d1bd 100644
--- a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h
+++ b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h
@@ -7,6 +7,7 @@
# include "qmldesignercorelib_global.h"
+# include <generatedcomponentutils.h>
# include <import.h>
# include <QObject>
@@ -62,6 +63,7 @@ private: // variables
QDir m_filePathDir;
QPointer<Model> m_model;
ExternalDependenciesInterface &m_externalDependencies;
+ GeneratedComponentUtils m_componentUtils;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index 33a100f7b2..1b965db66a 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -64,6 +64,8 @@
#include <qmlitemnode.h>
#include <rewriterview.h>
+#include <projectstorage/projectstorage.h>
+
#include <utils/hdrimage.h>
#include <coreplugin/messagemanager.h>
@@ -205,22 +207,17 @@ NodeInstanceView::~NodeInstanceView()
static bool isSkippedRootNode(const ModelNode &node)
{
- static const PropertyNameList skipList({"Qt.ListModel", "QtQuick.ListModel", "Qt.ListModel", "QtQuick.ListModel"});
-
- if (skipList.contains(node.type()))
- return true;
-
- return false;
+ return node.metaInfo().isQtQuickListModel();
}
static bool isSkippedNode(const ModelNode &node)
{
- static const PropertyNameList skipList({"QtQuick.XmlRole", "Qt.XmlRole", "QtQuick.ListElement", "Qt.ListElement"});
+ auto model = node.model();
- if (skipList.contains(node.type()))
- return true;
+ auto listElement = model->qtQmlModelsListElementMetaInfo();
+ auto xmlRole = model->qtQmlXmlListModelXmlListModelRoleMetaInfo();
- return false;
+ return node.metaInfo().isBasedOn(listElement, xmlRole);
}
static bool parentTakesOverRendering(const ModelNode &modelNode)
@@ -644,7 +641,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
TypeName(),
key.type};
m_nodeInstanceServer->changeAuxiliaryValues({{container}});
- };
+ }
break;
case AuxiliaryDataType::NodeInstanceAuxiliary:
@@ -656,7 +653,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
TypeName(),
key.type};
m_nodeInstanceServer->changeAuxiliaryValues({{container}});
- };
+ }
break;
case AuxiliaryDataType::NodeInstancePropertyOverwrite:
@@ -991,6 +988,8 @@ QRectF NodeInstanceView::sceneRect() const
return {};
}
+namespace {
+
QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList)
{
QList<ModelNode> filteredNodeList;
@@ -1003,14 +1002,12 @@ QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList)
return filteredNodeList;
}
-namespace {
bool shouldSendAuxiliary(const AuxiliaryDataKey &key)
{
return key.type == AuxiliaryDataType::NodeInstancePropertyOverwrite
|| key.type == AuxiliaryDataType::NodeInstanceAuxiliary || key == invisibleProperty
|| key == lockedProperty;
}
-} // namespace
bool parentIsBehavior(ModelNode node)
{
@@ -1024,6 +1021,31 @@ bool parentIsBehavior(ModelNode node)
return false;
}
+TypeName createQualifiedTypeName(const ModelNode &node)
+{
+ if (!node)
+ return {};
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto model = node.model();
+ auto exportedTypes = node.metaInfo().exportedTypeNamesForSourceId(model->fileUrlSourceId());
+ if (exportedTypes.size()) {
+ const auto &exportedType = exportedTypes.front();
+ Utils::PathString typeName = model->projectStorage()->moduleName(exportedType.moduleId);
+ typeName += '/';
+ typeName += exportedType.name;
+
+ return typeName.toQByteArray();
+ }
+
+ return {};
+#else
+ return node.type();
+#endif
+}
+
+} // namespace
+
CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
{
QList<ModelNode> nodeList = allModelNodes();
@@ -1079,8 +1101,9 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
nodeFlags |= InstanceContainer::ParentTakesOverRendering;
const auto modelNode = instance.modelNode();
+
InstanceContainer container(instance.instanceId(),
- modelNode.type(),
+ createQualifiedTypeName(modelNode),
modelNode.majorVersion(),
modelNode.minorVersion(),
ModelUtils::componentFilePath(modelNode),
@@ -1243,7 +1266,7 @@ CreateInstancesCommand NodeInstanceView::createCreateInstancesCommand(const QLis
const auto modelNode = instance.modelNode();
InstanceContainer container(instance.instanceId(),
- modelNode.type(),
+ createQualifiedTypeName(modelNode),
modelNode.majorVersion(),
modelNode.minorVersion(),
ModelUtils::componentFilePath(modelNode),
@@ -1850,7 +1873,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
ModelNodePreviewImageData imageData;
imageData.id = modelNode.id();
- imageData.type = QString::fromLatin1(modelNode.type());
+ imageData.type = QString::fromUtf8(createQualifiedTypeName(modelNode));
const double ratio = m_externalDependencies.formEditorDevicePixelRatio();
if (imageSource.isEmpty() && modelNode.metaInfo().isQtQuick3DTexture()) {
@@ -1923,7 +1946,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
imageData.pixmap = originalPixmap.scaled(dim, dim, Qt::KeepAspectRatio);
imageData.pixmap.setDevicePixelRatio(ratio);
imageData.time = modified;
- imageData.info = ImageUtils::imageInfo(imageSource);
+ imageData.info = ImageUtils::imageInfoString(imageSource);
m_imageDataMap.insert(imageData.id, imageData);
}
}
@@ -1958,7 +1981,7 @@ QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &model
if (m_imageDataMap.contains(id)) {
imageData = m_imageDataMap[id];
} else {
- imageData.type = QString::fromLatin1(modelNode.type());
+ imageData.type = QString::fromLatin1(createQualifiedTypeName(modelNode));
imageData.id = id;
m_imageDataMap.insert(id, imageData);
}
diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
index 85f904666c..b8c3e610be 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
@@ -919,8 +919,11 @@ const ObjectValue *NodeMetaInfoPrivate::getObjectValue() const
ContextPtr NodeMetaInfoPrivate::context() const
{
+#ifndef QDS_USE_PROJECTSTORAGE
if (m_model && m_model->rewriterView() && m_model->rewriterView()->scopeChain())
return m_model->rewriterView()->scopeChain()->context();
+#endif
+
return ContextPtr(nullptr);
}
@@ -1832,7 +1835,7 @@ PropertyName NodeMetaInfo::defaultPropertyName() const
{
if constexpr (useProjectStorage()) {
if (isValid()) {
- if (auto name = m_projectStorage->propertyName(typeData().defaultPropertyId)) {
+ if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) {
return name->toQByteArray();
}
}
@@ -1848,7 +1851,7 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const
{
if constexpr (useProjectStorage()) {
if (isValid()) {
- return PropertyMetaInfo(typeData().defaultPropertyId, m_projectStorage);
+ return PropertyMetaInfo(defaultPropertyDeclarationId(), m_projectStorage);
}
} else {
return property(defaultPropertyName());
@@ -1859,7 +1862,7 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const
bool NodeMetaInfo::hasDefaultProperty() const
{
if constexpr (useProjectStorage())
- return isValid() && bool(typeData().defaultPropertyId);
+ return isValid() && bool(defaultPropertyDeclarationId());
else
return !defaultPropertyName().isEmpty();
}
@@ -2086,6 +2089,14 @@ const Storage::Info::Type &NodeMetaInfo::typeData() const
return *m_typeData;
}
+PropertyDeclarationId NodeMetaInfo::defaultPropertyDeclarationId() const
+{
+ if (!m_defaultPropertyId)
+ m_defaultPropertyId.emplace(m_projectStorage->defaultPropertyDeclarationId(m_typeId));
+
+ return *m_defaultPropertyId;
+}
+
bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int minorVersion) const
{
if (!isValid()) {
@@ -2444,12 +2455,9 @@ bool NodeMetaInfo::usesCustomParser() const
if (!isValid())
return false;
- auto type = typeName();
- return type == "QtQuick.VisualItemModel" || type == "Qt.VisualItemModel"
- || type == "QtQuick.VisualDataModel" || type == "Qt.VisualDataModel"
- || type == "QtQuick.ListModel" || type == "Qt.ListModel"
- || type == "QtQml.Models.ListModel" || type == "QtQuick.XmlListModel"
- || type == "Qt.XmlListModel" || type == "QtQml.XmlListModel.XmlListModel";
+ auto type = simplifiedTypeName();
+ return type == "VisualItemModel" || type == "VisualDataModel" || type == "ListModel"
+ || type == "XmlListModel";
}
}
@@ -2763,7 +2771,7 @@ bool NodeMetaInfo::isQtQuick3DLight() const
}
}
-bool NodeMetaInfo::isQtQuickListElement() const
+bool NodeMetaInfo::isQtQmlModelsListElement() const
{
if constexpr (useProjectStorage()) {
using namespace Storage::Info;
diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp
index 403731d1c4..16d9217f6a 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp
@@ -44,6 +44,7 @@ SubComponentManager::SubComponentManager(Model *model,
ExternalDependenciesInterface &externalDependencies)
: m_model(model)
, m_externalDependencies{externalDependencies}
+ , m_componentUtils{externalDependencies}
{
connect(&m_watcher, &QFileSystemWatcher::directoryChanged,
this, [this](const QString &path) { parseDirectory(path); });
@@ -192,7 +193,7 @@ void SubComponentManager::parseDirectory(const QString &canonicalDirPath, bool a
if (!model() || !model()->rewriterView())
return;
- if (canonicalDirPath.endsWith(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER))) {
+ if (m_componentUtils.isImport3dPath(canonicalDirPath)) {
parseQuick3DAssetsDir(canonicalDirPath);
return;
}
@@ -345,8 +346,8 @@ void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QStri
bool addToLibrary)
{
if (!addToLibrary || !model()
- || fileInfo.path().contains(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER))
- || fileInfo.path().contains(QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER))) {
+ || m_componentUtils.isImport3dPath(fileInfo.path())
+ || m_componentUtils.isComposedEffectPath(fileInfo.path())) {
return;
}
@@ -395,7 +396,7 @@ void SubComponentManager::parseQuick3DAssetsDir(const QString &quick3DAssetsPath
QDir quick3DAssetsDir(quick3DAssetsPath);
QStringList assets = quick3DAssetsDir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
for (QString &asset : assets)
- asset.prepend(QString(Constants::QUICK_3D_ASSETS_FOLDER).mid(1) + '.');
+ asset.prepend(m_componentUtils.import3dTypePrefix() + '.');
// Create item library entries for Quick3D assets that are imported by document
for (auto &import : std::as_const(m_imports)) {
@@ -460,7 +461,8 @@ QStringList SubComponentManager::quick3DAssetPaths() const
const auto impPaths = importPaths();
QStringList retPaths;
for (const auto &impPath : impPaths) {
- const QString assetPath = impPath + QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
+ QString path3d = m_componentUtils.import3dTypePath();
+ const QString assetPath = impPath + '/' + path3d;
if (QFileInfo::exists(assetPath))
retPaths << assetPath;
}
@@ -520,7 +522,7 @@ void SubComponentManager::update(const QUrl &filePath, const Imports &imports)
// Remove old watched asset paths
const QStringList watchPaths = m_watcher.directories();
- const QString &quick3DAssetFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
+ const QString &quick3DAssetFolder = m_componentUtils.import3dTypePath();
for (const auto &watchPath : watchPaths) {
if (watchPath.endsWith(quick3DAssetFolder))
m_watcher.removePath(watchPath);
@@ -580,7 +582,7 @@ void SubComponentManager::addAndParseImport(const Import &import)
} else {
QString url = import.url();
- if (url.startsWith(QString(Constants::QUICK_3D_ASSETS_FOLDER).mid(1))) {
+ if (url.startsWith(m_componentUtils.import3dTypePrefix())) {
parseQuick3DAssetsItem(import.url());
return;
}
diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
index 061ab8ae2b..125c7195a8 100644
--- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
@@ -567,8 +567,10 @@ void AbstractView::disableWidget()
void AbstractView::enableWidget()
{
- if (hasWidget() && widgetInfo().widgetFlags == DesignerWidgetFlags::DisableOnError)
- widgetInfo().widget->setEnabled(true);
+ if (hasWidget()) {
+ if (auto info = widgetInfo(); info.widgetFlags == DesignerWidgetFlags::DisableOnError)
+ info.widget->setEnabled(true);
+ }
}
QString AbstractView::contextHelpId() const
diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp
index 141548047e..23c17dc61a 100644
--- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp
+++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp
@@ -37,10 +37,11 @@ BindingProperty::BindingProperty(const PropertyName &propertyName, const Interna
void BindingProperty::setExpression(const QString &expression)
{
- Internal::WriteLocker locker(model());
if (!isValid())
return;
+ Internal::WriteLocker locker(model());
+
if (isDynamic())
qWarning() << "Calling BindingProperty::setExpression on dynamic property.";
diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp
index a4cd31b2a8..4f0bfba1ce 100644
--- a/src/plugins/qmldesigner/designercore/model/model.cpp
+++ b/src/plugins/qmldesigner/designercore/model/model.cpp
@@ -1758,14 +1758,22 @@ Storage::Info::ExportedTypeName Model::exportedTypeNameForMetaInfo(const NodeMet
return {};
}
-const Imports &Model::possibleImports() const
+Imports Model::possibleImports() const
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ return {};
+#else
return d->m_possibleImportList;
+#endif
}
-const Imports &Model::usedImports() const
+Imports Model::usedImports() const
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ return {};
+#else
return d->m_usedImportList;
+#endif
}
void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved)
@@ -1773,6 +1781,7 @@ void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved)
d->changeImports(std::move(importsToBeAdded), std::move(importsToBeRemoved));
}
+#ifndef QDS_USE_PROJECTSTORAGE
void Model::setPossibleImports(Imports possibleImports)
{
auto tracer = d->traceToken.begin("possible imports"_t);
@@ -1784,7 +1793,9 @@ void Model::setPossibleImports(Imports possibleImports)
d->notifyPossibleImportsChanged(d->m_possibleImportList);
}
}
+#endif
+#ifndef QDS_USE_PROJECTSTORAGE
void Model::setUsedImports(Imports usedImports)
{
auto tracer = d->traceToken.begin("used imports"_t);
@@ -1796,6 +1807,7 @@ void Model::setUsedImports(Imports usedImports)
d->notifyUsedImportsChanged(d->m_usedImportList);
}
}
+#endif
static bool compareVersions(const Import &import1, const Import &import2, bool allowHigherVersion)
{
@@ -1869,8 +1881,9 @@ QString Model::generateNewId(const QString &prefixName,
int counter = 0;
- QString newBaseId = QStringView(u"%1").arg(firstCharToLower(prefixName));
- newBaseId.remove(QRegularExpression(QStringLiteral("[^a-zA-Z0-9_]")));
+ static const QRegularExpression nonWordCharsRegex("\\W");
+ QString newBaseId = firstCharToLower(prefixName);
+ newBaseId.remove(nonWordCharsRegex);
if (!newBaseId.isEmpty()) {
QChar firstChar = newBaseId.at(0);
@@ -2021,14 +2034,6 @@ bool Model::isImportPossible(const Import &import, bool ignoreAlias, bool allowH
return false;
}
-QString Model::pathForImport(const Import &import)
-{
- if (!rewriterView())
- return QString();
-
- return rewriterView()->pathForImport(import);
-}
-
QStringList Model::importPaths() const
{
if (rewriterView())
@@ -2205,6 +2210,16 @@ NodeMetaInfo Model::qtQmlModelsListElementMetaInfo() const
}
}
+NodeMetaInfo Model::qtQmlXmlListModelXmlListModelRoleMetaInfo() const
+{
+ if constexpr (useProjectStorage()) {
+ using namespace Storage::Info;
+ return createNodeMetaInfo<QtQml_XmlListModel, XmlListModelRole>();
+ } else {
+ return metaInfo("QtQml.XmlListModel.XmlListModelRole");
+ }
+}
+
NodeMetaInfo Model::qmlQtObjectMetaInfo() const
{
if constexpr (useProjectStorage()) {
diff --git a/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp b/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp
index a61f1001f9..27ac2e67ab 100644
--- a/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp
+++ b/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp
@@ -515,8 +515,7 @@ struct BindingFilter
struct TargetFilter
{
TargetFilter(NodeDependencies &dependencies, Model *model)
- : flowViewFlowActionAreaMetaInfo{model->flowViewFlowActionAreaMetaInfo()}
- , flowViewFlowTransitionMetaInfo{model->flowViewFlowTransitionMetaInfo()}
+ : flowViewFlowTransitionMetaInfo{model->flowViewFlowTransitionMetaInfo()}
, qtQuickPropertyChangesMetaInfo{model->qtQuickPropertyChangesMetaInfo()}
, qtQuickTimelineKeyframeGroupMetaInfo{model->qtQuickTimelineKeyframeGroupMetaInfo()}
, qtQuickPropertyAnimationMetaInfo{model->qtQuickPropertyAnimationMetaInfo()}
diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp
index cb3f482289..6c3e1ea50f 100644
--- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp
+++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp
@@ -9,6 +9,8 @@
#include <projectstorage/projectstorage.h>
#include <projectstorage/sourcepathcache.h>
+#include <coreplugin/messagebox.h>
+
#include <utils/expected.h>
#include <utils/ranges.h>
@@ -107,19 +109,19 @@ PropertyMetaInfo metainfo(const ModelNode &node, const PropertyName &propertyNam
return node.metaInfo().property(propertyName);
}
-QString componentFilePath(const PathCacheType &pathCache, const NodeMetaInfo &metaInfo)
+QString componentFilePath([[maybe_unused]] const PathCacheType &pathCache, const NodeMetaInfo &metaInfo)
{
- if constexpr (useProjectStorage()) {
- auto typeSourceId = metaInfo.sourceId();
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto typeSourceId = metaInfo.sourceId();
- if (typeSourceId && metaInfo.isFileComponent()) {
- return pathCache.sourcePath(typeSourceId).toQString();
- }
- } else {
- return metaInfo.componentFileName();
+ if (typeSourceId && metaInfo.isFileComponent()) {
+ return pathCache.sourcePath(typeSourceId).toQString();
}
return {};
+#else
+ return metaInfo.componentFileName();
+#endif
}
QString componentFilePath(const ModelNode &node)
diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
index 826856428b..6e3b739096 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
@@ -9,8 +9,9 @@
#include "bindingproperty.h"
#include "qmlanchors.h"
-#include <model.h>
#include <abstractview.h>
+#include <generatedcomponentutils.h>
+#include <model.h>
#include <coreplugin/icore.h>
@@ -196,7 +197,9 @@ QmlItemNode QmlItemNode::createQmlItemNodeForEffect(AbstractView *view,
auto createEffectNode = [=, &newQmlItemNode, &parentProperty]() {
const QString effectName = QFileInfo(effectPath).baseName();
- Import import = Import::createLibraryImport("Effects." + effectName, "1.0");
+ Import import = Import::createLibraryImport(GeneratedComponentUtils(view->externalDependencies())
+ .composedEffectsTypePrefix()
+ + '.' + effectName, "1.0");
try {
if (!view->model()->hasImport(import, true, true))
view->model()->changeImports({import}, {});
@@ -748,7 +751,6 @@ void QmlFlowActionAreaNode::assignTargetFlowItem(const QmlFlowTargetNode &flowIt
ModelNode transition = flowView.addTransition(flowParent.modelNode(),
flowItem.modelNode());
-
modelNode().bindingProperty("target").setExpression(transition.validId());
}
diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
index 4ae2261e60..17d40daca3 100644
--- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
@@ -933,10 +933,12 @@ bool RewriterView::renameId(const QString& oldId, const QString& newId)
return false;
}
+#ifndef QDS_USE_PROJECTSTORAGE
const QmlJS::ScopeChain *RewriterView::scopeChain() const
{
return textToModelMerger()->scopeChain();
}
+#endif
const QmlJS::Document *RewriterView::document() const
{
@@ -989,25 +991,6 @@ QString RewriterView::convertTypeToImportAlias(const QString &type) const
return result;
}
-QString RewriterView::pathForImport(const Import &import)
-{
- if (scopeChain() && scopeChain()->context() && document()) {
- const QString importStr = import.isFileImport() ? import.file() : import.url();
- const QmlJS::Imports *imports = scopeChain()->context()->imports(document());
-
- QmlJS::ImportInfo importInfo;
-
- for (const QmlJS::Import &qmljsImport : imports->all()) {
- if (qmljsImport.info.name() == importStr)
- importInfo = qmljsImport.info;
- }
- const QString importPath = importInfo.path();
- return importPath;
- }
-
- return QString();
-}
-
QStringList RewriterView::importDirectories() const
{
const QList<Utils::FilePath> list(m_textToModelMerger->vContext().paths.begin(),
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index f7be6cf5e4..1c1aba5feb 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -57,7 +57,7 @@ using namespace QmlJS;
using namespace Qt::StringLiterals;
static Q_LOGGING_CATEGORY(rewriterBenchmark, "qtc.rewriter.load", QtWarningMsg)
-static Q_LOGGING_CATEGORY(texttomodelMergerDebug, "qtc.texttomodelmerger.debug", QtDebugMsg)
+static Q_LOGGING_CATEGORY(texttomodelMergerLog, "qtc.texttomodelmerger", QtWarningMsg)
namespace {
@@ -67,17 +67,6 @@ bool isSupportedAttachedProperties(const QString &propertyName)
|| propertyName.startsWith(QLatin1String("InsightCategory."));
}
-bool isSupportedVersion(QmlDesigner::Version version)
-{
- if (version.major == 2)
- return version.minor <= 15;
-
- if (version.major == 6)
- return version.minor <= 6;
-
- return false;
-}
-
bool isGlobalQtEnums(QStringView value)
{
static constexpr auto list = Utils::to_array<std::u16string_view>(
@@ -95,6 +84,9 @@ bool isGlobalQtEnums(QStringView value)
u"TopToBottom", u"UpArrowCursor", u"Vertical", u"WaitCursor",
u"WhatsThisCursor", u"WheelFocus"});
+ if (value.toString().startsWith("Key_"))
+ return true;
+
return std::binary_search(std::begin(list),
std::end(list),
QmlDesigner::ModelUtils::toStdStringView(value));
@@ -122,12 +114,6 @@ bool isKnownEnumScopes(QStringView value)
!= std::end(list);
}
-bool supportedQtQuickVersion(const QmlDesigner::Import &import)
-{
- auto version = import.toVersion();
- return version.isEmpty() || isSupportedVersion(version);
-}
-
QString stripQuotes(const QString &str)
{
if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"')))
@@ -431,14 +417,19 @@ namespace Internal {
class ReadingContext
{
public:
- ReadingContext(const Snapshot &snapshot, const Document::Ptr &doc,
- const ViewerContext &vContext, Model *model)
+ ReadingContext([[maybe_unused]] const Snapshot &snapshot,
+ [[maybe_unused]] const Document::Ptr &doc,
+ [[maybe_unused]] const ViewerContext &vContext,
+ Model *model)
: m_doc(doc)
+#ifndef QDS_USE_PROJECTSTORAGE
, m_context(
- Link(snapshot, vContext, ModelManagerInterface::instance()->builtins(doc))
- (doc, &m_diagnosticLinkMessages))
+ Link(snapshot,
+ vContext,
+ ModelManagerInterface::instance()->builtins(doc))(doc, &m_diagnosticLinkMessages))
, m_scopeChain(doc, m_context)
, m_scopeBuilder(&m_scopeChain)
+#endif
, m_model(model)
{
}
@@ -446,12 +437,19 @@ public:
~ReadingContext() = default;
Document::Ptr doc() const
- { return m_doc; }
+ {
+ return m_doc;
+ }
+#ifndef QDS_USE_PROJECTSTORAGE
void enterScope(AST::Node *node)
{ m_scopeBuilder.push(node); }
- void leaveScope() { m_scopeBuilder.pop(); }
+ void leaveScope()
+ {
+ m_scopeBuilder.pop();
+ }
+#endif
std::tuple<NodeMetaInfo, TypeName> lookup(AST::UiQualifiedId *astTypeNode)
{
@@ -481,113 +479,6 @@ public:
return node.metaInfo().hasProperty(propertyName.toUtf8());
}
- /// When something is changed here, also change Check::checkScopeObjectMember in
- /// qmljscheck.cpp
- /// ### Maybe put this into the context as a helper function.
- ///
- bool lookupProperty(const QString &prefix,
- const AST::UiQualifiedId *id,
- const Value **property = nullptr,
- const ObjectValue **parentObject = nullptr,
- QString *name = nullptr)
- {
- QList<const ObjectValue *> scopeObjects = m_scopeChain.qmlScopeObjects();
- if (scopeObjects.isEmpty())
- return false;
-
- if (!id)
- return false; // ### error?
-
- if (id->name.isEmpty()) // possible after error recovery
- return false;
-
- QString propertyName;
- if (prefix.isEmpty())
- propertyName = id->name.toString();
- else
- propertyName = prefix;
-
- if (name)
- *name = propertyName;
-
- if (propertyName == u"id" && !id->next)
- return false; // ### should probably be a special value
-
- // attached properties
- bool isAttachedProperty = false;
- if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
- isAttachedProperty = true;
- if (const ObjectValue *qmlTypes = m_scopeChain.qmlTypes())
- scopeObjects += qmlTypes;
- }
-
- if (scopeObjects.isEmpty())
- return false;
-
- // global lookup for first part of id
- const ObjectValue *objectValue = nullptr;
- const Value *value = nullptr;
- for (int i = scopeObjects.size() - 1; i >= 0; --i) {
- objectValue = scopeObjects[i];
- value = objectValue->lookupMember(propertyName, m_context);
- if (value)
- break;
- }
- if (parentObject)
- *parentObject = objectValue;
- if (!value) {
- qCInfo(texttomodelMergerDebug) << Q_FUNC_INFO << "Skipping invalid property name" << propertyName;
- return false;
- }
-
- // can't look up members for attached properties
- if (isAttachedProperty)
- return false;
-
- // resolve references
- if (const Reference *ref = value->asReference())
- value = m_context->lookupReference(ref);
-
- // member lookup
- const AST::UiQualifiedId *idPart = id;
- if (prefix.isEmpty())
- idPart = idPart->next;
- for (; idPart; idPart = idPart->next) {
- objectValue = value_cast<ObjectValue>(value);
- if (! objectValue) {
-// if (idPart->name)
-// qDebug() << idPart->name->asString() << "has no property named"
-// << propertyName;
- return false;
- }
- if (parentObject)
- *parentObject = objectValue;
-
- if (idPart->name.isEmpty()) {
- // somebody typed "id." and error recovery still gave us a valid tree,
- // so just bail out here.
- return false;
- }
-
- propertyName = idPart->name.toString();
- if (name)
- *name = propertyName;
-
- value = objectValue->lookupMember(propertyName, m_context);
- if (! value) {
-// if (idPart->name)
-// qDebug() << "In" << idPart->name->asString() << ":"
-// << objectValue->className() << "has no property named"
-// << propertyName;
- return false;
- }
- }
-
- if (property)
- *property = value;
- return true;
- }
-
bool isArrayProperty(const AbstractProperty &property)
{
return ModelUtils::metainfo(property).isListProperty();
@@ -610,9 +501,9 @@ public:
if (!propertyMetaInfo.isValid()) {
const bool isAttached = !propertyName.isEmpty() && propertyName[0].isUpper();
// Only list elements might have unknown properties.
- if (!node.metaInfo().isQtQuickListElement() && !isAttached) {
- qCInfo(texttomodelMergerDebug)
- << Q_FUNC_INFO << "Unknown property"
+ if (!node.metaInfo().isQtQmlModelsListElement() && !isAttached) {
+ qCInfo(texttomodelMergerLog)
+ << Q_FUNC_INFO << "\nUnknown property"
<< propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line"
<< propertyId->identifierToken.startLine << "column"
<< propertyId->identifierToken.startColumn;
@@ -685,9 +576,12 @@ public:
return QVariant();
}
-
+#ifndef QDS_USE_PROJECTSTORAGE
const ScopeChain &scopeChain() const
- { return m_scopeChain; }
+ {
+ return m_scopeChain;
+ }
+#endif
QList<DiagnosticMessage> diagnosticLinkMessages() const
{ return m_diagnosticLinkMessages; }
@@ -695,9 +589,11 @@ public:
private:
Document::Ptr m_doc;
QList<DiagnosticMessage> m_diagnosticLinkMessages;
+#ifndef QDS_USE_PROJECTSTORAGE
ContextPtr m_context;
ScopeChain m_scopeChain;
ScopeBuilder m_scopeBuilder;
+#endif
Model *m_model;
};
@@ -841,6 +737,7 @@ constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"),
Equals(u"QtQuick.Controls.NativeStyle"),
Equals(u"QtQuick.Controls.Universal"),
Equals(u"QtQuick.Controls.Windows"),
+ Equals(u"QtQuick3D.MaterialEditor"),
StartsWith(u"QtQuick.LocalStorage"),
StartsWith(u"QtQuick.NativeStyle"),
StartsWith(u"QtQuick.Pdf"),
@@ -866,6 +763,7 @@ constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"),
StartsWith(u"QtWebSockets"),
StartsWith(u"QtWebView"));
+#ifndef QDS_USE_PROJECTSTORAGE
bool skipModule(QStringView moduleName)
{
return std::apply([=](const auto &...skipModule) { return (skipModule(moduleName) || ...); },
@@ -931,9 +829,11 @@ QmlDesigner::Imports createQt5Modules()
QmlDesigner::Import::createLibraryImport("QtQuick.Studio.MultiText", "1.0"),
QmlDesigner::Import::createLibraryImport("Qt.SafeRenderer", "2.0")};
}
+#endif
} // namespace
+#ifndef QDS_USE_PROJECTSTORAGE
void TextToModelMerger::setupPossibleImports()
{
if (!m_rewriterView->possibleImportsEnabled())
@@ -942,10 +842,10 @@ void TextToModelMerger::setupPossibleImports()
static QUrl lastProjectUrl;
auto &externalDependencies = m_rewriterView->externalDependencies();
auto projectUrl = externalDependencies.projectUrl();
+
auto allUsedImports = m_scopeChain->context()->imports(m_document.data())->all();
if (m_possibleModules.isEmpty() || projectUrl != lastProjectUrl) {
-
auto &externalDependencies = m_rewriterView->externalDependencies();
if (externalDependencies.isQt6Project()) {
ModuleScanner moduleScanner{[&](QStringView moduleName) {
@@ -975,7 +875,9 @@ void TextToModelMerger::setupPossibleImports()
if (m_rewriterView->isAttached())
m_rewriterView->model()->setPossibleImports(modules);
}
+#endif
+#ifndef QDS_USE_PROJECTSTORAGE
void TextToModelMerger::setupUsedImports()
{
const QmlJS::Imports *imports = m_scopeChain->context()->imports(m_document.data());
@@ -1010,6 +912,7 @@ void TextToModelMerger::setupUsedImports()
if (m_rewriterView->isAttached())
m_rewriterView->model()->setUsedImports(usedImports);
}
+#endif
Document::MutablePtr TextToModelMerger::createParsedDocument(const QUrl &url, const QString &data, QList<DocumentMessage> *errors)
{
@@ -1100,15 +1003,16 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH
m_vContext = ModelManagerInterface::instance()->projectVContext(Dialect::Qml, m_document);
ReadingContext ctxt(snapshot, m_document, m_vContext, m_rewriterView->model());
- m_scopeChain = QSharedPointer<const ScopeChain>(
- new ScopeChain(ctxt.scopeChain()));
+
+#ifndef QDS_USE_PROJECTSTORAGE
+ m_scopeChain = QSharedPointer<const ScopeChain>(new ScopeChain(ctxt.scopeChain()));
if (view()->checkLinkErrors()) {
qCInfo(rewriterBenchmark) << "linked:" << time.elapsed();
collectLinkErrors(&errors, ctxt);
}
-
setupPossibleImports();
+#endif
qCInfo(rewriterBenchmark) << "possible imports:" << time.elapsed();
@@ -1142,7 +1046,9 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH
qCInfo(rewriterBenchmark) << "synced nodes:" << time.elapsed();
+#ifndef QDS_USE_PROJECTSTORAGE
setupUsedImports();
+#endif
setActive(false);
@@ -1239,8 +1145,9 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
else if (!modelNode.nodeSource().isEmpty() || modelNode.nodeSourceType() != ModelNode::NodeWithoutSource)
clearImplicitComponentDelayed(modelNode, differenceHandler.isAmender());
-
+#ifndef QDS_USE_PROJECTSTORAGE
context->enterScope(astNode);
+#endif
QSet<PropertyName> modelPropertyNames = Utils::toSet(modelNode.propertyNames());
if (!modelNode.id().isEmpty())
@@ -1254,7 +1161,8 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
if (auto array = AST::cast<AST::UiArrayBinding *>(member)) {
const QString astPropertyName = toString(array->qualifiedId);
- if (isPropertyChangesType(typeName) || isConnectionsType(typeName) || context->lookupProperty(QString(), array->qualifiedId)) {
+ if (isPropertyChangesType(typeName) || isConnectionsType(typeName)
+ || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) {
AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8());
QList<AST::UiObjectMember *> arrayMembers;
for (AST::UiArrayMemberList *iter = array->members; iter; iter = iter->next)
@@ -1286,13 +1194,8 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
// Store Behaviours in the default property
defaultPropertyItems.append(member);
} else {
- const Value *propertyType = nullptr;
- const ObjectValue *containingObject = nullptr;
- if (context->lookupProperty({},
- binding->qualifiedId,
- &propertyType,
- &containingObject)
- || isPropertyChangesType(typeName) || isConnectionsType(typeName)) {
+ if (isPropertyChangesType(typeName) || isConnectionsType(typeName)
+ || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) {
AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8());
if (context->isArrayProperty(modelProperty))
syncArrayProperty(modelProperty, {member}, context, differenceHandler);
@@ -1396,7 +1299,9 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
differenceHandler.propertyAbsentFromQml(modelProperty);
}
+#ifndef QDS_USE_PROJECTSTORAGE
context->leaveScope();
+#endif
}
static QVariant parsePropertyExpression(AST::ExpressionNode *expressionNode)
@@ -1476,9 +1381,8 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN
}
if (isLiteralValue(script)) {
- if (isPropertyChangesType(modelNode.type())
- || isConnectionsType(modelNode.type())
- || isListElementType(modelNode.type())) {
+ if (isPropertyChangesType(modelNode.type()) || isConnectionsType(modelNode.type())
+ || isListElementType(modelNode.type())) {
AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8());
QVariant variantValue = parsePropertyScriptBinding(script);
if (!variantValue.isValid())
@@ -1512,15 +1416,14 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN
syncVariantProperty(modelProperty, enumValue, TypeName(), differenceHandler); // TODO: parse type
return astPropertyName.toUtf8();
} else { // Not an enum, so:
- if (isPropertyChangesType(modelNode.type())
- || isConnectionsType(modelNode.type())
- || context->lookupProperty(prefix, script->qualifiedId)
- || isSupportedAttachedProperties(astPropertyName)) {
+ if (isPropertyChangesType(modelNode.type()) || isConnectionsType(modelNode.type())
+ || isSupportedAttachedProperties(astPropertyName)
+ || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) {
AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8());
syncExpressionProperty(modelProperty, astValue, TypeName(), differenceHandler); // TODO: parse type
return astPropertyName.toUtf8();
} else {
- qWarning() << Q_FUNC_INFO << "Skipping invalid expression property" << astPropertyName
+ qCInfo(texttomodelMergerLog) << Q_FUNC_INFO << "\nSkipping invalid expression property" << astPropertyName
<< "for node type" << modelNode.type();
return PropertyName();
}
@@ -2232,42 +2135,31 @@ void TextToModelMerger::collectImportErrors(QList<DocumentMessage> *errors)
bool hasQtQuick = false;
for (const QmlDesigner::Import &import : m_rewriterView->model()->imports()) {
if (import.isLibraryImport() && import.url() == u"QtQuick") {
- if (supportedQtQuickVersion(import)) {
- hasQtQuick = true;
-
- auto &externalDependencies = m_rewriterView->externalDependencies();
- if (externalDependencies.hasStartupTarget()) {
- const bool qt6import = !import.hasVersion() || import.majorVersion() == 6;
-
- if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) {
- const QmlJS::DiagnosticMessage diagnosticMessage(
- QmlJS::Severity::Error,
- SourceLocation(0, 0, 0, 0),
- QCoreApplication::translate(
- "QmlDesigner::TextToModelMerger",
- "Qt Quick 6 is not supported with a Qt 5 kit."));
- errors->prepend(
- DocumentMessage(diagnosticMessage,
- QUrl::fromLocalFile(m_document->fileName().path())));
- }
- } else {
+ hasQtQuick = true;
+
+ auto &externalDependencies = m_rewriterView->externalDependencies();
+ if (externalDependencies.hasStartupTarget()) {
+ const bool qt6import = !import.hasVersion() || import.majorVersion() == 6;
+
+ if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) {
const QmlJS::DiagnosticMessage diagnosticMessage(
QmlJS::Severity::Error,
SourceLocation(0, 0, 0, 0),
- QCoreApplication::translate("QmlDesigner::TextToModelMerger",
- "The Design Mode requires a valid Qt kit."));
+ QCoreApplication::translate(
+ "QmlDesigner::TextToModelMerger",
+ "Qt Quick 6 is not supported with a Qt 5 kit."));
errors->prepend(
DocumentMessage(diagnosticMessage,
QUrl::fromLocalFile(m_document->fileName().path())));
}
} else {
- const QmlJS::DiagnosticMessage
- diagnosticMessage(QmlJS::Severity::Error,
- SourceLocation(0, 0, 0, 0),
- QCoreApplication::translate("QmlDesigner::TextToModelMerger",
- "Unsupported Qt Quick version."));
- errors->append(DocumentMessage(diagnosticMessage,
- QUrl::fromLocalFile(m_document->fileName().path())));
+ const QmlJS::DiagnosticMessage diagnosticMessage(
+ QmlJS::Severity::Error,
+ SourceLocation(0, 0, 0, 0),
+ QCoreApplication::translate("QmlDesigner::TextToModelMerger",
+ "The Design Mode requires a valid Qt kit."));
+ errors->prepend(DocumentMessage(diagnosticMessage,
+ QUrl::fromLocalFile(m_document->fileName().path())));
}
}
}
@@ -2276,8 +2168,10 @@ void TextToModelMerger::collectImportErrors(QList<DocumentMessage> *errors)
errors->append(DocumentMessage(QCoreApplication::translate("QmlDesigner::TextToModelMerger", "No import for Qt Quick found.")));
}
-void TextToModelMerger::collectSemanticErrorsAndWarnings(QList<DocumentMessage> *errors, QList<DocumentMessage> *warnings)
+void TextToModelMerger::collectSemanticErrorsAndWarnings(
+ [[maybe_unused]] QList<DocumentMessage> *errors, [[maybe_unused]] QList<DocumentMessage> *warnings)
{
+#ifndef QDS_USE_PROJECTSTORAGE
Check check(m_document, m_scopeChain->context());
check.disableMessage(StaticAnalysis::ErrPrototypeCycle);
check.disableMessage(StaticAnalysis::ErrCouldNotResolvePrototype);
@@ -2306,6 +2200,7 @@ void TextToModelMerger::collectSemanticErrorsAndWarnings(QList<DocumentMessage>
if (message.severity == Severity::Warning)
warnings->append(DocumentMessage(message.toDiagnosticMessage(), fileNameUrl));
}
+#endif
}
void TextToModelMerger::populateQrcMapping(const QString &filePath)
@@ -2410,6 +2305,9 @@ QSet<QPair<QString, QString> > TextToModelMerger::qrcMapping() const
QList<QmlTypeData> TextToModelMerger::getQMLSingletons() const
{
+#ifdef QDS_USE_PROJECTSTORAGE
+ return {};
+#else
QList<QmlTypeData> list;
if (!m_scopeChain || !m_scopeChain->document())
return list;
@@ -2440,6 +2338,7 @@ QList<QmlTypeData> TextToModelMerger::getQMLSingletons() const
}
}
return list;
+#endif
}
void TextToModelMerger::clearPossibleImportKeys()
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h
index f511906040..e22f747718 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h
@@ -37,15 +37,19 @@ public:
bool isActive() const;
void setupImports(const QmlJS::Document::Ptr &doc, DifferenceHandler &differenceHandler);
+#ifndef QDS_USE_PROJECTSTORAGE
void setupPossibleImports();
+#endif
void setupUsedImports();
bool load(const QString &data, DifferenceHandler &differenceHandler);
RewriterView *view() const
{ return m_rewriterView; }
+#ifndef QDS_USE_PROJECTSTORAGE
const QmlJS::ScopeChain *scopeChain() const
{ return m_scopeChain.data(); }
+#endif
const QmlJS::Document *document() const
{ return m_document.data(); }
@@ -141,7 +145,9 @@ private:
private:
RewriterView *m_rewriterView;
bool m_isActive;
+#ifndef QDS_USE_PROJECTSTORAGE
QSharedPointer<const QmlJS::ScopeChain> m_scopeChain;
+#endif
QmlJS::Document::Ptr m_document;
QTimer m_setupTimer;
QSet<ModelNode> m_setupComponentList;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
index 03c25dfac7..35658c005f 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
@@ -91,6 +91,7 @@ inline constexpr char QtMultimedia[] = "QtMultimedia";
inline constexpr char QtObject[] = "QtObject";
inline constexpr char QtQml[] = "QtQml";
inline constexpr char QtQml_Models[] = "QtQml.Models";
+inline constexpr char QtQml_XmlListModel[] = "QtQml.XmlListModel";
inline constexpr char QtQuick3D[] = "QtQuick3D";
inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D";
inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative";
@@ -131,6 +132,7 @@ inline constexpr char Transition[] = "Transition";
inline constexpr char UIntType[] = "uint";
inline constexpr char View3D[] = "View3D";
inline constexpr char Window[] = "Window";
+inline constexpr char XmlListModelRole[] = "XmlListModelRole";
inline constexpr char color[] = "color";
inline constexpr char date[] = "date";
inline constexpr char font[] = "font";
@@ -176,6 +178,7 @@ class CommonTypeCache
CacheType<QtMultimedia, SoundEffect>,
CacheType<QtQml_Models, ListElement>,
CacheType<QtQml_Models, ListModel>,
+ CacheType<QtQml_XmlListModel, XmlListModelRole>,
CacheType<QtQuick, BorderImage>,
CacheType<QtQuick, GridView>,
CacheType<QtQuick, Image>,
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h b/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h
index f3e275b8f3..48b3ba2700 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h
@@ -50,6 +50,18 @@ public:
explicit operator bool() const { return isValid(); }
+ template<typename String>
+ friend void convertToString(String &string, const FileStatus &fileStatus)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("source id", fileStatus.sourceId),
+ keyValue("size", fileStatus.size),
+ keyValue("last modified", fileStatus.lastModified));
+
+ convertToString(string, dict);
+ }
+
public:
SourceId sourceId;
long long size = -1;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h
index 078fd1ee98..28754a8560 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h
@@ -6,6 +6,7 @@
#include "filestatuscache.h"
#include "filesysteminterface.h"
#include "nonlockingmutex.h"
+#include "projectstoragefwd.h"
namespace Sqlite {
class Database;
@@ -16,12 +17,9 @@ namespace QmlDesigner {
template<typename ProjectStorage, typename Mutex>
class SourcePathCache;
-template<typename Database>
-class ProjectStorage;
-
class FileSystem : public FileSystemInterface
{
- using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>;
+ using PathCache = SourcePathCache<ProjectStorage, NonLockingMutex>;
public:
FileSystem(PathCache &sourcePathCache)
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp
index 3e493e8772..a7577d3ab7 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp
@@ -3,20 +3,4542 @@
#include "projectstorage.h"
-#include <tracing/qmldesignertracing.h>
-
#include <sqlitedatabase.h>
namespace QmlDesigner {
-NanotraceHR::StringViewCategory<projectStorageTracingStatus()> &projectStorageCategory()
+struct ProjectStorage::Statements
+{
+ Statements(Sqlite::Database &database)
+ : database{database}
+ {}
+
+ Sqlite::Database &database;
+ Sqlite::ReadWriteStatement<1, 2> insertTypeStatement{
+ "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database};
+ Sqlite::WriteStatement<5> updatePrototypeAndExtensionStatement{
+ "UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 "
+ "WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId "
+ "IS NOT ?4 OR extensionNameId IS NOT ?5)",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{
+ "SELECT typeId FROM exportedTypeNames WHERE name=?1", database};
+ mutable Sqlite::ReadStatement<1, 2> selectTypeIdByModuleIdAndExportedNameStatement{
+ "SELECT typeId FROM exportedTypeNames "
+ "WHERE moduleId=?1 AND name=?2 "
+ "ORDER BY majorVersion DESC, minorVersion DESC "
+ "LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement{
+ "SELECT typeId FROM exportedTypeNames "
+ "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3"
+ "ORDER BY minorVersion DESC "
+ "LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<1, 4> selectTypeIdByModuleIdAndExportedNameAndVersionStatement{
+ "SELECT typeId FROM exportedTypeNames "
+ "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3 AND minorVersion<=?4"
+ "ORDER BY minorVersion DESC "
+ "LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<3, 1> selectPropertyDeclarationResultByPropertyDeclarationIdStatement{
+ "SELECT propertyTypeId, propertyDeclarationId, propertyTraits "
+ "FROM propertyDeclarations "
+ "WHERE propertyDeclarationId=?1 "
+ "LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectSourceContextIdFromSourceContextsBySourceContextPathStatement{
+ "SELECT sourceContextId FROM sourceContexts WHERE sourceContextPath = ?", database};
+ mutable Sqlite::ReadStatement<1, 1> selectSourceContextPathFromSourceContextsBySourceContextIdStatement{
+ "SELECT sourceContextPath FROM sourceContexts WHERE sourceContextId = ?", database};
+ mutable Sqlite::ReadStatement<2> selectAllSourceContextsStatement{
+ "SELECT sourceContextPath, sourceContextId FROM sourceContexts", database};
+ Sqlite::WriteStatement<1> insertIntoSourceContextsStatement{
+ "INSERT INTO sourceContexts(sourceContextPath) VALUES (?)", database};
+ mutable Sqlite::ReadStatement<1, 2> selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement{
+ "SELECT sourceId FROM sources WHERE sourceContextId = ? AND sourceName = ?", database};
+ mutable Sqlite::ReadStatement<2, 1> selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement{
+ "SELECT sourceName, sourceContextId FROM sources WHERE sourceId = ?", database};
+ mutable Sqlite::ReadStatement<1, 1> selectSourceContextIdFromSourcesBySourceIdStatement{
+ "SELECT sourceContextId FROM sources WHERE sourceId = ?", database};
+ Sqlite::WriteStatement<2> insertIntoSourcesStatement{
+ "INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database};
+ mutable Sqlite::ReadStatement<3> selectAllSourcesStatement{
+ "SELECT sourceName, sourceContextId, sourceId FROM sources", database};
+ mutable Sqlite::ReadStatement<8, 1> selectTypeByTypeIdStatement{
+ "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, "
+ "pd.name "
+ "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON "
+ "defaultPropertyId=propertyDeclarationId "
+ "WHERE t.typeId=?",
+ database};
+ mutable Sqlite::ReadStatement<4, 1> selectExportedTypesByTypeIdStatement{
+ "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM "
+ "exportedTypeNames WHERE typeId=?",
+ database};
+ mutable Sqlite::ReadStatement<4, 2> selectExportedTypesByTypeIdAndSourceIdStatement{
+ "SELECT etn.moduleId, name, ifnull(etn.majorVersion, -1), ifnull(etn.minorVersion, -1) "
+ "FROM exportedTypeNames AS etn JOIN documentImports USING(moduleId) WHERE typeId=?1 AND "
+ "sourceId=?2",
+ database};
+ mutable Sqlite::ReadStatement<8> selectTypesStatement{
+ "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, "
+ "pd.name "
+ "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON "
+ "defaultPropertyId=propertyDeclarationId",
+ database};
+ Sqlite::WriteStatement<2> updateTypeTraitStatement{
+ "UPDATE types SET traits = ?2 WHERE typeId=?1", database};
+ Sqlite::WriteStatement<2> updateTypeAnnotationTraitStatement{
+ "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database};
+ Sqlite::ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{
+ "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN "
+ "carray(?2))",
+ database};
+ Sqlite::WriteStatement<1> deleteTypeNamesByTypeIdStatement{
+ "DELETE FROM exportedTypeNames WHERE typeId=?", database};
+ Sqlite::WriteStatement<1> deleteEnumerationDeclarationByTypeIdStatement{
+ "DELETE FROM enumerationDeclarations WHERE typeId=?", database};
+ Sqlite::WriteStatement<1> deletePropertyDeclarationByTypeIdStatement{
+ "DELETE FROM propertyDeclarations WHERE typeId=?", database};
+ Sqlite::WriteStatement<1> deleteFunctionDeclarationByTypeIdStatement{
+ "DELETE FROM functionDeclarations WHERE typeId=?", database};
+ Sqlite::WriteStatement<1> deleteSignalDeclarationByTypeIdStatement{
+ "DELETE FROM signalDeclarations WHERE typeId=?", database};
+ Sqlite::WriteStatement<1> deleteTypeStatement{"DELETE FROM types WHERE typeId=?", database};
+ mutable Sqlite::ReadStatement<4, 1> selectPropertyDeclarationsByTypeIdStatement{
+ "SELECT name, propertyTypeId, propertyTraits, (SELECT name FROM "
+ "propertyDeclarations WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM "
+ "propertyDeclarations AS pd WHERE typeId=?",
+ database};
+ Sqlite::ReadStatement<6, 1> selectPropertyDeclarationsForTypeIdStatement{
+ "SELECT name, propertyTraits, propertyTypeId, propertyImportedTypeNameId, "
+ "propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations "
+ "WHERE typeId=? ORDER BY name",
+ database};
+ Sqlite::ReadWriteStatement<1, 5> insertPropertyDeclarationStatement{
+ "INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, "
+ "propertyImportedTypeNameId, aliasPropertyDeclarationId) VALUES(?1, ?2, ?3, ?4, ?5, NULL) "
+ "RETURNING propertyDeclarationId",
+ database};
+ Sqlite::WriteStatement<4> updatePropertyDeclarationStatement{
+ "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, "
+ "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=NULL WHERE "
+ "propertyDeclarationId=?1",
+ database};
+ Sqlite::WriteStatement<3> updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement{
+ "WITH RECURSIVE "
+ " properties(aliasPropertyDeclarationId) AS ( "
+ " SELECT propertyDeclarationId FROM propertyDeclarations WHERE "
+ " aliasPropertyDeclarationId=?1 "
+ " UNION ALL "
+ " SELECT pd.propertyDeclarationId FROM "
+ " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) "
+ "UPDATE propertyDeclarations AS pd "
+ "SET propertyTypeId=?2, propertyTraits=?3 "
+ "FROM properties AS p "
+ "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId",
+ database};
+ Sqlite::WriteStatement<1> updatePropertyAliasDeclarationRecursivelyStatement{
+ "WITH RECURSIVE "
+ " propertyValues(propertyTypeId, propertyTraits) AS ("
+ " SELECT propertyTypeId, propertyTraits FROM propertyDeclarations "
+ " WHERE propertyDeclarationId=?1), "
+ " properties(aliasPropertyDeclarationId) AS ( "
+ " SELECT propertyDeclarationId FROM propertyDeclarations WHERE "
+ " aliasPropertyDeclarationId=?1 "
+ " UNION ALL "
+ " SELECT pd.propertyDeclarationId FROM "
+ " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) "
+ "UPDATE propertyDeclarations AS pd "
+ "SET propertyTypeId=pv.propertyTypeId, propertyTraits=pv.propertyTraits "
+ "FROM properties AS p, propertyValues AS pv "
+ "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId",
+ database};
+ Sqlite::WriteStatement<1> deletePropertyDeclarationStatement{
+ "DELETE FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
+ Sqlite::ReadStatement<3, 1> selectPropertyDeclarationsWithAliasForTypeIdStatement{
+ "SELECT name, propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations "
+ "WHERE typeId=? AND aliasPropertyDeclarationId IS NOT NULL ORDER BY name",
+ database};
+ Sqlite::WriteStatement<5> updatePropertyDeclarationWithAliasAndTypeStatement{
+ "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, "
+ "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=?5 WHERE "
+ "propertyDeclarationId=?1",
+ database};
+ Sqlite::ReadWriteStatement<1, 2> insertAliasPropertyDeclarationStatement{
+ "INSERT INTO propertyDeclarations(typeId, name) VALUES(?1, ?2) RETURNING "
+ "propertyDeclarationId",
+ database};
+ mutable Sqlite::ReadStatement<4, 1> selectFunctionDeclarationsForTypeIdStatement{
+ "SELECT name, returnTypeName, signature, functionDeclarationId FROM "
+ "functionDeclarations WHERE typeId=? ORDER BY name, signature",
+ database};
+ mutable Sqlite::ReadStatement<3, 1> selectFunctionDeclarationsForTypeIdWithoutSignatureStatement{
+ "SELECT name, returnTypeName, functionDeclarationId FROM "
+ "functionDeclarations WHERE typeId=? ORDER BY name",
+ database};
+ mutable Sqlite::ReadStatement<3, 1> selectFunctionParameterDeclarationsStatement{
+ "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), "
+ "json_extract(json_each.value, '$.tr') FROM functionDeclarations, "
+ "json_each(functionDeclarations.signature) WHERE functionDeclarationId=?",
+ database};
+ Sqlite::WriteStatement<4> insertFunctionDeclarationStatement{
+ "INSERT INTO functionDeclarations(typeId, name, returnTypeName, signature) VALUES(?1, ?2, "
+ "?3, ?4)",
+ database};
+ Sqlite::WriteStatement<3> updateFunctionDeclarationStatement{
+ "UPDATE functionDeclarations "
+ "SET returnTypeName=?2, signature=?3 "
+ "WHERE functionDeclarationId=?1",
+ database};
+ Sqlite::WriteStatement<1> deleteFunctionDeclarationStatement{
+ "DELETE FROM functionDeclarations WHERE functionDeclarationId=?", database};
+ mutable Sqlite::ReadStatement<3, 1> selectSignalDeclarationsForTypeIdStatement{
+ "SELECT name, signature, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER "
+ "BY name, signature",
+ database};
+ mutable Sqlite::ReadStatement<2, 1> selectSignalDeclarationsForTypeIdWithoutSignatureStatement{
+ "SELECT name, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER BY name",
+ database};
+ mutable Sqlite::ReadStatement<3, 1> selectSignalParameterDeclarationsStatement{
+ "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), "
+ "json_extract(json_each.value, '$.tr') FROM signalDeclarations, "
+ "json_each(signalDeclarations.signature) WHERE signalDeclarationId=?",
+ database};
+ Sqlite::WriteStatement<3> insertSignalDeclarationStatement{
+ "INSERT INTO signalDeclarations(typeId, name, signature) VALUES(?1, ?2, ?3)", database};
+ Sqlite::WriteStatement<2> updateSignalDeclarationStatement{
+ "UPDATE signalDeclarations SET signature=?2 WHERE signalDeclarationId=?1", database};
+ Sqlite::WriteStatement<1> deleteSignalDeclarationStatement{
+ "DELETE FROM signalDeclarations WHERE signalDeclarationId=?", database};
+ mutable Sqlite::ReadStatement<3, 1> selectEnumerationDeclarationsForTypeIdStatement{
+ "SELECT name, enumeratorDeclarations, enumerationDeclarationId FROM "
+ "enumerationDeclarations WHERE typeId=? ORDER BY name",
+ database};
+ mutable Sqlite::ReadStatement<2, 1> selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement{
+ "SELECT name, enumerationDeclarationId FROM enumerationDeclarations WHERE typeId=? ORDER "
+ "BY name",
+ database};
+ mutable Sqlite::ReadStatement<3, 1> selectEnumeratorDeclarationStatement{
+ "SELECT json_each.key, json_each.value, json_each.type!='null' FROM "
+ "enumerationDeclarations, json_each(enumerationDeclarations.enumeratorDeclarations) WHERE "
+ "enumerationDeclarationId=?",
+ database};
+ Sqlite::WriteStatement<3> insertEnumerationDeclarationStatement{
+ "INSERT INTO enumerationDeclarations(typeId, name, enumeratorDeclarations) VALUES(?1, ?2, "
+ "?3)",
+ database};
+ Sqlite::WriteStatement<2> updateEnumerationDeclarationStatement{
+ "UPDATE enumerationDeclarations SET enumeratorDeclarations=?2 WHERE "
+ "enumerationDeclarationId=?1",
+ database};
+ Sqlite::WriteStatement<1> deleteEnumerationDeclarationStatement{
+ "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database};
+ mutable Sqlite::ReadStatement<1, 1> selectModuleIdByNameStatement{
+ "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database};
+ mutable Sqlite::ReadWriteStatement<1, 1> insertModuleNameStatement{
+ "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database};
+ mutable Sqlite::ReadStatement<1, 1> selectModuleNameStatement{
+ "SELECT name FROM modules WHERE moduleId =?1", database};
+ mutable Sqlite::ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules",
+ database};
+ mutable Sqlite::ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{
+ "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database};
+ mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{
+ "SELECT typeId FROM exportedTypeNames WHERE moduleId IN carray(?1, ?2, 'int32') AND "
+ "name=?3",
+ database};
+ mutable Sqlite::ReadStatement<4> selectAllDocumentImportForSourceIdStatement{
+ "SELECT moduleId, majorVersion, minorVersion, sourceId "
+ "FROM documentImports ",
+ database};
+ mutable Sqlite::ReadStatement<5, 2> selectDocumentImportForSourceIdStatement{
+ "SELECT importId, sourceId, moduleId, majorVersion, minorVersion "
+ "FROM documentImports WHERE sourceId IN carray(?1) AND kind=?2 ORDER BY sourceId, "
+ "moduleId, majorVersion, minorVersion",
+ database};
+ Sqlite::ReadWriteStatement<1, 5> insertDocumentImportWithoutVersionStatement{
+ "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, "
+ "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING importId",
+ database};
+ Sqlite::ReadWriteStatement<1, 6> insertDocumentImportWithMajorVersionStatement{
+ "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, "
+ "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6) RETURNING importId",
+ database};
+ Sqlite::ReadWriteStatement<1, 7> insertDocumentImportWithVersionStatement{
+ "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, "
+ "minorVersion, parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) RETURNING "
+ "importId",
+ database};
+ Sqlite::WriteStatement<1> deleteDocumentImportStatement{
+ "DELETE FROM documentImports WHERE importId=?1", database};
+ Sqlite::WriteStatement<2> deleteDocumentImportsWithParentImportIdStatement{
+ "DELETE FROM documentImports WHERE sourceId=?1 AND parentImportId=?2", database};
+ Sqlite::WriteStatement<1> deleteDocumentImportsWithSourceIdsStatement{
+ "DELETE FROM documentImports WHERE sourceId IN carray(?1)", database};
+ mutable Sqlite::ReadStatement<1, 2> selectPropertyDeclarationIdByTypeIdAndNameStatement{
+ "SELECT propertyDeclarationId "
+ "FROM propertyDeclarations "
+ "WHERE typeId=?1 AND name=?2 "
+ "LIMIT 1",
+ database};
+ Sqlite::WriteStatement<2> updateAliasIdPropertyDeclarationStatement{
+ "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2 WHERE "
+ "aliasPropertyDeclarationId=?1",
+ database};
+ Sqlite::WriteStatement<2> updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement{
+ "UPDATE propertyDeclarations SET propertyTypeId=new.propertyTypeId, "
+ "propertyTraits=new.propertyTraits, aliasPropertyDeclarationId=?1 FROM (SELECT "
+ "propertyTypeId, propertyTraits FROM propertyDeclarations WHERE propertyDeclarationId=?1) "
+ "AS new WHERE aliasPropertyDeclarationId=?2",
+ database};
+ Sqlite::WriteStatement<1> updateAliasPropertyDeclarationToNullStatement{
+ "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL, propertyTypeId=NULL, "
+ "propertyTraits=NULL WHERE propertyDeclarationId=? AND (aliasPropertyDeclarationId IS NOT "
+ "NULL OR propertyTypeId IS NOT NULL OR propertyTraits IS NOT NULL)",
+ database};
+ Sqlite::ReadStatement<5, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{
+ "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, "
+ " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId "
+ "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target "
+ " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR "
+ " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId "
+ "WHERE alias.propertyTypeId=?1 "
+ "UNION ALL "
+ "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, "
+ " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId "
+ "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target "
+ " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR "
+ " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId "
+ "WHERE target.typeId=?1 "
+ "UNION ALL "
+ "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, "
+ " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId "
+ "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target "
+ " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR "
+ " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId "
+ "WHERE alias.propertyImportedTypeNameId IN "
+ " (SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) "
+ " WHERE typeId=?1)",
+ database};
+ Sqlite::ReadStatement<3, 1> selectAliasPropertiesDeclarationForPropertiesWithAliasIdStatement{
+ "WITH RECURSIVE "
+ " properties(propertyDeclarationId, propertyImportedTypeNameId, typeId, "
+ " aliasPropertyDeclarationId) AS ("
+ " SELECT propertyDeclarationId, propertyImportedTypeNameId, typeId, "
+ " aliasPropertyDeclarationId FROM propertyDeclarations WHERE "
+ " aliasPropertyDeclarationId=?1"
+ " UNION ALL "
+ " SELECT pd.propertyDeclarationId, pd.propertyImportedTypeNameId, pd.typeId, "
+ " pd.aliasPropertyDeclarationId FROM propertyDeclarations AS pd JOIN properties AS "
+ " p ON pd.aliasPropertyDeclarationId=p.propertyDeclarationId)"
+ "SELECT propertyDeclarationId, propertyImportedTypeNameId, aliasPropertyDeclarationId "
+ " FROM properties",
+ database};
+ Sqlite::ReadWriteStatement<3, 1> updatesPropertyDeclarationPropertyTypeToNullStatement{
+ "UPDATE propertyDeclarations SET propertyTypeId=NULL WHERE propertyTypeId=?1 AND "
+ "aliasPropertyDeclarationId IS NULL RETURNING typeId, propertyDeclarationId, "
+ "propertyImportedTypeNameId",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectPropertyNameStatement{
+ "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
+ Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{
+ "UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database};
+ Sqlite::ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{
+ "UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING "
+ "typeId, prototypeNameId",
+ database};
+ Sqlite::ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{
+ "UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING "
+ "typeId, extensionNameId",
+ database};
+ Sqlite::WriteStatement<2> updateTypePrototypeStatement{
+ "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database};
+ Sqlite::WriteStatement<2> updateTypeExtensionStatement{
+ "UPDATE types SET extensionId=?2 WHERE typeId=?1", database};
+ mutable Sqlite::ReadStatement<1, 1> selectPrototypeAndExtensionIdsStatement{
+ "WITH RECURSIVE "
+ " prototypes(typeId) AS ( "
+ " SELECT prototypeId FROM types WHERE typeId=?1 "
+ " UNION ALL "
+ " SELECT extensionId FROM types WHERE typeId=?1 "
+ " UNION ALL "
+ " SELECT prototypeId FROM types JOIN prototypes USING(typeId) "
+ " UNION ALL "
+ " SELECT extensionId FROM types JOIN prototypes USING(typeId)) "
+ "SELECT typeId FROM prototypes WHERE typeId IS NOT NULL",
+ database};
+ Sqlite::WriteStatement<3> updatePropertyDeclarationAliasIdAndTypeNameIdStatement{
+ "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2, "
+ "propertyImportedTypeNameId=?3 WHERE propertyDeclarationId=?1 AND "
+ "(aliasPropertyDeclarationId IS NOT ?2 OR propertyImportedTypeNameId IS NOT ?3)",
+ database};
+ Sqlite::WriteStatement<1> updatetPropertiesDeclarationValuesOfAliasStatement{
+ "WITH RECURSIVE "
+ " properties(propertyDeclarationId, propertyTypeId, propertyTraits) AS ( "
+ " SELECT aliasPropertyDeclarationId, propertyTypeId, propertyTraits FROM "
+ " propertyDeclarations WHERE propertyDeclarationId=?1 "
+ " UNION ALL "
+ " SELECT pd.aliasPropertyDeclarationId, pd.propertyTypeId, pd.propertyTraits FROM "
+ " propertyDeclarations AS pd JOIN properties USING(propertyDeclarationId)) "
+ "UPDATE propertyDeclarations AS pd SET propertyTypeId=p.propertyTypeId, "
+ " propertyTraits=p.propertyTraits "
+ "FROM properties AS p "
+ "WHERE pd.propertyDeclarationId=?1 AND p.propertyDeclarationId IS NULL AND "
+ " (pd.propertyTypeId IS NOT p.propertyTypeId OR pd.propertyTraits IS NOT "
+ " p.propertyTraits)",
+ database};
+ Sqlite::WriteStatement<1> updatePropertyDeclarationAliasIdToNullStatement{
+ "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL WHERE "
+ "propertyDeclarationId=?1",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectPropertyDeclarationIdsForAliasChainStatement{
+ "WITH RECURSIVE "
+ " properties(propertyDeclarationId) AS ( "
+ " SELECT aliasPropertyDeclarationId FROM propertyDeclarations WHERE "
+ " propertyDeclarationId=?1 "
+ " UNION ALL "
+ " SELECT aliasPropertyDeclarationId FROM propertyDeclarations JOIN properties "
+ " USING(propertyDeclarationId)) "
+ "SELECT propertyDeclarationId FROM properties",
+ database};
+ mutable Sqlite::ReadStatement<3> selectAllFileStatusesStatement{
+ "SELECT sourceId, size, lastModified FROM fileStatuses ORDER BY sourceId", database};
+ mutable Sqlite::ReadStatement<3, 1> selectFileStatusesForSourceIdsStatement{
+ "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId IN carray(?1) ORDER "
+ "BY sourceId",
+ database};
+ mutable Sqlite::ReadStatement<3, 1> selectFileStatusesForSourceIdStatement{
+ "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId=?1 ORDER BY sourceId",
+ database};
+ Sqlite::WriteStatement<3> insertFileStatusStatement{
+ "INSERT INTO fileStatuses(sourceId, size, lastModified) VALUES(?1, ?2, ?3)", database};
+ Sqlite::WriteStatement<1> deleteFileStatusStatement{
+ "DELETE FROM fileStatuses WHERE sourceId=?1", database};
+ Sqlite::WriteStatement<3> updateFileStatusStatement{
+ "UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database};
+ Sqlite::ReadStatement<1, 1> selectTypeIdBySourceIdStatement{
+ "SELECT typeId FROM types WHERE sourceId=?", database};
+ mutable Sqlite::ReadStatement<1, 3> selectImportedTypeNameIdStatement{
+ "SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 "
+ "AND name=?3 LIMIT 1",
+ database};
+ mutable Sqlite::ReadWriteStatement<1, 3> insertImportedTypeNameIdStatement{
+ "INSERT INTO importedTypeNames(kind, importOrSourceId, name) VALUES (?1, ?2, ?3) "
+ "RETURNING importedTypeNameId",
+ database};
+ mutable Sqlite::ReadStatement<1, 2> selectImportIdBySourceIdAndModuleIdStatement{
+ "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND majorVersion "
+ "IS NULL AND minorVersion IS NULL LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<1, 3> selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement{
+ "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND "
+ "majorVersion=?3 AND minorVersion IS NULL LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<1, 4> selectImportIdBySourceIdAndModuleIdAndVersionStatement{
+ "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND "
+ "majorVersion=?3 AND minorVersion=?4 LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectKindFromImportedTypeNamesStatement{
+ "SELECT kind FROM importedTypeNames WHERE importedTypeNameId=?1", database};
+ mutable Sqlite::ReadStatement<1, 1> selectNameFromImportedTypeNamesStatement{
+ "SELECT name FROM importedTypeNames WHERE importedTypeNameId=?1", database};
+ mutable Sqlite::ReadStatement<1, 1> selectTypeIdForQualifiedImportedTypeNameNamesStatement{
+ "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON "
+ "importOrSourceId=di.importId JOIN documentImports AS di2 ON di.sourceId=di2.sourceId AND "
+ "di.moduleId=di2.sourceModuleId "
+ "JOIN exportedTypeNames AS etn ON di2.moduleId=etn.moduleId WHERE "
+ "itn.kind=2 AND importedTypeNameId=?1 AND itn.name=etn.name AND "
+ "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS "
+ "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY etn.majorVersion DESC NULLS FIRST, "
+ "etn.minorVersion DESC NULLS FIRST LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectTypeIdForImportedTypeNameNamesStatement{
+ "WITH "
+ " importTypeNames(moduleId, name, kind, majorVersion, minorVersion) AS ( "
+ " SELECT moduleId, name, di.kind, majorVersion, minorVersion "
+ " FROM importedTypeNames AS itn JOIN documentImports AS di ON "
+ " importOrSourceId=sourceId "
+ " WHERE "
+ " importedTypeNameId=?1 AND itn.kind=1) "
+ "SELECT typeId FROM importTypeNames AS itn "
+ " JOIN exportedTypeNames AS etn USING(moduleId, name) "
+ "WHERE (itn.majorVersion IS NULL OR (itn.majorVersion=etn.majorVersion "
+ " AND (itn.minorVersion IS NULL OR itn.minorVersion>=etn.minorVersion))) "
+ "ORDER BY itn.kind, etn.majorVersion DESC NULLS FIRST, etn.minorVersion DESC NULLS FIRST "
+ "LIMIT 1",
+ database};
+ Sqlite::WriteStatement<0> deleteAllSourcesStatement{"DELETE FROM sources", database};
+ Sqlite::WriteStatement<0> deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database};
+ mutable Sqlite::ReadStatement<6, 1> selectExportedTypesForSourceIdsStatement{
+ "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, "
+ "exportedTypeNameId FROM exportedTypeNames WHERE typeId in carray(?1) ORDER BY moduleId, "
+ "name, majorVersion, minorVersion",
+ database};
+ Sqlite::WriteStatement<5> insertExportedTypeNamesWithVersionStatement{
+ "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, minorVersion, typeId) "
+ "VALUES(?1, ?2, ?3, ?4, ?5)",
+ database};
+ Sqlite::WriteStatement<4> insertExportedTypeNamesWithMajorVersionStatement{
+ "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, typeId) "
+ "VALUES(?1, ?2, ?3, ?4)",
+ database};
+ Sqlite::WriteStatement<3> insertExportedTypeNamesWithoutVersionStatement{
+ "INSERT INTO exportedTypeNames(moduleId, name, typeId) VALUES(?1, ?2, ?3)", database};
+ Sqlite::WriteStatement<1> deleteExportedTypeNameStatement{
+ "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database};
+ Sqlite::WriteStatement<2> updateExportedTypeNameTypeIdStatement{
+ "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database};
+ mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{
+ "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE "
+ "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId",
+ database};
+ Sqlite::WriteStatement<4> insertProjectDataStatement{
+ "INSERT INTO projectDatas(projectSourceId, sourceId, "
+ "moduleId, fileType) VALUES(?1, ?2, ?3, ?4)",
+ database};
+ Sqlite::WriteStatement<2> deleteProjectDataStatement{
+ "DELETE FROM projectDatas WHERE projectSourceId=?1 AND sourceId=?2", database};
+ Sqlite::WriteStatement<4> updateProjectDataStatement{
+ "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2",
+ database};
+ mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{
+ "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE "
+ "projectSourceId=?1",
+ database};
+ mutable Sqlite::ReadStatement<4, 1> selectProjectDataForSourceIdStatement{
+ "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE "
+ "sourceId=?1 LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{
+ "SELECT typeId FROM types WHERE sourceId IN carray(?1)", database};
+ mutable Sqlite::ReadStatement<6, 1> selectModuleExportedImportsForSourceIdStatement{
+ "SELECT moduleExportedImportId, moduleId, exportedModuleId, ifnull(majorVersion, -1), "
+ "ifnull(minorVersion, -1), isAutoVersion FROM moduleExportedImports WHERE moduleId IN "
+ "carray(?1) ORDER BY moduleId, exportedModuleId",
+ database};
+ Sqlite::WriteStatement<3> insertModuleExportedImportWithoutVersionStatement{
+ "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion) "
+ "VALUES (?1, ?2, ?3)",
+ database};
+ Sqlite::WriteStatement<4> insertModuleExportedImportWithMajorVersionStatement{
+ "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, "
+ "majorVersion) VALUES (?1, ?2, ?3, ?4)",
+ database};
+ Sqlite::WriteStatement<5> insertModuleExportedImportWithVersionStatement{
+ "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, "
+ "majorVersion, minorVersion) VALUES (?1, ?2, ?3, ?4, ?5)",
+ database};
+ Sqlite::WriteStatement<1> deleteModuleExportedImportStatement{
+ "DELETE FROM moduleExportedImports WHERE moduleExportedImportId=?1", database};
+ mutable Sqlite::ReadStatement<3, 3> selectModuleExportedImportsForModuleIdStatement{
+ "WITH RECURSIVE "
+ " imports(moduleId, majorVersion, minorVersion, moduleExportedImportId) AS ( "
+ " SELECT exportedModuleId, "
+ " iif(isAutoVersion=1, ?2, majorVersion), "
+ " iif(isAutoVersion=1, ?3, minorVersion), "
+ " moduleExportedImportId "
+ " FROM moduleExportedImports WHERE moduleId=?1 "
+ " UNION ALL "
+ " SELECT exportedModuleId, "
+ " iif(mei.isAutoVersion=1, i.majorVersion, mei.majorVersion), "
+ " iif(mei.isAutoVersion=1, i.minorVersion, mei.minorVersion), "
+ " mei.moduleExportedImportId "
+ " FROM moduleExportedImports AS mei JOIN imports AS i USING(moduleId)) "
+ "SELECT DISTINCT moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) "
+ "FROM imports",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectLocalPropertyDeclarationIdsForTypeStatement{
+ "SELECT propertyDeclarationId "
+ "FROM propertyDeclarations "
+ "WHERE typeId=? "
+ "ORDER BY propertyDeclarationId",
+ database};
+ mutable Sqlite::ReadStatement<1, 2> selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement{
+ "SELECT propertyDeclarationId "
+ "FROM propertyDeclarations "
+ "WHERE typeId=?1 AND name=?2 LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<4, 1> selectPropertyDeclarationForPropertyDeclarationIdStatement{
+ "SELECT typeId, name, propertyTraits, propertyTypeId "
+ "FROM propertyDeclarations "
+ "WHERE propertyDeclarationId=?1 LIMIT 1",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectSignalDeclarationNamesForTypeStatement{
+ "WITH RECURSIVE "
+ " all_prototype_and_extension(typeId, prototypeId) AS ("
+ " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
+ " UNION ALL "
+ " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
+ " typeChain(typeId) AS ("
+ " VALUES(?1)"
+ " UNION ALL "
+ " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain "
+ " USING(typeId)) "
+ "SELECT name FROM typeChain JOIN signalDeclarations "
+ " USING(typeId) ORDER BY name",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectFuncionDeclarationNamesForTypeStatement{
+ "WITH RECURSIVE "
+ " all_prototype_and_extension(typeId, prototypeId) AS ("
+ " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
+ " UNION ALL "
+ " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
+ " typeChain(typeId) AS ("
+ " VALUES(?1)"
+ " UNION ALL "
+ " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain "
+ " USING(typeId))"
+ "SELECT name FROM typeChain JOIN functionDeclarations "
+ " USING(typeId) ORDER BY name",
+ database};
+ mutable Sqlite::ReadStatement<2> selectTypesWithDefaultPropertyStatement{
+ "SELECT typeId, defaultPropertyId FROM types ORDER BY typeId", database};
+ Sqlite::WriteStatement<2> updateDefaultPropertyIdStatement{
+ "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database};
+ Sqlite::WriteStatement<1> updateDefaultPropertyIdToNullStatement{
+ "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database};
+ mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{
+ "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database};
+ mutable Sqlite::ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{
+ "SELECT defaultPropertyId FROM types WHERE typeId=?", database};
+ mutable Sqlite::ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{
+ "WITH RECURSIVE "
+ " all_prototype_and_extension(typeId, prototypeId) AS ("
+ " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
+ " UNION ALL "
+ " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
+ " prototypes(typeId, level) AS ("
+ " SELECT prototypeId, 0 FROM all_prototype_and_extension WHERE typeId=?"
+ " UNION ALL "
+ " SELECT prototypeId, p.level+1 FROM all_prototype_and_extension JOIN "
+ " prototypes AS p USING(typeId)) "
+ "SELECT typeId FROM prototypes ORDER BY level",
+ database};
+ Sqlite::WriteStatement<2> upsertPropertyEditorPathIdStatement{
+ "INSERT INTO propertyEditorPaths(typeId, pathSourceId) VALUES(?1, ?2) ON CONFLICT DO "
+ "UPDATE SET pathSourceId=excluded.pathSourceId WHERE pathSourceId IS NOT "
+ "excluded.pathSourceId",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectPropertyEditorPathIdStatement{
+ "SELECT pathSourceId FROM propertyEditorPaths WHERE typeId=?", database};
+ mutable Sqlite::ReadStatement<3, 1> selectPropertyEditorPathsForForSourceIdsStatement{
+ "SELECT typeId, pathSourceId, directoryId "
+ "FROM propertyEditorPaths "
+ "WHERE directoryId IN carray(?1) "
+ "ORDER BY typeId",
+ database};
+ Sqlite::WriteStatement<3> insertPropertyEditorPathStatement{
+ "INSERT INTO propertyEditorPaths(typeId, pathSourceId, directoryId) VALUES (?1, ?2, ?3)",
+ database};
+ Sqlite::WriteStatement<3> updatePropertyEditorPathsStatement{
+ "UPDATE propertyEditorPaths "
+ "SET pathSourceId=?2, directoryId=?3 "
+ "WHERE typeId=?1",
+ database};
+ Sqlite::WriteStatement<1> deletePropertyEditorPathStatement{
+ "DELETE FROM propertyEditorPaths WHERE typeId=?1", database};
+ mutable Sqlite::ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{
+ "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE "
+ "sourceId IN carray(?1) ORDER BY typeId",
+ database};
+ Sqlite::WriteStatement<6> insertTypeAnnotationStatement{
+ "INSERT INTO "
+ " typeAnnotations(typeId, sourceId, directorySourceId, iconPath, itemLibrary, hints) "
+ "VALUES(?1, ?2, ?3, ?4, ?5, ?6)",
+ database};
+ Sqlite::WriteStatement<4> updateTypeAnnotationStatement{
+ "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database};
+ Sqlite::WriteStatement<1> deleteTypeAnnotationStatement{
+ "DELETE FROM typeAnnotations WHERE typeId=?1", database};
+ mutable Sqlite::ReadStatement<1, 1> selectTypeIconPathStatement{
+ "SELECT iconPath FROM typeAnnotations WHERE typeId=?1", database};
+ mutable Sqlite::ReadStatement<2, 1> selectTypeHintsStatement{
+ "SELECT hints.key, hints.value "
+ "FROM typeAnnotations, json_each(typeAnnotations.hints) AS hints "
+ "WHERE typeId=?1 AND hints IS NOT NULL",
+ database};
+ mutable Sqlite::ReadStatement<1, 1> selectTypeAnnotationSourceIdsStatement{
+ "SELECT sourceId FROM typeAnnotations WHERE directorySourceId=?1 ORDER BY sourceId", database};
+ mutable Sqlite::ReadStatement<1, 0> selectTypeAnnotationDirectorySourceIdsStatement{
+ "SELECT DISTINCT directorySourceId FROM typeAnnotations ORDER BY directorySourceId", database};
+ mutable Sqlite::ReadStatement<9> selectItemLibraryEntriesStatement{
+ "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', "
+ " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', "
+ " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
+ "FROM typeAnnotations AS ta , json_each(ta.itemLibrary) AS i "
+ "WHERE ta.itemLibrary IS NOT NULL",
+ database};
+ mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{
+ "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', "
+ " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', "
+ " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
+ "FROM typeAnnotations AS ta, json_each(ta.itemLibrary) AS i "
+ "WHERE typeId=?1 AND ta.itemLibrary IS NOT NULL",
+ database};
+ mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{
+ "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', "
+ "i.value->>'$.category', "
+ " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', "
+ " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
+ "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i "
+ "WHERE typeId IN (SELECT DISTINCT typeId "
+ " FROM documentImports AS di JOIN exportedTypeNames "
+ " USING(moduleId) "
+ " WHERE di.sourceId=?)",
+ database};
+ mutable Sqlite::ReadStatement<3, 1> selectItemLibraryPropertiesStatement{
+ "SELECT p.value->>0, p.value->>1, p.value->>2 FROM json_each(?1) AS p", database};
+ mutable Sqlite::ReadStatement<1, 1> selectItemLibraryExtraFilePathsStatement{
+ "SELECT p.value FROM json_each(?1) AS p", database};
+ mutable Sqlite::ReadStatement<1, 1> selectTypeIdsByModuleIdStatement{
+ "SELECT DISTINCT typeId FROM exportedTypeNames WHERE moduleId=?", database};
+ mutable Sqlite::ReadStatement<1, 1> selectHeirTypeIdsStatement{
+ "WITH RECURSIVE "
+ " typeSelection(typeId) AS ("
+ " SELECT typeId FROM types WHERE prototypeId=?1 OR extensionId=?1"
+ " UNION ALL "
+ " SELECT t.typeId "
+ " FROM types AS t JOIN typeSelection AS ts "
+ " WHERE prototypeId=ts.typeId OR extensionId=ts.typeId)"
+ "SELECT typeId FROM typeSelection",
+ database};
+};
+
+class ProjectStorage::Initializer
+{
+public:
+ Initializer(Database &database, bool isInitialized)
+ {
+ if (!isInitialized) {
+ auto moduleIdColumn = createModulesTable(database);
+ createSourceContextsTable(database);
+ createSourcesTable(database);
+ createTypesAndePropertyDeclarationsTables(database, moduleIdColumn);
+ createExportedTypeNamesTable(database, moduleIdColumn);
+ createImportedTypeNamesTable(database);
+ createEnumerationsTable(database);
+ createFunctionsTable(database);
+ createSignalsTable(database);
+ createModuleExportedImportsTable(database, moduleIdColumn);
+ createDocumentImportsTable(database, moduleIdColumn);
+ createFileStatusesTable(database);
+ createProjectDatasTable(database);
+ createPropertyEditorPathsTable(database);
+ createTypeAnnotionsTable(database);
+ }
+ database.setIsInitialized(true);
+ }
+
+ void createSourceContextsTable(Database &database)
+ {
+ Sqlite::Table table;
+ table.setUseIfNotExists(true);
+ table.setName("sourceContexts");
+ table.addColumn("sourceContextId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
+ const Sqlite::Column &sourceContextPathColumn = table.addColumn("sourceContextPath");
+
+ table.addUniqueIndex({sourceContextPathColumn});
+
+ table.initialize(database);
+ }
+
+ void createSourcesTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("sources");
+ table.addColumn("sourceId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}});
+ const auto &sourceContextIdColumn = table.addColumn(
+ "sourceContextId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::NotNull{},
+ Sqlite::ForeignKey{"sourceContexts",
+ "sourceContextId",
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Cascade}});
+ const auto &sourceNameColumn = table.addColumn("sourceName", Sqlite::StrictColumnType::Text);
+ table.addUniqueIndex({sourceContextIdColumn, sourceNameColumn});
+
+ table.initialize(database);
+ }
+
+ void createTypesAndePropertyDeclarationsTables(
+ Database &database, [[maybe_unused]] const Sqlite::StrictColumn &foreignModuleIdColumn)
+ {
+ Sqlite::StrictTable typesTable;
+ typesTable.setUseIfNotExists(true);
+ typesTable.setName("types");
+ typesTable.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}});
+ auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
+ auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text);
+ typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer);
+ auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId",
+ typesTable,
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Restrict);
+ typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer);
+ auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId",
+ typesTable,
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Restrict);
+ typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer);
+ auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId",
+ Sqlite::StrictColumnType::Integer);
+ typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer);
+ typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn});
+ typesTable.addIndex({defaultPropertyIdColumn});
+ typesTable.addIndex({prototypeIdColumn});
+ typesTable.addIndex({extensionIdColumn});
+
+ typesTable.initialize(database);
+
+ {
+ Sqlite::StrictTable propertyDeclarationTable;
+ propertyDeclarationTable.setUseIfNotExists(true);
+ propertyDeclarationTable.setName("propertyDeclarations");
+ propertyDeclarationTable.addColumn("propertyDeclarationId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{}});
+ auto &typeIdColumn = propertyDeclarationTable.addColumn("typeId");
+ auto &nameColumn = propertyDeclarationTable.addColumn("name");
+ auto &propertyTypeIdColumn = propertyDeclarationTable.addForeignKeyColumn(
+ "propertyTypeId",
+ typesTable,
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Restrict);
+ propertyDeclarationTable.addColumn("propertyTraits", Sqlite::StrictColumnType::Integer);
+ propertyDeclarationTable.addColumn("propertyImportedTypeNameId",
+ Sqlite::StrictColumnType::Integer);
+ auto &aliasPropertyDeclarationIdColumn = propertyDeclarationTable.addForeignKeyColumn(
+ "aliasPropertyDeclarationId",
+ propertyDeclarationTable,
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Restrict);
+ auto &aliasPropertyDeclarationTailIdColumn = propertyDeclarationTable.addForeignKeyColumn(
+ "aliasPropertyDeclarationTailId",
+ propertyDeclarationTable,
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Restrict);
+
+ propertyDeclarationTable.addUniqueIndex({typeIdColumn, nameColumn});
+ propertyDeclarationTable.addIndex({propertyTypeIdColumn});
+ propertyDeclarationTable.addIndex({aliasPropertyDeclarationIdColumn},
+ "aliasPropertyDeclarationId IS NOT NULL");
+ propertyDeclarationTable.addIndex({aliasPropertyDeclarationTailIdColumn},
+ "aliasPropertyDeclarationTailId IS NOT NULL");
+
+ propertyDeclarationTable.initialize(database);
+ }
+ }
+
+ void createExportedTypeNamesTable(Database &database,
+ const Sqlite::StrictColumn &foreignModuleIdColumn)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("exportedTypeNames");
+ table.addColumn("exportedTypeNameId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{}});
+ auto &moduleIdColumn = table.addForeignKeyColumn("moduleId",
+ foreignModuleIdColumn,
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::NoAction);
+ auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
+ auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer);
+ auto &majorVersionColumn = table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer);
+ auto &minorVersionColumn = table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer);
+
+ table.addUniqueIndex({moduleIdColumn, nameColumn},
+ "majorVersion IS NULL AND minorVersion IS NULL");
+ table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn},
+ "majorVersion IS NOT NULL AND minorVersion IS NULL");
+ table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn, minorVersionColumn},
+ "majorVersion IS NOT NULL AND minorVersion IS NOT NULL");
+
+ table.addIndex({typeIdColumn});
+ table.addIndex({moduleIdColumn, nameColumn});
+
+ table.initialize(database);
+ }
+
+ void createImportedTypeNamesTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("importedTypeNames");
+ table.addColumn("importedTypeNameId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{}});
+ auto &importOrSourceIdColumn = table.addColumn("importOrSourceId");
+ auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
+ auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer);
+
+ table.addUniqueIndex({kindColumn, importOrSourceIdColumn, nameColumn});
+ table.addIndex({nameColumn});
+
+ table.initialize(database);
+ }
+
+ void createEnumerationsTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("enumerationDeclarations");
+ table.addColumn("enumerationDeclarationId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{}});
+ auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer);
+ auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
+ table.addColumn("enumeratorDeclarations", Sqlite::StrictColumnType::Text);
+
+ table.addUniqueIndex({typeIdColumn, nameColumn});
+
+ table.initialize(database);
+ }
+
+ void createFunctionsTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("functionDeclarations");
+ table.addColumn("functionDeclarationId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{}});
+ auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer);
+ auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
+ auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text);
+ table.addColumn("returnTypeName");
+
+ table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn});
+
+ table.initialize(database);
+ }
+
+ void createSignalsTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("signalDeclarations");
+ table.addColumn("signalDeclarationId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{}});
+ auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer);
+ auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
+ auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text);
+
+ table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn});
+
+ table.initialize(database);
+ }
+
+ Sqlite::StrictColumn createModulesTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("modules");
+ auto &modelIdColumn = table.addColumn("moduleId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{}});
+ auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
+
+ table.addUniqueIndex({nameColumn});
+
+ table.initialize(database);
+
+ return std::move(modelIdColumn);
+ }
+
+ void createModuleExportedImportsTable(Database &database,
+ const Sqlite::StrictColumn &foreignModuleIdColumn)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("moduleExportedImports");
+ table.addColumn("moduleExportedImportId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{}});
+ auto &moduleIdColumn = table.addForeignKeyColumn("moduleId",
+ foreignModuleIdColumn,
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Cascade,
+ Sqlite::Enforment::Immediate);
+ auto &sourceIdColumn = table.addColumn("exportedModuleId", Sqlite::StrictColumnType::Integer);
+ table.addColumn("isAutoVersion", Sqlite::StrictColumnType::Integer);
+ table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer);
+ table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer);
+
+ table.addUniqueIndex({sourceIdColumn, moduleIdColumn});
+
+ table.initialize(database);
+ }
+
+ void createDocumentImportsTable(Database &database,
+ const Sqlite::StrictColumn &foreignModuleIdColumn)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("documentImports");
+ table.addColumn("importId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}});
+ auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
+ auto &moduleIdColumn = table.addForeignKeyColumn("moduleId",
+ foreignModuleIdColumn,
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Cascade,
+ Sqlite::Enforment::Immediate);
+ auto &sourceModuleIdColumn = table.addForeignKeyColumn("sourceModuleId",
+ foreignModuleIdColumn,
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Cascade,
+ Sqlite::Enforment::Immediate);
+ auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer);
+ auto &majorVersionColumn = table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer);
+ auto &minorVersionColumn = table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer);
+ auto &parentImportIdColumn = table.addColumn("parentImportId",
+ Sqlite::StrictColumnType::Integer);
+
+ table.addUniqueIndex(
+ {sourceIdColumn, moduleIdColumn, kindColumn, sourceModuleIdColumn, parentImportIdColumn},
+ "majorVersion IS NULL AND minorVersion IS NULL");
+ table.addUniqueIndex({sourceIdColumn,
+ moduleIdColumn,
+ kindColumn,
+ sourceModuleIdColumn,
+ majorVersionColumn,
+ parentImportIdColumn},
+ "majorVersion IS NOT NULL AND minorVersion IS NULL");
+ table.addUniqueIndex({sourceIdColumn,
+ moduleIdColumn,
+ kindColumn,
+ sourceModuleIdColumn,
+ majorVersionColumn,
+ minorVersionColumn,
+ parentImportIdColumn},
+ "majorVersion IS NOT NULL AND minorVersion IS NOT NULL");
+
+ table.addIndex({sourceIdColumn, kindColumn});
+
+ table.initialize(database);
+ }
+
+ void createFileStatusesTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setName("fileStatuses");
+ table.addColumn("sourceId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{},
+ Sqlite::ForeignKey{"sources",
+ "sourceId",
+ Sqlite::ForeignKeyAction::NoAction,
+ Sqlite::ForeignKeyAction::Cascade}});
+ table.addColumn("size", Sqlite::StrictColumnType::Integer);
+ table.addColumn("lastModified", Sqlite::StrictColumnType::Integer);
+
+ table.initialize(database);
+ }
+
+ void createProjectDatasTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setUseWithoutRowId(true);
+ table.setName("projectDatas");
+ auto &projectSourceIdColumn = table.addColumn("projectSourceId",
+ Sqlite::StrictColumnType::Integer);
+ auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
+ table.addColumn("moduleId", Sqlite::StrictColumnType::Integer);
+ table.addColumn("fileType", Sqlite::StrictColumnType::Integer);
+
+ table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn});
+ table.addUniqueIndex({sourceIdColumn});
+
+ table.initialize(database);
+ }
+
+ void createPropertyEditorPathsTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setUseWithoutRowId(true);
+ table.setName("propertyEditorPaths");
+ table.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}});
+ table.addColumn("pathSourceId", Sqlite::StrictColumnType::Integer);
+ auto &directoryIdColumn = table.addColumn("directoryId", Sqlite::StrictColumnType::Integer);
+
+ table.addIndex({directoryIdColumn});
+
+ table.initialize(database);
+ }
+
+ void createTypeAnnotionsTable(Database &database)
+ {
+ Sqlite::StrictTable table;
+ table.setUseIfNotExists(true);
+ table.setUseWithoutRowId(true);
+ table.setName("typeAnnotations");
+ auto &typeIdColumn = table.addColumn("typeId",
+ Sqlite::StrictColumnType::Integer,
+ {Sqlite::PrimaryKey{}});
+ auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
+ auto &directorySourceIdColumn = table.addColumn("directorySourceId",
+ Sqlite::StrictColumnType::Integer);
+
+ table.addColumn("iconPath", Sqlite::StrictColumnType::Text);
+ table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text);
+ table.addColumn("hints", Sqlite::StrictColumnType::Text);
+
+ table.addUniqueIndex({sourceIdColumn, typeIdColumn});
+ table.addIndex({directorySourceIdColumn});
+
+ table.initialize(database);
+ }
+};
+
+ProjectStorage::ProjectStorage(Database &database, bool isInitialized)
+ : database{database}
+ , exclusiveTransaction{database}
+ , initializer{std::make_unique<ProjectStorage::Initializer>(database, isInitialized)}
+ , moduleCache{ModuleStorageAdapter{*this}}
+ , s{std::make_unique<ProjectStorage::Statements>(database)}
{
- thread_local NanotraceHR::StringViewCategory<projectStorageTracingStatus()>
- projectStorageCategory_{"project storage"_t, Tracing::eventQueue(), projectStorageCategory};
+ NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()};
+
+ exclusiveTransaction.commit();
- return projectStorageCategory_;
+ database.walCheckpointFull();
+
+ moduleCache.populate();
}
-} // namespace QmlDesigner
+ProjectStorage::~ProjectStorage() = default;
+
+void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackage package)
+{
+ NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory()};
+
+ TypeIds deletedTypeIds;
+ Sqlite::withImmediateTransaction(database, [&] {
+ AliasPropertyDeclarations insertedAliasPropertyDeclarations;
+ AliasPropertyDeclarations updatedAliasPropertyDeclarations;
+
+ AliasPropertyDeclarations relinkableAliasPropertyDeclarations;
+ PropertyDeclarations relinkablePropertyDeclarations;
+ Prototypes relinkablePrototypes;
+ Prototypes relinkableExtensions;
+
+ TypeIds updatedTypeIds;
+ updatedTypeIds.reserve(package.types.size());
+
+ TypeIds typeIdsToBeDeleted;
+
+ std::sort(package.updatedSourceIds.begin(), package.updatedSourceIds.end());
+
+ synchronizeFileStatuses(package.fileStatuses, package.updatedFileStatusSourceIds);
+ synchronizeImports(package.imports,
+ package.updatedSourceIds,
+ package.moduleDependencies,
+ package.updatedModuleDependencySourceIds,
+ package.moduleExportedImports,
+ package.updatedModuleIds);
+ synchronizeTypes(package.types,
+ updatedTypeIds,
+ insertedAliasPropertyDeclarations,
+ updatedAliasPropertyDeclarations,
+ relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes,
+ relinkableExtensions,
+ package.updatedSourceIds);
+ synchronizeTypeAnnotations(package.typeAnnotations, package.updatedTypeAnnotationSourceIds);
+ synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths,
+ package.updatedPropertyEditorQmlPathSourceIds);
+
+ deleteNotUpdatedTypes(updatedTypeIds,
+ package.updatedSourceIds,
+ typeIdsToBeDeleted,
+ relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes,
+ relinkableExtensions,
+ deletedTypeIds);
+
+ relink(relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes,
+ relinkableExtensions,
+ deletedTypeIds);
+
+ linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations);
+
+ synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds);
+
+ commonTypeCache_.resetTypeIds();
+ });
+
+ callRefreshMetaInfoCallback(deletedTypeIds);
+}
+
+void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"synchronize document imports"_t,
+ projectStorageCategory(),
+ keyValue("imports", imports),
+ keyValue("source id", sourceId)};
+
+ Sqlite::withImmediateTransaction(database, [&] {
+ synchronizeDocumentImports(imports, {sourceId}, Storage::Synchronization::ImportKind::Import);
+ });
+}
+
+void ProjectStorage::addObserver(ProjectStorageObserver *observer)
+{
+ NanotraceHR::Tracer tracer{"add observer"_t, projectStorageCategory()};
+ observers.push_back(observer);
+}
+
+void ProjectStorage::removeObserver(ProjectStorageObserver *observer)
+{
+ NanotraceHR::Tracer tracer{"remove observer"_t, projectStorageCategory()};
+ observers.removeOne(observer);
+}
+
+ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get module id"_t,
+ projectStorageCategory(),
+ keyValue("module name", moduleName)};
+
+ auto moduleId = moduleCache.id(moduleName);
+
+ tracer.end(keyValue("module id", moduleId));
+
+ return moduleId;
+}
+
+Utils::SmallString ProjectStorage::moduleName(ModuleId moduleId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get module name"_t,
+ projectStorageCategory(),
+ keyValue("module id", moduleId)};
+
+ if (!moduleId)
+ throw ModuleDoesNotExists{};
+
+ auto moduleName = moduleCache.value(moduleId);
+
+ tracer.end(keyValue("module name", moduleName));
+
+ return moduleName;
+}
+
+TypeId ProjectStorage::typeId(ModuleId moduleId,
+ Utils::SmallStringView exportedTypeName,
+ Storage::Version version) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type id by exported name"_t,
+ projectStorageCategory(),
+ keyValue("module id", moduleId),
+ keyValue("exported type name", exportedTypeName),
+ keyValue("version", version)};
+
+ TypeId typeId;
+
+ if (version.minor) {
+ typeId = s->selectTypeIdByModuleIdAndExportedNameAndVersionStatement.valueWithTransaction<TypeId>(
+ moduleId, exportedTypeName, version.major.value, version.minor.value);
+
+ } else if (version.major) {
+ typeId = s->selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement
+ .valueWithTransaction<TypeId>(moduleId, exportedTypeName, version.major.value);
+
+ } else {
+ typeId = s->selectTypeIdByModuleIdAndExportedNameStatement
+ .valueWithTransaction<TypeId>(moduleId, exportedTypeName);
+ }
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
+}
+
+TypeId ProjectStorage::typeId(ImportedTypeNameId typeNameId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type id by imported type name"_t,
+ projectStorageCategory(),
+ keyValue("imported type name id", typeNameId)};
+
+ auto typeId = Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); });
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
+}
+
+QVarLengthArray<TypeId, 256> ProjectStorage::typeIds(ModuleId moduleId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type ids by module id"_t,
+ projectStorageCategory(),
+ keyValue("module id", moduleId)};
+
+ auto typeIds = s->selectTypeIdsByModuleIdStatement
+ .valuesWithTransaction<QVarLengthArray<TypeId, 256>>(moduleId);
+
+ tracer.end(keyValue("type ids", typeIds));
+
+ return typeIds;
+}
+
+Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get exported type names by type id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto exportedTypenames = s->selectExportedTypesByTypeIdStatement
+ .valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId);
+
+ tracer.end(keyValue("exported type names", exportedTypenames));
+
+ return exportedTypenames;
+}
+
+Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId, SourceId sourceId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get exported type names by source id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("source id", sourceId)};
+
+ auto exportedTypenames = s->selectExportedTypesByTypeIdAndSourceIdStatement
+ .valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId,
+ sourceId);
+
+ tracer.end(keyValue("exported type names", exportedTypenames));
+
+ return exportedTypenames;
+}
+
+ImportId ProjectStorage::importId(const Storage::Import &import) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get import id by import"_t,
+ projectStorageCategory(),
+ keyValue("import", import)};
+
+ auto importId = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchImportId(import.sourceId, import);
+ });
+
+ tracer.end(keyValue("import id", importId));
+
+ return importId;
+}
+
+ImportedTypeNameId ProjectStorage::importedTypeNameId(ImportId importId,
+ Utils::SmallStringView typeName)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get imported type name id by import id"_t,
+ projectStorageCategory(),
+ keyValue("import id", importId),
+ keyValue("imported type name", typeName)};
+
+ auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported,
+ importId,
+ typeName);
+ });
+
+ tracer.end(keyValue("imported type name id", importedTypeNameId));
+
+ return importedTypeNameId;
+}
+
+ImportedTypeNameId ProjectStorage::importedTypeNameId(SourceId sourceId,
+ Utils::SmallStringView typeName)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get imported type name id by source id"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId),
+ keyValue("imported type name", typeName)};
+
+ auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported,
+ sourceId,
+ typeName);
+ });
+
+ tracer.end(keyValue("imported type name id", importedTypeNameId));
+
+ return importedTypeNameId;
+}
+
+QVarLengthArray<PropertyDeclarationId, 128> ProjectStorage::propertyDeclarationIds(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property declaration ids"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto propertyDeclarationIds = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchPropertyDeclarationIds(typeId);
+ });
+
+ std::sort(propertyDeclarationIds.begin(), propertyDeclarationIds.end());
+
+ tracer.end(keyValue("property declaration ids", propertyDeclarationIds));
+
+ return propertyDeclarationIds;
+}
+
+QVarLengthArray<PropertyDeclarationId, 128> ProjectStorage::localPropertyDeclarationIds(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get local property declaration ids"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto propertyDeclarationIds = s->selectLocalPropertyDeclarationIdsForTypeStatement
+ .valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>(
+ typeId);
+
+ tracer.end(keyValue("property declaration ids", propertyDeclarationIds));
+
+ return propertyDeclarationIds;
+}
+
+PropertyDeclarationId ProjectStorage::propertyDeclarationId(TypeId typeId,
+ Utils::SmallStringView propertyName) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property declaration id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", propertyName)};
+
+ auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchPropertyDeclarationId(typeId, propertyName);
+ });
+
+ tracer.end(keyValue("property declaration id", propertyDeclarationId));
+
+ return propertyDeclarationId;
+}
+
+PropertyDeclarationId ProjectStorage::localPropertyDeclarationId(TypeId typeId,
+ Utils::SmallStringView propertyName) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get local property declaration id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", propertyName)};
+
+ auto propertyDeclarationId = s->selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement
+ .valueWithTransaction<PropertyDeclarationId>(typeId,
+ propertyName);
+
+ tracer.end(keyValue("property declaration id", propertyDeclarationId));
+
+ return propertyDeclarationId;
+}
+
+PropertyDeclarationId ProjectStorage::defaultPropertyDeclarationId(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get default property declaration id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchDefaultPropertyDeclarationId(typeId);
+ });
+
+ tracer.end(keyValue("property declaration id", propertyDeclarationId));
+
+ return propertyDeclarationId;
+}
+
+std::optional<Storage::Info::PropertyDeclaration> ProjectStorage::propertyDeclaration(
+ PropertyDeclarationId propertyDeclarationId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property declaration"_t,
+ projectStorageCategory(),
+ keyValue("property declaration id", propertyDeclarationId)};
+
+ auto propertyDeclaration = s->selectPropertyDeclarationForPropertyDeclarationIdStatement
+ .optionalValueWithTransaction<Storage::Info::PropertyDeclaration>(
+ propertyDeclarationId);
+
+ tracer.end(keyValue("property declaration", propertyDeclaration));
+
+ return propertyDeclaration;
+}
+
+std::optional<Storage::Info::Type> ProjectStorage::type(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory(), keyValue("type id", typeId)};
+
+ auto type = s->selectInfoTypeByTypeIdStatement.optionalValueWithTransaction<Storage::Info::Type>(
+ typeId);
+
+ tracer.end(keyValue("type", type));
+
+ return type;
+}
+
+Utils::PathString ProjectStorage::typeIconPath(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type icon path"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto typeIconPath = s->selectTypeIconPathStatement.valueWithTransaction<Utils::PathString>(typeId);
+
+ tracer.end(keyValue("type icon path", typeIconPath));
+
+ return typeIconPath;
+}
+
+Storage::Info::TypeHints ProjectStorage::typeHints(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type hints"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto typeHints = s->selectTypeHintsStatement.valuesWithTransaction<Storage::Info::TypeHints, 4>(
+ typeId);
+
+ tracer.end(keyValue("type hints", typeHints));
+
+ return typeHints;
+}
+
+SmallSourceIds<4> ProjectStorage::typeAnnotationSourceIds(SourceId directoryId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type annotaion source ids"_t,
+ projectStorageCategory(),
+ keyValue("source id", directoryId)};
+
+ auto sourceIds = s->selectTypeAnnotationSourceIdsStatement.valuesWithTransaction<SmallSourceIds<4>>(
+ directoryId);
+
+ tracer.end(keyValue("source ids", sourceIds));
+
+ return sourceIds;
+}
+
+SmallSourceIds<64> ProjectStorage::typeAnnotationDirectorySourceIds() const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, projectStorageCategory()};
+
+ auto sourceIds = s->selectTypeAnnotationDirectorySourceIdsStatement
+ .valuesWithTransaction<SmallSourceIds<64>>();
+
+ tracer.end(keyValue("source ids", sourceIds));
+
+ return sourceIds;
+}
+
+Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get item library entries by type id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ using Storage::Info::ItemLibraryProperties;
+ Storage::Info::ItemLibraryEntries entries;
+
+ auto callback = [&](TypeId typeId_,
+ Utils::SmallStringView name,
+ Utils::SmallStringView iconPath,
+ Utils::SmallStringView category,
+ Utils::SmallStringView import,
+ Utils::SmallStringView toolTip,
+ Utils::SmallStringView properties,
+ Utils::SmallStringView extraFilePaths,
+ Utils::SmallStringView templatePath) {
+ auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath);
+ if (properties.size())
+ s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
+ if (extraFilePaths.size())
+ s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths);
+ };
+
+ s->selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId);
+
+ tracer.end(keyValue("item library entries", entries));
+
+ return entries;
+}
+
+Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId importId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get item library entries by import id"_t,
+ projectStorageCategory(),
+ keyValue("import id", importId)};
+
+ using Storage::Info::ItemLibraryProperties;
+ Storage::Info::ItemLibraryEntries entries;
+
+ auto callback = [&](TypeId typeId_,
+ Utils::SmallStringView name,
+ Utils::SmallStringView iconPath,
+ Utils::SmallStringView category,
+ Utils::SmallStringView import,
+ Utils::SmallStringView toolTip,
+ Utils::SmallStringView properties,
+ Utils::SmallStringView extraFilePaths,
+ Utils::SmallStringView templatePath) {
+ auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath);
+ if (properties.size())
+ s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
+ if (extraFilePaths.size())
+ s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths);
+ };
+
+ s->selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, importId);
+
+ tracer.end(keyValue("item library entries", entries));
+
+ return entries;
+}
+
+Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId sourceId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get item library entries by source id"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
+
+ using Storage::Info::ItemLibraryProperties;
+ Storage::Info::ItemLibraryEntries entries;
+
+ auto callback = [&](TypeId typeId,
+ Utils::SmallStringView name,
+ Utils::SmallStringView iconPath,
+ Utils::SmallStringView category,
+ Utils::SmallStringView import,
+ Utils::SmallStringView toolTip,
+ Utils::SmallStringView properties,
+ Utils::SmallStringView extraFilePaths,
+ Utils::SmallStringView templatePath) {
+ auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath);
+ if (properties.size())
+ s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
+ if (extraFilePaths.size())
+ s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths);
+ };
+
+ s->selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId);
+
+ tracer.end(keyValue("item library entries", entries));
+
+ return entries;
+}
+
+Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()};
+
+ using Storage::Info::ItemLibraryProperties;
+ Storage::Info::ItemLibraryEntries entries;
+
+ auto callback = [&](TypeId typeId,
+ Utils::SmallStringView name,
+ Utils::SmallStringView iconPath,
+ Utils::SmallStringView category,
+ Utils::SmallStringView import,
+ Utils::SmallStringView toolTip,
+ Utils::SmallStringView properties,
+ Utils::SmallStringView extraFilePaths,
+ Utils::SmallStringView templatePath) {
+ auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath);
+ if (properties.size())
+ s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
+ if (extraFilePaths.size())
+ s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths);
+ };
+
+ s->selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback);
+
+ tracer.end(keyValue("item library entries", entries));
+
+ return entries;
+}
+
+std::vector<Utils::SmallString> ProjectStorage::signalDeclarationNames(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get signal names"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto signalDeclarationNames = s->selectSignalDeclarationNamesForTypeStatement
+ .valuesWithTransaction<Utils::SmallString, 32>(typeId);
+
+ tracer.end(keyValue("signal names", signalDeclarationNames));
+
+ return signalDeclarationNames;
+}
+
+std::vector<Utils::SmallString> ProjectStorage::functionDeclarationNames(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get function names"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto functionDeclarationNames = s->selectFuncionDeclarationNamesForTypeStatement
+ .valuesWithTransaction<Utils::SmallString, 32>(typeId);
+
+ tracer.end(keyValue("function names", functionDeclarationNames));
+
+ return functionDeclarationNames;
+}
+
+std::optional<Utils::SmallString> ProjectStorage::propertyName(
+ PropertyDeclarationId propertyDeclarationId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get property name"_t,
+ projectStorageCategory(),
+ keyValue("property declaration id", propertyDeclarationId)};
+
+ auto propertyName = s->selectPropertyNameStatement.optionalValueWithTransaction<Utils::SmallString>(
+ propertyDeclarationId);
+
+ tracer.end(keyValue("property name", propertyName));
+
+ return propertyName;
+}
+
+SmallTypeIds<16> ProjectStorage::prototypeIds(TypeId type) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory(), keyValue("type id", type)};
+
+ auto prototypeIds = s->selectPrototypeAndExtensionIdsStatement
+ .valuesWithTransaction<SmallTypeIds<16>>(type);
+
+ tracer.end(keyValue("type ids", prototypeIds));
+
+ return prototypeIds;
+}
+
+SmallTypeIds<16> ProjectStorage::prototypeAndSelfIds(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()};
+
+ SmallTypeIds<16> prototypeAndSelfIds;
+ prototypeAndSelfIds.push_back(typeId);
+
+ s->selectPrototypeAndExtensionIdsStatement.readToWithTransaction(prototypeAndSelfIds, typeId);
+
+ tracer.end(keyValue("type ids", prototypeAndSelfIds));
+
+ return prototypeAndSelfIds;
+}
+
+SmallTypeIds<64> ProjectStorage::heirIds(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()};
+
+ auto heirIds = s->selectHeirTypeIdsStatement.valuesWithTransaction<SmallTypeIds<64>>(typeId);
+
+ tracer.end(keyValue("type ids", heirIds));
+
+ return heirIds;
+}
+
+bool ProjectStorage::isBasedOn(TypeId) const
+{
+ return false;
+}
+
+bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1) const
+{
+ return isBasedOn_(typeId, id1);
+}
+
+bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const
+{
+ return isBasedOn_(typeId, id1, id2);
+}
+
+bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const
+{
+ return isBasedOn_(typeId, id1, id2, id3);
+}
+
+bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const
+{
+ return isBasedOn_(typeId, id1, id2, id3, id4);
+}
+
+bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const
+{
+ return isBasedOn_(typeId, id1, id2, id3, id4, id5);
+}
+
+bool ProjectStorage::isBasedOn(
+ TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const
+{
+ return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6);
+}
+
+bool ProjectStorage::isBasedOn(
+ TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6, TypeId id7) const
+{
+ return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7);
+}
+
+TypeId ProjectStorage::fetchTypeIdByExportedName(Utils::SmallStringView name) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t,
+ projectStorageCategory(),
+ keyValue("exported type name", name)};
+
+ auto typeId = s->selectTypeIdByExportedNameStatement.valueWithTransaction<TypeId>(name);
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
+}
+
+TypeId ProjectStorage::fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds,
+ Utils::SmallStringView name) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id by module ids and exported name"_t,
+ projectStorageCategory(),
+ keyValue("module ids", NanotraceHR::array(moduleIds)),
+ keyValue("exported type name", name)};
+ auto typeId = s->selectTypeIdByModuleIdsAndExportedNameStatement.valueWithTransaction<TypeId>(
+ static_cast<void *>(moduleIds.data()), static_cast<long long>(moduleIds.size()), name);
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
+}
+
+TypeId ProjectStorage::fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id by name"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId),
+ keyValue("internal type name", name)};
+
+ auto typeId = s->selectTypeIdBySourceIdAndNameStatement.valueWithTransaction<TypeId>(sourceId,
+ name);
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
+}
+
+Storage::Synchronization::Type ProjectStorage::fetchTypeByTypeId(TypeId typeId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type by type id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto type = Sqlite::withDeferredTransaction(database, [&] {
+ auto type = s->selectTypeByTypeIdStatement.value<Storage::Synchronization::Type>(typeId);
+
+ type.exportedTypes = fetchExportedTypes(typeId);
+ type.propertyDeclarations = fetchPropertyDeclarations(type.typeId);
+ type.functionDeclarations = fetchFunctionDeclarations(type.typeId);
+ type.signalDeclarations = fetchSignalDeclarations(type.typeId);
+ type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId);
+
+ return type;
+ });
+
+ tracer.end(keyValue("type", type));
+
+ return type;
+}
+
+Storage::Synchronization::Types ProjectStorage::fetchTypes()
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch types"_t, projectStorageCategory()};
+
+ auto types = Sqlite::withDeferredTransaction(database, [&] {
+ auto types = s->selectTypesStatement.values<Storage::Synchronization::Type, 64>();
+
+ for (Storage::Synchronization::Type &type : types) {
+ type.exportedTypes = fetchExportedTypes(type.typeId);
+ type.propertyDeclarations = fetchPropertyDeclarations(type.typeId);
+ type.functionDeclarations = fetchFunctionDeclarations(type.typeId);
+ type.signalDeclarations = fetchSignalDeclarations(type.typeId);
+ type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId);
+ }
+
+ return types;
+ });
+
+ tracer.end(keyValue("type", types));
+
+ return types;
+}
+
+SourceContextId ProjectStorage::fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()};
+
+ auto sourceContextId = readSourceContextId(sourceContextPath);
+
+ return sourceContextId ? sourceContextId : writeSourceContextId(sourceContextPath);
+}
+
+SourceContextId ProjectStorage::fetchSourceContextId(Utils::SmallStringView sourceContextPath)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source context id"_t,
+ projectStorageCategory(),
+ keyValue("source context path", sourceContextPath)};
+
+ SourceContextId sourceContextId;
+ try {
+ sourceContextId = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchSourceContextIdUnguarded(sourceContextPath);
+ });
+ } catch (const Sqlite::ConstraintPreventsModification &) {
+ sourceContextId = fetchSourceContextId(sourceContextPath);
+ }
+
+ tracer.end(keyValue("source context id", sourceContextId));
+
+ return sourceContextId;
+}
+
+Utils::PathString ProjectStorage::fetchSourceContextPath(SourceContextId sourceContextId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source context path"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId)};
+
+ auto path = Sqlite::withDeferredTransaction(database, [&] {
+ auto optionalSourceContextPath = s->selectSourceContextPathFromSourceContextsBySourceContextIdStatement
+ .optionalValue<Utils::PathString>(sourceContextId);
+
+ if (!optionalSourceContextPath)
+ throw SourceContextIdDoesNotExists();
+
+ return std::move(*optionalSourceContextPath);
+ });
+
+ tracer.end(keyValue("source context path", path));
+
+ return path;
+}
+
+Cache::SourceContexts ProjectStorage::fetchAllSourceContexts() const
+{
+ NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory()};
+
+ return s->selectAllSourceContextsStatement.valuesWithTransaction<Cache::SourceContext, 128>();
+}
+
+SourceId ProjectStorage::fetchSourceId(SourceContextId sourceContextId,
+ Utils::SmallStringView sourceName)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source id"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId),
+ keyValue("source name", sourceName)};
+
+ auto sourceId = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchSourceIdUnguarded(sourceContextId, sourceName);
+ });
+
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
+}
+
+Cache::SourceNameAndSourceContextId ProjectStorage::fetchSourceNameAndSourceContextId(SourceId sourceId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source name and source context id"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
+
+ auto value = s->selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement
+ .valueWithTransaction<Cache::SourceNameAndSourceContextId>(sourceId);
+
+ if (!value.sourceContextId)
+ throw SourceIdDoesNotExists();
+
+ tracer.end(keyValue("source name", value.sourceName),
+ keyValue("source context id", value.sourceContextId));
+
+ return value;
+}
+
+void ProjectStorage::clearSources()
+{
+ Sqlite::withImmediateTransaction(database, [&] {
+ s->deleteAllSourceContextsStatement.execute();
+ s->deleteAllSourcesStatement.execute();
+ });
+}
+
+SourceContextId ProjectStorage::fetchSourceContextId(SourceId sourceId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source context id"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
+
+ auto sourceContextId = s->selectSourceContextIdFromSourcesBySourceIdStatement
+ .valueWithTransaction<SourceContextId>(sourceId);
+
+ if (!sourceContextId)
+ throw SourceIdDoesNotExists();
+
+ tracer.end(keyValue("source context id", sourceContextId));
+
+ return sourceContextId;
+}
+
+Cache::Sources ProjectStorage::fetchAllSources() const
+{
+ NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory()};
+
+ return s->selectAllSourcesStatement.valuesWithTransaction<Cache::Source, 1024>();
+}
+
+SourceId ProjectStorage::fetchSourceIdUnguarded(SourceContextId sourceContextId,
+ Utils::SmallStringView sourceName)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch source id unguarded"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId),
+ keyValue("source name", sourceName)};
+
+ auto sourceId = readSourceId(sourceContextId, sourceName);
+
+ if (!sourceId)
+ sourceId = writeSourceId(sourceContextId, sourceName);
+
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
+}
+
+FileStatuses ProjectStorage::fetchAllFileStatuses() const
+{
+ NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()};
+
+ return s->selectAllFileStatusesStatement.valuesWithTransaction<FileStatus>();
+}
+
+FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch file status"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
+
+ auto fileStatus = s->selectFileStatusesForSourceIdStatement.valueWithTransaction<FileStatus>(
+ sourceId);
+
+ tracer.end(keyValue("file status", fileStatus));
+
+ return fileStatus;
+}
+
+std::optional<Storage::Synchronization::ProjectData> ProjectStorage::fetchProjectData(SourceId sourceId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch project data"_t,
+ projectStorageCategory(),
+ keyValue("source id", sourceId)};
+
+ auto projectData = s->selectProjectDataForSourceIdStatement
+ .optionalValueWithTransaction<Storage::Synchronization::ProjectData>(
+ sourceId);
+
+ tracer.end(keyValue("project data", projectData));
+
+ return projectData;
+}
+
+Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas(SourceId projectSourceId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch project datas by source id"_t,
+ projectStorageCategory(),
+ keyValue("source id", projectSourceId)};
+
+ auto projectDatas = s->selectProjectDatasForSourceIdStatement
+ .valuesWithTransaction<Storage::Synchronization::ProjectData, 1024>(
+ projectSourceId);
+
+ tracer.end(keyValue("project datas", projectDatas));
+
+ return projectDatas;
+}
+
+Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas(
+ const SourceIds &projectSourceIds) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t,
+ projectStorageCategory(),
+ keyValue("source ids", projectSourceIds)};
+
+ auto projectDatas = s->selectProjectDatasForSourceIdsStatement
+ .valuesWithTransaction<Storage::Synchronization::ProjectData, 64>(
+ toIntegers(projectSourceIds));
+
+ tracer.end(keyValue("project datas", projectDatas));
+
+ return projectDatas;
+}
+
+void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId)
+{
+ Sqlite::ImmediateSessionTransaction transaction{database};
+
+ s->upsertPropertyEditorPathIdStatement.write(typeId, pathId);
+
+ transaction.commit();
+}
+
+SourceId ProjectStorage::propertyEditorPathId(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"property editor path id"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto sourceId = s->selectPropertyEditorPathIdStatement.valueWithTransaction<SourceId>(typeId);
+
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
+}
+
+Storage::Imports ProjectStorage::fetchDocumentImports() const
+{
+ NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()};
+
+ return s->selectAllDocumentImportForSourceIdStatement.valuesWithTransaction<Storage::Imports>();
+}
+
+void ProjectStorage::resetForTestsOnly()
+{
+ database.clearAllTablesForTestsOnly();
+ commonTypeCache_.clearForTestsOnly();
+ moduleCache.clearForTestOnly();
+}
+
+bool ProjectStorage::moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept
+{
+ return first < second;
+}
+
+ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch module id"_t,
+ projectStorageCategory(),
+ keyValue("module name", moduleName)};
+
+ auto moduleId = Sqlite::withDeferredTransaction(database, [&] {
+ return fetchModuleIdUnguarded(moduleName);
+ });
+
+ tracer.end(keyValue("module id", moduleId));
+
+ return moduleId;
+}
+
+Utils::PathString ProjectStorage::fetchModuleName(ModuleId id)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch module name"_t,
+ projectStorageCategory(),
+ keyValue("module id", id)};
+
+ auto moduleName = Sqlite::withDeferredTransaction(database,
+ [&] { return fetchModuleNameUnguarded(id); });
+
+ tracer.end(keyValue("module name", moduleName));
+
+ return moduleName;
+}
+
+ProjectStorage::Modules ProjectStorage::fetchAllModules() const
+{
+ NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()};
+
+ return s->selectAllModulesStatement.valuesWithTransaction<Module, 128>();
+}
+
+void ProjectStorage::callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"call refresh meta info callback"_t,
+ projectStorageCategory(),
+ keyValue("type ids", deletedTypeIds)};
+
+ if (deletedTypeIds.size()) {
+ for (ProjectStorageObserver *observer : observers)
+ observer->removedTypeIds(deletedTypeIds);
+ }
+}
+
+SourceIds ProjectStorage::filterSourceIdsWithoutType(const SourceIds &updatedSourceIds,
+ SourceIds &sourceIdsOfTypes)
+{
+ std::sort(sourceIdsOfTypes.begin(), sourceIdsOfTypes.end());
+
+ SourceIds sourceIdsWithoutTypeSourceIds;
+ sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size());
+ std::set_difference(updatedSourceIds.begin(),
+ updatedSourceIds.end(),
+ sourceIdsOfTypes.begin(),
+ sourceIdsOfTypes.end(),
+ std::back_inserter(sourceIdsWithoutTypeSourceIds));
+
+ return sourceIdsWithoutTypeSourceIds;
+}
+
+TypeIds ProjectStorage::fetchTypeIds(const SourceIds &sourceIds)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type ids"_t,
+ projectStorageCategory(),
+ keyValue("source ids", sourceIds)};
+
+ return s->selectTypeIdsForSourceIdsStatement.values<TypeId, 128>(toIntegers(sourceIds));
+}
+
+void ProjectStorage::unique(SourceIds &sourceIds)
+{
+ std::sort(sourceIds.begin(), sourceIds.end());
+ auto newEnd = std::unique(sourceIds.begin(), sourceIds.end());
+ sourceIds.erase(newEnd, sourceIds.end());
+}
+
+void ProjectStorage::synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"synchronize type traits"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("type traits", traits)};
+
+ s->updateTypeAnnotationTraitStatement.write(typeId, traits.annotation);
+}
+
+void ProjectStorage::updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations)
+{
+ NanotraceHR::Tracer tracer{"update type id in type annotations"_t, projectStorageCategory()};
+
+ for (auto &annotation : typeAnnotations) {
+ annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId,
+ annotation.typeName);
+ }
+
+ for (auto &annotation : typeAnnotations) {
+ if (!annotation.typeId)
+ qWarning() << moduleName(annotation.moduleId).toQString()
+ << annotation.typeName.toQString();
+ }
+
+ typeAnnotations.erase(std::remove_if(typeAnnotations.begin(),
+ typeAnnotations.end(),
+ [](const auto &annotation) { return !annotation.typeId; }),
+ typeAnnotations.end());
+}
+
+void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations,
+ const SourceIds &updatedTypeAnnotationSourceIds)
+{
+ NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory()};
+
+ using Storage::Synchronization::TypeAnnotation;
+
+ updateTypeIdInTypeAnnotations(typeAnnotations);
+
+ auto compareKey = [](auto &&first, auto &&second) { return first.typeId - second.typeId; };
+
+ std::sort(typeAnnotations.begin(), typeAnnotations.end(), [&](auto &&first, auto &&second) {
+ return first.typeId < second.typeId;
+ });
+
+ auto range = s->selectTypeAnnotationsForSourceIdsStatement.range<TypeAnnotationView>(
+ toIntegers(updatedTypeAnnotationSourceIds));
+
+ auto insert = [&](const TypeAnnotation &annotation) {
+ if (!annotation.sourceId)
+ throw TypeAnnotationHasInvalidSourceId{};
+
+ synchronizeTypeTraits(annotation.typeId, annotation.traits);
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert type annotations"_t,
+ projectStorageCategory(),
+ keyValue("type annotation", annotation)};
+
+ s->insertTypeAnnotationStatement.write(annotation.typeId,
+ annotation.sourceId,
+ annotation.directorySourceId,
+ annotation.iconPath,
+ createEmptyAsNull(annotation.itemLibraryJson),
+ createEmptyAsNull(annotation.hintsJson));
+ };
+
+ auto update = [&](const TypeAnnotationView &annotationFromDatabase,
+ const TypeAnnotation &annotation) {
+ synchronizeTypeTraits(annotation.typeId, annotation.traits);
+
+ if (annotationFromDatabase.iconPath != annotation.iconPath
+ || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson
+ || annotationFromDatabase.hintsJson != annotation.hintsJson) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update type annotations"_t,
+ projectStorageCategory(),
+ keyValue("type annotation from database",
+ annotationFromDatabase),
+ keyValue("type annotation", annotation)};
+
+ s->updateTypeAnnotationStatement.write(annotation.typeId,
+ annotation.iconPath,
+ createEmptyAsNull(annotation.itemLibraryJson),
+ createEmptyAsNull(annotation.hintsJson));
+ return Sqlite::UpdateChange::Update;
+ }
+
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const TypeAnnotationView &annotationFromDatabase) {
+ synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{});
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove type annotations"_t,
+ projectStorageCategory(),
+ keyValue("type annotation", annotationFromDatabase)};
+
+ s->deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId);
+ };
+
+ Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::synchronizeTypeTrait(const Storage::Synchronization::Type &type)
+{
+ s->updateTypeTraitStatement.write(type.typeId, type.traits.type);
+}
+
+void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types,
+ TypeIds &updatedTypeIds,
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions,
+ const SourceIds &updatedSourceIds)
+{
+ NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory()};
+
+ Storage::Synchronization::ExportedTypes exportedTypes;
+ exportedTypes.reserve(types.size() * 3);
+ SourceIds sourceIdsOfTypes;
+ sourceIdsOfTypes.reserve(updatedSourceIds.size());
+ SourceIds notUpdatedExportedSourceIds;
+ notUpdatedExportedSourceIds.reserve(updatedSourceIds.size());
+ SourceIds exportedSourceIds;
+ exportedSourceIds.reserve(types.size());
+
+ for (auto &type : types) {
+ if (!type.sourceId)
+ throw TypeHasInvalidSourceId{};
+
+ TypeId typeId = declareType(type);
+ synchronizeTypeTrait(type);
+ sourceIdsOfTypes.push_back(type.sourceId);
+ updatedTypeIds.push_back(typeId);
+ if (type.changeLevel != Storage::Synchronization::ChangeLevel::ExcludeExportedTypes) {
+ exportedSourceIds.push_back(type.sourceId);
+ extractExportedTypes(typeId, type, exportedTypes);
+ }
+ }
+
+ std::sort(types.begin(), types.end(), [](const auto &first, const auto &second) {
+ return first.typeId < second.typeId;
+ });
+
+ unique(exportedSourceIds);
+
+ SourceIds sourceIdsWithoutType = filterSourceIdsWithoutType(updatedSourceIds, sourceIdsOfTypes);
+ exportedSourceIds.insert(exportedSourceIds.end(),
+ sourceIdsWithoutType.begin(),
+ sourceIdsWithoutType.end());
+ TypeIds exportedTypeIds = fetchTypeIds(exportedSourceIds);
+ synchronizeExportedTypes(exportedTypeIds,
+ exportedTypes,
+ relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes,
+ relinkableExtensions);
+
+ syncPrototypesAndExtensions(types, relinkablePrototypes, relinkableExtensions);
+ resetDefaultPropertiesIfChanged(types);
+ resetRemovedAliasPropertyDeclarationsToNull(types, relinkableAliasPropertyDeclarations);
+ syncDeclarations(types,
+ insertedAliasPropertyDeclarations,
+ updatedAliasPropertyDeclarations,
+ relinkablePropertyDeclarations);
+ syncDefaultProperties(types);
+}
+
+void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas,
+ const SourceIds &updatedProjectSourceIds)
+{
+ NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()};
+
+ auto compareKey = [](auto &&first, auto &&second) {
+ auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId;
+ if (projectSourceIdDifference != 0)
+ return projectSourceIdDifference;
+
+ return first.sourceId - second.sourceId;
+ };
+
+ std::sort(projectDatas.begin(), projectDatas.end(), [&](auto &&first, auto &&second) {
+ return std::tie(first.projectSourceId, first.sourceId)
+ < std::tie(second.projectSourceId, second.sourceId);
+ });
+
+ auto range = s->selectProjectDatasForSourceIdsStatement.range<Storage::Synchronization::ProjectData>(
+ toIntegers(updatedProjectSourceIds));
+
+ auto insert = [&](const Storage::Synchronization::ProjectData &projectData) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert project data"_t,
+ projectStorageCategory(),
+ keyValue("project data", projectData)};
+
+ if (!projectData.projectSourceId)
+ throw ProjectDataHasInvalidProjectSourceId{};
+ if (!projectData.sourceId)
+ throw ProjectDataHasInvalidSourceId{};
+
+ s->insertProjectDataStatement.write(projectData.projectSourceId,
+ projectData.sourceId,
+ projectData.moduleId,
+ projectData.fileType);
+ };
+
+ auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase,
+ const Storage::Synchronization::ProjectData &projectData) {
+ if (projectDataFromDatabase.fileType != projectData.fileType
+ || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update project data"_t,
+ projectStorageCategory(),
+ keyValue("project data", projectData),
+ keyValue("project data from database", projectDataFromDatabase)};
+
+ s->updateProjectDataStatement.write(projectData.projectSourceId,
+ projectData.sourceId,
+ projectData.moduleId,
+ projectData.fileType);
+ return Sqlite::UpdateChange::Update;
+ }
+
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const Storage::Synchronization::ProjectData &projectData) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove project data"_t,
+ projectStorageCategory(),
+ keyValue("project data", projectData)};
+
+ s->deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId);
+ };
+
+ Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses,
+ const SourceIds &updatedSourceIds)
+{
+ NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory()};
+
+ auto compareKey = [](auto &&first, auto &&second) { return first.sourceId - second.sourceId; };
+
+ std::sort(fileStatuses.begin(), fileStatuses.end(), [&](auto &&first, auto &&second) {
+ return first.sourceId < second.sourceId;
+ });
+
+ auto range = s->selectFileStatusesForSourceIdsStatement.range<FileStatus>(
+ toIntegers(updatedSourceIds));
+
+ auto insert = [&](const FileStatus &fileStatus) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert file status"_t,
+ projectStorageCategory(),
+ keyValue("file status", fileStatus)};
+
+ if (!fileStatus.sourceId)
+ throw FileStatusHasInvalidSourceId{};
+ s->insertFileStatusStatement.write(fileStatus.sourceId,
+ fileStatus.size,
+ fileStatus.lastModified);
+ };
+
+ auto update = [&](const FileStatus &fileStatusFromDatabase, const FileStatus &fileStatus) {
+ if (fileStatusFromDatabase.lastModified != fileStatus.lastModified
+ || fileStatusFromDatabase.size != fileStatus.size) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update file status"_t,
+ projectStorageCategory(),
+ keyValue("file status", fileStatus),
+ keyValue("file status from database", fileStatusFromDatabase)};
+
+ s->updateFileStatusStatement.write(fileStatus.sourceId,
+ fileStatus.size,
+ fileStatus.lastModified);
+ return Sqlite::UpdateChange::Update;
+ }
+
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const FileStatus &fileStatus) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove file status"_t,
+ projectStorageCategory(),
+ keyValue("file status", fileStatus)};
+
+ s->deleteFileStatusStatement.write(fileStatus.sourceId);
+ };
+
+ Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::synchronizeImports(Storage::Imports &imports,
+ const SourceIds &updatedSourceIds,
+ Storage::Imports &moduleDependencies,
+ const SourceIds &updatedModuleDependencySourceIds,
+ Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
+ const ModuleIds &updatedModuleIds)
+{
+ NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()};
-template class QmlDesigner::ProjectStorage<Sqlite::Database>;
+ synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds);
+ NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()};
+ synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import);
+ importTracer.end();
+ NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t,
+ projectStorageCategory()};
+ synchronizeDocumentImports(moduleDependencies,
+ updatedModuleDependencySourceIds,
+ Storage::Synchronization::ImportKind::ModuleDependency);
+ moduleDependenciesTracer.end();
+}
+
+void ProjectStorage::synchromizeModuleExportedImports(
+ Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
+ const ModuleIds &updatedModuleIds)
+{
+ NanotraceHR::Tracer tracer{"synchronize module exported imports"_t, projectStorageCategory()};
+ std::sort(moduleExportedImports.begin(),
+ moduleExportedImports.end(),
+ [](auto &&first, auto &&second) {
+ return std::tie(first.moduleId, first.exportedModuleId)
+ < std::tie(second.moduleId, second.exportedModuleId);
+ });
+
+ auto range = s->selectModuleExportedImportsForSourceIdStatement
+ .range<Storage::Synchronization::ModuleExportedImportView>(
+ toIntegers(updatedModuleIds));
+
+ auto compareKey = [](const Storage::Synchronization::ModuleExportedImportView &view,
+ const Storage::Synchronization::ModuleExportedImport &import) -> long long {
+ auto moduleIdDifference = view.moduleId - import.moduleId;
+ if (moduleIdDifference != 0)
+ return moduleIdDifference;
+
+ return view.exportedModuleId - import.exportedModuleId;
+ };
+
+ auto insert = [&](const Storage::Synchronization::ModuleExportedImport &import) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert module exported import"_t,
+ projectStorageCategory(),
+ keyValue("module exported import", import),
+ keyValue("module id", import.moduleId)};
+ tracer.tick("exported module"_t, keyValue("module id", import.exportedModuleId));
+
+ if (import.version.minor) {
+ s->insertModuleExportedImportWithVersionStatement.write(import.moduleId,
+ import.exportedModuleId,
+ import.isAutoVersion,
+ import.version.major.value,
+ import.version.minor.value);
+ } else if (import.version.major) {
+ s->insertModuleExportedImportWithMajorVersionStatement.write(import.moduleId,
+ import.exportedModuleId,
+ import.isAutoVersion,
+ import.version.major.value);
+ } else {
+ s->insertModuleExportedImportWithoutVersionStatement.write(import.moduleId,
+ import.exportedModuleId,
+ import.isAutoVersion);
+ }
+ };
+
+ auto update = [](const Storage::Synchronization::ModuleExportedImportView &,
+ const Storage::Synchronization::ModuleExportedImport &) {
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const Storage::Synchronization::ModuleExportedImportView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove module exported import"_t,
+ projectStorageCategory(),
+ keyValue("module exported import view", view),
+ keyValue("module id", view.moduleId)};
+ tracer.tick("exported module"_t, keyValue("module id", view.exportedModuleId));
+
+ s->deleteModuleExportedImportStatement.write(view.moduleExportedImportId);
+ };
+
+ Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove);
+}
+
+ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch module id ungarded"_t,
+ projectStorageCategory(),
+ keyValue("module name", name)};
+
+ auto moduleId = s->selectModuleIdByNameStatement.value<ModuleId>(name);
+
+ if (!moduleId)
+ moduleId = s->insertModuleNameStatement.value<ModuleId>(name);
+
+ tracer.end(keyValue("module id", moduleId));
+
+ return moduleId;
+}
+
+Utils::PathString ProjectStorage::fetchModuleNameUnguarded(ModuleId id) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch module name ungarded"_t,
+ projectStorageCategory(),
+ keyValue("module id", id)};
+
+ auto moduleName = s->selectModuleNameStatement.value<Utils::PathString>(id);
+
+ if (moduleName.empty())
+ throw ModuleDoesNotExists{};
+
+ tracer.end(keyValue("module name", moduleName));
+
+ return moduleName;
+}
+
+void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType(
+ TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle alias property declarations with property type"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("relinkable alias property declarations",
+ relinkableAliasPropertyDeclarations)};
+
+ auto callback = [&](TypeId typeId_,
+ PropertyDeclarationId propertyDeclarationId,
+ ImportedTypeNameId propertyImportedTypeNameId,
+ PropertyDeclarationId aliasPropertyDeclarationId,
+ PropertyDeclarationId aliasPropertyDeclarationTailId) {
+ auto aliasPropertyName = s->selectPropertyNameStatement.value<Utils::SmallString>(
+ aliasPropertyDeclarationId);
+ Utils::SmallString aliasPropertyNameTail;
+ if (aliasPropertyDeclarationTailId)
+ aliasPropertyNameTail = s->selectPropertyNameStatement.value<Utils::SmallString>(
+ aliasPropertyDeclarationTailId);
+
+ relinkableAliasPropertyDeclarations.emplace_back(TypeId{typeId_},
+ PropertyDeclarationId{propertyDeclarationId},
+ ImportedTypeNameId{propertyImportedTypeNameId},
+ std::move(aliasPropertyName),
+ std::move(aliasPropertyNameTail));
+
+ s->updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId);
+ };
+
+ s->selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement.readCallback(callback, typeId);
+}
+
+void ProjectStorage::handlePropertyDeclarationWithPropertyType(
+ TypeId typeId, PropertyDeclarations &relinkablePropertyDeclarations)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle property declarations with property type"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("relinkable property declarations",
+ relinkablePropertyDeclarations)};
+
+ s->updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations,
+ typeId);
+}
+
+void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle prototypes"_t,
+ projectStorageCategory(),
+ keyValue("type id", prototypeId),
+ keyValue("relinkable prototypes", relinkablePrototypes)};
+
+ auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
+ relinkablePrototypes.emplace_back(typeId, prototypeNameId);
+ };
+
+ s->updatePrototypeIdToNullStatement.readCallback(callback, prototypeId);
+}
+
+void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"handle extension"_t,
+ projectStorageCategory(),
+ keyValue("type id", extensionId),
+ keyValue("relinkable extensions", relinkableExtensions)};
+
+ auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
+ relinkableExtensions.emplace_back(typeId, extensionNameId);
+ };
+
+ s->updateExtensionIdToNullStatement.readCallback(callback, extensionId);
+}
+
+void ProjectStorage::deleteType(TypeId typeId,
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"delete type"_t, projectStorageCategory(), keyValue("type id", typeId)};
+
+ handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations);
+ handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations);
+ handlePrototypes(typeId, relinkablePrototypes);
+ handleExtensions(typeId, relinkableExtensions);
+ s->deleteTypeNamesByTypeIdStatement.write(typeId);
+ s->deleteEnumerationDeclarationByTypeIdStatement.write(typeId);
+ s->deletePropertyDeclarationByTypeIdStatement.write(typeId);
+ s->deleteFunctionDeclarationByTypeIdStatement.write(typeId);
+ s->deleteSignalDeclarationByTypeIdStatement.write(typeId);
+ s->deleteTypeStatement.write(typeId);
+}
+
+void ProjectStorage::relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations,
+ const TypeIds &deletedTypeIds)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"relink alias properties"_t,
+ projectStorageCategory(),
+ keyValue("alias property declarations", aliasPropertyDeclarations),
+ keyValue("deleted type ids", deletedTypeIds)};
+
+ std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end());
+
+ Utils::set_greedy_difference(
+ aliasPropertyDeclarations.cbegin(),
+ aliasPropertyDeclarations.cend(),
+ deletedTypeIds.begin(),
+ deletedTypeIds.end(),
+ [&](const AliasPropertyDeclaration &alias) {
+ auto typeId = fetchTypeId(alias.aliasImportedTypeNameId);
+
+ if (!typeId)
+ throw TypeNameDoesNotExists{fetchImportedTypeName(alias.aliasImportedTypeNameId)};
+
+ auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded(
+ typeId, alias.aliasPropertyName);
+
+ s->updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId,
+ propertyTypeId,
+ propertyTraits,
+ alias.aliasImportedTypeNameId,
+ aliasId);
+ },
+ TypeCompare<AliasPropertyDeclaration>{});
+}
+
+void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration,
+ const TypeIds &deletedTypeIds)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"relink property declarations"_t,
+ projectStorageCategory(),
+ keyValue("relinkable property declarations",
+ relinkablePropertyDeclaration),
+ keyValue("deleted type ids", deletedTypeIds)};
+
+ std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end());
+
+ Utils::set_greedy_difference(
+ relinkablePropertyDeclaration.cbegin(),
+ relinkablePropertyDeclaration.cend(),
+ deletedTypeIds.begin(),
+ deletedTypeIds.end(),
+ [&](const PropertyDeclaration &property) {
+ TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId);
+
+ if (!propertyTypeId)
+ throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)};
+
+ s->updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId,
+ propertyTypeId);
+ },
+ TypeCompare<PropertyDeclaration>{});
+}
+
+void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds,
+ const SourceIds &updatedSourceIds,
+ const TypeIds &typeIdsToBeDeleted,
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions,
+ TypeIds &deletedTypeIds)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"delete not updated types"_t,
+ projectStorageCategory(),
+ keyValue("updated type ids", updatedTypeIds),
+ keyValue("updated source ids", updatedSourceIds),
+ keyValue("type ids to be deleted", typeIdsToBeDeleted)};
+
+ auto callback = [&](TypeId typeId) {
+ deletedTypeIds.push_back(typeId);
+ deleteType(typeId,
+ relinkableAliasPropertyDeclarations,
+ relinkablePropertyDeclarations,
+ relinkablePrototypes,
+ relinkableExtensions);
+ };
+
+ s->selectNotUpdatedTypesInSourcesStatement.readCallback(callback,
+ toIntegers(updatedSourceIds),
+ toIntegers(updatedTypeIds));
+ for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted)
+ callback(typeIdToBeDeleted);
+}
+
+void ProjectStorage::relink(AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions,
+ TypeIds &deletedTypeIds)
+{
+ NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory()};
+
+ std::sort(deletedTypeIds.begin(), deletedTypeIds.end());
+
+ relinkPrototypes(relinkablePrototypes, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) {
+ s->updateTypePrototypeStatement.write(typeId, prototypeId);
+ });
+ relinkPrototypes(relinkableExtensions, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) {
+ s->updateTypeExtensionStatement.write(typeId, prototypeId);
+ });
+ relinkPropertyDeclarations(relinkablePropertyDeclarations, deletedTypeIds);
+ relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds);
+}
+
+PropertyDeclarationId ProjectStorage::fetchAliasId(TypeId aliasTypeId,
+ Utils::SmallStringView aliasPropertyName,
+ Utils::SmallStringView aliasPropertyNameTail)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch alias id"_t,
+ projectStorageCategory(),
+ keyValue("alias type id", aliasTypeId),
+ keyValue("alias property name", aliasPropertyName),
+ keyValue("alias property name tail", aliasPropertyNameTail)};
+
+ if (aliasPropertyNameTail.empty())
+ return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName);
+
+ auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName);
+
+ return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias.propertyTypeId,
+ aliasPropertyNameTail);
+}
+
+void ProjectStorage::linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"link alias property declarations alias ids"_t,
+ projectStorageCategory(),
+ keyValue("alias property declarations", aliasDeclarations)};
+
+ for (const auto &aliasDeclaration : aliasDeclarations) {
+ auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId);
+
+ if (!aliasTypeId) {
+ throw TypeNameDoesNotExists{
+ fetchImportedTypeName(aliasDeclaration.aliasImportedTypeNameId)};
+ }
+
+ auto aliasId = fetchAliasId(aliasTypeId,
+ aliasDeclaration.aliasPropertyName,
+ aliasDeclaration.aliasPropertyNameTail);
+
+ s->updatePropertyDeclarationAliasIdAndTypeNameIdStatement.write(
+ aliasDeclaration.propertyDeclarationId, aliasId, aliasDeclaration.aliasImportedTypeNameId);
+ }
+}
+
+void ProjectStorage::updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update alias property declarations"_t,
+ projectStorageCategory(),
+ keyValue("alias property declarations", aliasDeclarations)};
+
+ for (const auto &aliasDeclaration : aliasDeclarations) {
+ s->updatetPropertiesDeclarationValuesOfAliasStatement.write(
+ aliasDeclaration.propertyDeclarationId);
+ s->updatePropertyAliasDeclarationRecursivelyStatement.write(
+ aliasDeclaration.propertyDeclarationId);
+ }
+}
+
+void ProjectStorage::checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"check alias property declarations cycles"_t,
+ projectStorageCategory(),
+ keyValue("alias property declarations", aliasDeclarations)};
+ for (const auto &aliasDeclaration : aliasDeclarations)
+ checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId);
+}
+
+void ProjectStorage::linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ const AliasPropertyDeclarations &updatedAliasPropertyDeclarations)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()};
+
+ linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations);
+ linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations);
+
+ checkAliasPropertyDeclarationCycles(insertedAliasPropertyDeclarations);
+ checkAliasPropertyDeclarationCycles(updatedAliasPropertyDeclarations);
+
+ updateAliasPropertyDeclarationValues(insertedAliasPropertyDeclarations);
+ updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations);
+}
+
+void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds,
+ Storage::Synchronization::ExportedTypes &exportedTypes,
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()};
+
+ std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) {
+ if (first.moduleId < second.moduleId)
+ return true;
+ else if (first.moduleId > second.moduleId)
+ return false;
+
+ auto nameCompare = Sqlite::compare(first.name, second.name);
+
+ if (nameCompare < 0)
+ return true;
+ else if (nameCompare > 0)
+ return false;
+
+ return first.version < second.version;
+ });
+
+ auto range = s->selectExportedTypesForSourceIdsStatement
+ .range<Storage::Synchronization::ExportedTypeView>(toIntegers(updatedTypeIds));
+
+ auto compareKey = [](const Storage::Synchronization::ExportedTypeView &view,
+ const Storage::Synchronization::ExportedType &type) -> long long {
+ auto moduleIdDifference = view.moduleId - type.moduleId;
+ if (moduleIdDifference != 0)
+ return moduleIdDifference;
+
+ auto nameDifference = Sqlite::compare(view.name, type.name);
+ if (nameDifference != 0)
+ return nameDifference;
+
+ auto versionDifference = view.version.major.value - type.version.major.value;
+ if (versionDifference != 0)
+ return versionDifference;
+
+ return view.version.minor.value - type.version.minor.value;
+ };
+
+ auto insert = [&](const Storage::Synchronization::ExportedType &type) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert exported type"_t,
+ projectStorageCategory(),
+ keyValue("exported type", type),
+ keyValue("type id", type.typeId),
+ keyValue("module id", type.moduleId)};
+ if (!type.moduleId)
+ throw QmlDesigner::ModuleDoesNotExists{};
+
+ try {
+ if (type.version) {
+ s->insertExportedTypeNamesWithVersionStatement.write(type.moduleId,
+ type.name,
+ type.version.major.value,
+ type.version.minor.value,
+ type.typeId);
+
+ } else if (type.version.major) {
+ s->insertExportedTypeNamesWithMajorVersionStatement.write(type.moduleId,
+ type.name,
+ type.version.major.value,
+ type.typeId);
+ } else {
+ s->insertExportedTypeNamesWithoutVersionStatement.write(type.moduleId,
+ type.name,
+ type.typeId);
+ }
+ } catch (const Sqlite::ConstraintPreventsModification &) {
+ throw QmlDesigner::ExportedTypeCannotBeInserted{type.name};
+ }
+ };
+
+ auto update = [&](const Storage::Synchronization::ExportedTypeView &view,
+ const Storage::Synchronization::ExportedType &type) {
+ if (view.typeId != type.typeId) {
+ NanotraceHR::Tracer tracer{"update exported type"_t,
+ projectStorageCategory(),
+ keyValue("exported type", type),
+ keyValue("exported type view", view),
+ keyValue("type id", type.typeId),
+ keyValue("module id", type.typeId)};
+
+ handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations);
+ handleAliasPropertyDeclarationsWithPropertyType(view.typeId,
+ relinkableAliasPropertyDeclarations);
+ handlePrototypes(view.typeId, relinkablePrototypes);
+ handleExtensions(view.typeId, relinkableExtensions);
+ s->updateExportedTypeNameTypeIdStatement.write(view.exportedTypeNameId, type.typeId);
+ return Sqlite::UpdateChange::Update;
+ }
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) {
+ NanotraceHR::Tracer tracer{"remove exported type"_t,
+ projectStorageCategory(),
+ keyValue("exported type", view),
+ keyValue("type id", view.typeId),
+ keyValue("module id", view.moduleId)};
+
+ handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations);
+ handleAliasPropertyDeclarationsWithPropertyType(view.typeId,
+ relinkableAliasPropertyDeclarations);
+ handlePrototypes(view.typeId, relinkablePrototypes);
+ handleExtensions(view.typeId, relinkableExtensions);
+ s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId);
+ };
+
+ Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::synchronizePropertyDeclarationsInsertAlias(
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ const Storage::Synchronization::PropertyDeclaration &value,
+ SourceId sourceId,
+ TypeId typeId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert property declaration to alias"_t,
+ projectStorageCategory(),
+ keyValue("property declaration", value)};
+
+ auto callback = [&](PropertyDeclarationId propertyDeclarationId) {
+ insertedAliasPropertyDeclarations.emplace_back(typeId,
+ propertyDeclarationId,
+ fetchImportedTypeNameId(value.typeName,
+ sourceId),
+ value.aliasPropertyName,
+ value.aliasPropertyNameTail);
+ return Sqlite::CallbackControl::Abort;
+ };
+
+ s->insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name);
+}
+
+QVarLengthArray<PropertyDeclarationId, 128> ProjectStorage::fetchPropertyDeclarationIds(
+ TypeId baseTypeId) const
+{
+ QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds;
+
+ s->selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, baseTypeId);
+
+ auto range = s->selectPrototypeAndExtensionIdsStatement.range<TypeId>(baseTypeId);
+
+ for (TypeId prototype : range) {
+ s->selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, prototype);
+ }
+
+ return propertyDeclarationIds;
+}
+
+PropertyDeclarationId ProjectStorage::fetchNextPropertyDeclarationId(
+ TypeId baseTypeId, Utils::SmallStringView propertyName) const
+{
+ auto range = s->selectPrototypeAndExtensionIdsStatement.range<TypeId>(baseTypeId);
+
+ for (TypeId prototype : range) {
+ auto propertyDeclarationId = s->selectPropertyDeclarationIdByTypeIdAndNameStatement
+ .value<PropertyDeclarationId>(prototype, propertyName);
+
+ if (propertyDeclarationId)
+ return propertyDeclarationId;
+ }
+
+ return PropertyDeclarationId{};
+}
+
+PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationId(TypeId typeId,
+ Utils::SmallStringView propertyName) const
+{
+ auto propertyDeclarationId = s->selectPropertyDeclarationIdByTypeIdAndNameStatement
+ .value<PropertyDeclarationId>(typeId, propertyName);
+
+ if (propertyDeclarationId)
+ return propertyDeclarationId;
+
+ return fetchNextPropertyDeclarationId(typeId, propertyName);
+}
+
+PropertyDeclarationId ProjectStorage::fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const
+{
+ auto range = s->selectPrototypeAndExtensionIdsStatement.range<TypeId>(baseTypeId);
+
+ for (TypeId prototype : range) {
+ auto propertyDeclarationId = s->selectDefaultPropertyDeclarationIdStatement
+ .value<PropertyDeclarationId>(prototype);
+
+ if (propertyDeclarationId)
+ return propertyDeclarationId;
+ }
+
+ return PropertyDeclarationId{};
+}
+
+PropertyDeclarationId ProjectStorage::fetchDefaultPropertyDeclarationId(TypeId typeId) const
+{
+ auto propertyDeclarationId = s->selectDefaultPropertyDeclarationIdStatement
+ .value<PropertyDeclarationId>(typeId);
+
+ if (propertyDeclarationId)
+ return propertyDeclarationId;
+
+ return fetchNextDefaultPropertyDeclarationId(typeId);
+}
+
+void ProjectStorage::synchronizePropertyDeclarationsInsertProperty(
+ const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert property declaration"_t,
+ projectStorageCategory(),
+ keyValue("property declaration", value)};
+
+ auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId);
+ auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId);
+
+ if (!propertyTypeId)
+ throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId};
+
+ auto propertyDeclarationId = s->insertPropertyDeclarationStatement.value<PropertyDeclarationId>(
+ typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId);
+
+ auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, value.name);
+ if (nextPropertyDeclarationId) {
+ s->updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId,
+ propertyDeclarationId);
+ s->updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement
+ .write(propertyDeclarationId, propertyTypeId, value.traits);
+ }
+}
+
+void ProjectStorage::synchronizePropertyDeclarationsUpdateAlias(
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
+ const Storage::Synchronization::PropertyDeclarationView &view,
+ const Storage::Synchronization::PropertyDeclaration &value,
+ SourceId sourceId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update property declaration to alias"_t,
+ projectStorageCategory(),
+ keyValue("property declaration", value),
+ keyValue("property declaration view", view)};
+
+ updatedAliasPropertyDeclarations.emplace_back(view.typeId,
+ view.id,
+ fetchImportedTypeNameId(value.typeName, sourceId),
+ value.aliasPropertyName,
+ value.aliasPropertyNameTail,
+ view.aliasId);
+}
+
+Sqlite::UpdateChange ProjectStorage::synchronizePropertyDeclarationsUpdateProperty(
+ const Storage::Synchronization::PropertyDeclarationView &view,
+ const Storage::Synchronization::PropertyDeclaration &value,
+ SourceId sourceId,
+ PropertyDeclarationIds &propertyDeclarationIds)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update property declaration"_t,
+ projectStorageCategory(),
+ keyValue("property declaration", value),
+ keyValue("property declaration view", view)};
+
+ auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId);
+
+ auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId);
+
+ if (!propertyTypeId)
+ throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId};
+
+ if (view.traits == value.traits && propertyTypeId == view.typeId
+ && propertyImportedTypeNameId == view.typeNameId)
+ return Sqlite::UpdateChange::No;
+
+ s->updatePropertyDeclarationStatement.write(view.id,
+ propertyTypeId,
+ value.traits,
+ propertyImportedTypeNameId);
+ s->updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(view.id,
+ propertyTypeId,
+ value.traits);
+ propertyDeclarationIds.push_back(view.id);
+
+ tracer.end(keyValue("updated", "yes"));
+
+ return Sqlite::UpdateChange::Update;
+}
+
+void ProjectStorage::synchronizePropertyDeclarations(
+ TypeId typeId,
+ Storage::Synchronization::PropertyDeclarations &propertyDeclarations,
+ SourceId sourceId,
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
+ PropertyDeclarationIds &propertyDeclarationIds)
+{
+ NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory()};
+
+ std::sort(propertyDeclarations.begin(), propertyDeclarations.end(), [](auto &&first, auto &&second) {
+ return Sqlite::compare(first.name, second.name) < 0;
+ });
+
+ auto range = s->selectPropertyDeclarationsForTypeIdStatement
+ .range<Storage::Synchronization::PropertyDeclarationView>(typeId);
+
+ auto compareKey = [](const Storage::Synchronization::PropertyDeclarationView &view,
+ const Storage::Synchronization::PropertyDeclaration &value) {
+ return Sqlite::compare(view.name, value.name);
+ };
+
+ auto insert = [&](const Storage::Synchronization::PropertyDeclaration &value) {
+ if (value.kind == Storage::Synchronization::PropertyKind::Alias) {
+ synchronizePropertyDeclarationsInsertAlias(insertedAliasPropertyDeclarations,
+ value,
+ sourceId,
+ typeId);
+ } else {
+ synchronizePropertyDeclarationsInsertProperty(value, sourceId, typeId);
+ }
+ };
+
+ auto update = [&](const Storage::Synchronization::PropertyDeclarationView &view,
+ const Storage::Synchronization::PropertyDeclaration &value) {
+ if (value.kind == Storage::Synchronization::PropertyKind::Alias) {
+ synchronizePropertyDeclarationsUpdateAlias(updatedAliasPropertyDeclarations,
+ view,
+ value,
+ sourceId);
+ propertyDeclarationIds.push_back(view.id);
+ } else {
+ return synchronizePropertyDeclarationsUpdateProperty(view,
+ value,
+ sourceId,
+ propertyDeclarationIds);
+ }
+
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const Storage::Synchronization::PropertyDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove property declaration"_t,
+ projectStorageCategory(),
+ keyValue("property declaratio viewn", view)};
+
+ auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, view.name);
+
+ if (nextPropertyDeclarationId) {
+ s->updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement
+ .write(nextPropertyDeclarationId, view.id);
+ }
+
+ s->updateDefaultPropertyIdToNullStatement.write(view.id);
+ s->deletePropertyDeclarationStatement.write(view.id);
+ propertyDeclarationIds.push_back(view.id);
+ };
+
+ Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull(
+ Storage::Synchronization::Type &type, PropertyDeclarationIds &propertyDeclarationIds)
+{
+ NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t,
+ projectStorageCategory()};
+
+ if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal)
+ return;
+
+ Storage::Synchronization::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations;
+
+ std::sort(aliasDeclarations.begin(), aliasDeclarations.end(), [](auto &&first, auto &&second) {
+ return Sqlite::compare(first.name, second.name) < 0;
+ });
+
+ auto range = s->selectPropertyDeclarationsWithAliasForTypeIdStatement
+ .range<AliasPropertyDeclarationView>(type.typeId);
+
+ auto compareKey = [](const AliasPropertyDeclarationView &view,
+ const Storage::Synchronization::PropertyDeclaration &value) {
+ return Sqlite::compare(view.name, value.name);
+ };
+
+ auto insert = [&](const Storage::Synchronization::PropertyDeclaration &) {};
+
+ auto update = [&](const AliasPropertyDeclarationView &,
+ const Storage::Synchronization::PropertyDeclaration &) {
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const AliasPropertyDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t,
+ projectStorageCategory(),
+ keyValue("alias property declaration view", view)};
+
+ s->updatePropertyDeclarationAliasIdToNullStatement.write(view.id);
+ propertyDeclarationIds.push_back(view.id);
+ };
+
+ Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull(
+ Storage::Synchronization::Types &types,
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations)
+{
+ NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, projectStorageCategory()};
+
+ PropertyDeclarationIds propertyDeclarationIds;
+ propertyDeclarationIds.reserve(types.size());
+
+ for (auto &&type : types)
+ resetRemovedAliasPropertyDeclarationsToNull(type, propertyDeclarationIds);
+
+ removeRelinkableEntries(relinkableAliasPropertyDeclarations,
+ propertyDeclarationIds,
+ PropertyCompare<AliasPropertyDeclaration>{});
+}
+
+ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import,
+ Storage::Synchronization::ImportKind importKind,
+ ModuleId sourceModuleId,
+ ImportId parentImportId)
+{
+ if (import.version.minor) {
+ return s->insertDocumentImportWithVersionStatement.value<ImportId>(import.sourceId,
+ import.moduleId,
+ sourceModuleId,
+ importKind,
+ import.version.major.value,
+ import.version.minor.value,
+ parentImportId);
+ } else if (import.version.major) {
+ return s->insertDocumentImportWithMajorVersionStatement.value<ImportId>(import.sourceId,
+ import.moduleId,
+ sourceModuleId,
+ importKind,
+ import.version.major.value,
+ parentImportId);
+ } else {
+ return s->insertDocumentImportWithoutVersionStatement.value<ImportId>(import.sourceId,
+ import.moduleId,
+ sourceModuleId,
+ importKind,
+ parentImportId);
+ }
+}
+
+void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
+ const SourceIds &updatedSourceIds,
+ Storage::Synchronization::ImportKind importKind)
+{
+ std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) {
+ return std::tie(first.sourceId, first.moduleId, first.version)
+ < std::tie(second.sourceId, second.moduleId, second.version);
+ });
+
+ auto range = s->selectDocumentImportForSourceIdStatement
+ .range<Storage::Synchronization::ImportView>(toIntegers(updatedSourceIds),
+ importKind);
+
+ auto compareKey = [](const Storage::Synchronization::ImportView &view,
+ const Storage::Import &import) -> long long {
+ auto sourceIdDifference = view.sourceId - import.sourceId;
+ if (sourceIdDifference != 0)
+ return sourceIdDifference;
+
+ auto moduleIdDifference = view.moduleId - import.moduleId;
+ if (moduleIdDifference != 0)
+ return moduleIdDifference;
+
+ auto versionDifference = view.version.major.value - import.version.major.value;
+ if (versionDifference != 0)
+ return versionDifference;
+
+ return view.version.minor.value - import.version.minor.value;
+ };
+
+ auto insert = [&](const Storage::Import &import) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert import"_t,
+ projectStorageCategory(),
+ keyValue("import", import),
+ keyValue("import kind", importKind),
+ keyValue("source id", import.sourceId),
+ keyValue("module id", import.moduleId)};
+
+ auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{});
+ auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) {
+ Storage::Import additionImport{exportedModuleId,
+ Storage::Version{majorVersion, minorVersion},
+ import.sourceId};
+
+ auto exportedImportKind = importKind == Storage::Synchronization::ImportKind::Import
+ ? Storage::Synchronization::ImportKind::ModuleExportedImport
+ : Storage::Synchronization::ImportKind::ModuleExportedModuleDependency;
+
+ NanotraceHR::Tracer tracer{"insert indirect import"_t,
+ projectStorageCategory(),
+ keyValue("import", import),
+ keyValue("import kind", exportedImportKind),
+ keyValue("source id", import.sourceId),
+ keyValue("module id", import.moduleId)};
+
+ auto indirectImportId = insertDocumentImport(additionImport,
+ exportedImportKind,
+ import.moduleId,
+ importId);
+
+ tracer.end(keyValue("import id", indirectImportId));
+ };
+
+ s->selectModuleExportedImportsForModuleIdStatement.readCallback(callback,
+ import.moduleId,
+ import.version.major.value,
+ import.version.minor.value);
+ tracer.end(keyValue("import id", importId));
+ };
+
+ auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) {
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const Storage::Synchronization::ImportView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove import"_t,
+ projectStorageCategory(),
+ keyValue("import", view),
+ keyValue("import id", view.importId),
+ keyValue("source id", view.sourceId),
+ keyValue("module id", view.moduleId)};
+
+ s->deleteDocumentImportStatement.write(view.importId);
+ s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId);
+ };
+
+ Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove);
+}
+
+Utils::PathString ProjectStorage::createJson(const Storage::Synchronization::ParameterDeclarations &parameters)
+{
+ NanotraceHR::Tracer tracer{"create json from parameter declarations"_t, projectStorageCategory()};
+
+ Utils::PathString json;
+ json.append("[");
+
+ Utils::SmallStringView comma{""};
+
+ for (const auto &parameter : parameters) {
+ json.append(comma);
+ comma = ",";
+ json.append(R"({"n":")");
+ json.append(parameter.name);
+ json.append(R"(","tn":")");
+ json.append(parameter.typeName);
+ if (parameter.traits == Storage::PropertyDeclarationTraits::None) {
+ json.append("\"}");
+ } else {
+ json.append(R"(","tr":)");
+ json.append(Utils::SmallString::number(to_underlying(parameter.traits)));
+ json.append("}");
+ }
+ }
+
+ json.append("]");
+
+ return json;
+}
+
+TypeId ProjectStorage::fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId,
+ Utils::SmallStringView name) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id by module id and exported name"_t,
+ projectStorageCategory(),
+ keyValue("module id", moduleId),
+ keyValue("exported name", name)};
+
+ return s->selectTypeIdByModuleIdAndExportedNameStatement.value<TypeId>(moduleId, name);
+}
+
+void ProjectStorage::addTypeIdToPropertyEditorQmlPaths(
+ Storage::Synchronization::PropertyEditorQmlPaths &paths)
+{
+ NanotraceHR::Tracer tracer{"add type id to property editor qml paths"_t, projectStorageCategory()};
+
+ for (auto &path : paths)
+ path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName);
+}
+
+void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths,
+ SourceIds updatedPropertyEditorQmlPathsSourceIds)
+{
+ using Storage::Synchronization::PropertyEditorQmlPath;
+ std::sort(paths.begin(), paths.end(), [](auto &&first, auto &&second) {
+ return first.typeId < second.typeId;
+ });
+
+ auto range = s->selectPropertyEditorPathsForForSourceIdsStatement.range<PropertyEditorQmlPathView>(
+ toIntegers(updatedPropertyEditorQmlPathsSourceIds));
+
+ auto compareKey = [](const PropertyEditorQmlPathView &view,
+ const PropertyEditorQmlPath &value) -> long long {
+ return view.typeId - value.typeId;
+ };
+
+ auto insert = [&](const PropertyEditorQmlPath &path) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert property editor paths"_t,
+ projectStorageCategory(),
+ keyValue("property editor qml path", path)};
+
+ if (path.typeId)
+ s->insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId);
+ };
+
+ auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update property editor paths"_t,
+ projectStorageCategory(),
+ keyValue("property editor qml path", value),
+ keyValue("property editor qml path view", view)};
+
+ if (value.pathId != view.pathId || value.directoryId != view.directoryId) {
+ s->updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId);
+
+ tracer.end(keyValue("updated", "yes"));
+
+ return Sqlite::UpdateChange::Update;
+ }
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const PropertyEditorQmlPathView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove property editor paths"_t,
+ projectStorageCategory(),
+ keyValue("property editor qml path view", view)};
+
+ s->deletePropertyEditorPathStatement.write(view.typeId);
+ };
+
+ Sqlite::insertUpdateDelete(range, paths, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::synchronizePropertyEditorQmlPaths(
+ Storage::Synchronization::PropertyEditorQmlPaths &paths,
+ SourceIds updatedPropertyEditorQmlPathsSourceIds)
+{
+ NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t, projectStorageCategory()};
+
+ addTypeIdToPropertyEditorQmlPaths(paths);
+ synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds);
+}
+
+void ProjectStorage::synchronizeFunctionDeclarations(
+ TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations)
+{
+ NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory()};
+
+ std::sort(functionsDeclarations.begin(),
+ functionsDeclarations.end(),
+ [](auto &&first, auto &&second) {
+ auto compare = Sqlite::compare(first.name, second.name);
+
+ if (compare == 0) {
+ Utils::PathString firstSignature{createJson(first.parameters)};
+ Utils::PathString secondSignature{createJson(second.parameters)};
+
+ return Sqlite::compare(firstSignature, secondSignature) < 0;
+ }
+
+ return compare < 0;
+ });
+
+ auto range = s->selectFunctionDeclarationsForTypeIdStatement
+ .range<Storage::Synchronization::FunctionDeclarationView>(typeId);
+
+ auto compareKey = [](const Storage::Synchronization::FunctionDeclarationView &view,
+ const Storage::Synchronization::FunctionDeclaration &value) {
+ auto nameKey = Sqlite::compare(view.name, value.name);
+ if (nameKey != 0)
+ return nameKey;
+
+ Utils::PathString valueSignature{createJson(value.parameters)};
+
+ return Sqlite::compare(view.signature, valueSignature);
+ };
+
+ auto insert = [&](const Storage::Synchronization::FunctionDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert function declaration"_t,
+ projectStorageCategory(),
+ keyValue("function declaration", value)};
+
+ Utils::PathString signature{createJson(value.parameters)};
+
+ s->insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature);
+ };
+
+ auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view,
+ const Storage::Synchronization::FunctionDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update function declaration"_t,
+ projectStorageCategory(),
+ keyValue("function declaration", value),
+ keyValue("function declaration view", view)};
+
+ Utils::PathString signature{createJson(value.parameters)};
+
+ if (value.returnTypeName == view.returnTypeName && signature == view.signature)
+ return Sqlite::UpdateChange::No;
+
+ s->updateFunctionDeclarationStatement.write(view.id, value.returnTypeName, signature);
+
+ tracer.end(keyValue("updated", "yes"));
+
+ return Sqlite::UpdateChange::Update;
+ };
+
+ auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove function declaration"_t,
+ projectStorageCategory(),
+ keyValue("function declaration view", view)};
+
+ s->deleteFunctionDeclarationStatement.write(view.id);
+ };
+
+ Sqlite::insertUpdateDelete(range, functionsDeclarations, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::synchronizeSignalDeclarations(
+ TypeId typeId, Storage::Synchronization::SignalDeclarations &signalDeclarations)
+{
+ NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory()};
+
+ std::sort(signalDeclarations.begin(), signalDeclarations.end(), [](auto &&first, auto &&second) {
+ auto compare = Sqlite::compare(first.name, second.name);
+
+ if (compare == 0) {
+ Utils::PathString firstSignature{createJson(first.parameters)};
+ Utils::PathString secondSignature{createJson(second.parameters)};
+
+ return Sqlite::compare(firstSignature, secondSignature) < 0;
+ }
+
+ return compare < 0;
+ });
+
+ auto range = s->selectSignalDeclarationsForTypeIdStatement
+ .range<Storage::Synchronization::SignalDeclarationView>(typeId);
+
+ auto compareKey = [](const Storage::Synchronization::SignalDeclarationView &view,
+ const Storage::Synchronization::SignalDeclaration &value) {
+ auto nameKey = Sqlite::compare(view.name, value.name);
+ if (nameKey != 0)
+ return nameKey;
+
+ Utils::PathString valueSignature{createJson(value.parameters)};
+
+ return Sqlite::compare(view.signature, valueSignature);
+ };
+
+ auto insert = [&](const Storage::Synchronization::SignalDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert signal declaration"_t,
+ projectStorageCategory(),
+ keyValue("signal declaration", value)};
+
+ Utils::PathString signature{createJson(value.parameters)};
+
+ s->insertSignalDeclarationStatement.write(typeId, value.name, signature);
+ };
+
+ auto update = [&]([[maybe_unused]] const Storage::Synchronization::SignalDeclarationView &view,
+ [[maybe_unused]] const Storage::Synchronization::SignalDeclaration &value) {
+ return Sqlite::UpdateChange::No;
+ };
+
+ auto remove = [&](const Storage::Synchronization::SignalDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove signal declaration"_t,
+ projectStorageCategory(),
+ keyValue("signal declaration view", view)};
+
+ s->deleteSignalDeclarationStatement.write(view.id);
+ };
+
+ Sqlite::insertUpdateDelete(range, signalDeclarations, compareKey, insert, update, remove);
+}
+
+Utils::PathString ProjectStorage::createJson(
+ const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"create json from enumerator declarations"_t, projectStorageCategory()};
+
+ Utils::PathString json;
+ json.append("{");
+
+ Utils::SmallStringView comma{"\""};
+
+ for (const auto &enumerator : enumeratorDeclarations) {
+ json.append(comma);
+ comma = ",\"";
+ json.append(enumerator.name);
+ if (enumerator.hasValue) {
+ json.append("\":\"");
+ json.append(Utils::SmallString::number(enumerator.value));
+ json.append("\"");
+ } else {
+ json.append("\":null");
+ }
+ }
+
+ json.append("}");
+
+ return json;
+}
+
+void ProjectStorage::synchronizeEnumerationDeclarations(
+ TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations)
+{
+ NanotraceHR::Tracer tracer{"synchronize enumeration declaration"_t, projectStorageCategory()};
+
+ std::sort(enumerationDeclarations.begin(),
+ enumerationDeclarations.end(),
+ [](auto &&first, auto &&second) {
+ return Sqlite::compare(first.name, second.name) < 0;
+ });
+
+ auto range = s->selectEnumerationDeclarationsForTypeIdStatement
+ .range<Storage::Synchronization::EnumerationDeclarationView>(typeId);
+
+ auto compareKey = [](const Storage::Synchronization::EnumerationDeclarationView &view,
+ const Storage::Synchronization::EnumerationDeclaration &value) {
+ return Sqlite::compare(view.name, value.name);
+ };
+
+ auto insert = [&](const Storage::Synchronization::EnumerationDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"insert enumeration declaration"_t,
+ projectStorageCategory(),
+ keyValue("enumeration declaration", value)};
+
+ Utils::PathString signature{createJson(value.enumeratorDeclarations)};
+
+ s->insertEnumerationDeclarationStatement.write(typeId, value.name, signature);
+ };
+
+ auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view,
+ const Storage::Synchronization::EnumerationDeclaration &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"update enumeration declaration"_t,
+ projectStorageCategory(),
+ keyValue("enumeration declaration", value),
+ keyValue("enumeration declaration view", view)};
+
+ Utils::PathString enumeratorDeclarations{createJson(value.enumeratorDeclarations)};
+
+ if (enumeratorDeclarations == view.enumeratorDeclarations)
+ return Sqlite::UpdateChange::No;
+
+ s->updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations);
+
+ tracer.end(keyValue("updated", "yes"));
+
+ return Sqlite::UpdateChange::Update;
+ };
+
+ auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &view) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"remove enumeration declaration"_t,
+ projectStorageCategory(),
+ keyValue("enumeration declaration view", view)};
+
+ s->deleteEnumerationDeclarationStatement.write(view.id);
+ };
+
+ Sqlite::insertUpdateDelete(range, enumerationDeclarations, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::extractExportedTypes(TypeId typeId,
+ const Storage::Synchronization::Type &type,
+ Storage::Synchronization::ExportedTypes &exportedTypes)
+{
+ for (const auto &exportedType : type.exportedTypes)
+ exportedTypes.emplace_back(exportedType.name, exportedType.version, typeId, exportedType.moduleId);
+}
+
+TypeId ProjectStorage::declareType(Storage::Synchronization::Type &type)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"declare type"_t,
+ projectStorageCategory(),
+ keyValue("source id", type.sourceId),
+ keyValue("type name", type.typeName)};
+
+ if (type.typeName.isEmpty()) {
+ type.typeId = s->selectTypeIdBySourceIdStatement.value<TypeId>(type.sourceId);
+
+ tracer.end(keyValue("type id", type.typeId));
+
+ return type.typeId;
+ }
+
+ type.typeId = s->insertTypeStatement.value<TypeId>(type.sourceId, type.typeName);
+
+ if (!type.typeId)
+ type.typeId = s->selectTypeIdBySourceIdAndNameStatement.value<TypeId>(type.sourceId,
+ type.typeName);
+
+ tracer.end(keyValue("type id", type.typeId));
+
+ return type.typeId;
+}
+
+void ProjectStorage::syncDeclarations(Storage::Synchronization::Type &type,
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
+ PropertyDeclarationIds &propertyDeclarationIds)
+{
+ NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory()};
+
+ if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal)
+ return;
+
+ synchronizePropertyDeclarations(type.typeId,
+ type.propertyDeclarations,
+ type.sourceId,
+ insertedAliasPropertyDeclarations,
+ updatedAliasPropertyDeclarations,
+ propertyDeclarationIds);
+ synchronizeFunctionDeclarations(type.typeId, type.functionDeclarations);
+ synchronizeSignalDeclarations(type.typeId, type.signalDeclarations);
+ synchronizeEnumerationDeclarations(type.typeId, type.enumerationDeclarations);
+}
+
+void ProjectStorage::syncDeclarations(Storage::Synchronization::Types &types,
+ AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
+ AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
+ PropertyDeclarations &relinkablePropertyDeclarations)
+{
+ NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory()};
+
+ PropertyDeclarationIds propertyDeclarationIds;
+ propertyDeclarationIds.reserve(types.size() * 10);
+
+ for (auto &&type : types)
+ syncDeclarations(type,
+ insertedAliasPropertyDeclarations,
+ updatedAliasPropertyDeclarations,
+ propertyDeclarationIds);
+
+ removeRelinkableEntries(relinkablePropertyDeclarations,
+ propertyDeclarationIds,
+ PropertyCompare<PropertyDeclaration>{});
+}
+
+void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &types)
+{
+ NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()};
+
+ auto range = s->selectTypesWithDefaultPropertyStatement.range<TypeWithDefaultPropertyView>();
+
+ auto compareKey = [](const TypeWithDefaultPropertyView &view,
+ const Storage::Synchronization::Type &value) {
+ return view.typeId - value.typeId;
+ };
+
+ auto insert = [&](const Storage::Synchronization::Type &) {
+
+ };
+
+ auto update = [&](const TypeWithDefaultPropertyView &view,
+ const Storage::Synchronization::Type &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"synchronize default properties by update"_t,
+ projectStorageCategory(),
+ keyValue("type id", value.typeId),
+ keyValue("value", value),
+ keyValue("view", view)};
+
+ PropertyDeclarationId valueDefaultPropertyId;
+ if (value.defaultPropertyName.size())
+ valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded(value.typeId,
+ value.defaultPropertyName)
+ .propertyDeclarationId;
+
+ if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId))
+ return Sqlite::UpdateChange::No;
+
+ s->updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId);
+
+ tracer.end(keyValue("updated", "yes"),
+ keyValue("default property id", valueDefaultPropertyId));
+
+ return Sqlite::UpdateChange::Update;
+ };
+
+ auto remove = [&](const TypeWithDefaultPropertyView &) {};
+
+ Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types)
+{
+ NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory()};
+
+ auto range = s->selectTypesWithDefaultPropertyStatement.range<TypeWithDefaultPropertyView>();
+
+ auto compareKey = [](const TypeWithDefaultPropertyView &view,
+ const Storage::Synchronization::Type &value) {
+ return view.typeId - value.typeId;
+ };
+
+ auto insert = [&](const Storage::Synchronization::Type &) {
+
+ };
+
+ auto update = [&](const TypeWithDefaultPropertyView &view,
+ const Storage::Synchronization::Type &value) {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"reset changed default properties by update"_t,
+ projectStorageCategory(),
+ keyValue("type id", value.typeId),
+ keyValue("value", value),
+ keyValue("view", view)};
+
+ PropertyDeclarationId valueDefaultPropertyId;
+ if (value.defaultPropertyName.size()) {
+ auto optionalValueDefaultPropertyId = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(
+ value.typeId, value.defaultPropertyName);
+ if (optionalValueDefaultPropertyId)
+ valueDefaultPropertyId = optionalValueDefaultPropertyId->propertyDeclarationId;
+ }
+
+ if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId))
+ return Sqlite::UpdateChange::No;
+
+ s->updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{});
+
+ tracer.end(keyValue("updated", "yes"));
+
+ return Sqlite::UpdateChange::Update;
+ };
+
+ auto remove = [&](const TypeWithDefaultPropertyView &) {};
+
+ Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove);
+}
+
+void ProjectStorage::checkForPrototypeChainCycle(TypeId typeId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"check for prototype chain cycle"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto callback = [=](TypeId currentTypeId) {
+ if (typeId == currentTypeId)
+ throw PrototypeChainCycle{};
+ };
+
+ s->selectPrototypeAndExtensionIdsStatement.readCallback(callback, typeId);
+}
+
+void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"check for alias chain cycle"_t,
+ projectStorageCategory(),
+ keyValue("property declaration id", propertyDeclarationId)};
+ auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) {
+ if (propertyDeclarationId == currentPropertyDeclarationId)
+ throw AliasChainCycle{};
+ };
+
+ s->selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback,
+ propertyDeclarationId);
+}
+
+std::pair<TypeId, ImportedTypeNameId> ProjectStorage::fetchImportedTypeNameIdAndTypeId(
+ const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t,
+ projectStorageCategory(),
+ keyValue("imported type name", typeName),
+ keyValue("source id", sourceId)};
+
+ TypeId typeId;
+ ImportedTypeNameId typeNameId;
+ if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) {
+ typeNameId = fetchImportedTypeNameId(typeName, sourceId);
+
+ typeId = fetchTypeId(typeNameId);
+
+ tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId));
+
+ if (!typeId)
+ throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId};
+ }
+
+ return {typeId, typeNameId};
+}
+
+void ProjectStorage::syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds)
+{
+ if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal)
+ return;
+
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"synchronize prototype and extension"_t,
+ projectStorageCategory(),
+ keyValue("prototype", type.prototype),
+ keyValue("extension", type.extension),
+ keyValue("type id", type.typeId),
+ keyValue("source id", type.sourceId)};
+
+ auto [prototypeId, prototypeTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.prototype,
+ type.sourceId);
+ auto [extensionId, extensionTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.extension,
+ type.sourceId);
+
+ s->updatePrototypeAndExtensionStatement.write(type.typeId,
+ prototypeId,
+ prototypeTypeNameId,
+ extensionId,
+ extensionTypeNameId);
+
+ if (prototypeId || extensionId)
+ checkForPrototypeChainCycle(type.typeId);
+
+ typeIds.push_back(type.typeId);
+
+ tracer.end(keyValue("prototype id", prototypeId),
+ keyValue("prototype type name id", prototypeTypeNameId),
+ keyValue("extension id", extensionId),
+ keyValue("extension type name id", extensionTypeNameId));
+}
+
+void ProjectStorage::syncPrototypesAndExtensions(Storage::Synchronization::Types &types,
+ Prototypes &relinkablePrototypes,
+ Prototypes &relinkableExtensions)
+{
+ NanotraceHR::Tracer tracer{"synchronize prototypes and extensions"_t, projectStorageCategory()};
+
+ TypeIds typeIds;
+ typeIds.reserve(types.size());
+
+ for (auto &type : types)
+ syncPrototypeAndExtension(type, typeIds);
+
+ removeRelinkableEntries(relinkablePrototypes, typeIds, TypeCompare<Prototype>{});
+ removeRelinkableEntries(relinkableExtensions, typeIds, TypeCompare<Prototype>{});
+}
+
+ImportId ProjectStorage::fetchImportId(SourceId sourceId, const Storage::Import &import) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id"_t,
+ projectStorageCategory(),
+ keyValue("import", import),
+ keyValue("source id", sourceId)};
+
+ ImportId importId;
+ if (import.version) {
+ importId = s->selectImportIdBySourceIdAndModuleIdAndVersionStatement.value<ImportId>(
+ sourceId, import.moduleId, import.version.major.value, import.version.minor.value);
+ } else if (import.version.major) {
+ importId = s->selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement
+ .value<ImportId>(sourceId, import.moduleId, import.version.major.value);
+ } else {
+ importId = s->selectImportIdBySourceIdAndModuleIdStatement.value<ImportId>(sourceId,
+ import.moduleId);
+ }
+
+ tracer.end(keyValue("import id", importId));
+
+ return importId;
+}
+
+ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId(
+ const Storage::Synchronization::ImportedTypeName &name, SourceId sourceId)
+{
+ struct Inspect
+ {
+ auto operator()(const Storage::Synchronization::ImportedType &importedType)
+ {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id"_t,
+ projectStorageCategory(),
+ keyValue("imported type name", importedType.name),
+ keyValue("source id", sourceId),
+ keyValue("type name kind", "exported"sv)};
+
+ return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported,
+ sourceId,
+ importedType.name);
+ }
+
+ auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType)
+ {
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id"_t,
+ projectStorageCategory(),
+ keyValue("imported type name", importedType.name),
+ keyValue("import", importedType.import),
+ keyValue("type name kind", "qualified exported"sv)};
+
+ ImportId importId = storage.fetchImportId(sourceId, importedType.import);
+
+ auto importedTypeNameId = storage.fetchImportedTypeNameId(
+ Storage::Synchronization::TypeNameKind::QualifiedExported, importId, importedType.name);
+
+ tracer.end(keyValue("import id", importId), keyValue("source id", sourceId));
+
+ return importedTypeNameId;
+ }
+
+ ProjectStorage &storage;
+ SourceId sourceId;
+ };
+
+ return std::visit(Inspect{*this, sourceId}, name);
+}
+
+TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id with type name kind"_t,
+ projectStorageCategory(),
+ keyValue("type name id", typeNameId)};
+
+ auto kind = s->selectKindFromImportedTypeNamesStatement.value<Storage::Synchronization::TypeNameKind>(
+ typeNameId);
+
+ auto typeId = fetchTypeId(typeNameId, kind);
+
+ tracer.end(keyValue("type id", typeId), keyValue("type name kind", kind));
+
+ return typeId;
+}
+
+Utils::SmallString ProjectStorage::fetchImportedTypeName(ImportedTypeNameId typeNameId) const
+{
+ return s->selectNameFromImportedTypeNamesStatement.value<Utils::SmallString>(typeNameId);
+}
+
+TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId,
+ Storage::Synchronization::TypeNameKind kind) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch type id"_t,
+ projectStorageCategory(),
+ keyValue("type name id", typeNameId),
+ keyValue("type name kind", kind)};
+
+ TypeId typeId;
+ if (kind == Storage::Synchronization::TypeNameKind::Exported) {
+ typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value<TypeId>(typeNameId);
+ } else {
+ typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value<TypeId>(typeNameId);
+ }
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
+}
+
+std::optional<ProjectStorage::FetchPropertyDeclarationResult>
+ProjectStorage::fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId,
+ Utils::SmallStringView name)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", name)};
+
+ auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name);
+ auto propertyDeclaration = s->selectPropertyDeclarationResultByPropertyDeclarationIdStatement
+ .optionalValue<FetchPropertyDeclarationResult>(
+ propertyDeclarationId);
+
+ tracer.end(keyValue("property declaration", propertyDeclaration));
+
+ return propertyDeclaration;
+}
+
+ProjectStorage::FetchPropertyDeclarationResult ProjectStorage::fetchPropertyDeclarationByTypeIdAndNameUngarded(
+ TypeId typeId, Utils::SmallStringView name)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch property declaration by type id and name ungarded"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", name)};
+
+ auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId, name);
+ tracer.end(keyValue("property declaration", propertyDeclaration));
+
+ if (propertyDeclaration)
+ return *propertyDeclaration;
+
+ throw PropertyNameDoesNotExists{};
+}
+
+PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationIdByTypeIdAndNameUngarded(
+ TypeId typeId, Utils::SmallStringView name)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch property declaration id by type id and name ungarded"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("property name", name)};
+
+ auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name);
+
+ tracer.end(keyValue("property declaration id", propertyDeclarationId));
+
+ if (propertyDeclarationId)
+ return propertyDeclarationId;
+
+ throw PropertyNameDoesNotExists{};
+}
+
+SourceContextId ProjectStorage::readSourceContextId(Utils::SmallStringView sourceContextPath)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"read source context id"_t,
+ projectStorageCategory(),
+ keyValue("source context path", sourceContextPath)};
+
+ auto sourceContextId = s->selectSourceContextIdFromSourceContextsBySourceContextPathStatement
+ .value<SourceContextId>(sourceContextPath);
+
+ tracer.end(keyValue("source context id", sourceContextId));
+
+ return sourceContextId;
+}
+
+SourceContextId ProjectStorage::writeSourceContextId(Utils::SmallStringView sourceContextPath)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"write source context id"_t,
+ projectStorageCategory(),
+ keyValue("source context path", sourceContextPath)};
+
+ s->insertIntoSourceContextsStatement.write(sourceContextPath);
+
+ auto sourceContextId = SourceContextId::create(static_cast<int>(database.lastInsertedRowId()));
+
+ tracer.end(keyValue("source context id", sourceContextId));
+
+ return sourceContextId;
+}
+
+SourceId ProjectStorage::writeSourceId(SourceContextId sourceContextId,
+ Utils::SmallStringView sourceName)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"write source id"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId),
+ keyValue("source name", sourceName)};
+
+ s->insertIntoSourcesStatement.write(sourceContextId, sourceName);
+
+ auto sourceId = SourceId::create(static_cast<int>(database.lastInsertedRowId()));
+
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
+}
+
+SourceId ProjectStorage::readSourceId(SourceContextId sourceContextId,
+ Utils::SmallStringView sourceName)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"read source id"_t,
+ projectStorageCategory(),
+ keyValue("source context id", sourceContextId),
+ keyValue("source name", sourceName)};
+
+ auto sourceId = s->selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement
+ .value<SourceId>(sourceContextId, sourceName);
+
+ tracer.end(keyValue("source id", sourceId));
+
+ return sourceId;
+}
+
+Storage::Synchronization::ExportedTypes ProjectStorage::fetchExportedTypes(TypeId typeId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch exported type"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto exportedTypes = s->selectExportedTypesByTypeIdStatement
+ .values<Storage::Synchronization::ExportedType, 12>(typeId);
+
+ tracer.end(keyValue("exported types", exportedTypes));
+
+ return exportedTypes;
+}
+
+Storage::Synchronization::PropertyDeclarations ProjectStorage::fetchPropertyDeclarations(TypeId typeId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch property declarations"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ auto propertyDeclarations = s->selectPropertyDeclarationsByTypeIdStatement
+ .values<Storage::Synchronization::PropertyDeclaration, 24>(typeId);
+
+ tracer.end(keyValue("property declarations", propertyDeclarations));
+
+ return propertyDeclarations;
+}
+
+Storage::Synchronization::FunctionDeclarations ProjectStorage::fetchFunctionDeclarations(TypeId typeId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch signal declarations"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ Storage::Synchronization::FunctionDeclarations functionDeclarations;
+
+ auto callback = [&](Utils::SmallStringView name,
+ Utils::SmallStringView returnType,
+ FunctionDeclarationId functionDeclarationId) {
+ auto &functionDeclaration = functionDeclarations.emplace_back(name, returnType);
+ functionDeclaration.parameters = s->selectFunctionParameterDeclarationsStatement
+ .values<Storage::Synchronization::ParameterDeclaration, 8>(
+ functionDeclarationId);
+ };
+
+ s->selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId);
+
+ tracer.end(keyValue("function declarations", functionDeclarations));
+
+ return functionDeclarations;
+}
+
+Storage::Synchronization::SignalDeclarations ProjectStorage::fetchSignalDeclarations(TypeId typeId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch signal declarations"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ Storage::Synchronization::SignalDeclarations signalDeclarations;
+
+ auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) {
+ auto &signalDeclaration = signalDeclarations.emplace_back(name);
+ signalDeclaration.parameters = s->selectSignalParameterDeclarationsStatement
+ .values<Storage::Synchronization::ParameterDeclaration, 8>(
+ signalDeclarationId);
+ };
+
+ s->selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId);
+
+ tracer.end(keyValue("signal declarations", signalDeclarations));
+
+ return signalDeclarations;
+}
+
+Storage::Synchronization::EnumerationDeclarations ProjectStorage::fetchEnumerationDeclarations(TypeId typeId)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch enumeration declarations"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId)};
+
+ Storage::Synchronization::EnumerationDeclarations enumerationDeclarations;
+
+ auto callback = [&](Utils::SmallStringView name,
+ EnumerationDeclarationId enumerationDeclarationId) {
+ enumerationDeclarations.emplace_back(
+ name,
+ s->selectEnumeratorDeclarationStatement
+ .values<Storage::Synchronization::EnumeratorDeclaration, 8>(enumerationDeclarationId));
+ };
+
+ s->selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement
+ .readCallback(callback, typeId);
+
+ tracer.end(keyValue("enumeration declarations", enumerationDeclarations));
+
+ return enumerationDeclarations;
+}
+
+template<typename... TypeIds>
+bool ProjectStorage::isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"is based on"_t,
+ projectStorageCategory(),
+ keyValue("type id", typeId),
+ keyValue("base type ids", NanotraceHR::array(baseTypeIds...))};
+
+ static_assert(((std::is_same_v<TypeId, TypeIds>) &&...), "Parameter must be a TypeId!");
+
+ if (((typeId == baseTypeIds) || ...)) {
+ tracer.end(keyValue("is based on", true));
+ return true;
+ }
+
+ auto range = s->selectPrototypeAndExtensionIdsStatement.rangeWithTransaction<TypeId>(typeId);
+
+ auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) {
+ return ((currentTypeId == baseTypeIds) || ...);
+ });
+
+ tracer.end(keyValue("is based on", isBasedOn));
+
+ return isBasedOn;
+}
+
+template<typename Id>
+ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind kind,
+ Id id,
+ Utils::SmallStringView typeName)
+{
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"fetch imported type name id"_t,
+ projectStorageCategory(),
+ keyValue("imported type name", typeName),
+ keyValue("kind", kind)};
+
+ auto importedTypeNameId = s->selectImportedTypeNameIdStatement.value<ImportedTypeNameId>(kind,
+ id,
+ typeName);
+
+ if (!importedTypeNameId)
+ importedTypeNameId = s->insertImportedTypeNameIdStatement.value<ImportedTypeNameId>(kind,
+ id,
+ typeName);
+
+ tracer.end(keyValue("imported type name id", importedTypeNameId));
+
+ return importedTypeNameId;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
index a770577a65..e7826f531b 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h
@@ -6,9 +6,12 @@
#include "commontypecache.h"
#include "projectstorageexceptions.h"
#include "projectstorageinterface.h"
+#include "projectstoragetypes.h"
#include "sourcepathcachetypes.h"
#include "storagecache.h"
+#include <tracing/qmldesignertracing.h>
+
#include <sqlitealgorithms.h>
#include <sqlitedatabase.h>
#include <sqlitetable.h>
@@ -28,414 +31,87 @@ namespace QmlDesigner {
using namespace NanotraceHR::Literals;
-constexpr NanotraceHR::Tracing projectStorageTracingStatus()
-{
-#ifdef ENABLE_PROJECT_STORAGE_TRACING
- return NanotraceHR::Tracing::IsEnabled;
-#else
- return NanotraceHR::Tracing::IsDisabled;
-#endif
-}
-
-[[gnu::pure]] NanotraceHR::StringViewCategory<projectStorageTracingStatus()> &projectStorageCategory();
+using ProjectStorageTracing::projectStorageCategory;
-template<typename Database>
class ProjectStorage final : public ProjectStorageInterface
{
- friend Storage::Info::CommonTypeCache<Database>;
+ using Database = Sqlite::Database;
+ friend Storage::Info::CommonTypeCache<ProjectStorageType>;
public:
- template<int ResultCount, int BindParameterCount = 0>
- using ReadStatement = typename Database::template ReadStatement<ResultCount, BindParameterCount>;
- template<int ResultCount, int BindParameterCount = 0>
- using ReadWriteStatement = typename Database::template ReadWriteStatement<ResultCount, BindParameterCount>;
- template<int BindParameterCount>
- using WriteStatement = typename Database::template WriteStatement<BindParameterCount>;
-
- ProjectStorage(Database &database, bool isInitialized)
- : database{database}
- , exclusiveTransaction{database}
- , initializer{database, isInitialized}
- {
- NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()};
-
- exclusiveTransaction.commit();
-
- database.walCheckpointFull();
-
- moduleCache.populate();
- }
-
- void synchronize(Storage::Synchronization::SynchronizationPackage package) override
- {
- NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory()};
-
- TypeIds deletedTypeIds;
- Sqlite::withImmediateTransaction(database, [&] {
- AliasPropertyDeclarations insertedAliasPropertyDeclarations;
- AliasPropertyDeclarations updatedAliasPropertyDeclarations;
-
- AliasPropertyDeclarations relinkableAliasPropertyDeclarations;
- PropertyDeclarations relinkablePropertyDeclarations;
- Prototypes relinkablePrototypes;
- Prototypes relinkableExtensions;
-
- TypeIds updatedTypeIds;
- updatedTypeIds.reserve(package.types.size());
-
- TypeIds typeIdsToBeDeleted;
-
- std::sort(package.updatedSourceIds.begin(), package.updatedSourceIds.end());
-
- synchronizeFileStatuses(package.fileStatuses, package.updatedFileStatusSourceIds);
- synchronizeImports(package.imports,
- package.updatedSourceIds,
- package.moduleDependencies,
- package.updatedModuleDependencySourceIds,
- package.moduleExportedImports,
- package.updatedModuleIds);
- synchronizeTypes(package.types,
- updatedTypeIds,
- insertedAliasPropertyDeclarations,
- updatedAliasPropertyDeclarations,
- relinkableAliasPropertyDeclarations,
- relinkablePropertyDeclarations,
- relinkablePrototypes,
- relinkableExtensions,
- package.updatedSourceIds);
- synchronizeTypeAnnotations(package.typeAnnotations,
- package.updatedTypeAnnotationSourceIds);
- synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths,
- package.updatedPropertyEditorQmlPathSourceIds);
-
- deleteNotUpdatedTypes(updatedTypeIds,
- package.updatedSourceIds,
- typeIdsToBeDeleted,
- relinkableAliasPropertyDeclarations,
- relinkablePropertyDeclarations,
- relinkablePrototypes,
- relinkableExtensions,
- deletedTypeIds);
-
- relink(relinkableAliasPropertyDeclarations,
- relinkablePropertyDeclarations,
- relinkablePrototypes,
- relinkableExtensions,
- deletedTypeIds);
-
- linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations);
-
- synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds);
-
- commonTypeCache_.resetTypeIds();
- });
-
- callRefreshMetaInfoCallback(deletedTypeIds);
- }
-
- void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override
- {
- NanotraceHR::Tracer tracer{"synchronize document imports"_t, projectStorageCategory()};
-
- Sqlite::withImmediateTransaction(database, [&] {
- synchronizeDocumentImports(imports,
- {sourceId},
- Storage::Synchronization::ImportKind::Import);
- });
- }
+ ProjectStorage(Database &database, bool isInitialized);
+ ~ProjectStorage();
- void addObserver(ProjectStorageObserver *observer) override { observers.push_back(observer); }
+ void synchronize(Storage::Synchronization::SynchronizationPackage package) override;
- void removeObserver(ProjectStorageObserver *observer) override
- {
- observers.removeOne(observer);
- }
+ void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override;
- ModuleId moduleId(Utils::SmallStringView moduleName) const override
- {
- NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory()};
-
- return moduleCache.id(moduleName);
- }
+ void addObserver(ProjectStorageObserver *observer) override;
- Utils::SmallString moduleName(ModuleId moduleId) const
- {
- NanotraceHR::Tracer tracer{"get module name"_t, projectStorageCategory()};
+ void removeObserver(ProjectStorageObserver *observer) override;
- if (!moduleId)
- throw ModuleDoesNotExists{};
+ ModuleId moduleId(Utils::SmallStringView moduleName) const override;
- return moduleCache.value(moduleId);
- }
+ Utils::SmallString moduleName(ModuleId moduleId) const override;
TypeId typeId(ModuleId moduleId,
Utils::SmallStringView exportedTypeName,
- Storage::Version version) const override
- {
- NanotraceHR::Tracer tracer{"get type id by exported name"_t, projectStorageCategory()};
-
- if (version.minor)
- return selectTypeIdByModuleIdAndExportedNameAndVersionStatement
- .template valueWithTransaction<TypeId>(moduleId,
- exportedTypeName,
- version.major.value,
- version.minor.value);
-
- if (version.major)
- return selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement
- .template valueWithTransaction<TypeId>(moduleId, exportedTypeName, version.major.value);
+ Storage::Version version) const override;
- return selectTypeIdByModuleIdAndExportedNameStatement
- .template valueWithTransaction<TypeId>(moduleId, exportedTypeName);
- }
-
- TypeId typeId(ImportedTypeNameId typeNameId) const override
- {
- NanotraceHR::Tracer tracer{"get type id by imported type name"_t, projectStorageCategory()};
-
- return Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); });
- }
+ TypeId typeId(ImportedTypeNameId typeNameId) const override;
- QVarLengthArray<TypeId, 256> typeIds(ModuleId moduleId) const override
- {
- NanotraceHR::Tracer tracer{"get type ids by module id"_t, projectStorageCategory()};
+ QVarLengthArray<TypeId, 256> typeIds(ModuleId moduleId) const override;
- return selectTypeIdsByModuleIdStatement
- .template valuesWithTransaction<QVarLengthArray<TypeId, 256>>(moduleId);
- }
+ Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override;
- Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get exported type names by type id"_t, projectStorageCategory()};
+ Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const override;
- return selectExportedTypesByTypeIdStatement
- .template valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId);
- }
+ ImportId importId(const Storage::Import &import) const override;
- Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId,
- SourceId sourceId) const override
- {
- NanotraceHR::Tracer tracer{"get exported type names by source id"_t, projectStorageCategory()};
+ ImportedTypeNameId importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) override;
- return selectExportedTypesByTypeIdAndSourceIdStatement
- .template valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId, sourceId);
- }
+ ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) override;
- ImportId importId(const Storage::Import &import) const override
- {
- NanotraceHR::Tracer tracer{"get import id by import"_t, projectStorageCategory()};
+ QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds(TypeId typeId) const override;
- return Sqlite::withDeferredTransaction(database, [&] {
- return fetchImportId(import.sourceId, import);
- });
- }
-
- ImportedTypeNameId importedTypeNameId(ImportId importId,
- Utils::SmallStringView typeName) override
- {
- NanotraceHR::Tracer tracer{"get imported type name id by import id"_t,
- projectStorageCategory()};
-
- return Sqlite::withDeferredTransaction(database, [&] {
- return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported,
- importId,
- typeName);
- });
- }
-
- ImportedTypeNameId importedTypeNameId(SourceId sourceId,
- Utils::SmallStringView typeName) override
- {
- NanotraceHR::Tracer tracer{"get imported type name id by source id"_t,
- projectStorageCategory()};
-
- return Sqlite::withDeferredTransaction(database, [&] {
- return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported,
- sourceId,
- typeName);
- });
- }
-
- QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get property declaration ids"_t, projectStorageCategory()};
-
- return selectPropertyDeclarationIdsForTypeStatement
- .template valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>(typeId);
- }
-
- QVarLengthArray<PropertyDeclarationId, 128> localPropertyDeclarationIds(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get local property declaration ids"_t, projectStorageCategory()};
-
- return selectLocalPropertyDeclarationIdsForTypeStatement
- .template valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>(typeId);
- }
+ QVarLengthArray<PropertyDeclarationId, 128> localPropertyDeclarationIds(TypeId typeId) const override;
PropertyDeclarationId propertyDeclarationId(TypeId typeId,
- Utils::SmallStringView propertyName) const override
- {
- NanotraceHR::Tracer tracer{"get property declaration id"_t, projectStorageCategory()};
-
- return selectPropertyDeclarationIdForTypeAndPropertyNameStatement
- .template valueWithTransaction<PropertyDeclarationId>(typeId, propertyName);
- }
+ Utils::SmallStringView propertyName) const override;
PropertyDeclarationId localPropertyDeclarationId(TypeId typeId,
- Utils::SmallStringView propertyName) const
- {
- NanotraceHR::Tracer tracer{"get local property declaration id"_t, projectStorageCategory()};
+ Utils::SmallStringView propertyName) const;
- return selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement
- .template valueWithTransaction<PropertyDeclarationId>(typeId, propertyName);
- }
+ PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const override;
std::optional<Storage::Info::PropertyDeclaration> propertyDeclaration(
- PropertyDeclarationId propertyDeclarationId) const override
- {
- NanotraceHR::Tracer tracer{"get property declaration"_t, projectStorageCategory()};
-
- return selectPropertyDeclarationForPropertyDeclarationIdStatement
- .template optionalValueWithTransaction<Storage::Info::PropertyDeclaration>(
- propertyDeclarationId);
- }
-
- std::optional<Storage::Info::Type> type(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory()};
+ PropertyDeclarationId propertyDeclarationId) const override;
- return selectInfoTypeByTypeIdStatement.template optionalValueWithTransaction<Storage::Info::Type>(
- typeId);
- }
+ std::optional<Storage::Info::Type> type(TypeId typeId) const override;
- Utils::PathString typeIconPath(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get type icon path"_t, projectStorageCategory()};
+ Utils::PathString typeIconPath(TypeId typeId) const override;
- return selectTypeIconPathStatement.template valueWithTransaction<Utils::PathString>(typeId);
- }
+ Storage::Info::TypeHints typeHints(TypeId typeId) const override;
- Storage::Info::TypeHints typeHints(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get type hints"_t, projectStorageCategory()};
+ SmallSourceIds<4> typeAnnotationSourceIds(SourceId directoryId) const override;
- return selectTypeHintsStatement.template valuesWithTransaction<Storage::Info::TypeHints, 4>(
- typeId);
- }
-
- Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get item library entries by type id"_t, projectStorageCategory()};
-
- using Storage::Info::ItemLibraryProperties;
- Storage::Info::ItemLibraryEntries entries;
-
- auto callback = [&](TypeId typeId_,
- Utils::SmallStringView name,
- Utils::SmallStringView iconPath,
- Utils::SmallStringView category,
- Utils::SmallStringView import,
- Utils::SmallStringView toolTip,
- Utils::SmallStringView properties,
- Utils::SmallStringView extraFilePaths,
- Utils::SmallStringView templatePath) {
- auto &last = entries.emplace_back(
- typeId_, name, iconPath, category, import, toolTip, templatePath);
- if (properties.size())
- selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
- if (extraFilePaths.size())
- selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths);
- };
-
- selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId);
-
- return entries;
- }
-
- Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override
- {
- NanotraceHR::Tracer tracer{"get item library entries by source id"_t,
- projectStorageCategory()};
-
- using Storage::Info::ItemLibraryProperties;
- Storage::Info::ItemLibraryEntries entries;
-
- auto callback = [&](TypeId typeId,
- Utils::SmallStringView name,
- Utils::SmallStringView iconPath,
- Utils::SmallStringView category,
- Utils::SmallStringView import,
- Utils::SmallStringView toolTip,
- Utils::SmallStringView properties,
- Utils::SmallStringView extraFilePaths,
- Utils::SmallStringView templatePath) {
- auto &last = entries.emplace_back(
- typeId, name, iconPath, category, import, toolTip, templatePath);
- if (properties.size())
- selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
- if (extraFilePaths.size())
- selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths);
- };
-
- selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId);
-
- return entries;
- }
+ SmallSourceIds<64> typeAnnotationDirectorySourceIds() const override;
- Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override
- {
- NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()};
-
- using Storage::Info::ItemLibraryProperties;
- Storage::Info::ItemLibraryEntries entries;
-
- auto callback = [&](TypeId typeId,
- Utils::SmallStringView name,
- Utils::SmallStringView iconPath,
- Utils::SmallStringView category,
- Utils::SmallStringView import,
- Utils::SmallStringView toolTip,
- Utils::SmallStringView properties,
- Utils::SmallStringView extraFilePaths,
- Utils::SmallStringView templatePath) {
- auto &last = entries.emplace_back(
- typeId, name, iconPath, category, import, toolTip, templatePath);
- if (properties.size())
- selectItemLibraryPropertiesStatement.readTo(last.properties, properties);
- if (extraFilePaths.size())
- selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths);
- };
-
- selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback);
-
- return entries;
- }
+ Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override;
- std::vector<Utils::SmallString> signalDeclarationNames(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get signal names"_t, projectStorageCategory()};
+ Storage::Info::ItemLibraryEntries itemLibraryEntries(ImportId importId) const;
- return selectSignalDeclarationNamesForTypeStatement
- .template valuesWithTransaction<Utils::SmallString, 32>(typeId);
- }
+ Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override;
- std::vector<Utils::SmallString> functionDeclarationNames(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get function names"_t, projectStorageCategory()};
+ Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override;
- return selectFuncionDeclarationNamesForTypeStatement
- .template valuesWithTransaction<Utils::SmallString, 32>(typeId);
- }
+ std::vector<Utils::SmallString> signalDeclarationNames(TypeId typeId) const override;
- std::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const override
- {
- NanotraceHR::Tracer tracer{"get property name"_t, projectStorageCategory()};
+ std::vector<Utils::SmallString> functionDeclarationNames(TypeId typeId) const override;
- return selectPropertyNameStatement.template optionalValueWithTransaction<Utils::SmallString>(
- propertyDeclarationId);
- }
+ std::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const override;
- const Storage::Info::CommonTypeCache<ProjectStorageInterface> &commonTypeCache() const override
+ const Storage::Info::CommonTypeCache<ProjectStorageType> &commonTypeCache() const override
{
return commonTypeCache_;
}
@@ -443,101 +119,70 @@ public:
template<const char *moduleName, const char *typeName>
TypeId commonTypeId() const
{
- NanotraceHR::Tracer tracer{"get type id from common type cache"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"get type id from common type cache"_t,
+ projectStorageCategory(),
+ keyValue("module name", std::string_view{moduleName}),
+ keyValue("type name", std::string_view{typeName})};
+
+ auto typeId = commonTypeCache_.typeId<moduleName, typeName>();
+
+ tracer.end(keyValue("type id", typeId));
- return commonTypeCache_.template typeId<moduleName, typeName>();
+ return typeId;
}
template<typename BuiltinType>
TypeId builtinTypeId() const
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t,
projectStorageCategory()};
- return commonTypeCache_.template builtinTypeId<BuiltinType>();
+ auto typeId = commonTypeCache_.builtinTypeId<BuiltinType>();
+
+ tracer.end(keyValue("type id", typeId));
+
+ return typeId;
}
template<const char *builtinType>
TypeId builtinTypeId() const
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t,
projectStorageCategory()};
- return commonTypeCache_.template builtinTypeId<builtinType>();
- }
+ auto typeId = commonTypeCache_.builtinTypeId<builtinType>();
- TypeIds prototypeIds(TypeId type) const override
- {
- NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory()};
+ tracer.end(keyValue("type id", typeId));
- return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction<TypeId, 16>(
- type);
+ return typeId;
}
- TypeIds prototypeAndSelfIds(TypeId type) const override
- {
- NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()};
-
- return selectPrototypeAndSelfIdsForTypeIdInOrderStatement
- .template valuesWithTransaction<TypeId, 16>(type);
- }
+ SmallTypeIds<16> prototypeIds(TypeId type) const override;
- TypeIds heirIds(TypeId typeId) const override
- {
- NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()};
+ SmallTypeIds<16> prototypeAndSelfIds(TypeId typeId) const override;
- return selectHeirTypeIdsStatement.template valuesWithTransaction<TypeId, 64>(typeId);
- }
+ SmallTypeIds<64> heirIds(TypeId typeId) const override;
template<typename... TypeIds>
- bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const
- {
- NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()};
+ bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const;
- static_assert(((std::is_same_v<TypeId, TypeIds>) &&...), "Parameter must be a TypeId!");
+ bool isBasedOn(TypeId) const;
- if (((typeId == baseTypeIds) || ...))
- return true;
+ bool isBasedOn(TypeId typeId, TypeId id1) const override;
- auto range = selectPrototypeIdsStatement.template rangeWithTransaction<TypeId>(typeId);
+ bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override;
- for ([[maybe_unused]] TypeId currentTypeId : range) {
- if (((currentTypeId == baseTypeIds) || ...))
- return true;
- }
-
- return false;
- }
-
- bool isBasedOn(TypeId typeId) const { return isBasedOn_(typeId); }
-
- bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); }
-
- bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override
- {
- return isBasedOn_(typeId, id1, id2);
- }
-
- bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override
- {
- return isBasedOn_(typeId, id1, id2, id3);
- }
+ bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override;
- bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override
- {
- return isBasedOn_(typeId, id1, id2, id3, id4);
- }
+ bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override;
- bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override
- {
- return isBasedOn_(typeId, id1, id2, id3, id4, id5);
- }
+ bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override;
- bool isBasedOn(
- TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const override
- {
- return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6);
- }
+ bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6)
+ const override;
bool isBasedOn(TypeId typeId,
TypeId id1,
@@ -546,251 +191,57 @@ public:
TypeId id4,
TypeId id5,
TypeId id6,
- TypeId id7) const override
- {
- return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7);
- }
-
- TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const
- {
- NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()};
-
- return selectTypeIdByExportedNameStatement.template valueWithTransaction<TypeId>(name);
- }
-
- TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, Utils::SmallStringView name) const
- {
- return selectTypeIdByModuleIdsAndExportedNameStatement.template valueWithTransaction<TypeId>(
- static_cast<void *>(moduleIds.data()), static_cast<long long>(moduleIds.size()), name);
- }
-
- TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name)
- {
- return selectTypeIdBySourceIdAndNameStatement.template valueWithTransaction<TypeId>(sourceId,
- name);
- }
-
- Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId)
- {
- return Sqlite::withDeferredTransaction(database, [&] {
- auto type = selectTypeByTypeIdStatement.template value<Storage::Synchronization::Type>(
- typeId);
-
- type.exportedTypes = fetchExportedTypes(typeId);
- type.propertyDeclarations = fetchPropertyDeclarations(type.typeId);
- type.functionDeclarations = fetchFunctionDeclarations(type.typeId);
- type.signalDeclarations = fetchSignalDeclarations(type.typeId);
- type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId);
-
- return type;
- });
- }
-
- Storage::Synchronization::Types fetchTypes()
- {
- return Sqlite::withDeferredTransaction(database, [&] {
- auto types = selectTypesStatement.template values<Storage::Synchronization::Type, 64>();
-
- for (Storage::Synchronization::Type &type : types) {
- type.exportedTypes = fetchExportedTypes(type.typeId);
- type.propertyDeclarations = fetchPropertyDeclarations(type.typeId);
- type.functionDeclarations = fetchFunctionDeclarations(type.typeId);
- type.signalDeclarations = fetchSignalDeclarations(type.typeId);
- type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId);
- }
-
- return types;
- });
- }
-
- bool fetchIsProtype(TypeId type, TypeId prototype)
- {
- return bool(selectPrototypeIdStatement.template valueWithTransaction<TypeId>(type, prototype));
- }
-
- auto fetchPrototypes(TypeId type)
- {
- return selectPrototypeIdsInOrderStatement.template rangeWithTransaction<TypeId>(type);
- }
-
- SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath)
- {
- NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()};
-
- auto sourceContextId = readSourceContextId(sourceContextPath);
-
- return sourceContextId ? sourceContextId : writeSourceContextId(sourceContextPath);
- }
-
- SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath)
- {
- NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()};
-
- try {
- return Sqlite::withDeferredTransaction(database, [&] {
- return fetchSourceContextIdUnguarded(sourceContextPath);
- });
- } catch (const Sqlite::ConstraintPreventsModification &) {
- return fetchSourceContextId(sourceContextPath);
- }
- }
-
- Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const
- {
- NanotraceHR::Tracer tracer{"fetch source context path"_t, projectStorageCategory()};
-
- return Sqlite::withDeferredTransaction(database, [&] {
- auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement
- .template optionalValue<Utils::PathString>(
- sourceContextId);
-
- if (!optionalSourceContextPath)
- throw SourceContextIdDoesNotExists();
-
- return std::move(*optionalSourceContextPath);
- });
- }
-
- auto fetchAllSourceContexts() const
- {
- NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory()};
-
- return selectAllSourceContextsStatement
- .template valuesWithTransaction<Cache::SourceContext, 128>();
- }
-
- SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
- {
- NanotraceHR::Tracer tracer{"fetch source id"_t, projectStorageCategory()};
-
- return Sqlite::withDeferredTransaction(database, [&] {
- return fetchSourceIdUnguarded(sourceContextId, sourceName);
- });
- }
-
- auto fetchSourceNameAndSourceContextId(SourceId sourceId) const
- {
- NanotraceHR::Tracer tracer{"fetch source name and source context id"_t,
- projectStorageCategory()};
-
- auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement
- .template valueWithTransaction<Cache::SourceNameAndSourceContextId>(sourceId);
-
- if (!value.sourceContextId)
- throw SourceIdDoesNotExists();
+ TypeId id7) const override;
- return value;
- }
-
- void clearSources()
- {
- Sqlite::withImmediateTransaction(database, [&] {
- deleteAllSourceContextsStatement.execute();
- deleteAllSourcesStatement.execute();
- });
- }
-
- SourceContextId fetchSourceContextId(SourceId sourceId) const
- {
- NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()};
-
- auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement
- .template valueWithTransaction<SourceContextId>(sourceId);
+ TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const;
- if (!sourceContextId)
- throw SourceIdDoesNotExists();
+ TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds,
+ Utils::SmallStringView name) const;
- return sourceContextId;
- }
-
- auto fetchAllSources() const
- {
- NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory()};
+ TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name);
- return selectAllSourcesStatement.template valuesWithTransaction<Cache::Source, 1024>();
- }
+ Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId);
- SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
- {
- NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, projectStorageCategory()};
+ Storage::Synchronization::Types fetchTypes();
- auto sourceId = readSourceId(sourceContextId, sourceName);
+ SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath);
- if (sourceId)
- return sourceId;
+ SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath);
- return writeSourceId(sourceContextId, sourceName);
- }
+ Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const;
- auto fetchAllFileStatuses() const
- {
- NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()};
+ Cache::SourceContexts fetchAllSourceContexts() const;
- return selectAllFileStatusesStatement.template rangeWithTransaction<FileStatus>();
- }
+ SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName);
- FileStatus fetchFileStatus(SourceId sourceId) const override
- {
- NanotraceHR::Tracer tracer{"fetch file status"_t, projectStorageCategory()};
+ Cache::SourceNameAndSourceContextId fetchSourceNameAndSourceContextId(SourceId sourceId) const;
- return selectFileStatusesForSourceIdStatement.template valueWithTransaction<FileStatus>(
- sourceId);
- }
+ void clearSources();
- std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const override
- {
- NanotraceHR::Tracer tracer{"fetch project data"_t, projectStorageCategory()};
+ SourceContextId fetchSourceContextId(SourceId sourceId) const;
- return selectProjectDataForSourceIdStatement
- .template optionalValueWithTransaction<Storage::Synchronization::ProjectData>(sourceId);
- }
+ Cache::Sources fetchAllSources() const;
- Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override
- {
- NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, projectStorageCategory()};
+ SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId,
+ Utils::SmallStringView sourceName);
- return selectProjectDatasForSourceIdStatement
- .template valuesWithTransaction<Storage::Synchronization::ProjectData, 1024>(
- projectSourceId);
- }
+ FileStatuses fetchAllFileStatuses() const;
- Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const
- {
- NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, projectStorageCategory()};
+ FileStatus fetchFileStatus(SourceId sourceId) const override;
- return selectProjectDatasForSourceIdsStatement
- .template valuesWithTransaction<Storage::Synchronization::ProjectData, 64>(
- toIntegers(projectSourceIds));
- }
+ std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const override;
- void setPropertyEditorPathId(TypeId typeId, SourceId pathId)
- {
- Sqlite::ImmediateSessionTransaction transaction{database};
+ Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override;
- upsertPropertyEditorPathIdStatement.write(typeId, pathId);
+ Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const;
- transaction.commit();
- }
+ void setPropertyEditorPathId(TypeId typeId, SourceId pathId);
- SourceId propertyEditorPathId(TypeId typeId) const override
- {
- return selectPropertyEditorPathIdStatement.template valueWithTransaction<SourceId>(typeId);
- }
+ SourceId propertyEditorPathId(TypeId typeId) const override;
- Storage::Imports fetchDocumentImports() const
- {
- NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()};
+ Storage::Imports fetchDocumentImports() const;
- return selectAllDocumentImportForSourceIdStatement
- .template valuesWithTransaction<Storage::Imports>();
- }
-
- void resetForTestsOnly()
- {
- database.clearAllTablesForTestsOnly();
- commonTypeCache_.clearForTestsOnly();
- moduleCache.clearForTestOnly();
- }
+ void resetForTestsOnly();
private:
class ModuleStorageAdapter
@@ -818,12 +269,11 @@ private:
}
};
+ using Modules = std::vector<Module>;
+
friend ModuleStorageAdapter;
- static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept
- {
- return first < second;
- }
+ static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept;
using ModuleCache = StorageCache<Utils::PathString,
Utils::SmallStringView,
@@ -833,29 +283,13 @@ private:
moduleNameLess,
Module>;
- ModuleId fetchModuleId(Utils::SmallStringView moduleName)
- {
- return Sqlite::withDeferredTransaction(database,
- [&] { return fetchModuleIdUnguarded(moduleName); });
- }
+ ModuleId fetchModuleId(Utils::SmallStringView moduleName);
- auto fetchModuleName(ModuleId id)
- {
- return Sqlite::withDeferredTransaction(database, [&] { return fetchModuleNameUnguarded(id); });
- }
+ Utils::PathString fetchModuleName(ModuleId id);
- auto fetchAllModules() const
- {
- return selectAllModulesStatement.template valuesWithTransaction<Module, 128>();
- }
+ Modules fetchAllModules() const;
- void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds)
- {
- if (deletedTypeIds.size()) {
- for (ProjectStorageObserver *observer : observers)
- observer->removedTypeIds(deletedTypeIds);
- }
- }
+ void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds);
class AliasPropertyDeclaration
{
@@ -882,6 +316,25 @@ private:
< std::tie(second.typeId, second.propertyDeclarationId);
}
+ template<typename String>
+ friend void convertToString(String &string,
+ const AliasPropertyDeclaration &aliasPropertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(
+ keyValue("type id", aliasPropertyDeclaration.typeId),
+ keyValue("property declaration id", aliasPropertyDeclaration.propertyDeclarationId),
+ keyValue("alias imported type name id",
+ aliasPropertyDeclaration.aliasImportedTypeNameId),
+ keyValue("alias property name", aliasPropertyDeclaration.aliasPropertyName),
+ keyValue("alias property name tail", aliasPropertyDeclaration.aliasPropertyNameTail),
+ keyValue("alias property declaration id",
+ aliasPropertyDeclaration.aliasPropertyDeclarationId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
PropertyDeclarationId propertyDeclarationId;
@@ -910,6 +363,20 @@ private:
< std::tie(second.typeId, second.propertyDeclarationId);
}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", propertyDeclaration.typeId),
+ keyValue("property declaration id",
+ propertyDeclaration.propertyDeclarationId),
+ keyValue("imported type name id",
+ propertyDeclaration.importedTypeNameId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
PropertyDeclarationId propertyDeclarationId;
@@ -931,6 +398,17 @@ private:
return first.typeId < second.typeId;
}
+ template<typename String>
+ friend void convertToString(String &string, const Prototype &prototype)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", prototype.typeId),
+ keyValue("prototype name id", prototype.prototypeNameId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
ImportedTypeNameId prototypeNameId;
@@ -970,37 +448,14 @@ private:
}
};
- SourceIds filterSourceIdsWithoutType(const SourceIds &updatedSourceIds, SourceIds &sourceIdsOfTypes)
- {
- std::sort(sourceIdsOfTypes.begin(), sourceIdsOfTypes.end());
+ SourceIds filterSourceIdsWithoutType(const SourceIds &updatedSourceIds,
+ SourceIds &sourceIdsOfTypes);
- SourceIds sourceIdsWithoutTypeSourceIds;
- sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size());
- std::set_difference(updatedSourceIds.begin(),
- updatedSourceIds.end(),
- sourceIdsOfTypes.begin(),
- sourceIdsOfTypes.end(),
- std::back_inserter(sourceIdsWithoutTypeSourceIds));
+ TypeIds fetchTypeIds(const SourceIds &sourceIds);
- return sourceIdsWithoutTypeSourceIds;
- }
+ void unique(SourceIds &sourceIds);
- TypeIds fetchTypeIds(const SourceIds &sourceIds)
- {
- return selectTypeIdsForSourceIdsStatement.template values<TypeId, 128>(toIntegers(sourceIds));
- }
-
- void unique(SourceIds &sourceIds)
- {
- std::sort(sourceIds.begin(), sourceIds.end());
- auto newEnd = std::unique(sourceIds.begin(), sourceIds.end());
- sourceIds.erase(newEnd, sourceIds.end());
- }
-
- void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits)
- {
- updateTypeAnnotationTraitStatement.write(typeId, traits.annotation);
- }
+ void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits);
class TypeAnnotationView
{
@@ -1015,6 +470,19 @@ private:
, hintsJson{hintsJson}
{}
+ template<typename String>
+ friend void convertToString(String &string, const TypeAnnotationView &typeAnnotationView)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", typeAnnotationView.typeId),
+ keyValue("icon path", typeAnnotationView.iconPath),
+ keyValue("item library json", typeAnnotationView.itemLibraryJson),
+ keyValue("hints json", typeAnnotationView.hintsJson));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
Utils::SmallStringView iconPath;
@@ -1022,75 +490,21 @@ private:
Utils::PathString hintsJson;
};
- void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations)
- {
- for (auto &annotation : typeAnnotations) {
- annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId,
- annotation.typeName);
- }
- }
+ void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations);
- void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations,
- const SourceIds &updatedTypeAnnotationSourceIds)
+ template<typename Value>
+ static Sqlite::ValueView createEmptyAsNull(const Value &value)
{
- NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory()};
-
- using Storage::Synchronization::TypeAnnotation;
-
- updateTypeIdInTypeAnnotations(typeAnnotations);
-
- auto compareKey = [](auto &&first, auto &&second) { return first.typeId - second.typeId; };
-
- std::sort(typeAnnotations.begin(), typeAnnotations.end(), [&](auto &&first, auto &&second) {
- return first.typeId < second.typeId;
- });
+ if (value.size())
+ return Sqlite::ValueView::create(value);
- auto range = selectTypeAnnotationsForSourceIdsStatement.template range<TypeAnnotationView>(
- toIntegers(updatedTypeAnnotationSourceIds));
-
- auto insert = [&](const TypeAnnotation &annotation) {
- if (!annotation.sourceId)
- throw TypeAnnotationHasInvalidSourceId{};
-
- synchronizeTypeTraits(annotation.typeId, annotation.traits);
-
- insertTypeAnnotationStatement.write(annotation.typeId,
- annotation.sourceId,
- annotation.iconPath,
- annotation.itemLibraryJson,
- annotation.hintsJson);
- };
-
- auto update = [&](const TypeAnnotationView &annotationFromDatabase,
- const TypeAnnotation &annotation) {
- synchronizeTypeTraits(annotation.typeId, annotation.traits);
-
- if (annotationFromDatabase.iconPath != annotation.iconPath
- || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson
- || annotationFromDatabase.hintsJson != annotation.hintsJson) {
- updateTypeAnnotationStatement.write(annotation.typeId,
- annotation.iconPath,
- annotation.itemLibraryJson,
- annotation.hintsJson);
- return Sqlite::UpdateChange::Update;
- }
-
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const TypeAnnotationView &annotationFromDatabase) {
- synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{});
-
- deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId);
- };
-
- Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove);
+ return Sqlite::ValueView{};
}
- void synchronizeTypeTrait(const Storage::Synchronization::Type &type)
- {
- updateTypeTraitStatement.write(type.typeId, type.traits.type);
- }
+ void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations,
+ const SourceIds &updatedTypeAnnotationSourceIds);
+
+ void synchronizeTypeTrait(const Storage::Synchronization::Type &type);
void synchronizeTypes(Storage::Synchronization::Types &types,
TypeIds &updatedTypeIds,
@@ -1100,384 +514,60 @@ private:
PropertyDeclarations &relinkablePropertyDeclarations,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions,
- const SourceIds &updatedSourceIds)
- {
- NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory()};
-
- Storage::Synchronization::ExportedTypes exportedTypes;
- exportedTypes.reserve(types.size() * 3);
- SourceIds sourceIdsOfTypes;
- sourceIdsOfTypes.reserve(updatedSourceIds.size());
- SourceIds notUpdatedExportedSourceIds;
- notUpdatedExportedSourceIds.reserve(updatedSourceIds.size());
- SourceIds exportedSourceIds;
- exportedSourceIds.reserve(types.size());
-
- for (auto &type : types) {
- if (!type.sourceId)
- throw TypeHasInvalidSourceId{};
-
- TypeId typeId = declareType(type);
- synchronizeTypeTrait(type);
- sourceIdsOfTypes.push_back(type.sourceId);
- updatedTypeIds.push_back(typeId);
- if (type.changeLevel != Storage::Synchronization::ChangeLevel::ExcludeExportedTypes) {
- exportedSourceIds.push_back(type.sourceId);
- extractExportedTypes(typeId, type, exportedTypes);
- }
- }
-
- std::sort(types.begin(), types.end(), [](const auto &first, const auto &second) {
- return first.typeId < second.typeId;
- });
-
- unique(exportedSourceIds);
-
- SourceIds sourceIdsWithoutType = filterSourceIdsWithoutType(updatedSourceIds,
- sourceIdsOfTypes);
- exportedSourceIds.insert(exportedSourceIds.end(),
- sourceIdsWithoutType.begin(),
- sourceIdsWithoutType.end());
- TypeIds exportedTypeIds = fetchTypeIds(exportedSourceIds);
- synchronizeExportedTypes(exportedTypeIds,
- exportedTypes,
- relinkableAliasPropertyDeclarations,
- relinkablePropertyDeclarations,
- relinkablePrototypes,
- relinkableExtensions);
-
- syncPrototypesAndExtensions(types, relinkablePrototypes, relinkableExtensions);
- resetDefaultPropertiesIfChanged(types);
- resetRemovedAliasPropertyDeclarationsToNull(types, relinkableAliasPropertyDeclarations);
- syncDeclarations(types,
- insertedAliasPropertyDeclarations,
- updatedAliasPropertyDeclarations,
- relinkablePropertyDeclarations);
- syncDefaultProperties(types);
- }
+ const SourceIds &updatedSourceIds);
void synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas,
- const SourceIds &updatedProjectSourceIds)
- {
- NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()};
-
- auto compareKey = [](auto &&first, auto &&second) {
- auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId;
- if (projectSourceIdDifference != 0)
- return projectSourceIdDifference;
-
- return first.sourceId - second.sourceId;
- };
-
- std::sort(projectDatas.begin(), projectDatas.end(), [&](auto &&first, auto &&second) {
- return std::tie(first.projectSourceId, first.sourceId)
- < std::tie(second.projectSourceId, second.sourceId);
- });
-
- auto range = selectProjectDatasForSourceIdsStatement
- .template range<Storage::Synchronization::ProjectData>(
- toIntegers(updatedProjectSourceIds));
-
- auto insert = [&](const Storage::Synchronization::ProjectData &projectData) {
- if (!projectData.projectSourceId)
- throw ProjectDataHasInvalidProjectSourceId{};
- if (!projectData.sourceId)
- throw ProjectDataHasInvalidSourceId{};
-
- insertProjectDataStatement.write(projectData.projectSourceId,
- projectData.sourceId,
- projectData.moduleId,
- projectData.fileType);
- };
-
- auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase,
- const Storage::Synchronization::ProjectData &projectData) {
- if (projectDataFromDatabase.fileType != projectData.fileType
- || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) {
- updateProjectDataStatement.write(projectData.projectSourceId,
- projectData.sourceId,
- projectData.moduleId,
- projectData.fileType);
- return Sqlite::UpdateChange::Update;
- }
-
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const Storage::Synchronization::ProjectData &projectData) {
- deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId);
- };
-
- Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove);
- }
+ const SourceIds &updatedProjectSourceIds);
- void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds)
- {
- NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory()};
-
- auto compareKey = [](auto &&first, auto &&second) {
- return first.sourceId - second.sourceId;
- };
-
- std::sort(fileStatuses.begin(), fileStatuses.end(), [&](auto &&first, auto &&second) {
- return first.sourceId < second.sourceId;
- });
-
- auto range = selectFileStatusesForSourceIdsStatement.template range<FileStatus>(
- toIntegers(updatedSourceIds));
-
- auto insert = [&](const FileStatus &fileStatus) {
- if (!fileStatus.sourceId)
- throw FileStatusHasInvalidSourceId{};
- insertFileStatusStatement.write(fileStatus.sourceId,
- fileStatus.size,
- fileStatus.lastModified);
- };
-
- auto update = [&](const FileStatus &fileStatusFromDatabase, const FileStatus &fileStatus) {
- if (fileStatusFromDatabase.lastModified != fileStatus.lastModified
- || fileStatusFromDatabase.size != fileStatus.size) {
- updateFileStatusStatement.write(fileStatus.sourceId,
- fileStatus.size,
- fileStatus.lastModified);
- return Sqlite::UpdateChange::Update;
- }
-
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const FileStatus &fileStatus) {
- deleteFileStatusStatement.write(fileStatus.sourceId);
- };
-
- Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove);
- }
+ void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds);
void synchronizeImports(Storage::Imports &imports,
const SourceIds &updatedSourceIds,
Storage::Imports &moduleDependencies,
const SourceIds &updatedModuleDependencySourceIds,
Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
- const ModuleIds &updatedModuleIds)
- {
- NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()};
-
- synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds);
- synchronizeDocumentImports(imports,
- updatedSourceIds,
- Storage::Synchronization::ImportKind::Import);
- synchronizeDocumentImports(moduleDependencies,
- updatedModuleDependencySourceIds,
- Storage::Synchronization::ImportKind::ModuleDependency);
- }
+ const ModuleIds &updatedModuleIds);
void synchromizeModuleExportedImports(
Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
- const ModuleIds &updatedModuleIds)
- {
- std::sort(moduleExportedImports.begin(),
- moduleExportedImports.end(),
- [](auto &&first, auto &&second) {
- return std::tie(first.moduleId, first.exportedModuleId)
- < std::tie(second.moduleId, second.exportedModuleId);
- });
-
- auto range = selectModuleExportedImportsForSourceIdStatement
- .template range<Storage::Synchronization::ModuleExportedImportView>(
- toIntegers(updatedModuleIds));
-
- auto compareKey = [](const Storage::Synchronization::ModuleExportedImportView &view,
- const Storage::Synchronization::ModuleExportedImport &import) -> long long {
- auto moduleIdDifference = view.moduleId - import.moduleId;
- if (moduleIdDifference != 0)
- return moduleIdDifference;
-
- return view.exportedModuleId - import.exportedModuleId;
- };
-
- auto insert = [&](const Storage::Synchronization::ModuleExportedImport &import) {
- if (import.version.minor) {
- insertModuleExportedImportWithVersionStatement.write(import.moduleId,
- import.exportedModuleId,
- import.isAutoVersion,
- import.version.major.value,
- import.version.minor.value);
- } else if (import.version.major) {
- insertModuleExportedImportWithMajorVersionStatement.write(import.moduleId,
- import.exportedModuleId,
- import.isAutoVersion,
- import.version.major.value);
- } else {
- insertModuleExportedImportWithoutVersionStatement.write(import.moduleId,
- import.exportedModuleId,
- import.isAutoVersion);
- }
- };
-
- auto update = [](const Storage::Synchronization::ModuleExportedImportView &,
- const Storage::Synchronization::ModuleExportedImport &) {
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const Storage::Synchronization::ModuleExportedImportView &view) {
- deleteModuleExportedImportStatement.write(view.moduleExportedImportId);
- };
-
- Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove);
- }
+ const ModuleIds &updatedModuleIds);
- ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override
- {
- auto moduleId = selectModuleIdByNameStatement.template value<ModuleId>(name);
+ ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override;
- if (moduleId)
- return moduleId;
-
- return insertModuleNameStatement.template value<ModuleId>(name);
- }
-
- auto fetchModuleNameUnguarded(ModuleId id) const
- {
- auto moduleName = selectModuleNameStatement.template value<Utils::PathString>(id);
-
- if (moduleName.empty())
- throw ModuleDoesNotExists{};
-
- return moduleName;
- }
+ Utils::PathString fetchModuleNameUnguarded(ModuleId id) const;
void handleAliasPropertyDeclarationsWithPropertyType(
- TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations)
- {
- auto callback = [&](TypeId typeId_,
- PropertyDeclarationId propertyDeclarationId,
- ImportedTypeNameId propertyImportedTypeNameId,
- PropertyDeclarationId aliasPropertyDeclarationId,
- PropertyDeclarationId aliasPropertyDeclarationTailId) {
- auto aliasPropertyName = selectPropertyNameStatement.template value<Utils::SmallString>(
- aliasPropertyDeclarationId);
- Utils::SmallString aliasPropertyNameTail;
- if (aliasPropertyDeclarationTailId)
- aliasPropertyNameTail = selectPropertyNameStatement.template value<Utils::SmallString>(
- aliasPropertyDeclarationTailId);
-
- relinkableAliasPropertyDeclarations
- .emplace_back(TypeId{typeId_},
- PropertyDeclarationId{propertyDeclarationId},
- ImportedTypeNameId{propertyImportedTypeNameId},
- std::move(aliasPropertyName),
- std::move(aliasPropertyNameTail));
-
- updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId);
- };
-
- selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement.readCallback(callback,
- typeId);
- }
+ TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations);
void handlePropertyDeclarationWithPropertyType(TypeId typeId,
- PropertyDeclarations &relinkablePropertyDeclarations)
- {
- updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations,
- typeId);
- }
-
- void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes)
- {
- auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
- relinkablePrototypes.emplace_back(typeId, prototypeNameId);
- };
+ PropertyDeclarations &relinkablePropertyDeclarations);
- updatePrototypeIdToNullStatement.readCallback(callback, prototypeId);
- }
+ void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes);
- void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions)
- {
- auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
- relinkableExtensions.emplace_back(typeId, extensionNameId);
- };
-
- updateExtensionIdToNullStatement.readCallback(callback, extensionId);
- }
+ void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions);
void deleteType(TypeId typeId,
AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
PropertyDeclarations &relinkablePropertyDeclarations,
Prototypes &relinkablePrototypes,
- Prototypes &relinkableExtensions)
- {
- handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations);
- handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations);
- handlePrototypes(typeId, relinkablePrototypes);
- handleExtensions(typeId, relinkableExtensions);
- deleteTypeNamesByTypeIdStatement.write(typeId);
- deleteEnumerationDeclarationByTypeIdStatement.write(typeId);
- deletePropertyDeclarationByTypeIdStatement.write(typeId);
- deleteFunctionDeclarationByTypeIdStatement.write(typeId);
- deleteSignalDeclarationByTypeIdStatement.write(typeId);
- deleteTypeStatement.write(typeId);
- }
+ Prototypes &relinkableExtensions);
void relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations,
- const TypeIds &deletedTypeIds)
- {
- NanotraceHR::Tracer tracer{"relink alias properties"_t, projectStorageCategory()};
-
- std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end());
-
- Utils::set_greedy_difference(
- aliasPropertyDeclarations.cbegin(),
- aliasPropertyDeclarations.cend(),
- deletedTypeIds.begin(),
- deletedTypeIds.end(),
- [&](const AliasPropertyDeclaration &alias) {
- auto typeId = fetchTypeId(alias.aliasImportedTypeNameId);
-
- if (!typeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(alias.aliasImportedTypeNameId)};
-
- auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded(
- typeId, alias.aliasPropertyName);
-
- updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId,
- propertyTypeId,
- propertyTraits,
- alias.aliasImportedTypeNameId,
- aliasId);
- },
- TypeCompare<AliasPropertyDeclaration>{});
- }
+ const TypeIds &deletedTypeIds);
void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration,
- const TypeIds &deletedTypeIds)
- {
- NanotraceHR::Tracer tracer{"relink properties"_t, projectStorageCategory()};
-
- std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end());
-
- Utils::set_greedy_difference(
- relinkablePropertyDeclaration.cbegin(),
- relinkablePropertyDeclaration.cend(),
- deletedTypeIds.begin(),
- deletedTypeIds.end(),
- [&](const PropertyDeclaration &property) {
- TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId);
-
- if (!propertyTypeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)};
-
- updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId,
- propertyTypeId);
- },
- TypeCompare<PropertyDeclaration>{});
- }
+ const TypeIds &deletedTypeIds);
template<typename Callable>
void relinkPrototypes(Prototypes &relinkablePrototypes,
const TypeIds &deletedTypeIds,
Callable updateStatement)
{
- NanotraceHR::Tracer tracer{"relink prototypes"_t, projectStorageCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"relink prototypes"_t,
+ projectStorageCategory(),
+ keyValue("relinkable prototypes", relinkablePrototypes),
+ keyValue("deleted type ids", deletedTypeIds)};
std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end());
@@ -1505,295 +595,66 @@ private:
PropertyDeclarations &relinkablePropertyDeclarations,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions,
- TypeIds &deletedTypeIds)
- {
- NanotraceHR::Tracer tracer{"delete not updated types"_t, projectStorageCategory()};
-
- auto callback = [&](TypeId typeId) {
- deletedTypeIds.push_back(typeId);
- deleteType(typeId,
- relinkableAliasPropertyDeclarations,
- relinkablePropertyDeclarations,
- relinkablePrototypes,
- relinkableExtensions);
- };
-
- selectNotUpdatedTypesInSourcesStatement.readCallback(callback,
- toIntegers(updatedSourceIds),
- toIntegers(updatedTypeIds));
- for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted)
- callback(typeIdToBeDeleted);
- }
+ TypeIds &deletedTypeIds);
void relink(AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
PropertyDeclarations &relinkablePropertyDeclarations,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions,
- TypeIds &deletedTypeIds)
- {
- NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory()};
-
- std::sort(deletedTypeIds.begin(), deletedTypeIds.end());
-
- relinkPrototypes(relinkablePrototypes, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) {
- updateTypePrototypeStatement.write(typeId, prototypeId);
- });
- relinkPrototypes(relinkableExtensions, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) {
- updateTypeExtensionStatement.write(typeId, prototypeId);
- });
- relinkPropertyDeclarations(relinkablePropertyDeclarations, deletedTypeIds);
- relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds);
- }
+ TypeIds &deletedTypeIds);
PropertyDeclarationId fetchAliasId(TypeId aliasTypeId,
Utils::SmallStringView aliasPropertyName,
- Utils::SmallStringView aliasPropertyNameTail)
- {
- if (aliasPropertyNameTail.empty())
- return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName);
-
- auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId,
- aliasPropertyName);
-
- return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias.propertyTypeId,
- aliasPropertyNameTail);
- }
+ Utils::SmallStringView aliasPropertyNameTail);
- void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations)
- {
- for (const auto &aliasDeclaration : aliasDeclarations) {
- auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId);
-
- if (!aliasTypeId) {
- throw TypeNameDoesNotExists{
- fetchImportedTypeName(aliasDeclaration.aliasImportedTypeNameId)};
- }
-
- auto aliasId = fetchAliasId(aliasTypeId,
- aliasDeclaration.aliasPropertyName,
- aliasDeclaration.aliasPropertyNameTail);
-
- updatePropertyDeclarationAliasIdAndTypeNameIdStatement
- .write(aliasDeclaration.propertyDeclarationId,
- aliasId,
- aliasDeclaration.aliasImportedTypeNameId);
- }
- }
+ void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations);
- void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations)
- {
- for (const auto &aliasDeclaration : aliasDeclarations) {
- updatetPropertiesDeclarationValuesOfAliasStatement.write(
- aliasDeclaration.propertyDeclarationId);
- updatePropertyAliasDeclarationRecursivelyStatement.write(
- aliasDeclaration.propertyDeclarationId);
- }
- }
+ void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations);
- void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations)
- {
- for (const auto &aliasDeclaration : aliasDeclarations)
- checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId);
- }
+ void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations);
void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
- const AliasPropertyDeclarations &updatedAliasPropertyDeclarations)
- {
- NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()};
-
- linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations);
- linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations);
-
- checkAliasPropertyDeclarationCycles(insertedAliasPropertyDeclarations);
- checkAliasPropertyDeclarationCycles(updatedAliasPropertyDeclarations);
-
- updateAliasPropertyDeclarationValues(insertedAliasPropertyDeclarations);
- updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations);
- }
+ const AliasPropertyDeclarations &updatedAliasPropertyDeclarations);
void synchronizeExportedTypes(const TypeIds &updatedTypeIds,
Storage::Synchronization::ExportedTypes &exportedTypes,
AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
PropertyDeclarations &relinkablePropertyDeclarations,
Prototypes &relinkablePrototypes,
- Prototypes &relinkableExtensions)
- {
- NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()};
-
- std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) {
- if (first.moduleId < second.moduleId)
- return true;
- else if (first.moduleId > second.moduleId)
- return false;
-
- auto nameCompare = Sqlite::compare(first.name, second.name);
-
- if (nameCompare < 0)
- return true;
- else if (nameCompare > 0)
- return false;
-
- return first.version < second.version;
- });
-
- auto range = selectExportedTypesForSourceIdsStatement
- .template range<Storage::Synchronization::ExportedTypeView>(
- toIntegers(updatedTypeIds));
-
- auto compareKey = [](const Storage::Synchronization::ExportedTypeView &view,
- const Storage::Synchronization::ExportedType &type) -> long long {
- auto moduleIdDifference = view.moduleId - type.moduleId;
- if (moduleIdDifference != 0)
- return moduleIdDifference;
-
- auto nameDifference = Sqlite::compare(view.name, type.name);
- if (nameDifference != 0)
- return nameDifference;
-
- auto versionDifference = view.version.major.value - type.version.major.value;
- if (versionDifference != 0)
- return versionDifference;
-
- return view.version.minor.value - type.version.minor.value;
- };
-
- auto insert = [&](const Storage::Synchronization::ExportedType &type) {
- if (!type.moduleId)
- throw QmlDesigner::ModuleDoesNotExists{};
-
- try {
- if (type.version) {
- insertExportedTypeNamesWithVersionStatement.write(type.moduleId,
- type.name,
- type.version.major.value,
- type.version.minor.value,
- type.typeId);
-
- } else if (type.version.major) {
- insertExportedTypeNamesWithMajorVersionStatement.write(type.moduleId,
- type.name,
- type.version.major.value,
- type.typeId);
- } else {
- insertExportedTypeNamesWithoutVersionStatement.write(type.moduleId,
- type.name,
- type.typeId);
- }
- } catch (const Sqlite::ConstraintPreventsModification &) {
- throw QmlDesigner::ExportedTypeCannotBeInserted{type.name};
- }
- };
-
- auto update = [&](const Storage::Synchronization::ExportedTypeView &view,
- const Storage::Synchronization::ExportedType &type) {
- if (view.typeId != type.typeId) {
- handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations);
- handleAliasPropertyDeclarationsWithPropertyType(view.typeId,
- relinkableAliasPropertyDeclarations);
- handlePrototypes(view.typeId, relinkablePrototypes);
- handleExtensions(view.typeId, relinkableExtensions);
- updateExportedTypeNameTypeIdStatement.write(view.exportedTypeNameId, type.typeId);
- return Sqlite::UpdateChange::Update;
- }
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) {
- handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations);
- handleAliasPropertyDeclarationsWithPropertyType(view.typeId,
- relinkableAliasPropertyDeclarations);
- handlePrototypes(view.typeId, relinkablePrototypes);
- handleExtensions(view.typeId, relinkableExtensions);
- deleteExportedTypeNameStatement.write(view.exportedTypeNameId);
- };
-
- Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove);
- }
+ Prototypes &relinkableExtensions);
void synchronizePropertyDeclarationsInsertAlias(
AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
const Storage::Synchronization::PropertyDeclaration &value,
SourceId sourceId,
- TypeId typeId)
- {
- auto callback = [&](PropertyDeclarationId propertyDeclarationId) {
- insertedAliasPropertyDeclarations.emplace_back(typeId,
- propertyDeclarationId,
- fetchImportedTypeNameId(value.typeName,
- sourceId),
- value.aliasPropertyName,
- value.aliasPropertyNameTail);
- return Sqlite::CallbackControl::Abort;
- };
-
- insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name);
- }
+ TypeId typeId);
+
+ QVarLengthArray<PropertyDeclarationId, 128> fetchPropertyDeclarationIds(TypeId baseTypeId) const;
+
+ PropertyDeclarationId fetchNextPropertyDeclarationId(TypeId baseTypeId,
+ Utils::SmallStringView propertyName) const;
+
+ PropertyDeclarationId fetchPropertyDeclarationId(TypeId typeId,
+ Utils::SmallStringView propertyName) const;
+
+ PropertyDeclarationId fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const;
+
+ PropertyDeclarationId fetchDefaultPropertyDeclarationId(TypeId typeId) const;
void synchronizePropertyDeclarationsInsertProperty(
- const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId)
- {
- auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId);
- auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId);
-
- if (!propertyTypeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)};
-
- auto propertyDeclarationId = insertPropertyDeclarationStatement.template value<PropertyDeclarationId>(
- typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId);
-
- auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement
- .template value<PropertyDeclarationId>(typeId,
- value.name);
- if (nextPropertyDeclarationId) {
- updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId,
- propertyDeclarationId);
- updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement
- .write(propertyDeclarationId, propertyTypeId, value.traits);
- }
- }
+ const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId);
void synchronizePropertyDeclarationsUpdateAlias(
AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
const Storage::Synchronization::PropertyDeclarationView &view,
const Storage::Synchronization::PropertyDeclaration &value,
- SourceId sourceId)
- {
- auto last = updatedAliasPropertyDeclarations.emplace_back(view.typeId,
- view.id,
- fetchImportedTypeNameId(value.typeName,
- sourceId),
- value.aliasPropertyName,
- value.aliasPropertyNameTail,
- view.aliasId);
- }
+ SourceId sourceId);
- auto synchronizePropertyDeclarationsUpdateProperty(
+ Sqlite::UpdateChange synchronizePropertyDeclarationsUpdateProperty(
const Storage::Synchronization::PropertyDeclarationView &view,
const Storage::Synchronization::PropertyDeclaration &value,
SourceId sourceId,
- PropertyDeclarationIds &propertyDeclarationIds)
- {
- auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId);
-
- auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId);
-
- if (!propertyTypeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)};
-
- if (view.traits == value.traits && propertyTypeId == view.typeId
- && propertyImportedTypeNameId == view.typeNameId)
- return Sqlite::UpdateChange::No;
-
- updatePropertyDeclarationStatement.write(view.id,
- propertyTypeId,
- value.traits,
- propertyImportedTypeNameId);
- updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(view.id,
- propertyTypeId,
- value.traits);
- propertyDeclarationIds.push_back(view.id);
- return Sqlite::UpdateChange::Update;
- }
+ PropertyDeclarationIds &propertyDeclarationIds);
void synchronizePropertyDeclarations(
TypeId typeId,
@@ -1801,270 +662,60 @@ private:
SourceId sourceId,
AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
- PropertyDeclarationIds &propertyDeclarationIds)
- {
- NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory()};
-
- std::sort(propertyDeclarations.begin(),
- propertyDeclarations.end(),
- [](auto &&first, auto &&second) {
- return Sqlite::compare(first.name, second.name) < 0;
- });
-
- auto range = selectPropertyDeclarationsForTypeIdStatement
- .template range<Storage::Synchronization::PropertyDeclarationView>(typeId);
-
- auto compareKey = [](const Storage::Synchronization::PropertyDeclarationView &view,
- const Storage::Synchronization::PropertyDeclaration &value) {
- return Sqlite::compare(view.name, value.name);
- };
-
- auto insert = [&](const Storage::Synchronization::PropertyDeclaration &value) {
- if (value.kind == Storage::Synchronization::PropertyKind::Alias) {
- synchronizePropertyDeclarationsInsertAlias(insertedAliasPropertyDeclarations,
- value,
- sourceId,
- typeId);
- } else {
- synchronizePropertyDeclarationsInsertProperty(value, sourceId, typeId);
- }
- };
-
- auto update = [&](const Storage::Synchronization::PropertyDeclarationView &view,
- const Storage::Synchronization::PropertyDeclaration &value) {
- if (value.kind == Storage::Synchronization::PropertyKind::Alias) {
- synchronizePropertyDeclarationsUpdateAlias(updatedAliasPropertyDeclarations,
- view,
- value,
- sourceId);
- propertyDeclarationIds.push_back(view.id);
- } else {
- return synchronizePropertyDeclarationsUpdateProperty(view,
- value,
- sourceId,
- propertyDeclarationIds);
- }
-
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const Storage::Synchronization::PropertyDeclarationView &view) {
- auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement
- .template value<PropertyDeclarationId>(typeId,
- view.name);
- if (nextPropertyDeclarationId) {
- updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement
- .write(nextPropertyDeclarationId, view.id);
- }
-
- updateDefaultPropertyIdToNullStatement.write(view.id);
- deletePropertyDeclarationStatement.write(view.id);
- propertyDeclarationIds.push_back(view.id);
- };
-
- Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove);
- }
+ PropertyDeclarationIds &propertyDeclarationIds);
- void resetRemovedAliasPropertyDeclarationsToNull(Storage::Synchronization::Type &type,
- PropertyDeclarationIds &propertyDeclarationIds)
+ class AliasPropertyDeclarationView
{
- if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal)
- return;
-
- Storage::Synchronization::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations;
+ public:
+ explicit AliasPropertyDeclarationView(Utils::SmallStringView name,
+ PropertyDeclarationId id,
+ PropertyDeclarationId aliasId)
+ : name{name}
+ , id{id}
+ , aliasId{aliasId}
+ {}
- class AliasPropertyDeclarationView
+ template<typename String>
+ friend void convertToString(String &string,
+ const AliasPropertyDeclarationView &aliasPropertyDeclarationView)
{
- public:
- explicit AliasPropertyDeclarationView(Utils::SmallStringView name,
- PropertyDeclarationId id,
- PropertyDeclarationId aliasId)
- : name{name}
- , id{id}
- , aliasId{aliasId}
- {}
-
- public:
- Utils::SmallStringView name;
- PropertyDeclarationId id;
- PropertyDeclarationId aliasId;
- };
-
- std::sort(aliasDeclarations.begin(), aliasDeclarations.end(), [](auto &&first, auto &&second) {
- return Sqlite::compare(first.name, second.name) < 0;
- });
-
- auto range = selectPropertyDeclarationsWithAliasForTypeIdStatement
- .template range<AliasPropertyDeclarationView>(type.typeId);
-
- auto compareKey = [](const AliasPropertyDeclarationView &view,
- const Storage::Synchronization::PropertyDeclaration &value) {
- return Sqlite::compare(view.name, value.name);
- };
-
- auto insert = [&](const Storage::Synchronization::PropertyDeclaration &) {};
-
- auto update = [&](const AliasPropertyDeclarationView &,
- const Storage::Synchronization::PropertyDeclaration &) {
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const AliasPropertyDeclarationView &view) {
- updatePropertyDeclarationAliasIdToNullStatement.write(view.id);
- propertyDeclarationIds.push_back(view.id);
- };
-
- Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove);
- }
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", aliasPropertyDeclarationView.name),
+ keyValue("id", aliasPropertyDeclarationView.id),
+ keyValue("alias id", aliasPropertyDeclarationView.aliasId));
- void resetRemovedAliasPropertyDeclarationsToNull(
- Storage::Synchronization::Types &types,
- AliasPropertyDeclarations &relinkableAliasPropertyDeclarations)
- {
- NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t,
- projectStorageCategory()};
+ convertToString(string, dict);
+ }
- PropertyDeclarationIds propertyDeclarationIds;
- propertyDeclarationIds.reserve(types.size());
+ public:
+ Utils::SmallStringView name;
+ PropertyDeclarationId id;
+ PropertyDeclarationId aliasId;
+ };
- for (auto &&type : types)
- resetRemovedAliasPropertyDeclarationsToNull(type, propertyDeclarationIds);
+ void resetRemovedAliasPropertyDeclarationsToNull(Storage::Synchronization::Type &type,
+ PropertyDeclarationIds &propertyDeclarationIds);
- removeRelinkableEntries(relinkableAliasPropertyDeclarations,
- propertyDeclarationIds,
- PropertyCompare<AliasPropertyDeclaration>{});
- }
+ void resetRemovedAliasPropertyDeclarationsToNull(
+ Storage::Synchronization::Types &types,
+ AliasPropertyDeclarations &relinkableAliasPropertyDeclarations);
ImportId insertDocumentImport(const Storage::Import &import,
Storage::Synchronization::ImportKind importKind,
ModuleId sourceModuleId,
- ImportId parentImportId)
- {
- if (import.version.minor) {
- return insertDocumentImportWithVersionStatement
- .template value<ImportId>(import.sourceId,
- import.moduleId,
- sourceModuleId,
- importKind,
- import.version.major.value,
- import.version.minor.value,
- parentImportId);
- } else if (import.version.major) {
- return insertDocumentImportWithMajorVersionStatement
- .template value<ImportId>(import.sourceId,
- import.moduleId,
- sourceModuleId,
- importKind,
- import.version.major.value,
- parentImportId);
- } else {
- return insertDocumentImportWithoutVersionStatement.template value<ImportId>(
- import.sourceId, import.moduleId, sourceModuleId, importKind, parentImportId);
- }
- }
+ ImportId parentImportId);
void synchronizeDocumentImports(Storage::Imports &imports,
const SourceIds &updatedSourceIds,
- Storage::Synchronization::ImportKind importKind)
- {
- std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) {
- return std::tie(first.sourceId, first.moduleId, first.version)
- < std::tie(second.sourceId, second.moduleId, second.version);
- });
-
- auto range = selectDocumentImportForSourceIdStatement
- .template range<Storage::Synchronization::ImportView>(toIntegers(
- updatedSourceIds),
- importKind);
-
- auto compareKey = [](const Storage::Synchronization::ImportView &view,
- const Storage::Import &import) -> long long {
- auto sourceIdDifference = view.sourceId - import.sourceId;
- if (sourceIdDifference != 0)
- return sourceIdDifference;
-
- auto moduleIdDifference = view.moduleId - import.moduleId;
- if (moduleIdDifference != 0)
- return moduleIdDifference;
-
- auto versionDifference = view.version.major.value - import.version.major.value;
- if (versionDifference != 0)
- return versionDifference;
-
- return view.version.minor.value - import.version.minor.value;
- };
-
- auto insert = [&](const Storage::Import &import) {
- auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{});
- auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) {
- Storage::Import additionImport{exportedModuleId,
- Storage::Version{majorVersion, minorVersion},
- import.sourceId};
-
- auto exportedImportKind = importKind == Storage::Synchronization::ImportKind::Import
- ? Storage::Synchronization::ImportKind::ModuleExportedImport
- : Storage::Synchronization::ImportKind::ModuleExportedModuleDependency;
-
- insertDocumentImport(additionImport, exportedImportKind, import.moduleId, importId);
- };
-
- selectModuleExportedImportsForModuleIdStatement.readCallback(callback,
- import.moduleId,
- import.version.major.value,
- import.version.minor.value);
- };
-
- auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) {
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const Storage::Synchronization::ImportView &view) {
- deleteDocumentImportStatement.write(view.importId);
- deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId);
- };
-
- Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove);
- }
+ Storage::Synchronization::ImportKind importKind);
- static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations &parameters)
- {
- Utils::PathString json;
- json.append("[");
-
- Utils::SmallStringView comma{""};
-
- for (const auto &parameter : parameters) {
- json.append(comma);
- comma = ",";
- json.append(R"({"n":")");
- json.append(parameter.name);
- json.append(R"(","tn":")");
- json.append(parameter.typeName);
- if (parameter.traits == Storage::PropertyDeclarationTraits::None) {
- json.append("\"}");
- } else {
- json.append(R"(","tr":)");
- json.append(Utils::SmallString::number(to_underlying(parameter.traits)));
- json.append("}");
- }
- }
-
- json.append("]");
-
- return json;
- }
+ static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations &parameters);
TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId,
- Utils::SmallStringView name) const override
- {
- return selectTypeIdByModuleIdAndExportedNameStatement.template value<TypeId>(moduleId, name);
- }
+ Utils::SmallStringView name) const override;
- void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths)
- {
- for (auto &path : paths)
- path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName);
- }
+ void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths);
class PropertyEditorQmlPathView
{
@@ -2075,6 +726,19 @@ private:
, directoryId{directoryId}
{}
+ template<typename String>
+ friend void convertToString(String &string,
+ const PropertyEditorQmlPathView &propertyEditorQmlPathView)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", propertyEditorQmlPathView.typeId),
+ keyValue("source id", propertyEditorQmlPathView.pathId),
+ keyValue("directory id", propertyEditorQmlPathView.directoryId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId typeId;
SourceId pathId;
@@ -2082,279 +746,33 @@ private:
};
void synchronizePropertyEditorPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths,
- SourceIds updatedPropertyEditorQmlPathsSourceIds)
- {
- using Storage::Synchronization::PropertyEditorQmlPath;
- std::sort(paths.begin(), paths.end(), [](auto &&first, auto &&second) {
- return first.typeId < second.typeId;
- });
-
- auto range = selectPropertyEditorPathsForForSourceIdsStatement
- .template range<PropertyEditorQmlPathView>(
- toIntegers(updatedPropertyEditorQmlPathsSourceIds));
-
- auto compareKey = [](const PropertyEditorQmlPathView &view,
- const PropertyEditorQmlPath &value) -> long long {
- return view.typeId - value.typeId;
- };
-
- auto insert = [&](const PropertyEditorQmlPath &path) {
- if (path.typeId)
- insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId);
- };
-
- auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) {
- if (value.pathId != view.pathId || value.directoryId != view.directoryId) {
- updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId);
- return Sqlite::UpdateChange::Update;
- }
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const PropertyEditorQmlPathView &view) {
- deletePropertyEditorPathStatement.write(view.typeId);
- };
-
- Sqlite::insertUpdateDelete(range, paths, compareKey, insert, update, remove);
- }
+ SourceIds updatedPropertyEditorQmlPathsSourceIds);
void synchronizePropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths,
- SourceIds updatedPropertyEditorQmlPathsSourceIds)
- {
- NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t,
- projectStorageCategory()};
-
- addTypeIdToPropertyEditorQmlPaths(paths);
- synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds);
- }
+ SourceIds updatedPropertyEditorQmlPathsSourceIds);
void synchronizeFunctionDeclarations(
- TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations)
- {
- NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory()};
-
- std::sort(functionsDeclarations.begin(),
- functionsDeclarations.end(),
- [](auto &&first, auto &&second) {
- auto compare = Sqlite::compare(first.name, second.name);
-
- if (compare == 0) {
- Utils::PathString firstSignature{createJson(first.parameters)};
- Utils::PathString secondSignature{createJson(second.parameters)};
-
- return Sqlite::compare(firstSignature, secondSignature) < 0;
- }
-
- return compare < 0;
- });
-
- auto range = selectFunctionDeclarationsForTypeIdStatement
- .template range<Storage::Synchronization::FunctionDeclarationView>(typeId);
-
- auto compareKey = [](const Storage::Synchronization::FunctionDeclarationView &view,
- const Storage::Synchronization::FunctionDeclaration &value) {
- auto nameKey = Sqlite::compare(view.name, value.name);
- if (nameKey != 0)
- return nameKey;
-
- Utils::PathString valueSignature{createJson(value.parameters)};
-
- return Sqlite::compare(view.signature, valueSignature);
- };
-
- auto insert = [&](const Storage::Synchronization::FunctionDeclaration &value) {
- Utils::PathString signature{createJson(value.parameters)};
-
- insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature);
- };
-
- auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view,
- const Storage::Synchronization::FunctionDeclaration &value) {
- Utils::PathString signature{createJson(value.parameters)};
-
- if (value.returnTypeName == view.returnTypeName)
- return Sqlite::UpdateChange::No;
-
- updateFunctionDeclarationStatement.write(view.id, value.returnTypeName);
-
- return Sqlite::UpdateChange::Update;
- };
-
- auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &view) {
- deleteFunctionDeclarationStatement.write(view.id);
- };
-
- Sqlite::insertUpdateDelete(range, functionsDeclarations, compareKey, insert, update, remove);
- }
+ TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations);
void synchronizeSignalDeclarations(TypeId typeId,
- Storage::Synchronization::SignalDeclarations &signalDeclarations)
- {
- NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory()};
-
- std::sort(signalDeclarations.begin(), signalDeclarations.end(), [](auto &&first, auto &&second) {
- auto compare = Sqlite::compare(first.name, second.name);
-
- if (compare == 0) {
- Utils::PathString firstSignature{createJson(first.parameters)};
- Utils::PathString secondSignature{createJson(second.parameters)};
-
- return Sqlite::compare(firstSignature, secondSignature) < 0;
- }
-
- return compare < 0;
- });
-
- auto range = selectSignalDeclarationsForTypeIdStatement
- .template range<Storage::Synchronization::SignalDeclarationView>(typeId);
-
- auto compareKey = [](const Storage::Synchronization::SignalDeclarationView &view,
- const Storage::Synchronization::SignalDeclaration &value) {
- auto nameKey = Sqlite::compare(view.name, value.name);
- if (nameKey != 0)
- return nameKey;
-
- Utils::PathString valueSignature{createJson(value.parameters)};
-
- return Sqlite::compare(view.signature, valueSignature);
- };
-
- auto insert = [&](const Storage::Synchronization::SignalDeclaration &value) {
- Utils::PathString signature{createJson(value.parameters)};
-
- insertSignalDeclarationStatement.write(typeId, value.name, signature);
- };
-
- auto update = [&]([[maybe_unused]] const Storage::Synchronization::SignalDeclarationView &view,
- [[maybe_unused]] const Storage::Synchronization::SignalDeclaration &value) {
- return Sqlite::UpdateChange::No;
- };
-
- auto remove = [&](const Storage::Synchronization::SignalDeclarationView &view) {
- deleteSignalDeclarationStatement.write(view.id);
- };
-
- Sqlite::insertUpdateDelete(range, signalDeclarations, compareKey, insert, update, remove);
- }
+ Storage::Synchronization::SignalDeclarations &signalDeclarations);
static Utils::PathString createJson(
- const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations)
- {
- Utils::PathString json;
- json.append("{");
-
- Utils::SmallStringView comma{"\""};
-
- for (const auto &enumerator : enumeratorDeclarations) {
- json.append(comma);
- comma = ",\"";
- json.append(enumerator.name);
- if (enumerator.hasValue) {
- json.append("\":\"");
- json.append(Utils::SmallString::number(enumerator.value));
- json.append("\"");
- } else {
- json.append("\":null");
- }
- }
-
- json.append("}");
-
- return json;
- }
+ const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations);
void synchronizeEnumerationDeclarations(
- TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations)
- {
- NanotraceHR::Tracer tracer{"synchronize enumeation declaration"_t, projectStorageCategory()};
-
- std::sort(enumerationDeclarations.begin(),
- enumerationDeclarations.end(),
- [](auto &&first, auto &&second) {
- return Sqlite::compare(first.name, second.name) < 0;
- });
-
- auto range = selectEnumerationDeclarationsForTypeIdStatement
- .template range<Storage::Synchronization::EnumerationDeclarationView>(typeId);
-
- auto compareKey = [](const Storage::Synchronization::EnumerationDeclarationView &view,
- const Storage::Synchronization::EnumerationDeclaration &value) {
- return Sqlite::compare(view.name, value.name);
- };
-
- auto insert = [&](const Storage::Synchronization::EnumerationDeclaration &value) {
- Utils::PathString signature{createJson(value.enumeratorDeclarations)};
-
- insertEnumerationDeclarationStatement.write(typeId, value.name, signature);
- };
-
- auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view,
- const Storage::Synchronization::EnumerationDeclaration &value) {
- Utils::PathString enumeratorDeclarations{createJson(value.enumeratorDeclarations)};
-
- if (enumeratorDeclarations == view.enumeratorDeclarations)
- return Sqlite::UpdateChange::No;
-
- updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations);
-
- return Sqlite::UpdateChange::Update;
- };
-
- auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &view) {
- deleteEnumerationDeclarationStatement.write(view.id);
- };
-
- Sqlite::insertUpdateDelete(range, enumerationDeclarations, compareKey, insert, update, remove);
- }
+ TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations);
void extractExportedTypes(TypeId typeId,
const Storage::Synchronization::Type &type,
- Storage::Synchronization::ExportedTypes &exportedTypes)
- {
- for (const auto &exportedType : type.exportedTypes)
- exportedTypes.emplace_back(exportedType.name,
- exportedType.version,
- typeId,
- exportedType.moduleId);
- }
-
- TypeId declareType(Storage::Synchronization::Type &type)
- {
- if (type.typeName.isEmpty()) {
- type.typeId = selectTypeIdBySourceIdStatement.template value<TypeId>(type.sourceId);
-
- return type.typeId;
- }
-
- type.typeId = insertTypeStatement.template value<TypeId>(type.sourceId, type.typeName);
+ Storage::Synchronization::ExportedTypes &exportedTypes);
- if (!type.typeId)
- type.typeId = selectTypeIdBySourceIdAndNameStatement.template value<TypeId>(type.sourceId,
- type.typeName);
-
- return type.typeId;
- }
+ TypeId declareType(Storage::Synchronization::Type &type);
void syncDeclarations(Storage::Synchronization::Type &type,
AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
- PropertyDeclarationIds &propertyDeclarationIds)
- {
- NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory()};
-
- if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal)
- return;
-
- synchronizePropertyDeclarations(type.typeId,
- type.propertyDeclarations,
- type.sourceId,
- insertedAliasPropertyDeclarations,
- updatedAliasPropertyDeclarations,
- propertyDeclarationIds);
- synchronizeFunctionDeclarations(type.typeId, type.functionDeclarations);
- synchronizeSignalDeclarations(type.typeId, type.signalDeclarations);
- synchronizeEnumerationDeclarations(type.typeId, type.enumerationDeclarations);
- }
+ PropertyDeclarationIds &propertyDeclarationIds);
template<typename Relinkable, typename Ids, typename Compare>
void removeRelinkableEntries(std::vector<Relinkable> &relinkables, Ids &ids, Compare compare)
@@ -2381,23 +799,7 @@ private:
void syncDeclarations(Storage::Synchronization::Types &types,
AliasPropertyDeclarations &insertedAliasPropertyDeclarations,
AliasPropertyDeclarations &updatedAliasPropertyDeclarations,
- PropertyDeclarations &relinkablePropertyDeclarations)
- {
- NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory()};
-
- PropertyDeclarationIds propertyDeclarationIds;
- propertyDeclarationIds.reserve(types.size() * 10);
-
- for (auto &&type : types)
- syncDeclarations(type,
- insertedAliasPropertyDeclarations,
- updatedAliasPropertyDeclarations,
- propertyDeclarationIds);
-
- removeRelinkableEntries(relinkablePropertyDeclarations,
- propertyDeclarationIds,
- PropertyCompare<PropertyDeclaration>{});
- }
+ PropertyDeclarations &relinkablePropertyDeclarations);
class TypeWithDefaultPropertyView
{
@@ -2407,240 +809,54 @@ private:
, defaultPropertyId{defaultPropertyId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const TypeWithDefaultPropertyView &view)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", view.typeId),
+ keyValue("property id", view.defaultPropertyId));
+
+ convertToString(string, dict);
+ }
+
TypeId typeId;
PropertyDeclarationId defaultPropertyId;
};
- void syncDefaultProperties(Storage::Synchronization::Types &types)
- {
- NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()};
-
- auto range = selectTypesWithDefaultPropertyStatement.template range<TypeWithDefaultPropertyView>();
-
- auto compareKey = [](const TypeWithDefaultPropertyView &view,
- const Storage::Synchronization::Type &value) {
- return view.typeId - value.typeId;
- };
-
- auto insert = [&](const Storage::Synchronization::Type &) {
-
- };
-
- auto update = [&](const TypeWithDefaultPropertyView &view,
- const Storage::Synchronization::Type &value) {
- PropertyDeclarationId valueDefaultPropertyId;
- if (value.defaultPropertyName.size())
- valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded(
- value.typeId, value.defaultPropertyName)
- .propertyDeclarationId;
-
- if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId))
- return Sqlite::UpdateChange::No;
-
- updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId);
-
- return Sqlite::UpdateChange::Update;
- };
+ void syncDefaultProperties(Storage::Synchronization::Types &types);
- auto remove = [&](const TypeWithDefaultPropertyView &) {};
+ void resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types);
- Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove);
- }
-
- void resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types)
- {
- NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory()};
-
- auto range = selectTypesWithDefaultPropertyStatement.template range<TypeWithDefaultPropertyView>();
-
- auto compareKey = [](const TypeWithDefaultPropertyView &view,
- const Storage::Synchronization::Type &value) {
- return view.typeId - value.typeId;
- };
-
- auto insert = [&](const Storage::Synchronization::Type &) {
-
- };
-
- auto update = [&](const TypeWithDefaultPropertyView &view,
- const Storage::Synchronization::Type &value) {
- PropertyDeclarationId valueDefaultPropertyId;
- if (value.defaultPropertyName.size()) {
- auto optionalValueDefaultPropertyId = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(
- value.typeId, value.defaultPropertyName);
- if (optionalValueDefaultPropertyId)
- valueDefaultPropertyId = optionalValueDefaultPropertyId->propertyDeclarationId;
- }
-
- if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId))
- return Sqlite::UpdateChange::No;
-
- updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{});
-
- return Sqlite::UpdateChange::Update;
- };
-
- auto remove = [&](const TypeWithDefaultPropertyView &) {};
-
- Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove);
- }
-
- void checkForPrototypeChainCycle(TypeId typeId) const
- {
- auto callback = [=](TypeId currentTypeId) {
- if (typeId == currentTypeId)
- throw PrototypeChainCycle{};
- };
-
- selectTypeIdsForPrototypeChainIdStatement.readCallback(callback, typeId);
- }
-
- void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const
- {
- auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) {
- if (propertyDeclarationId == currentPropertyDeclarationId)
- throw AliasChainCycle{};
- };
+ void checkForPrototypeChainCycle(TypeId typeId) const;
- selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback,
- propertyDeclarationId);
- }
+ void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const;
std::pair<TypeId, ImportedTypeNameId> fetchImportedTypeNameIdAndTypeId(
- const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId)
- {
- TypeId typeId;
- ImportedTypeNameId typeNameId;
- if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) {
- typeNameId = fetchImportedTypeNameId(typeName, sourceId);
-
- typeId = fetchTypeId(typeNameId);
-
- if (!typeId)
- throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId)};
- }
-
- return {typeId, typeNameId};
- }
-
- void syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds)
- {
- if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal)
- return;
+ const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId);
- auto [prototypeId, prototypeTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.prototype,
- type.sourceId);
- auto [extensionId, extensionTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.extension,
- type.sourceId);
-
- updatePrototypeAndExtensionStatement.write(type.typeId,
- prototypeId,
- prototypeTypeNameId,
- extensionId,
- extensionTypeNameId);
-
- if (prototypeId || extensionId)
- checkForPrototypeChainCycle(type.typeId);
-
- typeIds.push_back(type.typeId);
- }
+ void syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds);
void syncPrototypesAndExtensions(Storage::Synchronization::Types &types,
Prototypes &relinkablePrototypes,
- Prototypes &relinkableExtensions)
- {
- NanotraceHR::Tracer tracer{"synchronize prototypes"_t, projectStorageCategory()};
+ Prototypes &relinkableExtensions);
- TypeIds typeIds;
- typeIds.reserve(types.size());
-
- for (auto &type : types)
- syncPrototypeAndExtension(type, typeIds);
-
- removeRelinkableEntries(relinkablePrototypes, typeIds, TypeCompare<Prototype>{});
- removeRelinkableEntries(relinkableExtensions, typeIds, TypeCompare<Prototype>{});
- }
-
- ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const
- {
- if (import.version) {
- return selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value<ImportId>(
- sourceId, import.moduleId, import.version.major.value, import.version.minor.value);
- }
-
- if (import.version.major) {
- return selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement
- .template value<ImportId>(sourceId, import.moduleId, import.version.major.value);
- }
-
- return selectImportIdBySourceIdAndModuleIdStatement.template value<ImportId>(sourceId,
- import.moduleId);
- }
+ ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const;
ImportedTypeNameId fetchImportedTypeNameId(const Storage::Synchronization::ImportedTypeName &name,
- SourceId sourceId)
- {
- struct Inspect
- {
- auto operator()(const Storage::Synchronization::ImportedType &importedType)
- {
- return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported,
- sourceId,
- importedType.name);
- }
-
- auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType)
- {
- ImportId importId = storage.fetchImportId(sourceId, importedType.import);
-
- return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported,
- importId,
- importedType.name);
- }
-
- ProjectStorage &storage;
- SourceId sourceId;
- };
-
- return std::visit(Inspect{*this, sourceId}, name);
- }
+ SourceId sourceId);
template<typename Id>
ImportedTypeNameId fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind kind,
Id id,
- Utils::SmallStringView typeName)
- {
- auto importedTypeNameId = selectImportedTypeNameIdStatement
- .template value<ImportedTypeNameId>(kind, id, typeName);
-
- if (importedTypeNameId)
- return importedTypeNameId;
+ Utils::SmallStringView typeName);
- return insertImportedTypeNameIdStatement.template value<ImportedTypeNameId>(kind, id, typeName);
- }
-
- TypeId fetchTypeId(ImportedTypeNameId typeNameId) const
- {
- auto kind = selectKindFromImportedTypeNamesStatement
- .template value<Storage::Synchronization::TypeNameKind>(typeNameId);
-
- return fetchTypeId(typeNameId, kind);
- }
+ TypeId fetchTypeId(ImportedTypeNameId typeNameId) const;
- Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const
- {
- return selectNameFromImportedTypeNamesStatement.template value<Utils::SmallString>(typeNameId);
- }
+ Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const;
- TypeId fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const
- {
- if (kind == Storage::Synchronization::TypeNameKind::QualifiedExported) {
- return selectTypeIdForQualifiedImportedTypeNameNamesStatement.template value<TypeId>(
- typeNameId);
- }
-
- return selectTypeIdForImportedTypeNameNamesStatement.template value<TypeId>(typeNameId);
- }
+ TypeId fetchTypeId(ImportedTypeNameId typeNameId,
+ Storage::Synchronization::TypeNameKind kind) const;
class FetchPropertyDeclarationResult
{
@@ -2653,1303 +869,64 @@ private:
, propertyTraits{propertyTraits}
{}
+ template<typename String>
+ friend void convertToString(String &string, const FetchPropertyDeclarationResult &result)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("property type id", result.propertyTypeId),
+ keyValue("property declaration id", result.propertyDeclarationId),
+ keyValue("property traits", result.propertyTraits));
+
+ convertToString(string, dict);
+ }
+
public:
TypeId propertyTypeId;
PropertyDeclarationId propertyDeclarationId;
Storage::PropertyDeclarationTraits propertyTraits;
};
- auto fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId,
- Utils::SmallStringView name)
- {
- return selectPropertyDeclarationByTypeIdAndNameStatement
- .template optionalValue<FetchPropertyDeclarationResult>(typeId, name);
- }
+ std::optional<FetchPropertyDeclarationResult> fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(
+ TypeId typeId, Utils::SmallStringView name);
FetchPropertyDeclarationResult fetchPropertyDeclarationByTypeIdAndNameUngarded(
- TypeId typeId, Utils::SmallStringView name)
- {
- auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId,
- name);
-
- if (propertyDeclaration)
- return *propertyDeclaration;
-
- throw PropertyNameDoesNotExists{};
- }
+ TypeId typeId, Utils::SmallStringView name);
PropertyDeclarationId fetchPropertyDeclarationIdByTypeIdAndNameUngarded(TypeId typeId,
- Utils::SmallStringView name)
- {
- auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement
- .template value<PropertyDeclarationId>(typeId, name);
-
- if (propertyDeclarationId)
- return propertyDeclarationId;
-
- throw PropertyNameDoesNotExists{};
- }
-
- SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath)
- {
- return selectSourceContextIdFromSourceContextsBySourceContextPathStatement
- .template value<SourceContextId>(sourceContextPath);
- }
-
- SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath)
- {
- insertIntoSourceContextsStatement.write(sourceContextPath);
-
- return SourceContextId::create(database.lastInsertedRowId());
- }
-
- SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
- {
- insertIntoSourcesStatement.write(sourceContextId, sourceName);
-
- return SourceId::create(database.lastInsertedRowId());
- }
-
- SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
- {
- return selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement
- .template value<SourceId>(sourceContextId, sourceName);
- }
-
- auto fetchExportedTypes(TypeId typeId)
- {
- return selectExportedTypesByTypeIdStatement
- .template values<Storage::Synchronization::ExportedType, 12>(typeId);
- }
-
- auto fetchPropertyDeclarations(TypeId typeId)
- {
- return selectPropertyDeclarationsByTypeIdStatement
- .template values<Storage::Synchronization::PropertyDeclaration, 24>(typeId);
- }
-
- auto fetchFunctionDeclarations(TypeId typeId)
- {
- Storage::Synchronization::FunctionDeclarations functionDeclarations;
-
- auto callback = [&](Utils::SmallStringView name,
- Utils::SmallStringView returnType,
- FunctionDeclarationId functionDeclarationId) {
- auto &functionDeclaration = functionDeclarations.emplace_back(name, returnType);
- functionDeclaration.parameters = selectFunctionParameterDeclarationsStatement
- .template values<Storage::Synchronization::ParameterDeclaration,
- 8>(functionDeclarationId);
- };
-
- selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId);
+ Utils::SmallStringView name);
- return functionDeclarations;
- }
-
- auto fetchSignalDeclarations(TypeId typeId)
- {
- Storage::Synchronization::SignalDeclarations signalDeclarations;
-
- auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) {
- auto &signalDeclaration = signalDeclarations.emplace_back(name);
- signalDeclaration.parameters = selectSignalParameterDeclarationsStatement
- .template values<Storage::Synchronization::ParameterDeclaration,
- 8>(signalDeclarationId);
- };
-
- selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId);
+ SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath);
- return signalDeclarations;
- }
-
- auto fetchEnumerationDeclarations(TypeId typeId)
- {
- Storage::Synchronization::EnumerationDeclarations enumerationDeclarations;
+ SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath);
- auto callback = [&](Utils::SmallStringView name,
- EnumerationDeclarationId enumerationDeclarationId) {
- enumerationDeclarations.emplace_back(
- name,
- selectEnumeratorDeclarationStatement
- .template values<Storage::Synchronization::EnumeratorDeclaration, 8>(
- enumerationDeclarationId));
- };
+ SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName);
- selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement
- .readCallback(callback, typeId);
-
- return enumerationDeclarations;
- }
+ SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName);
- class Initializer
- {
- public:
- Initializer(Database &database, bool isInitialized)
- {
- if (!isInitialized) {
- auto moduleIdColumn = createModulesTable(database);
- createSourceContextsTable(database);
- createSourcesTable(database);
- createTypesAndePropertyDeclarationsTables(database, moduleIdColumn);
- createExportedTypeNamesTable(database, moduleIdColumn);
- createImportedTypeNamesTable(database);
- createEnumerationsTable(database);
- createFunctionsTable(database);
- createSignalsTable(database);
- createModuleExportedImportsTable(database, moduleIdColumn);
- createDocumentImportsTable(database, moduleIdColumn);
- createFileStatusesTable(database);
- createProjectDatasTable(database);
- createPropertyEditorPathsTable(database);
- createTypeAnnotionsTable(database);
- }
- database.setIsInitialized(true);
- }
+ Storage::Synchronization::ExportedTypes fetchExportedTypes(TypeId typeId);
- void createSourceContextsTable(Database &database)
- {
- Sqlite::Table table;
- table.setUseIfNotExists(true);
- table.setName("sourceContexts");
- table.addColumn("sourceContextId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
- const Sqlite::Column &sourceContextPathColumn = table.addColumn("sourceContextPath");
+ Storage::Synchronization::PropertyDeclarations fetchPropertyDeclarations(TypeId typeId);
- table.addUniqueIndex({sourceContextPathColumn});
+ Storage::Synchronization::FunctionDeclarations fetchFunctionDeclarations(TypeId typeId);
- table.initialize(database);
- }
+ Storage::Synchronization::SignalDeclarations fetchSignalDeclarations(TypeId typeId);
- void createSourcesTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("sources");
- table.addColumn("sourceId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}});
- const auto &sourceContextIdColumn = table.addColumn(
- "sourceContextId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::NotNull{},
- Sqlite::ForeignKey{"sourceContexts",
- "sourceContextId",
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Cascade}});
- const auto &sourceNameColumn = table.addColumn("sourceName",
- Sqlite::StrictColumnType::Text);
- table.addUniqueIndex({sourceContextIdColumn, sourceNameColumn});
-
- table.initialize(database);
- }
+ Storage::Synchronization::EnumerationDeclarations fetchEnumerationDeclarations(TypeId typeId);
- void createTypesAndePropertyDeclarationsTables(
- Database &database, [[maybe_unused]] const Sqlite::StrictColumn &foreignModuleIdColumn)
- {
- Sqlite::StrictTable typesTable;
- typesTable.setUseIfNotExists(true);
- typesTable.setName("types");
- typesTable.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}});
- auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
- auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text);
- typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer);
- auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId",
- typesTable,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Restrict);
- typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer);
- auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId",
- typesTable,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Restrict);
- typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer);
- auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId",
- Sqlite::StrictColumnType::Integer);
- typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer);
- typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn});
- typesTable.addIndex({defaultPropertyIdColumn});
- typesTable.addIndex({prototypeIdColumn});
- typesTable.addIndex({extensionIdColumn});
-
- typesTable.initialize(database);
-
- {
- Sqlite::StrictTable propertyDeclarationTable;
- propertyDeclarationTable.setUseIfNotExists(true);
- propertyDeclarationTable.setName("propertyDeclarations");
- propertyDeclarationTable.addColumn("propertyDeclarationId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{}});
- auto &typeIdColumn = propertyDeclarationTable.addColumn("typeId");
- auto &nameColumn = propertyDeclarationTable.addColumn("name");
- propertyDeclarationTable.addForeignKeyColumn("propertyTypeId",
- typesTable,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Restrict);
- propertyDeclarationTable.addColumn("propertyTraits",
- Sqlite::StrictColumnType::Integer);
- propertyDeclarationTable.addColumn("propertyImportedTypeNameId",
- Sqlite::StrictColumnType::Integer);
- auto &aliasPropertyDeclarationIdColumn = propertyDeclarationTable.addForeignKeyColumn(
- "aliasPropertyDeclarationId",
- propertyDeclarationTable,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Restrict);
- auto &aliasPropertyDeclarationTailIdColumn = propertyDeclarationTable.addForeignKeyColumn(
- "aliasPropertyDeclarationTailId",
- propertyDeclarationTable,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Restrict);
-
- propertyDeclarationTable.addUniqueIndex({typeIdColumn, nameColumn});
- propertyDeclarationTable.addIndex({aliasPropertyDeclarationIdColumn},
- "aliasPropertyDeclarationId IS NOT NULL");
- propertyDeclarationTable.addIndex({aliasPropertyDeclarationTailIdColumn},
- "aliasPropertyDeclarationTailId IS NOT NULL");
-
- propertyDeclarationTable.initialize(database);
- }
- }
+ class Initializer;
- void createExportedTypeNamesTable(Database &database,
- const Sqlite::StrictColumn &foreignModuleIdColumn)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("exportedTypeNames");
- table.addColumn("exportedTypeNameId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{}});
- auto &moduleIdColumn = table.addForeignKeyColumn("moduleId",
- foreignModuleIdColumn,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::NoAction);
- auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
- auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer);
- auto &majorVersionColumn = table.addColumn("majorVersion",
- Sqlite::StrictColumnType::Integer);
- auto &minorVersionColumn = table.addColumn("minorVersion",
- Sqlite::StrictColumnType::Integer);
-
- table.addUniqueIndex({moduleIdColumn, nameColumn},
- "majorVersion IS NULL AND minorVersion IS NULL");
- table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn},
- "majorVersion IS NOT NULL AND minorVersion IS NULL");
- table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn, minorVersionColumn},
- "majorVersion IS NOT NULL AND minorVersion IS NOT NULL");
-
- table.addIndex({typeIdColumn});
-
- table.initialize(database);
- }
-
- void createImportedTypeNamesTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("importedTypeNames");
- table.addColumn("importedTypeNameId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{}});
- auto &importOrSourceIdColumn = table.addColumn("importOrSourceId");
- auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
- auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer);
-
- table.addUniqueIndex({kindColumn, importOrSourceIdColumn, nameColumn});
-
- table.initialize(database);
- }
-
- void createEnumerationsTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("enumerationDeclarations");
- table.addColumn("enumerationDeclarationId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{}});
- auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer);
- auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
- table.addColumn("enumeratorDeclarations", Sqlite::StrictColumnType::Text);
-
- table.addUniqueIndex({typeIdColumn, nameColumn});
-
- table.initialize(database);
- }
-
- void createFunctionsTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("functionDeclarations");
- table.addColumn("functionDeclarationId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{}});
- auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer);
- auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
- auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text);
- table.addColumn("returnTypeName");
-
- table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn});
-
- table.initialize(database);
- }
-
- void createSignalsTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("signalDeclarations");
- table.addColumn("signalDeclarationId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{}});
- auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer);
- auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
- auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text);
-
- table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn});
-
- table.initialize(database);
- }
-
- Sqlite::StrictColumn createModulesTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("modules");
- auto &modelIdColumn = table.addColumn("moduleId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{}});
- auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text);
-
- table.addUniqueIndex({nameColumn});
-
- table.initialize(database);
-
- return std::move(modelIdColumn);
- }
-
- void createModuleExportedImportsTable(Database &database,
- const Sqlite::StrictColumn &foreignModuleIdColumn)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("moduleExportedImports");
- table.addColumn("moduleExportedImportId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{}});
- auto &moduleIdColumn = table.addForeignKeyColumn("moduleId",
- foreignModuleIdColumn,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Cascade,
- Sqlite::Enforment::Immediate);
- auto &sourceIdColumn = table.addColumn("exportedModuleId",
- Sqlite::StrictColumnType::Integer);
- table.addColumn("isAutoVersion", Sqlite::StrictColumnType::Integer);
- table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer);
- table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer);
-
- table.addUniqueIndex({sourceIdColumn, moduleIdColumn});
-
- table.initialize(database);
- }
-
- void createDocumentImportsTable(Database &database,
- const Sqlite::StrictColumn &foreignModuleIdColumn)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("documentImports");
- table.addColumn("importId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}});
- auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
- auto &moduleIdColumn = table.addForeignKeyColumn("moduleId",
- foreignModuleIdColumn,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Cascade,
- Sqlite::Enforment::Immediate);
- auto &sourceModuleIdColumn = table.addForeignKeyColumn("sourceModuleId",
- foreignModuleIdColumn,
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Cascade,
- Sqlite::Enforment::Immediate);
- auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer);
- auto &majorVersionColumn = table.addColumn("majorVersion",
- Sqlite::StrictColumnType::Integer);
- auto &minorVersionColumn = table.addColumn("minorVersion",
- Sqlite::StrictColumnType::Integer);
- auto &parentImportIdColumn = table.addColumn("parentImportId",
- Sqlite::StrictColumnType::Integer);
-
- table.addUniqueIndex({sourceIdColumn,
- moduleIdColumn,
- kindColumn,
- sourceModuleIdColumn,
- parentImportIdColumn},
- "majorVersion IS NULL AND minorVersion IS NULL");
- table.addUniqueIndex({sourceIdColumn,
- moduleIdColumn,
- kindColumn,
- sourceModuleIdColumn,
- majorVersionColumn,
- parentImportIdColumn},
- "majorVersion IS NOT NULL AND minorVersion IS NULL");
- table.addUniqueIndex({sourceIdColumn,
- moduleIdColumn,
- kindColumn,
- sourceModuleIdColumn,
- majorVersionColumn,
- minorVersionColumn,
- parentImportIdColumn},
- "majorVersion IS NOT NULL AND minorVersion IS NOT NULL");
-
- table.initialize(database);
- }
-
- void createFileStatusesTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setName("fileStatuses");
- table.addColumn("sourceId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{},
- Sqlite::ForeignKey{"sources",
- "sourceId",
- Sqlite::ForeignKeyAction::NoAction,
- Sqlite::ForeignKeyAction::Cascade}});
- table.addColumn("size", Sqlite::StrictColumnType::Integer);
- table.addColumn("lastModified", Sqlite::StrictColumnType::Integer);
-
- table.initialize(database);
- }
-
- void createProjectDatasTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setUseWithoutRowId(true);
- table.setName("projectDatas");
- auto &projectSourceIdColumn = table.addColumn("projectSourceId",
- Sqlite::StrictColumnType::Integer);
- auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
- table.addColumn("moduleId", Sqlite::StrictColumnType::Integer);
- table.addColumn("fileType", Sqlite::StrictColumnType::Integer);
-
- table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn});
- table.addUniqueIndex({sourceIdColumn});
-
- table.initialize(database);
- }
-
- void createPropertyEditorPathsTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setUseWithoutRowId(true);
- table.setName("propertyEditorPaths");
- table.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}});
- table.addColumn("pathSourceId", Sqlite::StrictColumnType::Integer);
- auto &directoryIdColumn = table.addColumn("directoryId",
- Sqlite::StrictColumnType::Integer);
-
- table.addIndex({directoryIdColumn});
-
- table.initialize(database);
- }
-
- void createTypeAnnotionsTable(Database &database)
- {
- Sqlite::StrictTable table;
- table.setUseIfNotExists(true);
- table.setUseWithoutRowId(true);
- table.setName("typeAnnotations");
- auto &typeIdColumn = table.addColumn("typeId",
- Sqlite::StrictColumnType::Integer,
- {Sqlite::PrimaryKey{}});
- auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
- table.addColumn("iconPath", Sqlite::StrictColumnType::Text);
- table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text);
- table.addColumn("hints", Sqlite::StrictColumnType::Text);
-
- table.addUniqueIndex({sourceIdColumn, typeIdColumn});
-
- table.initialize(database);
- }
- };
+ struct Statements;
public:
Database &database;
Sqlite::ExclusiveNonThrowingDestructorTransaction<Database> exclusiveTransaction;
- Initializer initializer;
+ std::unique_ptr<Initializer> initializer;
mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}};
- Storage::Info::CommonTypeCache<ProjectStorageInterface> commonTypeCache_{*this};
+ Storage::Info::CommonTypeCache<ProjectStorageType> commonTypeCache_{*this};
QVarLengthArray<ProjectStorageObserver *, 24> observers;
- ReadWriteStatement<1, 2> insertTypeStatement{
- "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database};
- WriteStatement<5> updatePrototypeAndExtensionStatement{
- "UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 "
- "WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId "
- "IS NOT ?4 OR extensionNameId IS NOT ?5)",
- database};
- mutable ReadStatement<1, 1> selectTypeIdByExportedNameStatement{
- "SELECT typeId FROM exportedTypeNames WHERE name=?1", database};
- mutable ReadStatement<1, 2> selectTypeIdByModuleIdAndExportedNameStatement{
- "SELECT typeId FROM exportedTypeNames "
- "WHERE moduleId=?1 AND name=?2 "
- "ORDER BY majorVersion DESC, minorVersion DESC "
- "LIMIT 1",
- database};
- mutable ReadStatement<1, 3> selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement{
- "SELECT typeId FROM exportedTypeNames "
- "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3"
- "ORDER BY minorVersion DESC "
- "LIMIT 1",
- database};
- mutable ReadStatement<1, 4> selectTypeIdByModuleIdAndExportedNameAndVersionStatement{
- "SELECT typeId FROM exportedTypeNames "
- "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3 AND minorVersion<=?4"
- "ORDER BY minorVersion DESC "
- "LIMIT 1",
- database};
- mutable ReadStatement<1, 2> selectPrototypeIdStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeSelection(typeId) AS ("
- " VALUES(?1) "
- " UNION ALL "
- " SELECT prototypeId FROM all_prototype_and_extension JOIN typeSelection "
- " USING(typeId))"
- "SELECT typeId FROM typeSelection WHERE typeId=?2 LIMIT 1",
- database};
- mutable ReadStatement<1, 2> selectPropertyDeclarationIdByTypeIdAndNameStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeSelection(typeId, level) AS ("
- " VALUES(?1, 0) "
- " UNION ALL "
- " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN "
- " typeSelection USING(typeId)) "
- "SELECT propertyDeclarationId FROM propertyDeclarations JOIN typeSelection USING(typeId) "
- " WHERE name=?2 ORDER BY level LIMIT 1",
- database};
- mutable ReadStatement<3, 2> selectPropertyDeclarationByTypeIdAndNameStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeSelection(typeId, level) AS ("
- " VALUES(?1, 0) "
- " UNION ALL "
- " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN "
- " typeSelection USING(typeId))"
- "SELECT propertyTypeId, propertyDeclarationId, propertyTraits "
- " FROM propertyDeclarations JOIN typeSelection USING(typeId) "
- " WHERE name=?2 ORDER BY level LIMIT 1",
- database};
- mutable ReadStatement<1, 1> selectPrototypeIdsInOrderStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeSelection(typeId, level) AS ("
- " VALUES(?1, 0) "
- " UNION ALL "
- " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN "
- " typeSelection USING(typeId) WHERE prototypeId IS NOT NULL) "
- "SELECT typeId FROM typeSelection ORDER BY level DESC",
- database};
- mutable ReadStatement<1, 1> selectSourceContextIdFromSourceContextsBySourceContextPathStatement{
- "SELECT sourceContextId FROM sourceContexts WHERE sourceContextPath = ?", database};
- mutable ReadStatement<1, 1> selectSourceContextPathFromSourceContextsBySourceContextIdStatement{
- "SELECT sourceContextPath FROM sourceContexts WHERE sourceContextId = ?", database};
- mutable ReadStatement<2> selectAllSourceContextsStatement{
- "SELECT sourceContextPath, sourceContextId FROM sourceContexts", database};
- WriteStatement<1> insertIntoSourceContextsStatement{
- "INSERT INTO sourceContexts(sourceContextPath) VALUES (?)", database};
- mutable ReadStatement<1, 2> selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement{
- "SELECT sourceId FROM sources WHERE sourceContextId = ? AND sourceName = ?", database};
- mutable ReadStatement<2, 1> selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement{
- "SELECT sourceName, sourceContextId FROM sources WHERE sourceId = ?", database};
- mutable ReadStatement<1, 1> selectSourceContextIdFromSourcesBySourceIdStatement{
- "SELECT sourceContextId FROM sources WHERE sourceId = ?", database};
- WriteStatement<2> insertIntoSourcesStatement{
- "INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database};
- mutable ReadStatement<3> selectAllSourcesStatement{
- "SELECT sourceName, sourceContextId, sourceId FROM sources", database};
- mutable ReadStatement<8, 1> selectTypeByTypeIdStatement{
- "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, "
- "pd.name "
- "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON "
- "defaultPropertyId=propertyDeclarationId "
- "WHERE t.typeId=?",
- database};
- mutable ReadStatement<4, 1> selectExportedTypesByTypeIdStatement{
- "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM "
- "exportedTypeNames WHERE typeId=?",
- database};
- mutable ReadStatement<4, 2> selectExportedTypesByTypeIdAndSourceIdStatement{
- "SELECT etn.moduleId, name, ifnull(etn.majorVersion, -1), ifnull(etn.minorVersion, -1) "
- "FROM exportedTypeNames AS etn JOIN documentImports USING(moduleId) WHERE typeId=?1 AND "
- "sourceId=?2",
- database};
- mutable ReadStatement<8> selectTypesStatement{
- "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, "
- "pd.name "
- "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON "
- "defaultPropertyId=propertyDeclarationId",
- database};
- WriteStatement<2> updateTypeTraitStatement{"UPDATE types SET traits = ?2 WHERE typeId=?1",
- database};
- WriteStatement<2> updateTypeAnnotationTraitStatement{
- "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database};
- ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{
- "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN "
- "carray(?2))",
- database};
- WriteStatement<1> deleteTypeNamesByTypeIdStatement{
- "DELETE FROM exportedTypeNames WHERE typeId=?", database};
- WriteStatement<1> deleteEnumerationDeclarationByTypeIdStatement{
- "DELETE FROM enumerationDeclarations WHERE typeId=?", database};
- WriteStatement<1> deletePropertyDeclarationByTypeIdStatement{
- "DELETE FROM propertyDeclarations WHERE typeId=?", database};
- WriteStatement<1> deleteFunctionDeclarationByTypeIdStatement{
- "DELETE FROM functionDeclarations WHERE typeId=?", database};
- WriteStatement<1> deleteSignalDeclarationByTypeIdStatement{
- "DELETE FROM signalDeclarations WHERE typeId=?", database};
- WriteStatement<1> deleteTypeStatement{"DELETE FROM types WHERE typeId=?", database};
- mutable ReadStatement<4, 1> selectPropertyDeclarationsByTypeIdStatement{
- "SELECT name, propertyTypeId, propertyTraits, (SELECT name FROM "
- "propertyDeclarations WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM "
- "propertyDeclarations AS pd WHERE typeId=?",
- database};
- ReadStatement<6, 1> selectPropertyDeclarationsForTypeIdStatement{
- "SELECT name, propertyTraits, propertyTypeId, propertyImportedTypeNameId, "
- "propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations "
- "WHERE typeId=? ORDER BY name",
- database};
- ReadWriteStatement<1, 5> insertPropertyDeclarationStatement{
- "INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, "
- "propertyImportedTypeNameId, aliasPropertyDeclarationId) VALUES(?1, ?2, ?3, ?4, ?5, NULL) "
- "RETURNING propertyDeclarationId",
- database};
- WriteStatement<4> updatePropertyDeclarationStatement{
- "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, "
- "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=NULL WHERE "
- "propertyDeclarationId=?1",
- database};
- WriteStatement<3> updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement{
- "WITH RECURSIVE "
- " properties(aliasPropertyDeclarationId) AS ( "
- " SELECT propertyDeclarationId FROM propertyDeclarations WHERE "
- " aliasPropertyDeclarationId=?1 "
- " UNION ALL "
- " SELECT pd.propertyDeclarationId FROM "
- " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) "
- "UPDATE propertyDeclarations AS pd "
- "SET propertyTypeId=?2, propertyTraits=?3 "
- "FROM properties AS p "
- "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId",
- database};
- WriteStatement<1> updatePropertyAliasDeclarationRecursivelyStatement{
- "WITH RECURSIVE "
- " propertyValues(propertyTypeId, propertyTraits) AS ("
- " SELECT propertyTypeId, propertyTraits FROM propertyDeclarations "
- " WHERE propertyDeclarationId=?1), "
- " properties(aliasPropertyDeclarationId) AS ( "
- " SELECT propertyDeclarationId FROM propertyDeclarations WHERE "
- " aliasPropertyDeclarationId=?1 "
- " UNION ALL "
- " SELECT pd.propertyDeclarationId FROM "
- " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) "
- "UPDATE propertyDeclarations AS pd "
- "SET propertyTypeId=pv.propertyTypeId, propertyTraits=pv.propertyTraits "
- "FROM properties AS p, propertyValues AS pv "
- "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId",
- database};
- WriteStatement<1> deletePropertyDeclarationStatement{
- "DELETE FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
- ReadStatement<3, 1> selectPropertyDeclarationsWithAliasForTypeIdStatement{
- "SELECT name, propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations "
- "WHERE typeId=? AND aliasPropertyDeclarationId IS NOT NULL ORDER BY name",
- database};
- WriteStatement<5> updatePropertyDeclarationWithAliasAndTypeStatement{
- "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, "
- "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=?5 WHERE "
- "propertyDeclarationId=?1",
- database};
- ReadWriteStatement<1, 2> insertAliasPropertyDeclarationStatement{
- "INSERT INTO propertyDeclarations(typeId, name) VALUES(?1, ?2) RETURNING "
- "propertyDeclarationId",
- database};
- mutable ReadStatement<4, 1> selectFunctionDeclarationsForTypeIdStatement{
- "SELECT name, returnTypeName, signature, functionDeclarationId FROM "
- "functionDeclarations WHERE typeId=? ORDER BY name, signature",
- database};
- mutable ReadStatement<3, 1> selectFunctionDeclarationsForTypeIdWithoutSignatureStatement{
- "SELECT name, returnTypeName, functionDeclarationId FROM "
- "functionDeclarations WHERE typeId=? ORDER BY name",
- database};
- mutable ReadStatement<3, 1> selectFunctionParameterDeclarationsStatement{
- "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), "
- "json_extract(json_each.value, '$.tr') FROM functionDeclarations, "
- "json_each(functionDeclarations.signature) WHERE functionDeclarationId=?",
- database};
- WriteStatement<4> insertFunctionDeclarationStatement{
- "INSERT INTO functionDeclarations(typeId, name, returnTypeName, signature) VALUES(?1, ?2, "
- "?3, ?4)",
- database};
- WriteStatement<2> updateFunctionDeclarationStatement{
- "UPDATE functionDeclarations SET returnTypeName=?2 WHERE functionDeclarationId=?1", database};
- WriteStatement<1> deleteFunctionDeclarationStatement{
- "DELETE FROM functionDeclarations WHERE functionDeclarationId=?", database};
- mutable ReadStatement<3, 1> selectSignalDeclarationsForTypeIdStatement{
- "SELECT name, signature, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER "
- "BY name, signature",
- database};
- mutable ReadStatement<2, 1> selectSignalDeclarationsForTypeIdWithoutSignatureStatement{
- "SELECT name, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER BY name",
- database};
- mutable ReadStatement<3, 1> selectSignalParameterDeclarationsStatement{
- "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), "
- "json_extract(json_each.value, '$.tr') FROM signalDeclarations, "
- "json_each(signalDeclarations.signature) WHERE signalDeclarationId=?",
- database};
- WriteStatement<3> insertSignalDeclarationStatement{
- "INSERT INTO signalDeclarations(typeId, name, signature) VALUES(?1, ?2, ?3)", database};
- WriteStatement<2> updateSignalDeclarationStatement{
- "UPDATE signalDeclarations SET signature=?2 WHERE signalDeclarationId=?1", database};
- WriteStatement<1> deleteSignalDeclarationStatement{
- "DELETE FROM signalDeclarations WHERE signalDeclarationId=?", database};
- mutable ReadStatement<3, 1> selectEnumerationDeclarationsForTypeIdStatement{
- "SELECT name, enumeratorDeclarations, enumerationDeclarationId FROM "
- "enumerationDeclarations WHERE typeId=? ORDER BY name",
- database};
- mutable ReadStatement<2, 1> selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement{
- "SELECT name, enumerationDeclarationId FROM enumerationDeclarations WHERE typeId=? ORDER "
- "BY name",
- database};
- mutable ReadStatement<3, 1> selectEnumeratorDeclarationStatement{
- "SELECT json_each.key, json_each.value, json_each.type!='null' FROM "
- "enumerationDeclarations, json_each(enumerationDeclarations.enumeratorDeclarations) WHERE "
- "enumerationDeclarationId=?",
- database};
- WriteStatement<3> insertEnumerationDeclarationStatement{
- "INSERT INTO enumerationDeclarations(typeId, name, enumeratorDeclarations) VALUES(?1, ?2, "
- "?3)",
- database};
- WriteStatement<2> updateEnumerationDeclarationStatement{
- "UPDATE enumerationDeclarations SET enumeratorDeclarations=?2 WHERE "
- "enumerationDeclarationId=?1",
- database};
- WriteStatement<1> deleteEnumerationDeclarationStatement{
- "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database};
- mutable ReadStatement<1, 1> selectModuleIdByNameStatement{
- "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database};
- mutable ReadWriteStatement<1, 1> insertModuleNameStatement{
- "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database};
- mutable ReadStatement<1, 1> selectModuleNameStatement{
- "SELECT name FROM modules WHERE moduleId =?1", database};
- mutable ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules", database};
- mutable ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{
- "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database};
- mutable ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{
- "SELECT typeId FROM exportedTypeNames WHERE moduleId IN carray(?1, ?2, 'int32') AND "
- "name=?3",
- database};
- mutable ReadStatement<4> selectAllDocumentImportForSourceIdStatement{
- "SELECT moduleId, majorVersion, minorVersion, sourceId "
- "FROM documentImports ",
- database};
- mutable ReadStatement<5, 2> selectDocumentImportForSourceIdStatement{
- "SELECT importId, sourceId, moduleId, majorVersion, minorVersion "
- "FROM documentImports WHERE sourceId IN carray(?1) AND kind=?2 ORDER BY sourceId, "
- "moduleId, majorVersion, minorVersion",
- database};
- ReadWriteStatement<1, 5> insertDocumentImportWithoutVersionStatement{
- "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, "
- "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING importId",
- database};
- ReadWriteStatement<1, 6> insertDocumentImportWithMajorVersionStatement{
- "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, "
- "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6) RETURNING importId",
- database};
- ReadWriteStatement<1, 7> insertDocumentImportWithVersionStatement{
- "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, "
- "minorVersion, parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) RETURNING "
- "importId",
- database};
- WriteStatement<1> deleteDocumentImportStatement{"DELETE FROM documentImports WHERE importId=?1",
- database};
- WriteStatement<2> deleteDocumentImportsWithParentImportIdStatement{
- "DELETE FROM documentImports WHERE sourceId=?1 AND parentImportId=?2", database};
- WriteStatement<1> deleteDocumentImportsWithSourceIdsStatement{
- "DELETE FROM documentImports WHERE sourceId IN carray(?1)", database};
- ReadStatement<1, 2> selectPropertyDeclarationIdPrototypeChainDownStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeSelection(typeId, level) AS ("
- " SELECT prototypeId, 0 FROM types WHERE typeId=?1 AND prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN "
- " typeSelection USING(typeId))"
- "SELECT propertyDeclarationId FROM typeSelection JOIN propertyDeclarations "
- " USING(typeId) WHERE name=?2 ORDER BY level LIMIT 1",
- database};
- WriteStatement<2> updateAliasIdPropertyDeclarationStatement{
- "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2 WHERE "
- "aliasPropertyDeclarationId=?1",
- database};
- WriteStatement<2> updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement{
- "UPDATE propertyDeclarations SET propertyTypeId=new.propertyTypeId, "
- "propertyTraits=new.propertyTraits, aliasPropertyDeclarationId=?1 FROM (SELECT "
- "propertyTypeId, propertyTraits FROM propertyDeclarations WHERE propertyDeclarationId=?1) "
- "AS new WHERE aliasPropertyDeclarationId=?2",
- database};
- WriteStatement<1> updateAliasPropertyDeclarationToNullStatement{
- "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL, propertyTypeId=NULL, "
- "propertyTraits=NULL WHERE propertyDeclarationId=? AND (aliasPropertyDeclarationId IS NOT "
- "NULL OR propertyTypeId IS NOT NULL OR propertyTraits IS NOT NULL)",
- database};
- ReadStatement<5, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{
- "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, "
- "alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId FROM "
- "propertyDeclarations AS alias JOIN propertyDeclarations AS target ON "
- "alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR "
- "alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId WHERE "
- "alias.propertyTypeId=?1 OR target.typeId=?1 OR alias.propertyImportedTypeNameId IN "
- "(SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) "
- "WHERE typeId=?1)",
- database};
- ReadStatement<3, 1> selectAliasPropertiesDeclarationForPropertiesWithAliasIdStatement{
- "WITH RECURSIVE "
- " properties(propertyDeclarationId, propertyImportedTypeNameId, typeId, "
- " aliasPropertyDeclarationId) AS ("
- " SELECT propertyDeclarationId, propertyImportedTypeNameId, typeId, "
- " aliasPropertyDeclarationId FROM propertyDeclarations WHERE "
- " aliasPropertyDeclarationId=?1"
- " UNION ALL "
- " SELECT pd.propertyDeclarationId, pd.propertyImportedTypeNameId, pd.typeId, "
- " pd.aliasPropertyDeclarationId FROM propertyDeclarations AS pd JOIN properties AS "
- " p ON pd.aliasPropertyDeclarationId=p.propertyDeclarationId)"
- "SELECT propertyDeclarationId, propertyImportedTypeNameId, aliasPropertyDeclarationId "
- " FROM properties",
- database};
- ReadWriteStatement<3, 1> updatesPropertyDeclarationPropertyTypeToNullStatement{
- "UPDATE propertyDeclarations SET propertyTypeId=NULL WHERE propertyTypeId=?1 AND "
- "aliasPropertyDeclarationId IS NULL RETURNING typeId, propertyDeclarationId, "
- "propertyImportedTypeNameId",
- database};
- mutable ReadStatement<1, 1> selectPropertyNameStatement{
- "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
- WriteStatement<2> updatePropertyDeclarationTypeStatement{
- "UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database};
- ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{
- "UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING "
- "typeId, prototypeNameId",
- database};
- ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{
- "UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING "
- "typeId, extensionNameId",
- database};
- WriteStatement<2> updateTypePrototypeStatement{
- "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database};
- WriteStatement<2> updateTypeExtensionStatement{
- "UPDATE types SET extensionId=?2 WHERE typeId=?1", database};
- mutable ReadStatement<1, 1> selectTypeIdsForPrototypeChainIdStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " prototypes(typeId) AS ("
- " SELECT prototypeId FROM all_prototype_and_extension WHERE typeId=?"
- " UNION ALL "
- " SELECT prototypeId FROM all_prototype_and_extension JOIN "
- " prototypes USING(typeId)) "
- "SELECT typeId FROM prototypes",
- database};
- WriteStatement<3> updatePropertyDeclarationAliasIdAndTypeNameIdStatement{
- "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2, "
- "propertyImportedTypeNameId=?3 WHERE propertyDeclarationId=?1 AND "
- "(aliasPropertyDeclarationId IS NOT ?2 OR propertyImportedTypeNameId IS NOT ?3)",
- database};
- WriteStatement<1> updatetPropertiesDeclarationValuesOfAliasStatement{
- "WITH RECURSIVE "
- " properties(propertyDeclarationId, propertyTypeId, propertyTraits) AS ( "
- " SELECT aliasPropertyDeclarationId, propertyTypeId, propertyTraits FROM "
- " propertyDeclarations WHERE propertyDeclarationId=?1 "
- " UNION ALL "
- " SELECT pd.aliasPropertyDeclarationId, pd.propertyTypeId, pd.propertyTraits FROM "
- " propertyDeclarations AS pd JOIN properties USING(propertyDeclarationId)) "
- "UPDATE propertyDeclarations AS pd SET propertyTypeId=p.propertyTypeId, "
- " propertyTraits=p.propertyTraits "
- "FROM properties AS p "
- "WHERE pd.propertyDeclarationId=?1 AND p.propertyDeclarationId IS NULL AND "
- " (pd.propertyTypeId IS NOT p.propertyTypeId OR pd.propertyTraits IS NOT "
- " p.propertyTraits)",
- database};
- WriteStatement<1> updatePropertyDeclarationAliasIdToNullStatement{
- "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL WHERE "
- "propertyDeclarationId=?1",
- database};
- mutable ReadStatement<1, 1> selectPropertyDeclarationIdsForAliasChainStatement{
- "WITH RECURSIVE "
- " properties(propertyDeclarationId) AS ( "
- " SELECT aliasPropertyDeclarationId FROM propertyDeclarations WHERE "
- " propertyDeclarationId=?1 "
- " UNION ALL "
- " SELECT aliasPropertyDeclarationId FROM propertyDeclarations JOIN properties "
- " USING(propertyDeclarationId)) "
- "SELECT propertyDeclarationId FROM properties",
- database};
- mutable ReadStatement<3> selectAllFileStatusesStatement{
- "SELECT sourceId, size, lastModified FROM fileStatuses ORDER BY sourceId", database};
- mutable ReadStatement<3, 1> selectFileStatusesForSourceIdsStatement{
- "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId IN carray(?1) ORDER "
- "BY sourceId",
- database};
- mutable ReadStatement<3, 1> selectFileStatusesForSourceIdStatement{
- "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId=?1 ORDER BY sourceId",
- database};
- WriteStatement<3> insertFileStatusStatement{
- "INSERT INTO fileStatuses(sourceId, size, lastModified) VALUES(?1, ?2, ?3)", database};
- WriteStatement<1> deleteFileStatusStatement{"DELETE FROM fileStatuses WHERE sourceId=?1",
- database};
- WriteStatement<3> updateFileStatusStatement{
- "UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database};
- ReadStatement<1, 1> selectTypeIdBySourceIdStatement{"SELECT typeId FROM types WHERE sourceId=?",
- database};
- mutable ReadStatement<1, 3> selectImportedTypeNameIdStatement{
- "SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 "
- "AND name=?3 LIMIT 1",
- database};
- mutable ReadWriteStatement<1, 3> insertImportedTypeNameIdStatement{
- "INSERT INTO importedTypeNames(kind, importOrSourceId, name) VALUES (?1, ?2, ?3) "
- "RETURNING importedTypeNameId",
- database};
- mutable ReadStatement<1, 2> selectImportIdBySourceIdAndModuleIdStatement{
- "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND majorVersion "
- "IS NULL AND minorVersion IS NULL LIMIT 1",
- database};
- mutable ReadStatement<1, 3> selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement{
- "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND "
- "majorVersion=?3 AND minorVersion IS NULL LIMIT 1",
- database};
- mutable ReadStatement<1, 4> selectImportIdBySourceIdAndModuleIdAndVersionStatement{
- "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND "
- "majorVersion=?3 AND minorVersion=?4 LIMIT 1",
- database};
- mutable ReadStatement<1, 1> selectKindFromImportedTypeNamesStatement{
- "SELECT kind FROM importedTypeNames WHERE importedTypeNameId=?1", database};
- mutable ReadStatement<1, 1> selectNameFromImportedTypeNamesStatement{
- "SELECT name FROM importedTypeNames WHERE importedTypeNameId=?1", database};
- mutable ReadStatement<1, 1> selectTypeIdForQualifiedImportedTypeNameNamesStatement{
- "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON "
- "importOrSourceId=di.importId JOIN documentImports AS di2 ON di.sourceId=di2.sourceId AND "
- "di.moduleId=di2.sourceModuleId "
- "JOIN exportedTypeNames AS etn ON di2.moduleId=etn.moduleId WHERE "
- "itn.kind=2 AND importedTypeNameId=?1 AND itn.name=etn.name AND "
- "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS "
- "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY etn.majorVersion DESC NULLS FIRST, "
- "etn.minorVersion DESC NULLS FIRST LIMIT 1",
- database};
- mutable ReadStatement<1, 1> selectTypeIdForImportedTypeNameNamesStatement{
- "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON "
- "importOrSourceId=sourceId JOIN exportedTypeNames AS etn USING(moduleId) WHERE "
- "itn.kind=1 AND importedTypeNameId=?1 AND itn.name=etn.name AND "
- "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS "
- "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY di.kind, etn.majorVersion DESC "
- "NULLS FIRST, etn.minorVersion DESC NULLS FIRST LIMIT 1",
- database};
- WriteStatement<0> deleteAllSourcesStatement{"DELETE FROM sources", database};
- WriteStatement<0> deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database};
- mutable ReadStatement<6, 1> selectExportedTypesForSourceIdsStatement{
- "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, "
- "exportedTypeNameId FROM exportedTypeNames WHERE typeId in carray(?1) ORDER BY moduleId, "
- "name, majorVersion, minorVersion",
- database};
- WriteStatement<5> insertExportedTypeNamesWithVersionStatement{
- "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, minorVersion, typeId) "
- "VALUES(?1, ?2, ?3, ?4, ?5)",
- database};
- WriteStatement<4> insertExportedTypeNamesWithMajorVersionStatement{
- "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, typeId) "
- "VALUES(?1, ?2, ?3, ?4)",
- database};
- WriteStatement<3> insertExportedTypeNamesWithoutVersionStatement{
- "INSERT INTO exportedTypeNames(moduleId, name, typeId) VALUES(?1, ?2, ?3)", database};
- WriteStatement<1> deleteExportedTypeNameStatement{
- "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database};
- WriteStatement<2> updateExportedTypeNameTypeIdStatement{
- "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database};
- mutable ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{
- "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE "
- "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId",
- database};
- WriteStatement<4> insertProjectDataStatement{
- "INSERT INTO projectDatas(projectSourceId, sourceId, "
- "moduleId, fileType) VALUES(?1, ?2, ?3, ?4)",
- database};
- WriteStatement<2> deleteProjectDataStatement{
- "DELETE FROM projectDatas WHERE projectSourceId=?1 AND sourceId=?2", database};
- WriteStatement<4> updateProjectDataStatement{
- "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2",
- database};
- mutable ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{
- "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE "
- "projectSourceId=?1",
- database};
- mutable ReadStatement<4, 1> selectProjectDataForSourceIdStatement{
- "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE "
- "sourceId=?1 LIMIT 1",
- database};
- mutable ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{
- "SELECT typeId FROM types WHERE sourceId IN carray(?1)", database};
- mutable ReadStatement<6, 1> selectModuleExportedImportsForSourceIdStatement{
- "SELECT moduleExportedImportId, moduleId, exportedModuleId, ifnull(majorVersion, -1), "
- "ifnull(minorVersion, -1), isAutoVersion FROM moduleExportedImports WHERE moduleId IN "
- "carray(?1) ORDER BY moduleId, exportedModuleId",
- database};
- WriteStatement<3> insertModuleExportedImportWithoutVersionStatement{
- "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion) "
- "VALUES (?1, ?2, ?3)",
- database};
- WriteStatement<4> insertModuleExportedImportWithMajorVersionStatement{
- "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, "
- "majorVersion) VALUES (?1, ?2, ?3, ?4)",
- database};
- WriteStatement<5> insertModuleExportedImportWithVersionStatement{
- "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, "
- "majorVersion, minorVersion) VALUES (?1, ?2, ?3, ?4, ?5)",
- database};
- WriteStatement<1> deleteModuleExportedImportStatement{
- "DELETE FROM moduleExportedImports WHERE moduleExportedImportId=?1", database};
- mutable ReadStatement<3, 3> selectModuleExportedImportsForModuleIdStatement{
- "WITH RECURSIVE "
- " imports(moduleId, majorVersion, minorVersion, moduleExportedImportId) AS ( "
- " SELECT exportedModuleId, "
- " iif(isAutoVersion=1, ?2, majorVersion), "
- " iif(isAutoVersion=1, ?3, minorVersion), "
- " moduleExportedImportId "
- " FROM moduleExportedImports WHERE moduleId=?1 "
- " UNION ALL "
- " SELECT exportedModuleId, "
- " iif(mei.isAutoVersion=1, i.majorVersion, mei.majorVersion), "
- " iif(mei.isAutoVersion=1, i.minorVersion, mei.minorVersion), "
- " mei.moduleExportedImportId "
- " FROM moduleExportedImports AS mei JOIN imports AS i USING(moduleId)) "
- "SELECT DISTINCT moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) "
- "FROM imports",
- database};
- mutable ReadStatement<1, 1> selectPropertyDeclarationIdsForTypeStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeChain(typeId) AS ("
- " VALUES(?1)"
- " UNION ALL "
- " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain "
- " USING(typeId))"
- "SELECT propertyDeclarationId FROM typeChain JOIN propertyDeclarations "
- " USING(typeId) ORDER BY propertyDeclarationId",
- database};
- mutable ReadStatement<1, 1> selectLocalPropertyDeclarationIdsForTypeStatement{
- "SELECT propertyDeclarationId "
- "FROM propertyDeclarations "
- "WHERE typeId=? "
- "ORDER BY propertyDeclarationId",
- database};
- mutable ReadStatement<1, 2> selectPropertyDeclarationIdForTypeAndPropertyNameStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeChain(typeId, level) AS ("
- " VALUES(?1, 0)"
- " UNION ALL "
- " SELECT prototypeId, typeChain.level + 1 FROM all_prototype_and_extension JOIN "
- " typeChain USING(typeId))"
- "SELECT propertyDeclarationId FROM typeChain JOIN propertyDeclarations "
- " USING(typeId) WHERE name=?2 ORDER BY level LIMIT 1",
- database};
- mutable ReadStatement<1, 2> selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement{
- "SELECT propertyDeclarationId "
- "FROM propertyDeclarations "
- "WHERE typeId=?1 AND name=?2 LIMIT 1",
- database};
- mutable ReadStatement<4, 1> selectPropertyDeclarationForPropertyDeclarationIdStatement{
- "SELECT typeId, name, propertyTraits, propertyTypeId "
- "FROM propertyDeclarations "
- "WHERE propertyDeclarationId=?1 LIMIT 1",
- database};
- mutable ReadStatement<1, 1> selectSignalDeclarationNamesForTypeStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeChain(typeId) AS ("
- " VALUES(?1)"
- " UNION ALL "
- " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain "
- " USING(typeId)) "
- "SELECT name FROM typeChain JOIN signalDeclarations "
- " USING(typeId) ORDER BY name",
- database};
- mutable ReadStatement<1, 1> selectFuncionDeclarationNamesForTypeStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeChain(typeId) AS ("
- " VALUES(?1)"
- " UNION ALL "
- " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain "
- " USING(typeId))"
- "SELECT name FROM typeChain JOIN functionDeclarations "
- " USING(typeId) ORDER BY name",
- database};
- mutable ReadStatement<2> selectTypesWithDefaultPropertyStatement{
- "SELECT typeId, defaultPropertyId FROM types ORDER BY typeId", database};
- WriteStatement<2> updateDefaultPropertyIdStatement{
- "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database};
- WriteStatement<1> updateDefaultPropertyIdToNullStatement{
- "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database};
- mutable ReadStatement<4, 1> selectInfoTypeByTypeIdStatement{
- "SELECT defaultPropertyId, sourceId, traits, annotationTraits FROM types WHERE typeId=?",
- database};
- mutable ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " prototypes(typeId, level) AS ("
- " SELECT prototypeId, 0 FROM all_prototype_and_extension WHERE typeId=?"
- " UNION ALL "
- " SELECT prototypeId, p.level+1 FROM all_prototype_and_extension JOIN "
- " prototypes AS p USING(typeId)) "
- "SELECT typeId FROM prototypes ORDER BY level",
- database};
- mutable ReadStatement<1, 1> selectPrototypeAndSelfIdsForTypeIdInOrderStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeChain(typeId, level) AS ("
- " VALUES(?1, 0)"
- " UNION ALL "
- " SELECT prototypeId, tc.level+1 FROM all_prototype_and_extension JOIN "
- " typeChain AS tc USING(typeId)) "
- "SELECT typeId FROM typeChain ORDER BY level",
- database};
- mutable ReadStatement<1, 1> selectPrototypeIdsStatement{
- "WITH RECURSIVE "
- " all_prototype_and_extension(typeId, prototypeId) AS ("
- " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL"
- " UNION ALL "
- " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL),"
- " typeSelection(typeId) AS ("
- " SELECT prototypeId FROM all_prototype_and_extension WHERE typeId=?1 "
- " UNION ALL "
- " SELECT prototypeId FROM all_prototype_and_extension JOIN typeSelection "
- " USING(typeId))"
- "SELECT typeId FROM typeSelection",
- database};
- WriteStatement<2> upsertPropertyEditorPathIdStatement{
- "INSERT INTO propertyEditorPaths(typeId, pathSourceId) VALUES(?1, ?2) ON CONFLICT DO "
- "UPDATE SET pathSourceId=excluded.pathSourceId WHERE pathSourceId IS NOT "
- "excluded.pathSourceId",
- database};
- mutable ReadStatement<1, 1> selectPropertyEditorPathIdStatement{
- "SELECT pathSourceId FROM propertyEditorPaths WHERE typeId=?", database};
- mutable ReadStatement<3, 1> selectPropertyEditorPathsForForSourceIdsStatement{
- "SELECT typeId, pathSourceId, directoryId "
- "FROM propertyEditorPaths "
- "WHERE directoryId IN carray(?1) "
- "ORDER BY typeId",
- database};
- WriteStatement<3> insertPropertyEditorPathStatement{
- "INSERT INTO propertyEditorPaths(typeId, pathSourceId, directoryId) VALUES (?1, ?2, ?3)",
- database};
- WriteStatement<3> updatePropertyEditorPathsStatement{"UPDATE propertyEditorPaths "
- "SET pathSourceId=?2, directoryId=?3 "
- "WHERE typeId=?1",
- database};
- WriteStatement<1> deletePropertyEditorPathStatement{
- "DELETE FROM propertyEditorPaths WHERE typeId=?1", database};
- mutable ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{
- "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE "
- "sourceId IN carray(?1) ORDER BY typeId",
- database};
- WriteStatement<5> insertTypeAnnotationStatement{
- "INSERT INTO typeAnnotations(typeId, sourceId, iconPath, itemLibrary, hints) VALUES(?1, "
- "?2, ?3, ?4, ?5)",
- database};
- WriteStatement<4> updateTypeAnnotationStatement{
- "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database};
- WriteStatement<1> deleteTypeAnnotationStatement{"DELETE FROM typeAnnotations WHERE typeId=?1",
- database};
- mutable ReadStatement<1, 1> selectTypeIconPathStatement{
- "SELECT iconPath FROM typeAnnotations WHERE typeId=?1", database};
- mutable ReadStatement<2, 1> selectTypeHintsStatement{
- "SELECT hints.key, hints.value "
- "FROM typeAnnotations, json_each(typeAnnotations.hints) AS hints "
- "WHERE typeId=?1",
- database};
- mutable ReadStatement<9> selectItemLibraryEntriesStatement{
- "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', "
- " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', "
- " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
- "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i",
- database};
- mutable ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{
- "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', "
- " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', "
- " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
- "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i "
- "WHERE typeId=?1",
- database};
- mutable ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{
- "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', "
- " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', "
- " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' "
- "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i "
- "WHERE typeId IN (SELECT DISTINCT typeId "
- " FROM documentImports AS di JOIN exportedTypeNames USING(moduleId) "
- " WHERE di.sourceId=?)",
- database};
- mutable ReadStatement<3, 1> selectItemLibraryPropertiesStatement{
- "SELECT p.value->>0, p.value->>1, p.value->>2 FROM json_each(?1) AS p", database};
- mutable ReadStatement<1, 1> selectItemLibraryExtraFilePathsStatement{
- "SELECT p.value FROM json_each(?1) AS p", database};
- mutable ReadStatement<1, 1> selectTypeIdsByModuleIdStatement{
- "SELECT DISTINCT typeId FROM exportedTypeNames WHERE moduleId=?", database};
- mutable ReadStatement<1, 1> selectHeirTypeIdsStatement{
- "WITH RECURSIVE "
- " typeSelection(typeId) AS ("
- " SELECT typeId FROM types WHERE prototypeId=?1 OR extensionId=?1"
- " UNION ALL "
- " SELECT t.typeId "
- " FROM types AS t JOIN typeSelection AS ts "
- " WHERE prototypeId=ts.typeId OR extensionId=ts.typeId)"
- "SELECT typeId FROM typeSelection",
- database};
+ std::unique_ptr<Statements> s;
};
-extern template class ProjectStorage<Sqlite::Database>;
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
index efe9bc58f5..a5dc60c4fa 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
@@ -3,87 +3,186 @@
#include "projectstorageexceptions.h"
+#include <tracing/qmldesignertracing.h>
+
namespace QmlDesigner {
+using namespace NanotraceHR::Literals;
+using NanotraceHR::keyValue;
+
+namespace {
+auto &category()
+{
+ return ProjectStorageTracing::projectStorageCategory();
+}
+} // namespace
+
+NoSourcePathForInvalidSourceId::NoSourcePathForInvalidSourceId()
+{
+ category().threadEvent("NoSourcePathForInvalidSourceId"_t);
+}
+
const char *NoSourcePathForInvalidSourceId::what() const noexcept
{
return "You cannot get a file path for an invalid file path id!";
}
+NoSourceContextPathForInvalidSourceContextId::NoSourceContextPathForInvalidSourceContextId()
+{
+ category().threadEvent("NoSourceContextPathForInvalidSourceContextId"_t);
+}
+
const char *NoSourceContextPathForInvalidSourceContextId::what() const noexcept
{
return "You cannot get a directory path for an invalid directory path id!";
}
+SourceContextIdDoesNotExists::SourceContextIdDoesNotExists()
+{
+ category().threadEvent("SourceContextIdDoesNotExists"_t);
+}
+
const char *SourceContextIdDoesNotExists::what() const noexcept
{
return "The source context id does not exist in the database!";
}
+SourceIdDoesNotExists::SourceIdDoesNotExists()
+{
+ category().threadEvent("SourceIdDoesNotExists"_t);
+}
+
const char *SourceIdDoesNotExists::what() const noexcept
{
return "The source id does not exist in the database!";
}
+TypeHasInvalidSourceId::TypeHasInvalidSourceId()
+{
+ category().threadEvent("TypeHasInvalidSourceId"_t);
+}
+
const char *TypeHasInvalidSourceId::what() const noexcept
{
return "The source id is invalid!";
}
+ModuleDoesNotExists::ModuleDoesNotExists()
+{
+ category().threadEvent("ModuleDoesNotExists"_t);
+}
+
const char *ModuleDoesNotExists::what() const noexcept
{
return "The module does not exist!";
}
+ModuleAlreadyExists::ModuleAlreadyExists()
+{
+ category().threadEvent("ModuleAlreadyExists"_t);
+}
+
const char *ModuleAlreadyExists::what() const noexcept
{
return "The module does already exist!";
}
-TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view errorMessage)
- : ProjectStorageErrorWithMessage{"TypeNameDoesNotExists"sv, errorMessage}
-{}
+TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId)
+ : ProjectStorageErrorWithMessage{
+ "TypeNameDoesNotExists"sv,
+ Utils::SmallString::join(
+ {"type: ", typeName, ", source id: ", Utils::SmallString::number(sourceId.internalId())})}
+{
+ category().threadEvent("TypeNameDoesNotExists"_t,
+ keyValue("type name", typeName),
+ keyValue("source id", sourceId));
+}
+
+PropertyNameDoesNotExists::PropertyNameDoesNotExists()
+{
+ category().threadEvent("PropertyNameDoesNotExists"_t);
+}
const char *PropertyNameDoesNotExists::what() const noexcept
{
return "The property name does not exist!";
}
+PrototypeChainCycle::PrototypeChainCycle()
+{
+ category().threadEvent("PrototypeChainCycle"_t);
+}
+
const char *PrototypeChainCycle::what() const noexcept
{
return "There is a prototype chain cycle!";
}
+AliasChainCycle::AliasChainCycle()
+{
+ category().threadEvent("AliasChainCycle"_t);
+}
+
const char *AliasChainCycle::what() const noexcept
{
return "There is a prototype chain cycle!";
}
+CannotParseQmlTypesFile::CannotParseQmlTypesFile()
+{
+ category().threadEvent("CannotParseQmlTypesFile"_t);
+}
+
const char *CannotParseQmlTypesFile::what() const noexcept
{
return "Cannot parse qml types file!";
}
+CannotParseQmlDocumentFile::CannotParseQmlDocumentFile()
+{
+ category().threadEvent("CannotParseQmlDocumentFile"_t);
+}
+
const char *CannotParseQmlDocumentFile::what() const noexcept
{
return "Cannot parse qml types file!";
}
+ProjectDataHasInvalidProjectSourceId::ProjectDataHasInvalidProjectSourceId()
+{
+ category().threadEvent("ProjectDataHasInvalidProjectSourceId"_t);
+}
+
const char *ProjectDataHasInvalidProjectSourceId::what() const noexcept
{
return "The project source id is invalid!";
}
+ProjectDataHasInvalidSourceId::ProjectDataHasInvalidSourceId()
+{
+ category().threadEvent("ProjectDataHasInvalidSourceId"_t);
+}
+
const char *ProjectDataHasInvalidSourceId::what() const noexcept
{
return "The source id is invalid!";
}
+ProjectDataHasInvalidModuleId::ProjectDataHasInvalidModuleId()
+{
+ category().threadEvent("ProjectDataHasInvalidModuleId"_t);
+}
+
const char *ProjectDataHasInvalidModuleId::what() const noexcept
{
return "The module id is invalid!";
}
+FileStatusHasInvalidSourceId::FileStatusHasInvalidSourceId()
+{
+ category().threadEvent("FileStatusHasInvalidSourceId"_t);
+}
+
const char *FileStatusHasInvalidSourceId::what() const noexcept
{
return "The source id in file status is invalid!";
@@ -110,7 +209,14 @@ const char *ProjectStorageErrorWithMessage::what() const noexcept
ExportedTypeCannotBeInserted::ExportedTypeCannotBeInserted(std::string_view errorMessage)
: ProjectStorageErrorWithMessage{"ExportedTypeCannotBeInserted"sv, errorMessage}
-{}
+{
+ category().threadEvent("ExportedTypeCannotBeInserted"_t, keyValue("error message", errorMessage));
+}
+
+TypeAnnotationHasInvalidSourceId::TypeAnnotationHasInvalidSourceId()
+{
+ category().threadEvent("TypeAnnotationHasInvalidSourceId"_t);
+}
const char *TypeAnnotationHasInvalidSourceId::what() const noexcept
{
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
index 412dd4a9ff..d85f1f7f9e 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
@@ -5,6 +5,8 @@
#include "../include/qmldesignercorelib_global.h"
+#include "projectstorageids.h"
+
#include <exception>
namespace QmlDesigner {
@@ -13,15 +15,19 @@ using namespace std::literals::string_view_literals;
class QMLDESIGNERCORE_EXPORT ProjectStorageError : public std::exception
{
+protected:
+ ProjectStorageError() = default;
+
public:
const char *what() const noexcept override;
};
class ProjectStorageErrorWithMessage : public ProjectStorageError
{
-public:
+protected:
ProjectStorageErrorWithMessage(std::string_view error, std::string_view errorMessage);
+public:
const char *what() const noexcept override;
public:
@@ -31,42 +37,49 @@ public:
class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : public ProjectStorageError
{
public:
+ NoSourcePathForInvalidSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : public ProjectStorageError
{
public:
+ NoSourceContextPathForInvalidSourceContextId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : public ProjectStorageError
{
public:
+ SourceContextIdDoesNotExists();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : public ProjectStorageError
{
public:
+ SourceIdDoesNotExists();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : public ProjectStorageError
{
public:
+ TypeHasInvalidSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : public ProjectStorageError
{
public:
+ ModuleDoesNotExists();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : public ProjectStorageError
{
public:
+ ModuleAlreadyExists();
const char *what() const noexcept override;
};
@@ -79,66 +92,76 @@ public:
class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : public ProjectStorageErrorWithMessage
{
public:
- TypeNameDoesNotExists(std::string_view errorMessage);
+ TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId = SourceId{});
};
class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : public ProjectStorageError
{
public:
+ PropertyNameDoesNotExists();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : public ProjectStorageError
{
public:
+ PrototypeChainCycle();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT AliasChainCycle : public ProjectStorageError
{
public:
+ AliasChainCycle();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : public ProjectStorageError
{
public:
+ CannotParseQmlTypesFile();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : public ProjectStorageError
{
public:
+ CannotParseQmlDocumentFile();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : public ProjectStorageError
{
public:
+ ProjectDataHasInvalidProjectSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : public ProjectStorageError
{
public:
+ ProjectDataHasInvalidSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : public ProjectStorageError
{
public:
+ ProjectDataHasInvalidModuleId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : public ProjectStorageError
{
public:
+ FileStatusHasInvalidSourceId();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT TypeAnnotationHasInvalidSourceId : public ProjectStorageError
{
public:
+ TypeAnnotationHasInvalidSourceId();
const char *what() const noexcept override;
};
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h
index b33c609509..cbb7d4265a 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h
@@ -11,7 +11,6 @@ namespace QmlDesigner {
class ProjectStorageInterface;
class SourcePathCacheInterface;
-template<typename Database>
class ProjectStorage;
template<typename Type>
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h
index 427c0ff8d6..9f0c134ed3 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h
@@ -24,6 +24,20 @@ constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumerat
enum class FlagIs : unsigned int { False, Set, True };
+template<typename String>
+void convertToString(String &string, const FlagIs &flagIs)
+{
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+
+ if (flagIs == FlagIs::False)
+ convertToString(string, false);
+ else if (flagIs == FlagIs::True)
+ convertToString(string, true);
+ else
+ convertToString(string, "is set");
+}
+
} // namespace QmlDesigner
namespace QmlDesigner::Storage {
@@ -46,6 +60,18 @@ constexpr bool operator&(PropertyDeclarationTraits first, PropertyDeclarationTra
return static_cast<int>(first) & static_cast<int>(second);
}
+template<typename String>
+void convertToString(String &string, const PropertyDeclarationTraits &traits)
+{
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("is read only", traits & PropertyDeclarationTraits::IsReadOnly),
+ keyValue("is pointer", traits & PropertyDeclarationTraits::IsPointer),
+ keyValue("is list", traits & PropertyDeclarationTraits::IsList));
+
+ convertToString(string, dict);
+}
+
enum class TypeTraitsKind : unsigned int {
None,
Reference,
@@ -53,6 +79,25 @@ enum class TypeTraitsKind : unsigned int {
Sequence,
};
+template<typename String>
+void convertToString(String &string, const TypeTraitsKind &kind)
+{
+ switch (kind) {
+ case TypeTraitsKind::None:
+ convertToString(string, "None");
+ break;
+ case TypeTraitsKind::Reference:
+ convertToString(string, "Reference");
+ break;
+ case TypeTraitsKind::Value:
+ convertToString(string, "Value");
+ break;
+ case TypeTraitsKind::Sequence:
+ convertToString(string, "Sequence");
+ break;
+ }
+}
+
struct TypeTraits
{
constexpr TypeTraits()
@@ -100,6 +145,35 @@ struct TypeTraits
return first.type == second.type && first.annotation == second.annotation;
}
+ template<typename String>
+ friend void convertToString(String &string, const TypeTraits &typeTraits)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(
+ keyValue("kind", typeTraits.kind),
+ keyValue("is enum", typeTraits.isEnum),
+ keyValue("is file component", typeTraits.isFileComponent),
+ keyValue("is project component", typeTraits.isProjectComponent),
+ keyValue("is in project module", typeTraits.isInProjectModule),
+ keyValue("uses custom parser", typeTraits.usesCustomParser),
+ keyValue("can be container", typeTraits.canBeContainer),
+ keyValue("force clip", typeTraits.forceClip),
+ keyValue("does layout children", typeTraits.doesLayoutChildren),
+ keyValue("can be dropped in form editor", typeTraits.canBeDroppedInFormEditor),
+ keyValue("can be dropped in navigator", typeTraits.canBeDroppedInNavigator),
+ keyValue("can be dropped in view 3D", typeTraits.canBeDroppedInView3D),
+ keyValue("is movable", typeTraits.isMovable),
+ keyValue("is resizable", typeTraits.isResizable),
+ keyValue("has form editor item", typeTraits.hasFormEditorItem),
+ keyValue("is stacked container", typeTraits.isStackedContainer),
+ keyValue("takes over rendering of children", typeTraits.takesOverRenderingOfChildren),
+ keyValue("visible in navigator", typeTraits.visibleInNavigator),
+ keyValue("visible in library", typeTraits.visibleInLibrary));
+
+ convertToString(string, dict);
+ }
+
union {
struct
{
@@ -202,10 +276,22 @@ public:
explicit operator bool() const { return major && minor; }
+ template<typename String>
+ friend void convertToString(String &string, const Version &version)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("major version", version.major.value),
+ keyValue("minor version", version.minor.value));
+
+ convertToString(string, dict);
+ }
+
public:
VersionNumber major;
VersionNumber minor;
};
+
} // namespace QmlDesigner::Storage
namespace QmlDesigner::Storage::Info {
@@ -217,6 +303,17 @@ struct TypeHint
, expression{expression}
{}
+ template<typename String>
+ friend void convertToString(String &string, const TypeHint &typeHint)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", typeHint.name),
+ keyValue("expression", typeHint.expression));
+
+ convertToString(string, dict);
+ }
+
Utils::SmallString name;
Utils::PathString expression;
};
@@ -231,6 +328,18 @@ struct ItemLibraryProperty
, value{value}
{}
+ template<typename String>
+ friend void convertToString(String &string, const ItemLibraryProperty &property)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", property.name),
+ keyValue("type", property.type),
+ keyValue("value", property.value));
+
+ convertToString(string, dict);
+ }
+
Utils::SmallString name;
Utils::SmallString type;
Sqlite::Value value;
@@ -274,6 +383,24 @@ struct ItemLibraryEntry
, properties{std::move(properties)}
{}
+ template<typename String>
+ friend void convertToString(String &string, const ItemLibraryEntry &entry)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", entry.typeId),
+ keyValue("name", entry.name),
+ keyValue("icon path", entry.iconPath),
+ keyValue("category", entry.category),
+ keyValue("import", entry.import),
+ keyValue("tool tip", entry.toolTip),
+ keyValue("template path", entry.templatePath),
+ keyValue("properties", entry.properties),
+ keyValue("extra file paths", entry.extraFilePaths));
+
+ convertToString(string, dict);
+ }
+
TypeId typeId;
Utils::SmallString name;
Utils::PathString iconPath;
@@ -321,6 +448,18 @@ public:
< std::tie(second.moduleId, second.name, second.version);
}
+ template<typename String>
+ friend void convertToString(String &string, const ExportedTypeName &exportedTypeName)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", exportedTypeName.name),
+ keyValue("version", exportedTypeName.version),
+ keyValue("module id", exportedTypeName.moduleId));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
Storage::Version version;
@@ -342,6 +481,19 @@ public:
, propertyTypeId{propertyTypeId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type id", propertyDeclaration.typeId),
+ keyValue("name", propertyDeclaration.name),
+ keyValue("traits", propertyDeclaration.traits),
+ keyValue("property type id", propertyDeclaration.propertyTypeId));
+
+ convertToString(string, dict);
+ }
+
TypeId typeId;
::Utils::SmallString name;
PropertyDeclarationTraits traits;
@@ -351,22 +503,26 @@ public:
class Type
{
public:
- Type(PropertyDeclarationId defaultPropertyId,
- SourceId sourceId,
- long long typeTraits,
- long long typeAnnotationTraits)
- : defaultPropertyId{defaultPropertyId}
- , sourceId{sourceId}
+ Type(SourceId sourceId, long long typeTraits, long long typeAnnotationTraits)
+ : sourceId{sourceId}
, traits{typeTraits, typeAnnotationTraits}
{}
- Type(PropertyDeclarationId defaultPropertyId, SourceId sourceId, TypeTraits traits)
- : defaultPropertyId{defaultPropertyId}
- , sourceId{sourceId}
+ Type(SourceId sourceId, TypeTraits traits)
+ : sourceId{sourceId}
, traits{traits}
{}
- PropertyDeclarationId defaultPropertyId;
+ template<typename String>
+ friend void convertToString(String &string, const Type &type)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("source id", type.sourceId), keyValue("traits", type.traits));
+
+ convertToString(string, dict);
+ }
+
SourceId sourceId;
TypeTraits traits;
};
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h
index 266c6ee7ca..971e635517 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h
@@ -32,6 +32,7 @@ public:
virtual void removeObserver(ProjectStorageObserver *observer) = 0;
virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0;
+ virtual Utils::SmallString moduleName(ModuleId moduleId) const = 0;
virtual std::optional<Storage::Info::PropertyDeclaration>
propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0;
virtual TypeId typeId(ModuleId moduleId,
@@ -54,7 +55,10 @@ public:
virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId,
::Utils::SmallStringView propertyName) const
= 0;
+ virtual PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const = 0;
virtual std::optional<Storage::Info::Type> type(TypeId typeId) const = 0;
+ virtual SmallSourceIds<4> typeAnnotationSourceIds(SourceId directoryId) const = 0;
+ virtual SmallSourceIds<64> typeAnnotationDirectorySourceIds() const = 0;
virtual Utils::PathString typeIconPath(TypeId typeId) const = 0;
virtual Storage::Info::TypeHints typeHints(TypeId typeId) const = 0;
virtual Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const = 0;
@@ -64,9 +68,9 @@ public:
virtual std::vector<::Utils::SmallString> functionDeclarationNames(TypeId typeId) const = 0;
virtual std::optional<::Utils::SmallString>
propertyName(PropertyDeclarationId propertyDeclarationId) const = 0;
- virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0;
- virtual TypeIds prototypeIds(TypeId type) const = 0;
- virtual TypeIds heirIds(TypeId typeId) const = 0;
+ virtual SmallTypeIds<16> prototypeAndSelfIds(TypeId type) const = 0;
+ virtual SmallTypeIds<16> prototypeIds(TypeId type) const = 0;
+ virtual SmallTypeIds<64> heirIds(TypeId typeId) const = 0;
virtual bool isBasedOn(TypeId, TypeId) const = 0;
virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0;
virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0;
@@ -80,7 +84,7 @@ public:
virtual std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const = 0;
virtual SourceId propertyEditorPathId(TypeId typeId) const = 0;
- virtual const Storage::Info::CommonTypeCache<ProjectStorageInterface> &commonTypeCache() const = 0;
+ virtual const Storage::Info::CommonTypeCache<ProjectStorageType> &commonTypeCache() const = 0;
template<const char *moduleName, const char *typeName>
TypeId commonTypeId() const
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h
index a9185d91e8..04f11096bd 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h
@@ -12,6 +12,28 @@ namespace QmlDesigner {
enum class SourceType : int { Qml, QmlUi, QmlTypes, QmlDir, Directory };
+template<typename String>
+void convertToString(String &string, SourceType sourceType)
+{
+ switch (sourceType) {
+ case SourceType::Qml:
+ convertToString(string, "Qml");
+ break;
+ case SourceType::QmlUi:
+ convertToString(string, "QmlUi");
+ break;
+ case SourceType::QmlTypes:
+ convertToString(string, "QmlTypes");
+ break;
+ case SourceType::QmlDir:
+ convertToString(string, "QmlDir");
+ break;
+ case SourceType::Directory:
+ convertToString(string, "Directory");
+ break;
+ }
+}
+
class ProjectChunkId
{
public:
@@ -46,6 +68,17 @@ public:
friend bool operator<(ProjectChunkId first, ProjectPartId second) { return first.id < second; }
friend bool operator<(ProjectPartId first, ProjectChunkId second) { return first < second.id; }
+
+ template<typename String>
+ friend void convertToString(String &string, const ProjectChunkId &id)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("project part id", id.id),
+ keyValue("source type", id.sourceType));
+
+ convertToString(string, dict);
+ }
};
using ProjectChunkIds = std::vector<ProjectChunkId>;
@@ -67,6 +100,16 @@ public:
return first.id == second.id && first.sourceIds == second.sourceIds;
}
+ template<typename String>
+ friend void convertToString(String &string, const IdPaths &idPaths)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("id", idPaths.id), keyValue("source ids", idPaths.sourceIds));
+
+ convertToString(string, dict);
+ }
+
public:
ProjectChunkId id;
SourceIds sourceIds;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
index 18c3931249..8d810d94bd 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h
@@ -7,6 +7,7 @@
#include "projectstorageids.h"
#include "projectstorageinfotypes.h"
+#include <nanotrace/nanotracehr.h>
#include <sqlite/sqlitevalue.h>
#include <utils/smallstring.h>
@@ -45,6 +46,17 @@ public:
< std::tie(second.sourceId, second.moduleId, second.version);
}
+ template<typename String>
+ friend void convertToString(String &string, const Import &import)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("module id", import.moduleId),
+ keyValue("source id", import.sourceId),
+ keyValue("version", import.version));
+ convertToString(string, dict);
+ }
+
public:
Storage::Version version;
ModuleId moduleId;
@@ -57,11 +69,50 @@ namespace Synchronization {
enum class TypeNameKind { Exported = 1, QualifiedExported = 2 };
+template<typename String>
+void convertToString(String &string, const TypeNameKind &kind)
+{
+ switch (kind) {
+ case TypeNameKind::Exported:
+ convertToString(string, "Exported");
+ break;
+ case TypeNameKind::QualifiedExported:
+ convertToString(string, "QualifiedExported");
+ break;
+ }
+}
+
enum class FileType : char { QmlTypes, QmlDocument };
+template<typename String>
+void convertToString(String &string, const FileType &type)
+{
+ switch (type) {
+ case FileType::QmlTypes:
+ convertToString(string, "QmlTypes");
+ break;
+ case FileType::QmlDocument:
+ convertToString(string, "QmlDocument");
+ break;
+ }
+}
+
enum class IsQualified : int { No, Yes };
-inline int operator-(IsQualified first, IsQualified second)
+template<typename String>
+void convertToString(String &string, const IsQualified &isQualified)
+{
+ switch (isQualified) {
+ case IsQualified::No:
+ convertToString(string, "No");
+ break;
+ case IsQualified::Yes:
+ convertToString(string, "Yes");
+ break;
+ }
+}
+
+inline int operator-(IsQualified first, const IsQualified &second)
{
return static_cast<int>(first) - static_cast<int>(second);
}
@@ -78,6 +129,25 @@ enum class ImportKind : char {
ModuleExportedModuleDependency
};
+template<typename String>
+void convertToString(String &string, const ImportKind &kind)
+{
+ switch (kind) {
+ case ImportKind::Import:
+ convertToString(string, "Import");
+ break;
+ case ImportKind::ModuleDependency:
+ convertToString(string, "ModuleDependency");
+ break;
+ case ImportKind::ModuleExportedImport:
+ convertToString(string, "ModuleExportedImport");
+ break;
+ case ImportKind::ModuleExportedModuleDependency:
+ convertToString(string, "ModuleExportedModuleDependency");
+ break;
+ }
+}
+
class ImportView
{
public:
@@ -97,6 +167,19 @@ public:
&& first.version == second.version;
}
+ template<typename String>
+ friend void convertToString(String &string, const ImportView &import)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("import id", import.importId),
+ keyValue("source id", import.sourceId),
+ keyValue("module id", import.moduleId),
+ keyValue("version", import.version));
+
+ convertToString(string, dict);
+ }
+
public:
ImportId importId;
SourceId sourceId;
@@ -106,6 +189,19 @@ public:
enum class IsAutoVersion : char { No, Yes };
+template<typename String>
+void convertToString(String &string, const IsAutoVersion &isAutoVersion)
+{
+ switch (isAutoVersion) {
+ case IsAutoVersion::No:
+ convertToString(string, "No");
+ break;
+ case IsAutoVersion::Yes:
+ convertToString(string, "Yes");
+ break;
+ }
+}
+
constexpr bool operator<(IsAutoVersion first, IsAutoVersion second)
{
return to_underlying(first) < to_underlying(second);
@@ -137,6 +233,19 @@ public:
< std::tie(second.moduleId, second.exportedModuleId, second.isAutoVersion, second.version);
}
+ template<typename String>
+ friend void convertToString(String &string, const ModuleExportedImport &import)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("module id", import.moduleId),
+ keyValue("exported module id", import.exportedModuleId),
+ keyValue("version", import.version),
+ keyValue("is auto version", import.isAutoVersion));
+
+ convertToString(string, dict);
+ }
+
public:
Storage::Version version;
ModuleId moduleId;
@@ -171,6 +280,20 @@ public:
&& first.version == second.version && first.isAutoVersion == second.isAutoVersion;
}
+ template<typename String>
+ friend void convertToString(String &string, const ModuleExportedImportView &import)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("module exported import id", import.moduleExportedImportId),
+ keyValue("module id", import.moduleId),
+ keyValue("exported module id", import.exportedModuleId),
+ keyValue("version", import.version),
+ keyValue("is auto version", import.isAutoVersion));
+
+ convertToString(string, dict);
+ }
+
public:
ModuleExportedImportId moduleExportedImportId;
Storage::Version version;
@@ -192,6 +315,16 @@ public:
return first.name == second.name;
}
+ template<typename String>
+ friend void convertToString(String &string, const ImportedType &importedType)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", importedType.name));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString name;
};
@@ -210,6 +343,17 @@ public:
return first.name == second.name && first.import == second.import;
}
+ template<typename String>
+ friend void convertToString(String &string, const QualifiedImportedType &importedType)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", importedType.name),
+ keyValue("import", importedType.import));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString name;
Import import;
@@ -264,6 +408,19 @@ public:
< std::tie(second.moduleId, second.name, second.version);
}
+ template<typename String>
+ friend void convertToString(String &string, const ExportedType &exportedType)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", exportedType.name),
+ keyValue("module id", exportedType.moduleId),
+ keyValue("type id", exportedType.typeId),
+ keyValue("version", exportedType.version));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
Storage::Version version;
@@ -295,6 +452,20 @@ public:
, exportedTypeNameId{exportedTypeNameId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const ExportedTypeView &exportedType)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", exportedType.name),
+ keyValue("module id", exportedType.moduleId),
+ keyValue("type id", exportedType.typeId),
+ keyValue("version", exportedType.version),
+ keyValue("version", exportedType.exportedTypeNameId));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
Storage::Version version;
@@ -305,6 +476,43 @@ public:
using ImportedTypeName = std::variant<ImportedType, QualifiedImportedType>;
+template<typename String>
+void convertToString(String &string, const ImportedTypeName &typeName)
+{
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+
+ struct Dispatcher
+ {
+ static const QmlDesigner::Storage::Import &nullImport()
+ {
+ static QmlDesigner::Storage::Import import;
+
+ return import;
+ }
+
+ void operator()(const QmlDesigner::Storage::Synchronization::ImportedType &importedType) const
+ {
+ auto dict = dictonary(keyValue("name", importedType.name));
+
+ convertToString(string, dict);
+ }
+
+ void operator()(
+ const QmlDesigner::Storage::Synchronization::QualifiedImportedType &qualifiedImportedType) const
+ {
+ auto dict = dictonary(keyValue("name", qualifiedImportedType.name),
+ keyValue("import", qualifiedImportedType.import));
+
+ convertToString(string, dict);
+ }
+
+ String &string;
+ };
+
+ std::visit(Dispatcher{string}, typeName);
+}
+
class EnumeratorDeclaration
{
public:
@@ -325,6 +533,18 @@ public:
&& first.hasValue == second.hasValue;
}
+ template<typename String>
+ friend void convertToString(String &string, const EnumeratorDeclaration &enumeratorDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", enumeratorDeclaration.name),
+ keyValue("value", enumeratorDeclaration.value),
+ keyValue("has value", enumeratorDeclaration.hasValue));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
long long value = 0;
@@ -349,6 +569,18 @@ public:
&& first.enumeratorDeclarations == second.enumeratorDeclarations;
}
+ template<typename String>
+ friend void convertToString(String &string, const EnumerationDeclaration &enumerationDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", enumerationDeclaration.name),
+ keyValue("enumerator declarations",
+ enumerationDeclaration.enumeratorDeclarations));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString name;
EnumeratorDeclarations enumeratorDeclarations;
@@ -368,6 +600,20 @@ public:
, id{id}
{}
+ template<typename String>
+ friend void convertToString(String &string,
+ const EnumerationDeclarationView &enumerationDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", enumerationDeclaration.name),
+ keyValue("enumerator declarations",
+ enumerationDeclaration.enumeratorDeclarations),
+ keyValue("id", enumerationDeclaration.id));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
::Utils::SmallStringView enumeratorDeclarations;
@@ -392,6 +638,18 @@ public:
&& first.traits == second.traits;
}
+ template<typename String>
+ friend void convertToString(String &string, const ParameterDeclaration &parameterDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", parameterDeclaration.name),
+ keyValue("type name", parameterDeclaration.typeName),
+ keyValue("traits", parameterDeclaration.traits));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
TypeNameString typeName;
@@ -418,6 +676,17 @@ public:
return first.name == second.name && first.parameters == second.parameters;
}
+ template<typename String>
+ friend void convertToString(String &string, const SignalDeclaration &signalDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", signalDeclaration.name),
+ keyValue("parameters", signalDeclaration.parameters));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
ParameterDeclarations parameters;
@@ -437,6 +706,18 @@ public:
, id{id}
{}
+ template<typename String>
+ friend void convertToString(String &string, const SignalDeclarationView &signalDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", signalDeclaration.name),
+ keyValue("signature", signalDeclaration.signature),
+ keyValue("id", signalDeclaration.id));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
::Utils::SmallStringView signature;
@@ -467,6 +748,18 @@ public:
&& first.parameters == second.parameters;
}
+ template<typename String>
+ friend void convertToString(String &string, const FunctionDeclaration &functionDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", functionDeclaration.name),
+ keyValue("return type name", functionDeclaration.returnTypeName),
+ keyValue("parameters", functionDeclaration.parameters));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
TypeNameString returnTypeName;
@@ -489,6 +782,19 @@ public:
, id{id}
{}
+ template<typename String>
+ friend void convertToString(String &string, const FunctionDeclarationView &functionDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", functionDeclaration.name),
+ keyValue("return type name", functionDeclaration.returnTypeName),
+ keyValue("signature", functionDeclaration.signature),
+ keyValue("id", functionDeclaration.id));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
::Utils::SmallStringView returnTypeName;
@@ -498,6 +804,19 @@ public:
enum class PropertyKind { Property, Alias };
+template<typename String>
+void convertToString(String &string, const PropertyKind &kind)
+{
+ switch (kind) {
+ case PropertyKind::Property:
+ convertToString(string, "Property");
+ break;
+ case PropertyKind::Alias:
+ convertToString(string, "Alias");
+ break;
+ }
+}
+
class PropertyDeclaration
{
public:
@@ -567,6 +886,24 @@ public:
&& first.traits == second.traits && first.kind == second.kind;
}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", propertyDeclaration.name),
+ keyValue("type name", propertyDeclaration.typeName),
+ keyValue("alias property name", propertyDeclaration.aliasPropertyName),
+ keyValue("alias property name tail",
+ propertyDeclaration.aliasPropertyNameTail),
+ keyValue("traits", propertyDeclaration.traits),
+ keyValue("type id", propertyDeclaration.typeId),
+ keyValue("property type id", propertyDeclaration.propertyTypeId),
+ keyValue("kind", propertyDeclaration.kind));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallString name;
ImportedTypeName typeName;
@@ -597,6 +934,21 @@ public:
, aliasId{aliasId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyDeclarationView &propertyDeclaration)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("name", propertyDeclaration.name),
+ keyValue("traits", propertyDeclaration.traits),
+ keyValue("type id", propertyDeclaration.typeId),
+ keyValue("type name id", propertyDeclaration.typeNameId),
+ keyValue("id", propertyDeclaration.id),
+ keyValue("alias id", propertyDeclaration.aliasId));
+
+ convertToString(string, dict);
+ }
+
public:
::Utils::SmallStringView name;
PropertyDeclarationTraits traits = {};
@@ -608,6 +960,22 @@ public:
enum class ChangeLevel : char { Full, Minimal, ExcludeExportedTypes };
+template<typename String>
+void convertToString(String &string, const ChangeLevel &changeLevel)
+{
+ switch (changeLevel) {
+ case ChangeLevel::Full:
+ convertToString(string, "Full");
+ break;
+ case ChangeLevel::Minimal:
+ convertToString(string, "Minimal");
+ break;
+ case ChangeLevel::ExcludeExportedTypes:
+ convertToString(string, "ExcludeExportedTypes");
+ break;
+ }
+}
+
class Type
{
public:
@@ -717,6 +1085,27 @@ public:
&& first.sourceId == second.sourceId;
}
+ template<typename String>
+ friend void convertToString(String &string, const Type &type)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type name", type.typeName),
+ keyValue("prototype", type.prototype),
+ keyValue("extension", type.extension),
+ keyValue("exported types", type.exportedTypes),
+ keyValue("property declarations", type.propertyDeclarations),
+ keyValue("function declarations", type.functionDeclarations),
+ keyValue("signal declarations", type.signalDeclarations),
+ keyValue("enumeration declarations", type.enumerationDeclarations),
+ keyValue("traits", type.traits),
+ keyValue("source id", type.sourceId),
+ keyValue("change level", type.changeLevel),
+ keyValue("default property name", type.defaultPropertyName));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString typeName;
::Utils::SmallString defaultPropertyName;
@@ -747,6 +1136,20 @@ public:
, moduleId{moduleId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const PropertyEditorQmlPath &propertyEditorQmlPath)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type name", propertyEditorQmlPath.typeName),
+ keyValue("type id", propertyEditorQmlPath.typeId),
+ keyValue("path id", propertyEditorQmlPath.pathId),
+ keyValue("directory id", propertyEditorQmlPath.directoryId),
+ keyValue("module id", propertyEditorQmlPath.moduleId));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString typeName;
TypeId typeId;
@@ -774,6 +1177,19 @@ public:
&& first.fileType == second.fileType;
}
+ template<typename String>
+ friend void convertToString(String &string, const ProjectData &projectData)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("project source id", projectData.projectSourceId),
+ keyValue("source id", projectData.sourceId),
+ keyValue("module id", projectData.moduleId),
+ keyValue("file type", projectData.fileType));
+
+ convertToString(string, dict);
+ }
+
public:
SourceId projectSourceId;
SourceId sourceId;
@@ -786,10 +1202,13 @@ using ProjectDatas = std::vector<ProjectData>;
class TypeAnnotation
{
public:
- TypeAnnotation(SourceId sourceId)
+ TypeAnnotation(SourceId sourceId, SourceId directorySourceId)
: sourceId{sourceId}
+ , directorySourceId{directorySourceId}
{}
+
TypeAnnotation(SourceId sourceId,
+ SourceId directorySourceId,
Utils::SmallStringView typeName,
ModuleId moduleId,
Utils::SmallStringView iconPath,
@@ -803,8 +1222,26 @@ public:
, sourceId{sourceId}
, moduleId{moduleId}
, traits{traits}
+ , directorySourceId{directorySourceId}
{}
+ template<typename String>
+ friend void convertToString(String &string, const TypeAnnotation &typeAnnotation)
+ {
+ using NanotraceHR::dictonary;
+ using NanotraceHR::keyValue;
+ auto dict = dictonary(keyValue("type name", typeAnnotation.typeName),
+ keyValue("icon path", typeAnnotation.iconPath),
+ keyValue("item library json", typeAnnotation.itemLibraryJson),
+ keyValue("hints json", typeAnnotation.hintsJson),
+ keyValue("type id", typeAnnotation.typeId),
+ keyValue("source id", typeAnnotation.sourceId),
+ keyValue("module id", typeAnnotation.moduleId),
+ keyValue("traits", typeAnnotation.traits));
+
+ convertToString(string, dict);
+ }
+
public:
TypeNameString typeName;
Utils::PathString iconPath;
@@ -814,6 +1251,7 @@ public:
SourceId sourceId;
ModuleId moduleId;
TypeTraits traits;
+ SourceId directorySourceId;
};
using TypeAnnotations = std::vector<TypeAnnotation>;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
index 62fcf310f6..761d6371ef 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
@@ -11,6 +11,9 @@
#include "qmltypesparserinterface.h"
#include "sourcepath.h"
#include "sourcepathcache.h"
+#include "typeannotationreader.h"
+
+#include <tracing/qmldesignertracing.h>
#include <sqlitedatabase.h>
@@ -21,6 +24,26 @@
#include <functional>
namespace QmlDesigner {
+constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory;
+using NanotraceHR::keyValue;
+using Tracer = ProjectStorageTracing::Category::TracerType;
+
+template<typename String>
+void convertToString(String &string, const ProjectStorageUpdater::FileState &state)
+{
+ switch (state) {
+ case ProjectStorageUpdater::FileState::Changed:
+ convertToString(string, "Changed");
+ break;
+ case ProjectStorageUpdater::FileState::NotChanged:
+ convertToString(string, "NotChanged");
+ break;
+ case ProjectStorageUpdater::FileState::NotExists:
+ convertToString(string, "NotExists");
+ break;
+ }
+}
+
namespace {
QStringList filterMultipleEntries(QStringList qmlTypes)
@@ -110,10 +133,15 @@ SourceIds filterNotUpdatedSourceIds(SourceIds updatedSourceIds, SourceIds notUpd
return filteredUpdatedSourceIds;
}
-void addSourceIds(SourceIds &sourceIds, const Storage::Synchronization::ProjectDatas &projectDatas)
+void addSourceIds(SourceIds &sourceIds,
+ const Storage::Synchronization::ProjectDatas &projectDatas,
+ TracerLiteral message,
+ Tracer &tracer)
{
- for (const auto &projectData : projectDatas)
+ for (const auto &projectData : projectDatas) {
+ tracer.tick(message, keyValue("source id", projectData.sourceId));
sourceIds.push_back(projectData.sourceId);
+ }
}
Storage::Version convertVersion(LanguageUtils::ComponentVersion version)
@@ -131,34 +159,71 @@ Storage::Synchronization::IsAutoVersion convertToIsAutoVersion(QmlDirParser::Imp
void addDependencies(Storage::Imports &dependencies,
SourceId sourceId,
const QList<QmlDirParser::Import> &qmldirDependencies,
- ProjectStorageInterface &projectStorage)
+ ProjectStorageInterface &projectStorage,
+ TracerLiteral message,
+ Tracer &tracer)
{
for (const QmlDirParser::Import &qmldirDependency : qmldirDependencies) {
ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module}
+ "-cppnative");
- dependencies.emplace_back(moduleId, Storage::Version{}, sourceId);
+ auto &import = dependencies.emplace_back(moduleId, Storage::Version{}, sourceId);
+ tracer.tick(message, keyValue("import", import));
}
}
+void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &imports,
+ ModuleId moduleId,
+ ModuleId exportedModuleId,
+ Storage::Version version,
+ Storage::Synchronization::IsAutoVersion isAutoVersion,
+ std::string_view moduleName,
+ std::string_view exportedModuleName)
+{
+ NanotraceHR::Tracer tracer{"add module exported imports"_t,
+ category(),
+ keyValue("module id", moduleId),
+ keyValue("exported module id", exportedModuleId),
+ keyValue("version", version),
+ keyValue("is auto version", isAutoVersion),
+ keyValue("module name", moduleName),
+ keyValue("exported module name", exportedModuleName)};
+
+ imports.emplace_back(moduleId, exportedModuleId, version, isAutoVersion);
+}
+
void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &imports,
ModuleId moduleId,
ModuleId cppModuleId,
+ std::string_view moduleName,
+ std::string_view cppModuleName,
const QList<QmlDirParser::Import> &qmldirImports,
ProjectStorageInterface &projectStorage)
{
- for (const QmlDirParser::Import &qmldirImport : qmldirImports) {
- ModuleId exportedModuleId = projectStorage.moduleId(Utils::PathString{qmldirImport.module});
- imports.emplace_back(moduleId,
- exportedModuleId,
- convertVersion(qmldirImport.version),
- convertToIsAutoVersion(qmldirImport.flags));
+ NanotraceHR::Tracer tracer{"add module exported imports"_t,
+ category(),
+ keyValue("cpp module id", cppModuleId),
+ keyValue("module id", moduleId)};
- ModuleId exportedCppModuleId = projectStorage.moduleId(
- Utils::PathString{qmldirImport.module} + "-cppnative");
- imports.emplace_back(cppModuleId,
- exportedCppModuleId,
- Storage::Version{},
- Storage::Synchronization::IsAutoVersion::No);
+ for (const QmlDirParser::Import &qmldirImport : qmldirImports) {
+ Utils::PathString exportedModuleName{qmldirImport.module};
+ ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName);
+ addModuleExportedImport(imports,
+ moduleId,
+ exportedModuleId,
+ convertVersion(qmldirImport.version),
+ convertToIsAutoVersion(qmldirImport.flags),
+ moduleName,
+ exportedModuleName);
+
+ exportedModuleName += "-cppnative";
+ ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName);
+ addModuleExportedImport(imports,
+ cppModuleId,
+ exportedCppModuleId,
+ Storage::Version{},
+ Storage::Synchronization::IsAutoVersion::No,
+ cppModuleName,
+ exportedModuleName);
}
}
@@ -182,8 +247,14 @@ std::vector<IdPaths> createIdPaths(ProjectStorageUpdater::WatchedSourceIdsIds wa
void ProjectStorageUpdater::update(QStringList directories,
QStringList qmlTypesPaths,
- const QString &propertyEditorResourcesPath)
+ const QString &propertyEditorResourcesPath,
+ const QStringList &typeAnnotationPaths)
{
+ NanotraceHR::Tracer tracer{"update"_t,
+ category(),
+ keyValue("directories", directories),
+ keyValue("qml types paths", qmlTypesPaths)};
+
Storage::Synchronization::SynchronizationPackage package;
WatchedSourceIdsIds watchedSourceIds{Utils::span{directories}.size()};
NotUpdatedSourceIds notUpdatedSourceIds{Utils::span{directories}.size()};
@@ -191,6 +262,7 @@ void ProjectStorageUpdater::update(QStringList directories,
updateDirectories(directories, package, notUpdatedSourceIds, watchedSourceIds);
updateQmlTypes(qmlTypesPaths, package, notUpdatedSourceIds, watchedSourceIds);
updatePropertyEditorPaths(propertyEditorResourcesPath, package, notUpdatedSourceIds);
+ updateTypeAnnotations(typeAnnotationPaths, package, notUpdatedSourceIds);
package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds),
std::move(notUpdatedSourceIds.sourceIds));
@@ -198,7 +270,11 @@ void ProjectStorageUpdater::update(QStringList directories,
std::move(package.updatedFileStatusSourceIds),
std::move(notUpdatedSourceIds.fileStatusSourceIds));
- m_projectStorage.synchronize(std::move(package));
+ try {
+ m_projectStorage.synchronize(std::move(package));
+ } catch (...) {
+ qWarning() << "Project storage could not been updated!";
+ }
m_pathWatcher.updateIdPaths(createIdPaths(watchedSourceIds, m_projectPartId));
}
@@ -211,11 +287,16 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
if (qmlTypesPaths.empty())
return;
+ NanotraceHR::Tracer tracer{"update qmltypes file"_t, category()};
+
ModuleId moduleId = m_projectStorage.moduleId("QML-cppnative");
for (const QString &qmlTypesPath : qmlTypesPaths) {
SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath});
watchedSourceIdsIds.qmltypesSourceIds.push_back(sourceId);
+ tracer.tick("append watched qml types source id"_t,
+ keyValue("source id", sourceId),
+ keyValue("qml types path", qmlTypesPath));
Storage::Synchronization::ProjectData projectData{sourceId,
sourceId,
@@ -228,7 +309,9 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
notUpdatedSourceIds);
if (state == FileState::Changed) {
+ tracer.tick("append project data"_t, keyValue("project data", projectData));
package.projectDatas.push_back(std::move(projectData));
+ tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId));
package.updatedProjectSourceIds.push_back(sourceId);
}
}
@@ -246,13 +329,86 @@ ProjectStorageUpdater::FileState combineState(FileStates... fileStates)
return ProjectStorageUpdater::FileState::NotExists;
}
+
} // namespace
+void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPath,
+ FileState qmldirState,
+ SourcePath qmldirSourcePath,
+ SourceId qmldirSourceId,
+ SourceId directorySourceId,
+ SourceContextId directoryId,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ WatchedSourceIdsIds &watchedSourceIdsIds,
+ Tracer &tracer)
+{
+ QmlDirParser parser;
+ if (qmldirState != FileState::NotExists)
+ parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath}));
+
+ if (qmldirState != FileState::NotChanged) {
+ tracer.tick("append updated source id"_t, keyValue("module id", qmldirSourceId));
+ package.updatedSourceIds.push_back(qmldirSourceId);
+ }
+
+ Utils::PathString moduleName{parser.typeNamespace()};
+ ModuleId moduleId = m_projectStorage.moduleId(moduleName);
+ Utils::PathString cppModuleName = moduleName + "-cppnative";
+ ModuleId cppModuleId = m_projectStorage.moduleId(cppModuleName);
+ ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath);
+
+ auto imports = filterMultipleEntries(parser.imports());
+
+ addModuleExportedImports(package.moduleExportedImports,
+ moduleId,
+ cppModuleId,
+ moduleName,
+ cppModuleName,
+ imports,
+ m_projectStorage);
+ tracer.tick("append updated module id"_t, keyValue("module id", moduleId));
+ package.updatedModuleIds.push_back(moduleId);
+
+ const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
+ addSourceIds(package.updatedSourceIds, qmlProjectDatas, "append updated source id"_t, tracer);
+ addSourceIds(package.updatedFileStatusSourceIds,
+ qmlProjectDatas,
+ "append updated file status source id"_t,
+ tracer);
+
+ auto qmlTypes = filterMultipleEntries(parser.typeInfos());
+
+ if (!qmlTypes.isEmpty()) {
+ parseTypeInfos(qmlTypes,
+ filterMultipleEntries(parser.dependencies()),
+ imports,
+ directorySourceId,
+ directoryPath,
+ cppModuleId,
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIdsIds);
+ }
+ parseQmlComponents(
+ createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath),
+ directorySourceId,
+ directoryId,
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIdsIds,
+ qmldirState);
+ tracer.tick("append updated project source id"_t, keyValue("module id", moduleId));
+ package.updatedProjectSourceIds.push_back(directorySourceId);
+}
+
void ProjectStorageUpdater::updateDirectories(const QStringList &directories,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIdsIds)
{
+ NanotraceHR::Tracer tracer{"update directories"_t, category()};
+
for (const QString &directory : directories)
updateDirectory({directory}, package, notUpdatedSourceIds, watchedSourceIdsIds);
}
@@ -262,8 +418,10 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIdsIds)
{
+ NanotraceHR::Tracer tracer{"update directory"_t, category(), keyValue("directory", directoryPath)};
+
SourcePath qmldirSourcePath{directoryPath + "/qmldir"};
- auto [directoryId, qmlDirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath);
+ auto [directoryId, qmldirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath);
SourcePath directorySourcePath{directoryPath + "/."};
auto directorySourceId = m_pathCache.sourceId(directorySourcePath);
@@ -271,62 +429,28 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa
if (directoryState != FileState::NotExists)
watchedSourceIdsIds.directorySourceIds.push_back(directorySourceId);
- auto qmldirState = fileState(qmlDirSourceId, package, notUpdatedSourceIds);
+ auto qmldirState = fileState(qmldirSourceId, package, notUpdatedSourceIds);
if (qmldirState != FileState::NotExists)
- watchedSourceIdsIds.qmldirSourceIds.push_back(qmlDirSourceId);
+ watchedSourceIdsIds.qmldirSourceIds.push_back(qmldirSourceId);
switch (combineState(directoryState, qmldirState)) {
case FileState::Changed: {
- QmlDirParser parser;
- if (qmldirState != FileState::NotExists)
- parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath}));
-
- if (qmldirState != FileState::NotChanged)
- package.updatedSourceIds.push_back(qmlDirSourceId);
-
- Utils::PathString moduleName{parser.typeNamespace()};
- ModuleId moduleId = m_projectStorage.moduleId(moduleName);
- ModuleId cppModuleId = m_projectStorage.moduleId(moduleName + "-cppnative");
- ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath);
-
- auto imports = filterMultipleEntries(parser.imports());
-
- addModuleExportedImports(package.moduleExportedImports,
- moduleId,
- cppModuleId,
- imports,
- m_projectStorage);
- package.updatedModuleIds.push_back(moduleId);
-
- const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
- addSourceIds(package.updatedSourceIds, qmlProjectDatas);
- addSourceIds(package.updatedFileStatusSourceIds, qmlProjectDatas);
-
- auto qmlTypes = filterMultipleEntries(parser.typeInfos());
-
- if (!qmlTypes.isEmpty()) {
- parseTypeInfos(qmlTypes,
- filterMultipleEntries(parser.dependencies()),
- imports,
- directorySourceId,
- directoryPath,
- cppModuleId,
- package,
- notUpdatedSourceIds,
- watchedSourceIdsIds);
- }
- parseQmlComponents(
- createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath),
- directorySourceId,
- directoryId,
- package,
- notUpdatedSourceIds,
- watchedSourceIdsIds,
- qmldirState);
- package.updatedProjectSourceIds.push_back(directorySourceId);
+ tracer.tick("update directory changed"_t);
+ updateDirectoryChanged(directoryPath,
+ qmldirState,
+ qmldirSourcePath,
+ qmldirSourceId,
+ directorySourceId,
+ directoryId,
+ package,
+ notUpdatedSourceIds,
+ watchedSourceIdsIds,
+ tracer);
break;
}
case FileState::NotChanged: {
+ tracer.tick("update directory not changed"_t);
+
parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId),
package,
notUpdatedSourceIds,
@@ -334,19 +458,32 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa
break;
}
case FileState::NotExists: {
+ tracer.tick("update directory don't exits"_t);
+
package.updatedFileStatusSourceIds.push_back(directorySourceId);
- package.updatedFileStatusSourceIds.push_back(qmlDirSourceId);
+ package.updatedFileStatusSourceIds.push_back(qmldirSourceId);
package.updatedProjectSourceIds.push_back(directorySourceId);
- package.updatedSourceIds.push_back(qmlDirSourceId);
+ package.updatedSourceIds.push_back(qmldirSourceId);
auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) {
+ tracer.tick("append updated source id"_t, keyValue("source id", projectData.sourceId));
package.updatedSourceIds.push_back(projectData.sourceId);
+ tracer.tick("append updated file status source id"_t,
+ keyValue("source id", projectData.sourceId));
package.updatedFileStatusSourceIds.push_back(projectData.sourceId);
}
break;
}
}
+
+ tracer.end(keyValue("qmldir source path", qmldirSourcePath),
+ keyValue("directory source path", directorySourcePath),
+ keyValue("directory id", directoryId),
+ keyValue("qmldir source id", qmldirSourceId),
+ keyValue("directory source source id", directorySourceId),
+ keyValue("qmldir state", qmldirState),
+ keyValue("directory state", directoryState));
}
void ProjectStorageUpdater::updatePropertyEditorPaths(
@@ -354,6 +491,10 @@ void ProjectStorageUpdater::updatePropertyEditorPaths(
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds)
{
+ NanotraceHR::Tracer tracer{"update property editor paths"_t,
+ category(),
+ keyValue("property editor resources path", propertyEditorResourcesPath)};
+
if (propertyEditorResourcesPath.isEmpty())
return;
@@ -373,12 +514,134 @@ void ProjectStorageUpdater::updatePropertyEditorPaths(
}
}
+namespace {
+
+template<typename SourceIds1, typename SourceIds2>
+SmallSourceIds<16> mergedSourceIds(const SourceIds1 &sourceIds1, const SourceIds2 &sourceIds2)
+{
+ SmallSourceIds<16> mergedSourceIds;
+
+ std::set_union(sourceIds1.begin(),
+ sourceIds1.end(),
+ sourceIds2.begin(),
+ sourceIds2.end(),
+ std::back_inserter(mergedSourceIds));
+
+ return mergedSourceIds;
+}
+} // namespace
+
+void ProjectStorageUpdater::updateTypeAnnotations(const QStringList &directoryPaths,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds)
+{
+ NanotraceHR::Tracer tracer("update type annotations"_t, category());
+
+ std::map<SourceId, SmallSourceIds<16>> updatedSourceIdsDictonary;
+
+ for (SourceId directoryId : m_projectStorage.typeAnnotationDirectorySourceIds())
+ updatedSourceIdsDictonary[directoryId] = {};
+
+ for (const auto &directoryPath : directoryPaths)
+ updateTypeAnnotations(directoryPath, package, notUpdatedSourceIds, updatedSourceIdsDictonary);
+
+ updateTypeAnnotationDirectories(package, notUpdatedSourceIds, updatedSourceIdsDictonary);
+}
+
void ProjectStorageUpdater::updateTypeAnnotations(
- const QString & /*propertyEditorResourcesPath*/,
- Storage::Synchronization::SynchronizationPackage & /*package*/,
- NotUpdatedSourceIds & /*notUpdatedSourceIds*/)
+ const QString &rootDirectoryPath,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ std::map<SourceId, SmallSourceIds<16>> &updatedSourceIdsDictonary)
+{
+ NanotraceHR::Tracer tracer("update type annotation directory"_t,
+ category(),
+ keyValue("path", rootDirectoryPath));
+
+ if (rootDirectoryPath.isEmpty())
+ return;
+
+ QDirIterator directoryIterator{rootDirectoryPath,
+ {"*.metainfo"},
+ QDir::NoDotAndDotDot | QDir::Files,
+ QDirIterator::Subdirectories};
+
+ while (directoryIterator.hasNext()) {
+ auto fileInfo = directoryIterator.nextFileInfo();
+ auto filePath = fileInfo.filePath();
+ SourceId sourceId = m_pathCache.sourceId(SourcePath{filePath});
+
+ auto directoryPath = fileInfo.canonicalPath();
+
+ SourceId directorySourceId = m_pathCache.sourceId(SourcePath{directoryPath + "/."});
+
+ auto state = fileState(sourceId, package, notUpdatedSourceIds);
+ if (state == FileState::Changed)
+ updateTypeAnnotation(directoryPath, fileInfo.filePath(), sourceId, directorySourceId, package);
+
+ if (state != FileState::NotChanged)
+ updatedSourceIdsDictonary[directorySourceId].push_back(sourceId);
+ }
+}
+
+void ProjectStorageUpdater::updateTypeAnnotationDirectories(
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ std::map<SourceId, SmallSourceIds<16>> &updatedSourceIdsDictonary)
+{
+ for (auto &[directorySourceId, updatedSourceIds] : updatedSourceIdsDictonary) {
+ auto directoryState = fileState(directorySourceId, package, notUpdatedSourceIds);
+
+ if (directoryState != FileState::NotChanged) {
+ auto existingTypeAnnotationSourceIds = m_projectStorage.typeAnnotationSourceIds(
+ directorySourceId);
+
+ std::sort(updatedSourceIds.begin(), updatedSourceIds.end());
+
+ auto changedSourceIds = mergedSourceIds(existingTypeAnnotationSourceIds, updatedSourceIds);
+ package.updatedTypeAnnotationSourceIds.insert(package.updatedTypeAnnotationSourceIds.end(),
+ changedSourceIds.begin(),
+ changedSourceIds.end());
+ } else {
+ package.updatedTypeAnnotationSourceIds.insert(package.updatedTypeAnnotationSourceIds.end(),
+ updatedSourceIds.begin(),
+ updatedSourceIds.end());
+ }
+ }
+}
+
+namespace {
+QString contentFromFile(const QString &path)
+{
+ QFile file{path};
+ if (file.open(QIODevice::ReadOnly))
+ return QString::fromUtf8(file.readAll());
+
+ return {};
+}
+} // namespace
+
+void ProjectStorageUpdater::updateTypeAnnotation(const QString &directoryPath,
+ const QString &filePath,
+ SourceId sourceId,
+ SourceId directorySourceId,
+ Storage::Synchronization::SynchronizationPackage &package)
{
- // const auto typeAnnotations = dir.entryInfoList({"*.metainfo"}, QDir::Files);
+ NanotraceHR::Tracer tracer{"update type annotation path"_t,
+ category(),
+ keyValue("path", filePath),
+ keyValue("directory path", directoryPath)};
+
+ Storage::TypeAnnotationReader reader{m_projectStorage};
+
+ auto annotations = reader.parseTypeAnnotation(contentFromFile(filePath),
+ directoryPath,
+ sourceId,
+ directorySourceId);
+ auto &typeAnnotations = package.typeAnnotations;
+ package.typeAnnotations.insert(typeAnnotations.end(),
+ std::make_move_iterator(annotations.begin()),
+ std::make_move_iterator(annotations.end()));
}
void ProjectStorageUpdater::updatePropertyEditorPath(
@@ -386,6 +649,13 @@ void ProjectStorageUpdater::updatePropertyEditorPath(
Storage::Synchronization::SynchronizationPackage &package,
SourceId directorySourceId)
{
+ NanotraceHR::Tracer tracer{"update property editor path"_t,
+ category(),
+ keyValue("directory path", directoryPath),
+ keyValue("directory source id", directorySourceId)};
+
+ tracer.tick("append updated property editor qml path source id"_t,
+ keyValue("source id", directorySourceId));
package.updatedPropertyEditorQmlPathSourceIds.push_back(directorySourceId);
auto dir = QDir{directoryPath};
const auto fileInfos = dir.entryInfoList({"*Pane.qml", "*Specifics.qml"}, QDir::Files);
@@ -398,6 +668,11 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath(
Storage::Synchronization::SynchronizationPackage &package,
SourceId directorySourceId)
{
+ NanotraceHR::Tracer tracer{"update property editor file path"_t,
+ category(),
+ keyValue("directory path", path),
+ keyValue("directory source id", directorySourceId)};
+
QRegularExpression regex{R"xo(.+\/(\w+)\/(\w+)(Specifics|Pane).qml)xo"};
auto match = regex.match(path);
QString oldModuleName;
@@ -410,7 +685,12 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath(
}
Storage::TypeNameString typeName{match.capturedView(2)};
SourceId pathId = m_pathCache.sourceId(SourcePath{path});
- package.propertyEditorQmlPaths.emplace_back(moduleId, typeName, pathId, directorySourceId);
+ const auto &paths = package.propertyEditorQmlPaths.emplace_back(moduleId,
+ typeName,
+ pathId,
+ directorySourceId);
+ tracer.tick("append property editor qml paths"_t,
+ keyValue("property editor qml paths", paths));
}
}
@@ -447,6 +727,10 @@ bool contains(const Container &container, Id id)
void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &changedIdPaths)
{
+ NanotraceHR::Tracer tracer{"paths with ids changed"_t,
+ category(),
+ keyValue("id paths", changedIdPaths)};
+
m_changedIdPaths.insert(m_changedIdPaths.end(), changedIdPaths.begin(), changedIdPaths.end());
Storage::Synchronization::SynchronizationPackage package;
@@ -539,21 +823,35 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos,
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIds)
{
+ NanotraceHR::Tracer tracer{"parse type infos"_t,
+ category(),
+ keyValue("directory source id", directorySourceId),
+ keyValue("directory path", directoryPath),
+ keyValue("module id", moduleId)};
+
for (const QString &typeInfo : typeInfos) {
+ NanotraceHR::Tracer tracer{"parse type info"_t, category(), keyValue("type info", typeInfo)};
+
Utils::PathString qmltypesPath = Utils::PathString::join(
{directoryPath, "/", Utils::SmallString{typeInfo}});
SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmltypesPath});
+ tracer.tick("append qmltypes source id"_t, keyValue("source id", sourceId));
watchedSourceIds.qmltypesSourceIds.push_back(sourceId);
addDependencies(package.moduleDependencies,
sourceId,
joinImports(qmldirDependencies, qmldirImports),
- m_projectStorage);
+ m_projectStorage,
+ "append module dependency"_t,
+ tracer);
+
+ tracer.tick("append module dependenct source source id"_t, keyValue("source id", sourceId));
package.updatedModuleDependencySourceIds.push_back(sourceId);
auto projectData = package.projectDatas.emplace_back(
directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes);
+ tracer.tick("append project data"_t, keyValue("source id", sourceId));
parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds);
}
@@ -564,6 +862,8 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIds)
{
+ NanotraceHR::Tracer tracer{"parse project datas"_t, category()};
+
for (const Storage::Synchronization::ProjectData &projectData : projectDatas) {
switch (projectData.fileType) {
case Storage::Synchronization::FileType::QmlTypes: {
@@ -577,6 +877,7 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr
watchedSourceIds.qmlSourceIds.push_back(projectData.sourceId);
parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds);
+ break;
}
}
}
@@ -587,9 +888,14 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds) -> FileState
{
+ NanotraceHR::Tracer tracer{"parse type info"_t,
+ category(),
+ keyValue("qmltypes path", qmltypesPath)};
+
auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds);
switch (state) {
case FileState::Changed: {
+ tracer.tick("append updated source ids"_t, keyValue("source id", projectData.sourceId));
package.updatedSourceIds.push_back(projectData.sourceId);
const auto content = m_fileSystem.contentAsQString(QString{qmltypesPath});
@@ -597,14 +903,16 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec
break;
}
case FileState::NotChanged: {
+ tracer.tick("append not updated source ids"_t, keyValue("source id", projectData.sourceId));
notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId);
break;
}
case FileState::NotExists:
throw CannotParseQmlTypesFile{};
- break;
}
+ tracer.end(keyValue("state", state));
+
return state;
}
@@ -617,6 +925,14 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
WatchedSourceIdsIds &watchedSourceIds,
FileState qmldirState)
{
+ NanotraceHR::Tracer tracer{"parse qml component"_t,
+ category(),
+ keyValue("relative file path", relativeFilePath),
+ keyValue("directory path", directoryPath),
+ keyValue("exported types", exportedTypes),
+ keyValue("directory source id", directorySourceId),
+ keyValue("qmldir state", qmldirState)};
+
if (std::find(relativeFilePath.begin(), relativeFilePath.end(), '+') != relativeFilePath.end())
return;
@@ -626,16 +942,18 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
Storage::Synchronization::Type type;
auto state = fileState(sourceId, package, notUpdatedSourceIds);
+ tracer.tick("append watched qml source id"_t, keyValue("source id", sourceId));
watchedSourceIds.qmlSourceIds.push_back(sourceId);
switch (state) {
case FileState::NotChanged:
if (qmldirState == FileState::NotExists) {
+ tracer.tick("append not updated source id"_t, keyValue("source id", sourceId));
notUpdatedSourceIds.sourceIds.emplace_back(sourceId);
- package.projectDatas.emplace_back(directorySourceId,
- sourceId,
- ModuleId{},
- Storage::Synchronization::FileType::QmlDocument);
+
+ const auto &projectData = package.projectDatas.emplace_back(
+ directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument);
+ tracer.tick("append project data"_t, keyValue("project data", projectData));
return;
}
@@ -649,11 +967,11 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
break;
}
- package.projectDatas.emplace_back(directorySourceId,
- sourceId,
- ModuleId{},
- Storage::Synchronization::FileType::QmlDocument);
+ const auto &projectData = package.projectDatas.emplace_back(
+ directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument);
+ tracer.tick("append project data"_t, keyValue("project data", projectData));
+ tracer.tick("append updated source id"_t, keyValue("source id", sourceId));
package.updatedSourceIds.push_back(sourceId);
type.typeName = SourcePath{qmlFilePath}.name();
@@ -661,6 +979,8 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
type.sourceId = sourceId;
type.exportedTypes = std::move(exportedTypes);
+ tracer.end(keyValue("type", type));
+
package.types.push_back(std::move(type));
}
@@ -668,10 +988,13 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds)
{
+ NanotraceHR::Tracer tracer{"parse qml component"_t, category(), keyValue("source id", sourceId)};
+
auto state = fileState(sourceId, package, notUpdatedSourceIds);
if (state == FileState::NotChanged)
return;
+ tracer.tick("append updated source id"_t, keyValue("source id", sourceId));
package.updatedSourceIds.push_back(sourceId);
if (state == FileState::NotExists)
@@ -687,6 +1010,8 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId,
type.sourceId = sourceId;
type.changeLevel = Storage::Synchronization::ChangeLevel::ExcludeExportedTypes;
+ tracer.end(keyValue("type", type));
+
package.types.push_back(std::move(type));
}
@@ -733,6 +1058,12 @@ void ProjectStorageUpdater::parseQmlComponents(Components components,
WatchedSourceIdsIds &watchedSourceIdsIds,
FileState qmldirState)
{
+ NanotraceHR::Tracer tracer{"parse qml components"_t,
+ category(),
+ keyValue("directory source id", directorySourceId),
+ keyValue("directory id", directoryId),
+ keyValue("qmldir state", qmldirState)};
+
std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) {
return first.fileName < second.fileName;
});
@@ -760,22 +1091,37 @@ ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState(
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds) const
{
+ NanotraceHR::Tracer tracer{"update property editor paths"_t,
+ category(),
+ keyValue("source id", sourceId)};
+
auto currentFileStatus = m_fileStatusCache.find(sourceId);
if (!currentFileStatus.isValid()) {
+ tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId));
package.updatedFileStatusSourceIds.push_back(sourceId);
+
+ tracer.end(keyValue("state", FileState::NotExists));
return FileState::NotExists;
}
auto projectStorageFileStatus = m_projectStorage.fetchFileStatus(sourceId);
if (!projectStorageFileStatus.isValid() || projectStorageFileStatus != currentFileStatus) {
+ tracer.tick("append file status"_t, keyValue("file status", sourceId));
package.fileStatuses.push_back(currentFileStatus);
+
+ tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId));
package.updatedFileStatusSourceIds.push_back(sourceId);
+
+ tracer.end(keyValue("state", FileState::Changed));
return FileState::Changed;
}
+ tracer.tick("append not updated file status source id"_t, keyValue("source id", sourceId));
notUpdatedSourceIds.fileStatusSourceIds.push_back(sourceId);
+
+ tracer.end(keyValue("state", FileState::NotChanged));
return FileState::NotChanged;
}
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
index 187a2219d0..640969fe99 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
@@ -11,10 +11,15 @@
#include "projectstoragetypes.h"
#include "sourcepath.h"
+#include <modelfwd.h>
+
+#include <tracing/qmldesignertracing.h>
+
#include <qmljs/parser/qmldirparser_p.h>
#include <QStringList>
+#include <map>
#include <vector>
namespace Sqlite {
@@ -27,7 +32,6 @@ class ProjectStorageInterface;
template<typename ProjectStorage, typename Mutex>
class SourcePathCache;
class FileStatusCache;
-template<typename Database>
class ProjectStorage;
class QmlDocumentParserInterface;
class QmlTypesParserInterface;
@@ -35,10 +39,10 @@ class QmlTypesParserInterface;
class ProjectStorageUpdater final : public ProjectStoragePathWatcherNotifierInterface
{
public:
- using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>;
+ using PathCache = SourcePathCache<ProjectStorage, NonLockingMutex>;
ProjectStorageUpdater(FileSystemInterface &fileSystem,
- ProjectStorageInterface &projectStorage,
+ ProjectStorageType &projectStorage,
FileStatusCache &fileStatusCache,
PathCache &pathCache,
QmlDocumentParserInterface &qmlDocumentParser,
@@ -57,7 +61,8 @@ public:
void update(QStringList directories,
QStringList qmlTypesPaths,
- const QString &propertyEditorResourcesPath);
+ const QString &propertyEditorResourcesPath,
+ const QStringList &typeAnnotationPaths);
void pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) override;
void pathsChanged(const SourceIds &filePathIds) override;
@@ -141,13 +146,35 @@ private:
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds,
WatchedSourceIdsIds &watchedSourceIdsIds);
+ void updateDirectoryChanged(std::string_view directoryPath,
+ FileState qmldirState,
+ SourcePath qmldirSourcePath,
+ SourceId qmldirSourceId,
+ SourceId directorySourceId,
+ SourceContextId directoryId,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ WatchedSourceIdsIds &watchedSourceIdsIds,
+ ProjectStorageTracing::Category::TracerType &tracer);
void updatePropertyEditorPaths(const QString &propertyEditorResourcesPath,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds);
- void updateTypeAnnotations(const QString &propertyEditorResourcesPath,
+ void updateTypeAnnotations(const QString &directoryPath,
+ Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ std::map<SourceId, SmallSourceIds<16>> &updatedSourceIdsDictonary);
+ void updateTypeAnnotationDirectories(Storage::Synchronization::SynchronizationPackage &package,
+ NotUpdatedSourceIds &notUpdatedSourceIds,
+ std::map<SourceId, SmallSourceIds<16>> &updatedSourceIdsDictonary);
+ void updateTypeAnnotations(const QStringList &directoryPath,
Storage::Synchronization::SynchronizationPackage &package,
NotUpdatedSourceIds &notUpdatedSourceIds);
+ void updateTypeAnnotation(const QString &directoryPath,
+ const QString &filePath,
+ SourceId sourceId,
+ SourceId directorySourceId,
+ Storage::Synchronization::SynchronizationPackage &package);
void updatePropertyEditorPath(const QString &path,
Storage::Synchronization::SynchronizationPackage &package,
SourceId directorySourceId);
@@ -197,7 +224,7 @@ private:
private:
std::vector<IdPaths> m_changedIdPaths;
FileSystemInterface &m_fileSystem;
- ProjectStorageInterface &m_projectStorage;
+ ProjectStorageType &m_projectStorage;
FileStatusCache &m_fileStatusCache;
PathCache &m_pathCache;
QmlDocumentParserInterface &m_qmlDocumentParser;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
index f9eb8080f7..27efa8d530 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
@@ -10,6 +10,8 @@
#include <sqlitedatabase.h>
+#include <tracing/qmldesignertracing.h>
+
#ifdef QDS_BUILD_QMLPARSER
#include <private/qqmldomtop_p.h>
#endif
@@ -21,6 +23,10 @@ namespace QmlDesigner {
#ifdef QDS_BUILD_QMLPARSER
+constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory;
+using NanotraceHR::keyValue;
+using Tracer = ProjectStorageTracing::Category::TracerType;
+
namespace QmlDom = QQmlJS::Dom;
namespace Synchronization = Storage::Synchronization;
@@ -84,6 +90,11 @@ QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports,
Utils::SmallStringView directoryPath,
QmlDocumentParser::ProjectStorage &storage)
{
+ NanotraceHR::Tracer tracer{"create qualified imports"_t,
+ category(),
+ keyValue("sourceId", sourceId),
+ keyValue("directoryPath", directoryPath)};
+
QualifiedImports qualifiedImports;
for (const QmlDom::Import &qmlImport : qmlImports) {
@@ -92,6 +103,8 @@ QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports,
createImport(qmlImport, sourceId, directoryPath, storage));
}
+ tracer.end(keyValue("qualified imports", qualifiedImports));
+
return qualifiedImports;
}
@@ -280,6 +293,11 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon
SourceId sourceId,
Utils::SmallStringView directoryPath)
{
+ NanotraceHR::Tracer tracer{"qml document parser parse"_t,
+ category(),
+ keyValue("sourceId", sourceId),
+ keyValue("directoryPath", directoryPath)};
+
Storage::Synchronization::Type type;
using Option = QmlDom::DomEnvironment::Option;
@@ -335,7 +353,7 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon
m_storage);
type.prototype = createImportedTypeName(qmlObject.name(), qualifiedImports);
-
+ type.defaultPropertyName = qmlObject.localDefaultPropertyName();
addImports(imports, qmlFile->imports(), sourceId, directoryPath, m_storage);
addPropertyDeclarations(type, qmlObject, qualifiedImports, file);
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h
index b8ab4ec4b1..1b494a2f69 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h
@@ -15,7 +15,7 @@ class SourcePathCache;
class QmlDocumentParser final : public QmlDocumentParserInterface
{
public:
- using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>;
+ using ProjectStorage = QmlDesigner::ProjectStorage;
using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>;
#ifdef QDS_BUILD_QMLPARSER
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
index 3768535299..104338e514 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
@@ -5,8 +5,12 @@
#include "projectstorage.h"
+#include <tracing/qmldesignertracing.h>
+
#include <sqlitedatabase.h>
+#include <utils/span.h>
+
#ifdef QDS_BUILD_QMLPARSER
#include <private/qqmldomtop_p.h>
#include <private/qqmljstypedescriptionreader_p.h>
@@ -21,6 +25,10 @@ namespace QmlDesigner {
#ifdef QDS_BUILD_QMLPARSER
+constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory;
+using NanotraceHR::keyValue;
+using Tracer = ProjectStorageTracing::Category::TracerType;
+
namespace QmlDom = QQmlJS::Dom;
namespace {
@@ -31,6 +39,8 @@ using Storage::TypeNameString;
ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQmlJSExportedScope> &objects)
{
+ NanotraceHR::Tracer tracer{"parse qmltypes file"_t, category()};
+
ComponentWithoutNamespaces componentWithoutNamespaces;
for (const auto &object : objects) {
@@ -46,13 +56,15 @@ ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQml
name);
}
+ tracer.end(keyValue("components without namespace", componentWithoutNamespaces));
+
return componentWithoutNamespaces;
}
-void appendImports(Storage::Imports &imports,
- const QString &dependency,
- SourceId sourceId,
- QmlTypesParser::ProjectStorage &storage)
+const Storage::Import &appendImports(Storage::Imports &imports,
+ const QString &dependency,
+ SourceId sourceId,
+ QmlTypesParser::ProjectStorage &storage)
{
auto spaceFound = std::find_if(dependency.begin(), dependency.end(), [](QChar c) {
return c.isSpace();
@@ -62,7 +74,7 @@ void appendImports(Storage::Imports &imports,
moduleName.append("-cppnative");
ModuleId cppModuleId = storage.moduleId(moduleName);
- imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
+ return imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
}
void addImports(Storage::Imports &imports,
@@ -71,13 +83,25 @@ void addImports(Storage::Imports &imports,
QmlTypesParser::ProjectStorage &storage,
ModuleId cppModuleId)
{
- for (const QString &dependency : dependencies)
- appendImports(imports, dependency, sourceId, storage);
+ NanotraceHR::Tracer tracer{
+ "add imports"_t,
+ category(),
+ keyValue("source id", sourceId),
+ keyValue("module id", cppModuleId),
+ };
+
+ for (const QString &dependency : dependencies) {
+ const auto &import = appendImports(imports, dependency, sourceId, storage);
+ tracer.tick("append import"_t, keyValue("import", import), keyValue("dependency", dependency));
+ }
- imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
+ const auto &import = imports.emplace_back(cppModuleId, Storage::Version{}, sourceId);
+ tracer.tick("append import"_t, keyValue("import", import));
- if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId)
- imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId);
+ if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) {
+ const auto &import = imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId);
+ tracer.tick("append import"_t, keyValue("import", import));
+ }
}
Storage::TypeTraits createAccessTypeTraits(QQmlJSScope::AccessSemantics accessSematics)
@@ -412,6 +436,11 @@ void addType(Storage::Synchronization::Types &types,
QmlTypesParser::ProjectStorage &storage,
const ComponentWithoutNamespaces &componentNameWithoutNamespace)
{
+ NanotraceHR::Tracer tracer{"add type"_t,
+ category(),
+ keyValue("source id", sourceId),
+ keyValue("module id", cppModuleId)};
+
const auto &component = *exportScope.scope;
auto [functionsDeclarations, signalDeclarations] = createFunctionAndSignals(
@@ -421,7 +450,7 @@ void addType(Storage::Synchronization::Types &types,
auto exports = exportScope.exports;
auto enumerationTypes = addEnumerationTypes(types, typeName, sourceId, cppModuleId, enumerations);
- types.emplace_back(
+ const auto &type = types.emplace_back(
Utils::SmallStringView{typeName},
Storage::Synchronization::ImportedType{TypeNameString{component.baseTypeName()}},
Storage::Synchronization::ImportedType{TypeNameString{component.extensionTypeName()}},
@@ -431,7 +460,37 @@ void addType(Storage::Synchronization::Types &types,
createProperties(component.ownProperties(), enumerationTypes, componentNameWithoutNamespace),
std::move(functionsDeclarations),
std::move(signalDeclarations),
- createEnumeration(enumerations));
+ createEnumeration(enumerations),
+ Storage::Synchronization::ChangeLevel::Full,
+ Utils::SmallString{component.ownDefaultPropertyName()});
+ tracer.end(keyValue("type", type));
+}
+
+using namespace Qt::StringLiterals;
+
+constexpr auto skipLists = std::make_tuple(
+ std::pair{"QtQuick.Templates-cppnative"sv, std::array{"QQuickItem"_L1}});
+
+Utils::span<const QLatin1StringView> getSkipList(std::string_view moduleName)
+{
+ static constexpr Utils::span<const QLatin1StringView> emptySkipList;
+ auto currentSkipList = emptySkipList;
+
+ std::apply(
+ [&](const auto &entry) {
+ if (entry.first == moduleName)
+ currentSkipList = entry.second;
+ },
+ skipLists);
+
+ return currentSkipList;
+}
+
+bool skipType(const QQmlJSExportedScope &object, Utils::span<const QLatin1StringView> skipList)
+{
+ return std::any_of(skipList.begin(), skipList.end(), [&](const QLatin1StringView skip) {
+ return object.scope->internalName() == skip;
+ });
}
void addTypes(Storage::Synchronization::Types &types,
@@ -440,15 +499,22 @@ void addTypes(Storage::Synchronization::Types &types,
QmlTypesParser::ProjectStorage &storage,
const ComponentWithoutNamespaces &componentNameWithoutNamespaces)
{
+ NanotraceHR::Tracer tracer{"add types"_t, category()};
types.reserve(Utils::usize(objects) + types.size());
- for (const auto &object : objects)
+ const auto skipList = getSkipList(storage.moduleName(projectData.moduleId));
+
+ for (const auto &object : objects) {
+ if (skipType(object, skipList))
+ continue;
+
addType(types,
projectData.sourceId,
projectData.moduleId,
object,
storage,
componentNameWithoutNamespaces);
+ }
}
} // namespace
@@ -458,6 +524,8 @@ void QmlTypesParser::parse(const QString &sourceContent,
Storage::Synchronization::Types &types,
const Storage::Synchronization::ProjectData &projectData)
{
+ NanotraceHR::Tracer tracer{"qmltypes parser parse"_t, category()};
+
QQmlJSTypeDescriptionReader reader({}, sourceContent);
QList<QQmlJSExportedScope> components;
QStringList dependencies;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
index 7c41925f30..4a6427501b 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
@@ -4,6 +4,7 @@
#pragma once
#include "nonlockingmutex.h"
+#include "projectstoragefwd.h"
#include "qmltypesparserinterface.h"
namespace Sqlite {
@@ -12,17 +13,13 @@ class Database;
namespace QmlDesigner {
-template<typename Database>
-class ProjectStorage;
-
template<typename ProjectStorage, typename Mutex>
class SourcePathCache;
class QmlTypesParser final : public QmlTypesParserInterface
{
public:
- using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>;
-
+ using ProjectStorage = QmlDesigner::ProjectStorage;
#ifdef QDS_BUILD_QMLPARSER
QmlTypesParser(ProjectStorage &storage)
: m_storage{storage}
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h
index 837e58d48a..b655c5cc34 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h
@@ -117,6 +117,12 @@ public:
std::ptrdiff_t slashIndex() const { return m_slashIndex; }
+ template<typename String>
+ friend void convertToString(String &string, const SourcePath &path)
+ {
+ convertToString(string, path.toStringView());
+ }
+
private:
std::ptrdiff_t m_slashIndex = -1;
};
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h
index 5feaf30d00..1ef8ba7f21 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h
@@ -125,4 +125,6 @@ public:
SourceContextId sourceContextId;
};
+using SourceNameAndSourceContextIds = std::vector<SourceNameAndSourceContextId>;
+
} // namespace QmlDesigner::Cache
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp
index b829e9db36..67a63542bc 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp
@@ -27,11 +27,11 @@ constexpr auto propertyElementName = "Property"_L1;
constexpr auto extraFileElementName = "ExtraFile"_L1;
} // namespace
-Synchronization::TypeAnnotations TypeAnnotationReader::parseTypeAnnotation(const QString &content,
- const QString &directoryPath,
- SourceId sourceId)
+Synchronization::TypeAnnotations TypeAnnotationReader::parseTypeAnnotation(
+ const QString &content, const QString &directoryPath, SourceId sourceId, SourceId directorySourceId)
{
m_sourceId = sourceId;
+ m_directorySourceId = directorySourceId;
m_directoryPath = directoryPath;
m_parserState = ParsingDocument;
if (!SimpleAbstractStreamReader::readFromSource(content)) {
@@ -178,7 +178,7 @@ TypeAnnotationReader::ParserSate TypeAnnotationReader::readDocument(const QStrin
TypeAnnotationReader::ParserSate TypeAnnotationReader::readMetaInfoRootElement(const QString &name)
{
if (name == typeElementName) {
- m_typeAnnotations.emplace_back(m_sourceId);
+ m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId);
m_itemLibraryEntries = json::array();
return ParsingType;
} else {
@@ -277,7 +277,7 @@ void TypeAnnotationReader::readItemLibraryEntryProperty(QStringView name, const
} else if (name == "category"_L1) {
m_itemLibraryEntries.back()["category"] = value;
} else if (name == "libraryIcon"_L1) {
- m_itemLibraryEntries.back()["iconPath"] = value;
+ m_itemLibraryEntries.back()["iconPath"] = absoluteFilePathForDocument(variant.toString());
} else if (name == "version"_L1) {
// setVersion(value.toString());
} else if (name == "requiredImport"_L1) {
@@ -427,8 +427,8 @@ void TypeAnnotationReader::setVersion(const QString &versionNumber)
int minor = 0;
if (!versionNumber.isEmpty()) {
- int val;
- bool ok;
+ int val = -1;
+ bool ok = false;
if (versionNumber.contains('.'_L1)) {
val = versionNumber.split('.'_L1).constFirst().toInt(&ok);
major = ok ? val : major;
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h
index 9332d5bed9..a320493ee2 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h
@@ -49,7 +49,8 @@ public:
Synchronization::TypeAnnotations parseTypeAnnotation(const QString &content,
const QString &directoryPath,
- SourceId sourceId);
+ SourceId sourceId,
+ SourceId directorySourceId);
QStringList errors();
@@ -124,6 +125,7 @@ private:
json m_itemLibraryEntries;
Property m_currentProperty;
SourceId m_sourceId;
+ SourceId m_directorySourceId;
};
} // namespace QmlDesigner::Storage
diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp
index d49c6156a6..b5798b713d 100644
--- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp
+++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp
@@ -3,40 +3,48 @@
#include "qmldesignertracing.h"
+#include <sqlitebasestatement.h>
+
namespace QmlDesigner {
+
+using namespace NanotraceHR::Literals;
+
namespace Tracing {
namespace {
using TraceFile = NanotraceHR::TraceFile<tracingStatus()>;
-TraceFile &traceFile()
+auto &traceFile()
{
- static TraceFile traceFile{"qml_designer.json"};
- return traceFile;
+ if constexpr (std::is_same_v<Sqlite::TraceFile, TraceFile>) {
+ return Sqlite::traceFile();
+ } else {
+ static TraceFile traceFile{"tracing.json"};
+ return traceFile;
+ }
}
} // namespace
EventQueue &eventQueue()
{
- thread_local NanotraceHR::EventQueueData<NanotraceHR::StringViewTraceEvent, 10000, tracingStatus()>
- stringViewEventQueueData(traceFile());
+ thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewTraceEvent, tracingStatus()>
+ stringViewEventQueue(traceFile());
- return stringViewEventQueueData;
+ return stringViewEventQueue;
}
EventQueueWithStringArguments &eventQueueWithStringArguments()
{
- thread_local NanotraceHR::
- EventQueueData<NanotraceHR::StringViewWithStringArgumentsTraceEvent, 1000, tracingStatus()>
- stringViewWithStringArgumentsEventQueueData(traceFile());
+ thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewWithStringArgumentsTraceEvent, tracingStatus()>
+ stringViewWithStringArgumentsEventQueue(traceFile());
- return stringViewWithStringArgumentsEventQueueData;
+ return stringViewWithStringArgumentsEventQueue;
}
StringEventQueue &stringEventQueue()
{
- thread_local NanotraceHR::EventQueueData<NanotraceHR::StringTraceEvent, 1000, tracingStatus()> eventQueue(
+ thread_local NanotraceHR::EventQueue<NanotraceHR::StringTraceEvent, tracingStatus()> eventQueue(
traceFile());
return eventQueue;
@@ -46,7 +54,6 @@ StringEventQueue &stringEventQueue()
namespace ModelTracing {
namespace {
-using namespace NanotraceHR::Literals;
thread_local Category category_{"model"_t, Tracing::stringEventQueue(), category};
@@ -58,4 +65,27 @@ Category &category()
}
} // namespace ModelTracing
+
+namespace ProjectStorageTracing {
+
+Category &projectStorageCategory()
+{
+ thread_local Category category{"project storage"_t,
+ Tracing::eventQueueWithStringArguments(),
+ projectStorageCategory};
+
+ return category;
+}
+
+Category &projectStorageUpdaterCategory()
+{
+ thread_local Category category{"project storage updater"_t,
+ Tracing::eventQueueWithStringArguments(),
+ projectStorageCategory};
+
+ return category;
+}
+
+} // namespace ProjectStorageTracing
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h
index 31058260d6..3a33834c70 100644
--- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h
+++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h
@@ -44,4 +44,22 @@ using AsynchronousToken = Category::AsynchronousTokenType;
[[gnu::pure]] QMLDESIGNERCORE_EXPORT Category &category();
} // namespace ModelTracing
+
+namespace ProjectStorageTracing {
+constexpr NanotraceHR::Tracing projectStorageTracingStatus()
+{
+#ifdef ENABLE_PROJECT_STORAGE_TRACING
+ return NanotraceHR::Tracing::IsEnabled;
+#else
+ return NanotraceHR::Tracing::IsDisabled;
+#endif
+}
+
+using Category = NanotraceHR::StringViewWithStringArgumentsCategory<projectStorageTracingStatus()>;
+
+[[gnu::pure]] Category &projectStorageCategory();
+
+[[gnu::pure]] Category &projectStorageUpdaterCategory();
+
+} // namespace ProjectStorageTracing
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp
index fcaac762ba..92d80680a9 100644
--- a/src/plugins/qmldesigner/documentmanager.cpp
+++ b/src/plugins/qmldesigner/documentmanager.cpp
@@ -130,7 +130,7 @@ static void openComponentSourcePropertyOfLoader(const ModelNode &modelNode)
}
Core::EditorManager::openEditor(FilePath::fromString(
- componentModelNode.metaInfo().componentFileName()),
+ ModelUtils::componentFilePath(componentModelNode)),
Utils::Id(),
Core::EditorManager::DoNotMakeVisible);
}
@@ -230,7 +230,9 @@ void DocumentManager::setCurrentDesignDocument(Core::IEditor *editor)
auto found = m_designDocuments.find(editor);
if (found == m_designDocuments.end()) {
auto &inserted = m_designDocuments[editor] = std::make_unique<DesignDocument>(
- m_projectManager.projectStorageDependencies(), m_externalDependencies);
+ editor->document()->filePath().toString(),
+ m_projectManager.projectStorageDependencies(),
+ m_externalDependencies);
m_currentDesignDocument = inserted.get();
m_currentDesignDocument->setEditor(editor);
} else {
@@ -266,6 +268,11 @@ void DocumentManager::resetPossibleImports()
}
}
+const GeneratedComponentUtils &DocumentManager::generatedComponentUtils() const
+{
+ return m_generatedComponentUtils;
+}
+
bool DocumentManager::goIntoComponent(const ModelNode &modelNode)
{
QImage image = QmlDesignerPlugin::instance()->viewManager().takeFormEditorScreenshot();
@@ -537,6 +544,13 @@ Utils::FilePath DocumentManager::currentResourcePath()
if (contentFilePath.exists())
return contentFilePath;
+ const auto project = ProjectManager::startupProject();
+ const QString baseName = project->rootProjectDirectory().baseName() + "Content";
+
+ contentFilePath = resourcePath.pathAppended(baseName);
+ if (contentFilePath.exists())
+ return contentFilePath;
+
return resourcePath;
}
diff --git a/src/plugins/qmldesigner/documentmanager.h b/src/plugins/qmldesigner/documentmanager.h
index 090630e5fe..6447694339 100644
--- a/src/plugins/qmldesigner/documentmanager.h
+++ b/src/plugins/qmldesigner/documentmanager.h
@@ -5,6 +5,8 @@
#include "qmldesigner_global.h"
+#include <generatedcomponentutils.h>
+
#include <QObject>
#include <QList>
#include <QLoggingCategory>
@@ -31,6 +33,7 @@ public:
ExternalDependenciesInterface &externalDependencies)
: m_projectManager{projectManager}
, m_externalDependencies{externalDependencies}
+ , m_generatedComponentUtils(externalDependencies)
{}
void setCurrentDesignDocument(Core::IEditor *editor);
@@ -41,6 +44,8 @@ public:
void resetPossibleImports();
+ const GeneratedComponentUtils &generatedComponentUtils() const;
+
static bool goIntoComponent(const ModelNode &modelNode);
static void goIntoComponent(const QString &fileName);
@@ -64,6 +69,7 @@ private:
QPointer<DesignDocument> m_currentDesignDocument;
QmlDesignerProjectManager &m_projectManager;
ExternalDependenciesInterface &m_externalDependencies;
+ GeneratedComponentUtils m_generatedComponentUtils;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/documentwarningwidget.cpp b/src/plugins/qmldesigner/documentwarningwidget.cpp
index 9fb2b87635..e1b8346a66 100644
--- a/src/plugins/qmldesigner/documentwarningwidget.cpp
+++ b/src/plugins/qmldesigner/documentwarningwidget.cpp
@@ -39,8 +39,7 @@ DocumentWarningWidget::DocumentWarningWidget(QWidget *parent)
m_messageLabel->setWordWrap(true);
m_messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
- m_ignoreWarningsCheckBox->setText(tr("Always ignore these warnings about features "
- "not supported by Qt Quick Designer."));
+ m_ignoreWarningsCheckBox->setText(tr("Turn off warnings about unsupported Qt Design Studio features."));
connect(m_navigateLabel, &QLabel::linkActivated, this, [this](const QString &link) {
if (link == QLatin1String("goToCode")) {
@@ -93,7 +92,7 @@ void DocumentWarningWidget::refreshContent()
m_ignoreWarningsCheckBox->hide();
m_continueButton->setText(tr("OK"));
} else {
- m_headerLabel->setText(tr("This QML file contains features which are not supported by Qt Quick Designer at:"));
+ m_headerLabel->setText(tr("This QML file contains features which are not supported by Qt Design Studio at:"));
{
QSignalBlocker blocker(m_ignoreWarningsCheckBox);
m_ignoreWarningsCheckBox->setChecked(!warningsEnabled());
diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp
index 6e8a2b6024..7819c6b49d 100644
--- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp
+++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp
@@ -126,7 +126,7 @@ void PuppetEnvironmentBuilder::addRendering() const
{
m_environment.set("QML_BAD_GUI_RENDER_LOOP", "true");
m_environment.set("QML_PUPPET_MODE", "true");
- m_environment.set("QML_DISABLE_DISK_CACHE", "true");
+ //m_environment.set("QML_DISABLE_DISK_CACHE", "true");
m_environment.set("QMLPUPPET_RENDER_EFFECTS", "true");
if (!m_environment.hasKey("QT_SCREEN_SCALE_FACTORS") && !m_environment.hasKey("QT_SCALE_FACTOR"))
m_environment.set("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h
index db63d894fe..fb691097f7 100644
--- a/src/plugins/qmldesigner/qmldesignerconstants.h
+++ b/src/plugins/qmldesigner/qmldesignerconstants.h
@@ -78,15 +78,19 @@ inline constexpr char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig";
inline constexpr char EDIT3D_CAMERA_SPEED_CONFIG[] = "QmlDesigner.Editor3D.CameraSpeedConfig";
inline constexpr char QML_DESIGNER_SUBFOLDER[] = "/designer/";
-inline constexpr char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles";
+inline constexpr char COMPONENT_BUNDLES_TYPE[] = "ComponentBundles";
+inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "GeneratedComponents";
inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json";
-inline constexpr char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets";
+inline constexpr char OLD_QUICK_3D_ASSETS_FOLDER[] = "Quick3DAssets";
+inline constexpr char QUICK_3D_COMPONENTS_FOLDER[] = "QtQuick3D";
inline constexpr char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon";
inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json";
inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options";
inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene";
-inline constexpr char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports";
-inline constexpr char DEFAULT_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects";
+inline constexpr char OLD_ASSET_IMPORT_FOLDER[] = "asset_imports";
+inline constexpr char OLD_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects";
+inline constexpr char OLD_EFFECTS_FOLDER[] = "Effects";
+inline constexpr char COMPOSED_EFFECTS_TYPE[] = "ComposedEffects";
inline constexpr char MATERIAL_LIB_ID[] = "__materialLibrary__";
inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[]
diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
index 7e28849fbb..9602bf050f 100644
--- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
+++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
@@ -41,6 +41,7 @@
#include <QDirIterator>
#include <QFileSystemWatcher>
+#include <QLibraryInfo>
#include <QQmlEngine>
using namespace std::chrono;
@@ -180,7 +181,7 @@ public:
pathCache.sourceId(SourcePath{project->projectDirectory().toString() + "/."}).internalId())}
{}
Sqlite::Database database;
- ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()};
+ ProjectStorage storage{database, database.isInitialized()};
PathCacheType pathCache{storage};
FileSystem fileSystem{pathCache};
FileStatusCache fileStatusCache{fileSystem};
@@ -281,7 +282,7 @@ AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache()
}
namespace {
-[[maybe_unused]] ProjectStorage<Sqlite::Database> *dummyProjectStorage()
+[[maybe_unused]] ProjectStorage *dummyProjectStorage()
{
return nullptr;
}
@@ -374,6 +375,10 @@ void collectQmldirPaths(const QString &path, QStringList &qmldirPaths)
{
QDirIterator dirIterator{path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories};
+ QString rootQmldirPath = path + "/qmldir";
+ if (!skipPath(path) && QFileInfo::exists(rootQmldirPath))
+ qmldirPaths.push_back(path);
+
while (dirIterator.hasNext()) {
auto directoryPath = dirIterator.next();
@@ -401,17 +406,16 @@ void collectQmldirPaths(const QString &path, QStringList &qmldirPaths)
collectQmldirPaths(qmlPath(target).toString(), qmldirPaths);
}
-[[maybe_unused]] void qtQmldirPathsForLiteDesigner(::ProjectExplorer::Target *target,
- QStringList &qmldirPaths)
+[[maybe_unused]] void qtQmldirPathsForLiteDesigner(QStringList &qmldirPaths)
{
if constexpr (useProjectStorage()) {
- auto qmlRootPath = qmlPath(target).toString();
+ auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
collectQmldirPaths(qmlRootPath + "/QtQml", qmldirPaths);
collectQmldirPaths(qmlRootPath + "/QtQuick", qmldirPaths);
}
}
-QStringList directories(::ProjectExplorer::Target *target)
+[[maybe_unused]] QStringList directories(::ProjectExplorer::Target *target)
{
if (!target)
return {};
@@ -419,12 +423,8 @@ QStringList directories(::ProjectExplorer::Target *target)
QStringList qmldirPaths;
qmldirPaths.reserve(100);
- if constexpr (isUsingQmlDesignerLite()) {
- qtQmldirPathsForLiteDesigner(target, qmldirPaths);
- } else {
- qtQmldirPaths(target, qmldirPaths);
- projectQmldirPaths(target, qmldirPaths);
- }
+ qtQmldirPaths(target, qmldirPaths);
+ projectQmldirPaths(target, qmldirPaths);
std::sort(qmldirPaths.begin(), qmldirPaths.end());
qmldirPaths.erase(std::unique(qmldirPaths.begin(), qmldirPaths.end()), qmldirPaths.end());
@@ -432,7 +432,20 @@ QStringList directories(::ProjectExplorer::Target *target)
return qmldirPaths;
}
-QStringList qmlTypes(::ProjectExplorer::Target *target)
+[[maybe_unused]] QStringList directoriesForLiteDesigner()
+{
+ QStringList qmldirPaths;
+ qmldirPaths.reserve(100);
+
+ qtQmldirPathsForLiteDesigner(qmldirPaths);
+
+ std::sort(qmldirPaths.begin(), qmldirPaths.end());
+ qmldirPaths.erase(std::unique(qmldirPaths.begin(), qmldirPaths.end()), qmldirPaths.end());
+
+ return qmldirPaths;
+}
+
+[[maybe_unused]] QStringList qmlTypes(::ProjectExplorer::Target *target)
{
if (!target)
return {};
@@ -440,10 +453,26 @@ QStringList qmlTypes(::ProjectExplorer::Target *target)
QStringList qmldirPaths;
qmldirPaths.reserve(2);
- const QString installDirectory = qmlPath(target).toString();
+ const QString qmlRootPath = qmlPath(target).toString();
- qmldirPaths.append(installDirectory + "/builtins.qmltypes");
- qmldirPaths.append(installDirectory + "/jsroot.qmltypes");
+ qmldirPaths.append(qmlRootPath + "/builtins.qmltypes");
+ qmldirPaths.append(qmlRootPath + "/jsroot.qmltypes");
+
+ qmldirPaths.append(
+ Core::ICore::resourcePath("qmldesigner/projectstorage/fake.qmltypes").toString());
+
+ return qmldirPaths;
+}
+
+[[maybe_unused]] QStringList qmlTypesForLiteDesigner()
+{
+ QStringList qmldirPaths;
+ qmldirPaths.reserve(2);
+
+ const auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
+
+ qmldirPaths.append(qmlRootPath + "/builtins.qmltypes");
+ qmldirPaths.append(qmlRootPath + "/jsroot.qmltypes");
qmldirPaths.append(
Core::ICore::resourcePath("qmldesigner/projectstorage/fake.qmltypes").toString());
@@ -461,6 +490,11 @@ QString propertyEditorResourcesPath()
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString();
}
+QString qtCreatorItemLibraryPath()
+{
+ return Core::ICore::resourcePath("qmldesigner/itemLibrary").toString();
+}
+
} // namespace
void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project)
@@ -594,9 +628,17 @@ void QmlDesignerProjectManager::update()
if (!m_projectData || !m_projectData->projectStorageData)
return;
- m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget),
- qmlTypes(m_projectData->activeTarget),
- propertyEditorResourcesPath());
+ if constexpr (isUsingQmlDesignerLite()) {
+ m_projectData->projectStorageData->updater.update(directoriesForLiteDesigner(),
+ qmlTypesForLiteDesigner(),
+ propertyEditorResourcesPath(),
+ {qtCreatorItemLibraryPath()});
+ } else {
+ m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget),
+ qmlTypes(m_projectData->activeTarget),
+ propertyEditorResourcesPath(),
+ {qtCreatorItemLibraryPath()});
+ }
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
index 3f3cdb7910..46cb42b60d 100644
--- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
+++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
@@ -374,7 +374,6 @@ MetaInfo {
name: "Keyframe"
category: "none"
version: "1.0"
- requiredImport: "none"
}
}
@@ -386,7 +385,6 @@ MetaInfo {
name: "KeyframeGroup"
category: "none"
version: "1.0"
- requiredImport: "none"
}
}
diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp
index 344e2700d9..17c75a0285 100644
--- a/src/plugins/qmldesigner/settingspage.cpp
+++ b/src/plugins/qmldesigner/settingspage.cpp
@@ -135,7 +135,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie
m_useDefaultPuppetRadioButton = new QRadioButton(tr("Use fallback QML emulation layer"));
m_useDefaultPuppetRadioButton->setToolTip(
- tr("If you select this radio button, Qt Quick Designer always uses the "
+ tr("If you select this radio button, Qt Design Studio always uses the "
"QML emulation layer (QML Puppet) located at the following path."));
m_useDefaultPuppetRadioButton->setChecked(true);
@@ -167,7 +167,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie
m_designerWarningsCheckBox = new QCheckBox(
tr("Warn about unsupported features in .ui.qml files"));
m_designerWarningsCheckBox->setToolTip(
- tr("Warns about QML features that are not properly supported by the Qt Quick Designer."));
+ tr("Warns about QML features that are not properly supported by the Qt Design Studio."));
m_designerWarningsUiQmlfiles = new QCheckBox(
tr("Warn about using .qml files instead of .ui.qml files"));
diff --git a/src/plugins/qmldesigner/utils/asset.cpp b/src/plugins/qmldesigner/utils/asset.cpp
index 2984a4d890..ec1e4312e4 100644
--- a/src/plugins/qmldesigner/utils/asset.cpp
+++ b/src/plugins/qmldesigner/utils/asset.cpp
@@ -1,15 +1,19 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include <QImageReader>
-
#include "asset.h"
+#include "hdrimage.h"
+
+#include <QImageReader>
+#include <QPixmap>
+
namespace QmlDesigner {
Asset::Asset(const QString &filePath)
: m_filePath(filePath)
{
+ m_fileName = filePath.split('/').last();
const QStringList split = filePath.split('.');
if (split.size() > 1)
m_suffix = "*." + split.last().toLower();
@@ -105,6 +109,19 @@ bool Asset::isSupported(const QString &path)
return supportedSuffixes().contains(path);
}
+QPixmap Asset::pixmap(const QSize &size) const
+{
+ if (!isImage() && !isHdrFile())
+ return {};
+
+ QPixmap icon = isHdrFile() ? HdrImage{m_filePath}.toPixmap() : QPixmap{m_filePath};
+
+ if (size.isValid())
+ icon = icon.scaled(size, Qt::KeepAspectRatio);
+
+ return icon;
+}
+
Asset::Type Asset::type() const
{
return m_type;
@@ -175,6 +192,11 @@ const QString Asset::id() const
return m_filePath;
}
+const QString Asset::fileName() const
+{
+ return m_fileName;
+}
+
bool Asset::isSupported() const
{
return m_type != Asset::Type::Unknown;
diff --git a/src/plugins/qmldesigner/utils/asset.h b/src/plugins/qmldesigner/utils/asset.h
index cb09f3a5ee..a5c5899f34 100644
--- a/src/plugins/qmldesigner/utils/asset.h
+++ b/src/plugins/qmldesigner/utils/asset.h
@@ -3,8 +3,11 @@
#pragma once
+#include <QSize>
#include <QString>
+QT_FORWARD_DECLARE_CLASS(QPixmap)
+
namespace QmlDesigner {
class Asset
@@ -37,7 +40,9 @@ public:
const QString suffix() const;
const QString id() const;
+ const QString fileName() const;
bool hasSuffix() const;
+ QPixmap pixmap(const QSize &size = {}) const;
Type type() const;
bool isImage() const;
@@ -58,6 +63,7 @@ private:
void resolveType();
QString m_filePath;
+ QString m_fileName;
QString m_suffix;
Type m_type = Unknown;
};
diff --git a/src/plugins/qmldesigner/utils/imageutils.cpp b/src/plugins/qmldesigner/utils/imageutils.cpp
index 8fa3131cd3..42df6184b9 100644
--- a/src/plugins/qmldesigner/utils/imageutils.cpp
+++ b/src/plugins/qmldesigner/utils/imageutils.cpp
@@ -11,7 +11,7 @@
namespace QmlDesigner {
-QString ImageUtils::imageInfo(const QSize &dimensions, qint64 sizeInBytes)
+QString ImageUtils::imageInfoString(const QSize &dimensions, qint64 sizeInBytes)
{
return QLatin1String("%1 x %2\n%3")
.arg(QString::number(dimensions.width()),
@@ -20,7 +20,7 @@ QString ImageUtils::imageInfo(const QSize &dimensions, qint64 sizeInBytes)
sizeInBytes, 2, QLocale::DataSizeTraditionalFormat));
}
-QString QmlDesigner::ImageUtils::imageInfo(const QString &path)
+QPair<QSize, qint64> QmlDesigner::ImageUtils::imageInfo(const QString &path)
{
QFileInfo info(path);
if (!info.exists())
@@ -52,7 +52,13 @@ QString QmlDesigner::ImageUtils::imageInfo(const QString &path)
if (width <= 0 || height <= 0)
return {};
- return imageInfo(QSize(width, height), info.size());
+ return {QSize(width, height), info.size()};
+}
+
+QString ImageUtils::imageInfoString(const QString &path)
+{
+ QPair<QSize, qint64> info = imageInfo(path);
+ return imageInfoString(info.first, info.second);
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/imageutils.h b/src/plugins/qmldesigner/utils/imageutils.h
index a4036614a3..dae64423bb 100644
--- a/src/plugins/qmldesigner/utils/imageutils.h
+++ b/src/plugins/qmldesigner/utils/imageutils.h
@@ -12,8 +12,9 @@ class ImageUtils
public:
ImageUtils();
- static QString imageInfo(const QSize &dimensions, qint64 sizeInBytes);
- static QString imageInfo(const QString &path);
+ static QPair<QSize, qint64> imageInfo(const QString &path);
+ static QString imageInfoString(const QString &path);
+ static QString imageInfoString(const QSize &dimensions, qint64 sizeInBytes);
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesignerbase/utils/designerpaths.cpp b/src/plugins/qmldesignerbase/utils/designerpaths.cpp
index da4ce8ce08..f938c58e04 100644
--- a/src/plugins/qmldesignerbase/utils/designerpaths.cpp
+++ b/src/plugins/qmldesignerbase/utils/designerpaths.cpp
@@ -20,9 +20,7 @@ Utils::FilePath defaultExamplesPath()
Utils::FilePath defaultBundlesPath()
{
- QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost()
- ? QStandardPaths::HomeLocation
- : QStandardPaths::DocumentsLocation;
+ QStandardPaths::StandardLocation location = QStandardPaths::DocumentsLocation;
return Utils::FilePath::fromString(QStandardPaths::writableLocation(location))
.pathAppended("QtDesignStudio/bundles");
diff --git a/src/plugins/qmlpreview/CMakeLists.txt b/src/plugins/qmlpreview/CMakeLists.txt
index 4c1889143a..440b407b70 100644
--- a/src/plugins/qmlpreview/CMakeLists.txt
+++ b/src/plugins/qmlpreview/CMakeLists.txt
@@ -4,7 +4,7 @@ add_qtc_plugin(QmlPreview
DEPENDS QmlJS Qt::QmlPrivate
PLUGIN_DEPENDS
Core ProjectExplorer QmlJSTools QtSupport
- ResourceEditor QmlProjectManager
+ ResourceEditor QmlProjectManager TextEditor
SOURCES
qmlpreviewclient.cpp qmlpreviewclient.h
qmlpreviewconnectionmanager.cpp qmlpreviewconnectionmanager.h
diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp
index 31638f7662..70f905448e 100644
--- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp
+++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp
@@ -166,12 +166,8 @@ QmlPreviewPluginPrivate::QmlPreviewPluginPrivate(QmlPreviewPlugin *parent)
runPreviewAction->setEnabled(ProjectManager::startupProject() != nullptr);
connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, runPreviewAction,
&QAction::setEnabled);
- connect(runPreviewAction, &QAction::triggered, this, [runPreviewAction, this] {
+ connect(runPreviewAction, &QAction::triggered, this, [&, runPreviewAction] {
runPreviewAction->setEnabled(false);
- attachToEditorManager();
- setDirty();
- onEditorChanged(Core::EditorManager::currentEditor());
-
if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current())
m_localeIsoCode = multiLanguageAspect->currentLocale();
bool skipDeploy = false;
@@ -425,7 +421,7 @@ void QmlPreviewPluginPrivate::onEditorAboutToClose(Core::IEditor *editor)
void QmlPreviewPluginPrivate::setDirty()
{
m_dirty = true;
- QTimer::singleShot(1000, this, [this](){
+ QTimer::singleShot(1000, this, [&](){
if (m_dirty && m_lastEditor) {
m_dirty = false;
checkEditor();
@@ -435,6 +431,10 @@ void QmlPreviewPluginPrivate::setDirty()
void QmlPreviewPlugin::addPreview(RunControl *preview)
{
+ d->attachToEditorManager();
+ d->setDirty();
+ d->onEditorChanged(Core::EditorManager::currentEditor());
+
d->m_runningPreviews.append(preview);
emit runningPreviewsChanged(d->m_runningPreviews);
}
diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt
index ab96a5f7df..68bb545011 100644
--- a/src/plugins/qmlprojectmanager/CMakeLists.txt
+++ b/src/plugins/qmlprojectmanager/CMakeLists.txt
@@ -42,14 +42,10 @@ extend_qtc_plugin(QmlProjectManager
PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/cmakegen
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/cmakegen
SOURCES
- checkablefiletreeitem.cpp checkablefiletreeitem.h
- cmakegeneratordialog.cpp cmakegeneratordialog.h
- cmakegeneratordialogtreemodel.cpp cmakegeneratordialogtreemodel.h
- cmakeprojectconverter.cpp cmakeprojectconverter.h
- cmakeprojectconverterdialog.cpp cmakeprojectconverterdialog.h
- generatecmakelists.cpp generatecmakelists.h
- generatecmakelistsconstants.h
cmakegenerator.cpp cmakegenerator.h
+ cmakewriter.cpp cmakewriter.h
+ cmakewriterv0.cpp cmakewriterv0.h
+ cmakewriterv1.cpp cmakewriterv1.h
boilerplate.qrc
)
diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
index a0b8263900..00e501d6f8 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
+++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp
@@ -93,6 +93,7 @@ QmlBuildSystem::QmlBuildSystem(Target *target)
connect(target->project(), &Project::projectFileIsDirty, this, [this] {
refresh(RefreshOptions::Project);
m_cmakeGen->initialize(qmlProject());
+ m_cmakeGen->updateMenuAction();
updateMcuBuildStep(project()->activeTarget(), qtForMCUs());
});
@@ -501,6 +502,17 @@ void QmlBuildSystem::setPrimaryLanguage(QString language)
m_projectItem->setPrimaryLanguage(language);
}
+bool QmlBuildSystem::enableCMakeGeneration() const
+{
+ return m_projectItem->enableCMakeGeneration();
+}
+
+void QmlBuildSystem::setEnableCMakeGeneration(bool enable)
+{
+ if (enable != enableCMakeGeneration())
+ m_projectItem->setEnableCMakeGeneration(enable);
+}
+
void QmlBuildSystem::refreshFiles(const QSet<QString> & /*added*/, const QSet<QString> &removed)
{
if (m_blockFilesUpdate) {
diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
index 524af5cd1b..e3f9b99adb 100644
--- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
+++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h
@@ -83,6 +83,9 @@ public:
QString primaryLanguage() const;
void setPrimaryLanguage(QString language);
+ bool enableCMakeGeneration() const;
+ void setEnableCMakeGeneration(bool enable);
+
bool forceFreeType() const;
bool widgetApp() const;
diff --git a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc
index 10fab59838..4960324ab7 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc
+++ b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc
@@ -1,15 +1,15 @@
<RCC>
- <qresource prefix="/boilerplatetemplates">
- <file>gencmakeroot.tpl</file>
- <file>gencmakemodule.tpl</file>
- <file>gencmakeheadercomponents.tpl</file>
- <file>qmlprojectmaincpp.tpl</file>
- <file>qmlprojectmaincppheader.tpl</file>
- <file>qmlprojectenvheader.tpl</file>
- <file>qmlprojectmodules.tpl</file>
- <file>qmlprojectmaincmakelists.tpl</file>
- <file>qmlprojectmodulecmakelists.tpl</file>
- <file>qmlprojectmainqml.tpl</file>
- <file>qmlprojectappmainqml.tpl</file>
+ <qresource prefix="/templates">
+ <file alias="cmakeroot_v0">templates/cmakeroot_v0.tpl</file>
+ <file alias="cmakeroot_v1">templates/cmakeroot_v1.tpl</file>
+ <file alias="main_cpp_v0">templates/main_cpp_v0.tpl</file>
+ <file alias="main_cpp_v1">templates/main_cpp_v1.tpl</file>
+ <file alias="cmakemodule_v1">templates/cmakemodule_v1.tpl</file>
+ <file alias="insight">templates/insight.tpl</file>
+ <file alias="qmlcomponents">templates/qmlcomponents.tpl</file>
+ <file alias="environment_h">templates/environment_h.tpl</file>
+ <file alias="import_qml_components_h">templates/import_qml_components_h.tpl</file>
+ <file alias="qtquickcontrols_conf">templates/qtquickcontrols2_conf.tpl</file>
+ <file alias="cmake_shared">templates/cmakelists_txt_shared.tpl</file>
</qresource>
</RCC>
diff --git a/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.cpp b/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.cpp
deleted file mode 100644
index 9eb45d9150..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "checkablefiletreeitem.h"
-
-using namespace Utils;
-
-namespace QmlProjectManager {
-
-CheckableFileTreeItem::CheckableFileTreeItem(const FilePath &filePath)
- :QStandardItem(filePath.toString())
-{
- Qt::ItemFlags itemFlags = flags();
- if (!isDir())
- itemFlags |= Qt::ItemIsUserCheckable;
- itemFlags &= ~(Qt::ItemIsEditable | Qt::ItemIsSelectable);
- setFlags(itemFlags);
-}
-
-const FilePath CheckableFileTreeItem::toFilePath() const
-{
- return FilePath::fromString(text());
-}
-
-bool CheckableFileTreeItem::isFile() const
-{
- return FilePath::fromString(text()).isFile();
-}
-
-bool CheckableFileTreeItem::isDir() const
-{
- return FilePath::fromString(text()).isDir();
-}
-
-void CheckableFileTreeItem::setChecked(bool checked)
-{
- this->checked = checked;
-}
-
-bool CheckableFileTreeItem::isChecked() const
-{
- return this->checked;
-}
-
-} //QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.h b/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.h
deleted file mode 100644
index c3c5145301..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#ifndef CHECKABLEFILETREEITEM_H
-#define CHECKABLEFILETREEITEM_H
-
-#include <utils/fileutils.h>
-
-#include <QStandardItem>
-
-namespace QmlProjectManager {
-
-class CheckableFileTreeItem : public QStandardItem
-{
-public:
- explicit CheckableFileTreeItem(const Utils::FilePath &text = Utils::FilePath());
-
- const Utils::FilePath toFilePath() const;
- bool isFile() const;
- bool isDir() const;
-
- bool isChecked() const;
- void setChecked(bool checked);
-
-private:
- bool checked;
-};
-
-} //QmlProjectManager
-
-#endif // CHECKABLEFILETREEITEM_H
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp
index 736ac04ec6..659c544ebd 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp
@@ -2,13 +2,23 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "cmakegenerator.h"
-#include "generatecmakelistsconstants.h"
+
+#include "qmlprojectmanager/qmlproject.h"
+#include "qmlprojectmanager/qmlprojectconstants.h"
+#include "qmlprojectmanager/qmlprojectmanagertr.h"
#include "projectexplorer/projectmanager.h"
#include "projectexplorer/projectnodes.h"
-#include "qmlprojectmanager/qmlproject.h"
+#include "projectexplorer/taskhub.h"
+#include "utils/filenamevalidatinglineedit.h"
+
+#include "coreplugin/actionmanager/actionmanager.h"
+#include "coreplugin/actionmanager/actioncontainer.h"
+
+#include <QDirIterator>
#include <QRegularExpression>
+#include <QMenu>
#include <set>
@@ -16,30 +26,64 @@ namespace QmlProjectManager {
namespace GenerateCmake {
-const char TEMPLATE_CMAKELISTS_ROOT[] = ":/boilerplatetemplates/gencmakeroot.tpl";
-const char TEMPLATE_CMAKELISTS_MODULE[] = ":/boilerplatetemplates/gencmakemodule.tpl";
+void CMakeGenerator::createMenuAction(QObject *parent)
+{
+ Core::ActionContainer *fileMenu = Core::ActionManager::actionContainer(
+ Core::Constants::M_FILE);
+ Core::ActionContainer *exportMenu = Core::ActionManager::createMenu(
+ QmlProjectManager::Constants::EXPORT_MENU);
+
+ exportMenu->menu()->setTitle(Tr::tr("Export Project"));
+ exportMenu->appendGroup(QmlProjectManager::Constants::G_EXPORT_GENERATE);
+ fileMenu->addMenu(exportMenu, Core::Constants::G_FILE_EXPORT);
+
+ auto action = new QAction(Tr::tr("Enable Automatic CMake Generation"), parent);
+ action->setEnabled(false);
+ action->setCheckable(true);
+
+ Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.EnableCMakeGeneration");
+ exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE);
+
+ QObject::connect(
+ ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
+ [action]() {
+ if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) {
+ action->setEnabled(!buildSystem->qtForMCUs());
+ action->setChecked(buildSystem->enableCMakeGeneration());
+ }
+ }
+ );
+
+ QObject::connect(action, &QAction::toggled, [](bool checked) {
+ if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem())
+ buildSystem->setEnableCMakeGeneration(checked);
+ });
+}
+
+void CMakeGenerator::logIssue(ProjectExplorer::Task::TaskType type, const QString &text, const Utils::FilePath &file)
+{
+ ProjectExplorer::BuildSystemTask task(type, text, file);
+ ProjectExplorer::TaskHub::addTask(task);
+ ProjectExplorer::TaskHub::requestPopup();
+}
-const char TEMPLATE_SOURCE_MAIN[] = ":/boilerplatetemplates/qmlprojectmaincpp.tpl";
-const char TEMPLATE_HEADER_IMPORT_COMPS[] = ":/boilerplatetemplates/gencmakeheadercomponents.tpl";
-const char TEMPLATE_HEADER_IMPORT_PLUGINS[] = ":/boilerplatetemplates/qmlprojectmaincppheader.tpl";
-const char TEMPLATE_HEADER_ENVIRONMENT[] = ":/boilerplatetemplates/qmlprojectenvheader.tpl";
+void CMakeGenerator::updateMenuAction()
+{
+ QTC_ASSERT(buildSystem(), return);
-const char DO_NOT_EDIT_FILE_COMMENT[]
- = "### This file is automatically generated by Qt Design Studio.\n"
- "### Do not change\n\n";
+ Core::Command *cmd = Core::ActionManager::command("QmlProject.EnableCMakeGeneration");
+ if (!cmd)
+ return;
-const char TEMPLATE_BIG_RESOURCES[] = R"(
-qt6_add_resources(%1 %2
- BIG_RESOURCES
- PREFIX "%3"
- VERSION 1.0
- FILES %4
-))";
+ QAction *action = cmd->action();
+ if (!action)
+ return;
-const char TEMPLATE_LINK_LIBRARIES[] = R"(
-target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
-%3
-))";
+ bool enabled = buildSystem()->enableCMakeGeneration();
+ if (enabled != action->isChecked())
+ action->setChecked(enabled);
+}
CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent)
: QObject(parent)
@@ -47,6 +91,44 @@ CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent)
, m_buildSystem(bs)
{}
+const QmlProject *CMakeGenerator::qmlProject() const
+{
+ if (m_buildSystem)
+ return m_buildSystem->qmlProject();
+ return nullptr;
+}
+
+const QmlBuildSystem *CMakeGenerator::buildSystem() const
+{
+ return m_buildSystem;
+}
+
+bool CMakeGenerator::findFile(const Utils::FilePath& file) const
+{
+ return findFile(m_root, file);
+}
+
+bool CMakeGenerator::isRootNode(const NodePtr &node) const
+{
+ return node->name == "Main";
+}
+
+bool CMakeGenerator::hasChildModule(const NodePtr &node) const
+{
+ for (const NodePtr &child : node->subdirs) {
+ if (child->type == Node::Type::Module)
+ return true;
+ if (hasChildModule(child))
+ return true;
+ }
+ return false;
+}
+
+QString CMakeGenerator::projectName() const
+{
+ return m_projectName;
+}
+
void CMakeGenerator::setEnabled(bool enabled)
{
m_enabled = enabled;
@@ -57,11 +139,11 @@ void CMakeGenerator::initialize(QmlProject *project)
if (!m_enabled)
return;
- m_srcs.clear();
m_moduleNames.clear();
+ m_writer = CMakeWriter::create(this);
m_root = std::make_shared<Node>();
- m_root->module = true;
+ m_root->type = Node::Type::App;
m_root->uri = QString("Main");
m_root->name = QString("Main");
m_root->dir = project->rootProjectDirectory();
@@ -73,7 +155,9 @@ void CMakeGenerator::initialize(QmlProject *project)
parseSourceTree();
createCMakeFiles(m_root);
- createEntryPoints(m_root);
+ createSourceFiles();
+
+ compareWithFileSystem(m_root);
}
void CMakeGenerator::update(const QSet<QString> &added, const QSet<QString> &removed)
@@ -81,304 +165,96 @@ void CMakeGenerator::update(const QSet<QString> &added, const QSet<QString> &rem
if (!m_enabled)
return;
+ QTC_ASSERT(m_writer, return);
+
std::set<NodePtr> dirtyModules;
for (const QString &add : added) {
const Utils::FilePath path = Utils::FilePath::fromString(add);
- if (auto node = findOrCreateNode(m_root, path)) {
+ if (auto node = findOrCreateNode(m_root, path.parentDir())) {
insertFile(node, path);
if (auto module = findModuleFor(node))
dirtyModules.insert(module);
} else {
- qDebug() << "CmakeGen: Failed to find Folder node " << path;
+ QString text("Failed to find Folder for file");
+ logIssue(ProjectExplorer::Task::Error, text, path);
}
}
for (const QString &remove : removed) {
const Utils::FilePath path = Utils::FilePath::fromString(remove);
- if (auto node = findNode(m_root, path)) {
+ if (auto node = findNode(m_root, path.parentDir())) {
removeFile(node, path);
if (auto module = findModuleFor(node))
dirtyModules.insert(module);
}
}
- for (auto module : dirtyModules)
- createModuleCMakeFile(module);
-}
-
-std::vector<Utils::FilePath> CMakeGenerator::files(const NodePtr &node,
- const FileGetter &getter) const
-{
- std::vector<Utils::FilePath> out = getter(node);
- for (const CMakeGenerator::NodePtr &child : node->subdirs) {
- if (child->module)
- continue;
-
- auto childFiles = files(child, getter);
- out.insert(out.end(), childFiles.begin(), childFiles.end());
- }
- return out;
-}
-
-std::vector<Utils::FilePath> CMakeGenerator::qmlFiles(const NodePtr &node) const
-{
- return files(node, [](const NodePtr &n) { return n->files; });
+ createCMakeFiles(m_root);
+ createSourceFiles();
}
-std::vector<Utils::FilePath> CMakeGenerator::singletons(const NodePtr &node) const
+bool CMakeGenerator::isQml(const Utils::FilePath &path) const
{
- return files(node, [](const NodePtr &n) { return n->singletons; });
+ const QString suffix = path.suffix();
+ return suffix == "qml" || suffix == "ui.qml";
}
-std::vector<Utils::FilePath> CMakeGenerator::resources(const NodePtr &node) const
+bool CMakeGenerator::isResource(const Utils::FilePath &path) const
{
- return files(node, [](const NodePtr &n) { return n->resources; });
+ static const QStringList suffixes = {
+ "json", "mesh", "dae", "qad", "hints", "png", "hdr", "ttf", "jpg",
+ "jpeg", "js", "qsb", "frag", "frag.qsb", "vert", "vert.qsb", "svg",
+ "ktx", "bmp", "gif", "webp", "tiff"};
+ return suffixes.contains(path.suffix(), Qt::CaseInsensitive);
}
-std::vector<Utils::FilePath> CMakeGenerator::sources(const NodePtr &node) const
+bool CMakeGenerator::ignoreFile(const Utils::FilePath &path) const
{
- return files(node, [](const NodePtr &n) { return n->sources; });
+ static const QStringList suffixes = { "hints" };
+ return suffixes.contains(path.suffix(), Qt::CaseInsensitive);
}
void CMakeGenerator::createCMakeFiles(const NodePtr &node) const
{
+ QTC_ASSERT(m_writer, return);
+
if (isRootNode(node))
- createMainCMakeFile(node);
+ m_writer->writeRootCMakeFile(node);
- if (node->module || hasChildModule(node))
- createModuleCMakeFile(node);
+ if (node->type == Node::Type::Module || (hasChildModule(node)))
+ m_writer->writeModuleCMakeFile(node, m_root);
for (const NodePtr &n : node->subdirs)
createCMakeFiles(n);
}
-void CMakeGenerator::createMainCMakeFile(const NodePtr &node) const
-{
- const QString appName = m_projectName + "App";
-
- const QString qtcontrolsConfFile = makeEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF);
-
- QString fileSection = "";
- if (!qtcontrolsConfFile.isEmpty())
- fileSection = QString(" FILES\n %1").arg(qtcontrolsConfFile);
-
- const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_ROOT);
- const QString fileContent = fileTemplate.arg(appName, m_srcs.join(" "), fileSection);
-
- const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt");
- writeFile(file, fileContent);
-}
-
-void CMakeGenerator::createModuleCMakeFile(const NodePtr &node) const
+void CMakeGenerator::createSourceFiles() const
{
- Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt");
+ QTC_ASSERT(m_writer, return);
- if (!node->module && hasChildModule(node)) {
- QString content(DO_NOT_EDIT_FILE_COMMENT);
- content.append(makeSubdirectoriesBlock(node));
- writeFile(writeToFile, content);
- return;
- }
-
- QString templatePrefix;
- templatePrefix.append(makeSubdirectoriesBlock(node));
- templatePrefix.append(makeSingletonBlock(node));
-
- auto [resources, bigResources] = makeResourcesBlocks(node);
- QString moduleContent;
- moduleContent.append(makeQmlFilesBlock(node));
- moduleContent.append(resources);
-
- QString templatePostfix;
- templatePostfix.append(bigResources);
-
- if (isRootNode(node)) {
- writeToFile = node->dir.pathAppended("qmlModules");
- QString pluginNames;
- for (const QString &moduleName : m_moduleNames)
- pluginNames.append(" " + moduleName + "plugin\n");
-
- templatePostfix += QString::fromUtf8(TEMPLATE_LINK_LIBRARIES, -1).arg(pluginNames);
+ NodePtr sourceNode = {};
+ for (const NodePtr &child : m_root->subdirs) {
+ if (child->name == m_writer->sourceDirName())
+ sourceNode = child;
}
- const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_MODULE);
- const QString fileContent
- = fileTemplate.arg(node->name, node->uri, templatePrefix, moduleContent, templatePostfix);
-
- writeFile(writeToFile, fileContent);
-}
-
-void CMakeGenerator::createEntryPoints(const NodePtr &node) const
-{
- createMainCppFile(node);
-}
-
-void CMakeGenerator::createMainCppFile(const NodePtr &node) const
-{
- const Utils::FilePath srcDir = node->dir.pathAppended(Constants::DIRNAME_CPP);
- if (!srcDir.exists()) {
- srcDir.createDir();
-
- const Utils::FilePath componentsHeaderPath = srcDir.pathAppended(
- "import_qml_components_plugins.h");
-
- const QString componentsHeaderContent = readTemplate(TEMPLATE_HEADER_IMPORT_COMPS);
- writeFile(componentsHeaderPath, componentsHeaderContent);
-
- const Utils::FilePath cppFilePath = srcDir.pathAppended("main.cpp");
- const QString cppContent = readTemplate(TEMPLATE_SOURCE_MAIN);
- writeFile(cppFilePath, cppContent);
-
- const Utils::FilePath envHeaderPath = srcDir.pathAppended("app_environment.h");
- if (m_buildSystem) {
- QString environment;
- const QString qtcontrolsConfFile = makeEnvironmentVariable(
- Constants::ENV_VARIABLE_CONTROLCONF);
- for (Utils::EnvironmentItem &envItem : m_buildSystem->environment()) {
- QString key = envItem.name;
- QString value = envItem.value;
- if (value == qtcontrolsConfFile)
- value.prepend(":/");
- environment.append(QString(" qputenv(\"%1\", \"%2\");\n").arg(key).arg(value));
- }
- const QString envHeaderContent
- = readTemplate(TEMPLATE_HEADER_ENVIRONMENT).arg(environment);
- writeFile(envHeaderPath, envHeaderContent);
- }
- }
-
- QString moduleContent;
- for (const QString &module : m_moduleNames)
- moduleContent.append(QString("Q_IMPORT_QML_PLUGIN(%1)\n").arg(module + "Plugin"));
-
- const QString headerContent = readTemplate(TEMPLATE_HEADER_IMPORT_PLUGINS).arg(moduleContent);
- const Utils::FilePath headerFilePath = srcDir.pathAppended("import_qml_plugins.h");
- writeFile(headerFilePath, headerContent);
-}
-
-void CMakeGenerator::writeFile(const Utils::FilePath &path, const QString &content) const
-{
- QFile fileHandle(path.toString());
- fileHandle.open(QIODevice::WriteOnly);
- QTextStream stream(&fileHandle);
- stream << content;
- fileHandle.close();
-}
-
-QString CMakeGenerator::makeRelative(const NodePtr &node, const Utils::FilePath &path) const
-{
- const QString dir = node->dir.toString();
- return "\"" + Utils::FilePath::calcRelativePath(path.toString(), dir) + "\"";
-}
-
-QString CMakeGenerator::makeEnvironmentVariable(const QString &key) const
-{
- QString value;
- if (m_buildSystem) {
- auto envItems = m_buildSystem->environment();
- auto confEnv = std::find_if(envItems.begin(),
- envItems.end(),
- [key](Utils::EnvironmentItem &item) { return item.name == key; });
- if (confEnv != envItems.end())
- value = confEnv->value;
- }
- return value;
-}
-
-QString CMakeGenerator::makeSingletonBlock(const NodePtr &node) const
-{
- const QString setProperties(
- "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n");
-
- QString str;
- for (const Utils::FilePath &path : node->singletons)
- str.append(setProperties.arg(path.fileName()).arg("QT_QML_SINGLETON_TYPE").arg("true"));
- return str;
-}
-
-QString CMakeGenerator::makeSubdirectoriesBlock(const NodePtr &node) const
-{
- QString str;
- for (const NodePtr &n : node->subdirs) {
- if (n->module || hasChildModule(n))
- str.append(QString("add_subdirectory(%1)\n").arg(n->dir.fileName()));
- }
- return str;
-}
-
-QString CMakeGenerator::makeQmlFilesBlock(const NodePtr &node) const
-{
- QString qmlFileContent;
- for (const Utils::FilePath &path : qmlFiles(node))
- qmlFileContent.append(QString(" %1\n").arg(makeRelative(node, path)));
-
- if (isRootNode(node) && qmlFileContent.isEmpty())
- qmlFileContent.append(QString(" %1\n").arg("\"main.qml\""));
-
- QString str;
- if (!qmlFileContent.isEmpty())
- str.append(QString(" QML_FILES\n%1").arg(qmlFileContent));
-
- return str;
-}
-
-std::tuple<QString, QString> CMakeGenerator::makeResourcesBlocks(const NodePtr &node) const
-{
- QString resourcesOut;
- QString bigResourcesOut;
-
- QString resourceFiles;
- std::vector<QString> bigResources;
- for (const Utils::FilePath &path : resources(node)) {
- if (path.fileSize() > 5000000) {
- bigResources.push_back(makeRelative(node, path));
- continue;
- }
- resourceFiles.append(QString(" %1\n").arg(makeRelative(node, path)));
- }
-
- if (!resourceFiles.isEmpty())
- resourcesOut.append(QString(" RESOURCES\n%1").arg(resourceFiles));
-
- QString templatePostfix;
- if (!bigResources.empty()) {
- QString resourceContent;
- for (const QString &res : bigResources)
- resourceContent.append(QString("\n %1").arg(res));
-
- const QString prefixPath = QString(node->uri).replace('.', '/');
- const QString prefix = "/qt/qml/" + prefixPath;
- const QString resourceName = node->name + "BigResource";
-
- bigResourcesOut = QString::fromUtf8(TEMPLATE_BIG_RESOURCES, -1)
- .arg(node->name, resourceName, prefix, resourceContent);
- }
-
- return {resourcesOut, bigResourcesOut};
-}
-
-QString CMakeGenerator::readTemplate(const QString &templatePath) const
-{
- QFile templatefile(templatePath);
- templatefile.open(QIODevice::ReadOnly);
- QTextStream stream(&templatefile);
- QString content = stream.readAll();
- templatefile.close();
- return content;
+ if (sourceNode)
+ m_writer->writeSourceFiles(sourceNode, m_root);
}
void CMakeGenerator::readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const
{
- node->module = true;
+ node->type = Node::Type::Module;
QFile f(filePath.toString());
f.open(QIODevice::ReadOnly);
QTextStream stream(&f);
Utils::FilePath dir = filePath.parentDir();
+ static const QRegularExpression whitespaceRegex("\\s+");
while (!stream.atEnd()) {
const QString line = stream.readLine();
- const QStringList tokenizedLine = line.split(QRegularExpression("\\s+"));
+ const QStringList tokenizedLine = line.split(whitespaceRegex);
const QString maybeFileName = tokenizedLine.last();
if (tokenizedLine.first().compare("module", Qt::CaseInsensitive) == 0) {
node->uri = tokenizedLine.last();
@@ -392,11 +268,11 @@ void CMakeGenerator::readQmlDir(const Utils::FilePath &filePath, NodePtr &node)
f.close();
}
-CMakeGenerator::NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const
+NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const
{
NodePtr current = node;
while (current->parent) {
- if (current->module)
+ if (current->type == Node::Type::Module)
return current;
current = current->parent;
@@ -404,11 +280,10 @@ CMakeGenerator::NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const
return nullptr;
}
-CMakeGenerator::NodePtr CMakeGenerator::findNode(NodePtr &node, const Utils::FilePath &path) const
+NodePtr CMakeGenerator::findNode(NodePtr &node, const Utils::FilePath &path) const
{
- const Utils::FilePath parentDir = path.parentDir();
for (NodePtr &child : node->subdirs) {
- if (child->dir == parentDir)
+ if (child->dir == path)
return child;
if (path.isChildOf(child->dir))
return findNode(child, path);
@@ -416,8 +291,7 @@ CMakeGenerator::NodePtr CMakeGenerator::findNode(NodePtr &node, const Utils::Fil
return nullptr;
}
-CMakeGenerator::NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node,
- const Utils::FilePath &path) const
+NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node, const Utils::FilePath &path) const
{
if (auto found = findNode(node, path))
return found;
@@ -425,31 +299,74 @@ CMakeGenerator::NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node,
if (!path.isChildOf(node->dir))
return nullptr;
- const Utils::FilePath parentDir = path.parentDir();
- const Utils::FilePath relative = parentDir.relativeChildPath(node->dir);
+ auto findSubDir = [](NodePtr &node, const Utils::FilePath &path) -> NodePtr {
+ for (NodePtr child : node->subdirs) {
+ if (child->dir == path)
+ return child;
+ }
+ return nullptr;
+ };
+
+ const Utils::FilePath relative = path.relativeChildPath(node->dir);
const QChar separator = relative.pathComponentSeparator();
const QList<QStringView> components = relative.pathView().split(separator);
- NodePtr last = node;
+ NodePtr lastNode = node;
for (const auto &comp : components) {
+
+ Utils::FilePath subPath = lastNode->dir.pathAppended(comp.toString());
+ if (NodePtr sub = findSubDir(lastNode, subPath)) {
+ lastNode = sub;
+ continue;
+ }
NodePtr newNode = std::make_shared<Node>();
- newNode->parent = last;
+ newNode->parent = lastNode;
newNode->name = comp.toString();
- newNode->dir = last->dir.pathAppended(comp.toString());
- last->subdirs.push_back(newNode);
- last = newNode;
+ newNode->dir = subPath;
+ lastNode->subdirs.push_back(newNode);
+ lastNode = newNode;
}
- return last;
+ return lastNode;
+}
+
+bool findFileWithGetter(const Utils::FilePath &file, const NodePtr &node, const FileGetter &getter)
+{
+ for (const auto &f : getter(node)) {
+ if (f == file)
+ return true;
+ }
+ for (const auto &subdir : node->subdirs) {
+ if (findFileWithGetter(file, subdir, getter))
+ return true;
+ }
+ return false;
+}
+
+bool CMakeGenerator::findFile(const NodePtr &node, const Utils::FilePath &file) const
+{
+ if (isResource(file)) {
+ return findFileWithGetter(file, node, [](const NodePtr &n) { return n->resources; });
+ } else if (isQml(file)) {
+ if (findFileWithGetter(file, node, [](const NodePtr &n) { return n->files; }))
+ return true;
+ else if (findFileWithGetter(file, node, [](const NodePtr &n) { return n->singletons; }))
+ return true;
+ }
+ return false;
}
void CMakeGenerator::insertFile(NodePtr &node, const Utils::FilePath &path) const
{
+ QString error;
+ if (!Utils::FileNameValidatingLineEdit::validateFileName(path.fileName(), false, &error))
+ logIssue(ProjectExplorer::Task::Error, error, path);
+
if (path.fileName() == "qmldir") {
readQmlDir(path, node);
- } else if (path.suffix() == "qml" || path.suffix() == "ui.qml") {
- node->files.push_back(path);
} else if (path.suffix() == "cpp") {
node->sources.push_back(path);
+ } else if (isQml(path)) {
+ node->files.push_back(path);
} else if (isResource(path)) {
node->resources.push_back(path);
}
@@ -458,12 +375,12 @@ void CMakeGenerator::insertFile(NodePtr &node, const Utils::FilePath &path) cons
void CMakeGenerator::removeFile(NodePtr &node, const Utils::FilePath &path) const
{
if (path.fileName() == "qmldir") {
- node->module = false;
+ node->type = Node::Type::Folder;
node->singletons.clear();
node->uri = "";
node->name = path.parentDir().fileName();
- } else if (path.suffix() == "qml") {
+ } else if (isQml(path)) {
auto iter = std::find(node->files.begin(), node->files.end(), path);
if (iter != node->files.end())
node->files.erase(iter);
@@ -474,33 +391,9 @@ void CMakeGenerator::removeFile(NodePtr &node, const Utils::FilePath &path) cons
}
}
-bool CMakeGenerator::isRootNode(const NodePtr &node) const
-{
- return node->name == "Main";
-}
-
-bool CMakeGenerator::hasChildModule(const NodePtr &node) const
-{
- for (const NodePtr &child : node->subdirs) {
- if (child->module)
- return true;
- if (hasChildModule(child))
- return true;
- }
- return false;
-}
-
-bool CMakeGenerator::isResource(const Utils::FilePath &path) const
-{
- static const QStringList suffixes = {
- "json", "mesh", "dae", "qad", "hints", "png", "hdr", "ttf", "jpg", "JPG",
- "js", "qsb", "frag", "frag.qsb", "vert", "vert.qsb", "svg", "ktx"};
- return suffixes.contains(path.suffix());
-}
-
void CMakeGenerator::printModules(const NodePtr &node) const
{
- if (node->module)
+ if (node->type == Node::Type::Module)
qDebug() << "Module: " << node->name;
for (const auto &child : node->subdirs)
@@ -516,7 +409,27 @@ void CMakeGenerator::printNodeTree(const NodePtr &generatorNode, size_t indent)
return str;
};
+ QString typeString;
+ switch (generatorNode->type)
+ {
+ case Node::Type::App:
+ typeString = "Node::Type::App";
+ break;
+ case Node::Type::Folder:
+ typeString = "Node::Type::Folder";
+ break;
+ case Node::Type::Module:
+ typeString = "Node::Type::Module";
+ break;
+ case Node::Type::Library:
+ typeString = "Node::Type::Library";
+ break;
+ default:
+ typeString = "Node::Type::Undefined";
+ }
+
qDebug() << addIndent(indent) << "GeneratorNode: " << generatorNode->name;
+ qDebug() << addIndent(indent) << "type: " << typeString;
qDebug() << addIndent(indent) << "directory: " << generatorNode->dir;
qDebug() << addIndent(indent) << "files: " << generatorNode->files;
qDebug() << addIndent(indent) << "singletons: " << generatorNode->singletons;
@@ -532,7 +445,7 @@ void CMakeGenerator::parseNodeTree(NodePtr &generatorNode,
{
for (const auto *childNode : folderNode->nodes()) {
if (const auto *subFolderNode = childNode->asFolderNode()) {
- CMakeGenerator::NodePtr childGeneratorNode = std::make_shared<Node>();
+ NodePtr childGeneratorNode = std::make_shared<Node>();
childGeneratorNode->parent = generatorNode;
childGeneratorNode->dir = subFolderNode->filePath();
childGeneratorNode->name = subFolderNode->displayName();
@@ -544,25 +457,55 @@ void CMakeGenerator::parseNodeTree(NodePtr &generatorNode,
}
}
- if (generatorNode->name == "content")
- generatorNode->module = true;
+ if (m_writer)
+ m_writer->transformNode(generatorNode);
- if (generatorNode->module)
+ if (generatorNode->type == Node::Type::Module)
m_moduleNames.push_back(generatorNode->name);
}
void CMakeGenerator::parseSourceTree()
{
- m_srcs.clear();
- const QString srcDir = m_root->dir.pathAppended(Constants::DIRNAME_CPP).path();
- QDirIterator it(srcDir, QStringList({"*.cpp"}), QDir::Files, QDirIterator::Subdirectories);
+ QTC_ASSERT(m_writer, return);
+
+ const Utils::FilePath srcDir = m_root->dir.pathAppended(m_writer->sourceDirName());
+ QDirIterator it(srcDir.path(), {"*.cpp"}, QDir::Files, QDirIterator::Subdirectories);
+
+ NodePtr srcNode = std::make_shared<Node>();
+ srcNode->parent = m_root;
+ srcNode->type = Node::Type::App;
+ srcNode->dir = srcDir;
+ srcNode->uri = srcDir.baseName();
+ srcNode->name = srcNode->uri;
+
while (it.hasNext()) {
- QString relative = Utils::FilePath::calcRelativePath(it.next(), m_root->dir.path());
- m_srcs.push_back(relative);
+ auto next = it.next();
+ srcNode->sources.push_back(Utils::FilePath::fromString(next));
+ }
+
+ if (srcNode->sources.empty())
+ srcNode->sources.push_back(srcDir.pathAppended("main.cpp"));
+
+ if (m_writer)
+ m_writer->transformNode(srcNode);
+
+ m_root->subdirs.push_back(srcNode);
+}
+
+void CMakeGenerator::compareWithFileSystem(const NodePtr &node) const
+{
+ std::vector<Utils::FilePath> files;
+ QDirIterator iter(node->dir.path(), QDir::Files, QDirIterator::Subdirectories);
+
+ while (iter.hasNext()) {
+ auto next = Utils::FilePath::fromString(iter.next());
+ if (isResource(next) && !findFile(next) && !ignoreFile(next))
+ files.push_back(next);
}
- if (m_srcs.empty())
- m_srcs.push_back("src/main.cpp");
+ const QString text("File is not part of the project");
+ for (const auto &file : files)
+ logIssue(ProjectExplorer::Task::Warning, text, file);
}
} // namespace GenerateCmake
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h
index f89257ac5d..6077166d5b 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h
@@ -2,7 +2,10 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
+#include "cmakewriter.h"
+
#include "utils/filepath.h"
+#include "projectexplorer/task.h"
#include <QObject>
@@ -22,80 +25,56 @@ class CMakeGenerator : public QObject
Q_OBJECT
public:
+ static void createMenuAction(QObject *parent);
+ static void logIssue(ProjectExplorer::Task::TaskType type, const QString &text, const Utils::FilePath &file);
+
CMakeGenerator(QmlBuildSystem *bs, QObject *parent = nullptr);
- void setEnabled(bool enabled);
+ QString projectName() const;
- void initialize(QmlProject *project);
+ const QmlProject *qmlProject() const;
+ const QmlBuildSystem *buildSystem() const;
+
+ bool findFile(const Utils::FilePath &file) const;
+ bool isRootNode(const NodePtr &node) const;
+ bool hasChildModule(const NodePtr &node) const;
+ void setEnabled(bool enabled);
+ void initialize(QmlProject *project);
void update(const QSet<QString> &added, const QSet<QString> &removed);
+ void updateMenuAction();
private:
- struct Node
- {
- std::shared_ptr<Node> parent = nullptr;
- bool module = false;
-
- QString uri;
- QString name;
- Utils::FilePath dir;
-
- std::vector<std::shared_ptr<Node>> subdirs;
- std::vector<Utils::FilePath> files;
- std::vector<Utils::FilePath> singletons;
- std::vector<Utils::FilePath> resources;
- std::vector<Utils::FilePath> sources;
- };
-
- using NodePtr = std::shared_ptr<Node>;
- using FileGetter = std::function<std::vector<Utils::FilePath>(const NodePtr &)>;
-
- std::vector<Utils::FilePath> files(const NodePtr &node, const FileGetter &getter) const;
- std::vector<Utils::FilePath> qmlFiles(const NodePtr &node) const;
- std::vector<Utils::FilePath> singletons(const NodePtr &node) const;
- std::vector<Utils::FilePath> resources(const NodePtr &node) const;
- std::vector<Utils::FilePath> sources(const NodePtr &node) const;
+ bool isQml(const Utils::FilePath &path) const;
+ bool isResource(const Utils::FilePath &path) const;
+ bool ignoreFile(const Utils::FilePath &path) const;
void createCMakeFiles(const NodePtr &node) const;
- void createMainCMakeFile(const NodePtr &node) const;
- void createModuleCMakeFile(const NodePtr &node) const;
+ void createSourceFiles() const;
- void createEntryPoints(const NodePtr &node) const;
- void createMainCppFile(const NodePtr &node) const;
- void writeFile(const Utils::FilePath &path, const QString &content) const;
-
- QString makeRelative(const NodePtr &node, const Utils::FilePath &path) const;
- QString makeEnvironmentVariable(const QString &key) const;
- QString makeSingletonBlock(const NodePtr &node) const;
- QString makeSubdirectoriesBlock(const NodePtr &node) const;
- QString makeQmlFilesBlock(const NodePtr &node) const;
- std::tuple<QString, QString> makeResourcesBlocks(const NodePtr &node) const;
-
- QString readTemplate(const QString &templatePath) const;
void readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const;
-
NodePtr findModuleFor(const NodePtr &node) const;
NodePtr findNode(NodePtr &node, const Utils::FilePath &path) const;
NodePtr findOrCreateNode(NodePtr &node, const Utils::FilePath &path) const;
+ bool findFile(const NodePtr &node, const Utils::FilePath &file) const;
void insertFile(NodePtr &node, const Utils::FilePath &path) const;
void removeFile(NodePtr &node, const Utils::FilePath &path) const;
- bool isRootNode(const NodePtr &node) const;
- bool hasChildModule(const NodePtr &node) const;
- bool isResource(const Utils::FilePath &path) const;
-
void printModules(const NodePtr &generatorNode) const;
void printNodeTree(const NodePtr &generatorNode, size_t indent = 0) const;
void parseNodeTree(NodePtr &generatorNode, const ProjectExplorer::FolderNode *folderNode);
void parseSourceTree();
+ void compareWithFileSystem(const NodePtr &node) const;
+
bool m_enabled = false;
+ CMakeWriter::Ptr m_writer = {};
+
QString m_projectName = {};
NodePtr m_root = {};
- QStringList m_srcs = {};
- std::vector<QString> m_moduleNames = {};
+ QStringList m_moduleNames = {};
QmlBuildSystem *m_buildSystem = nullptr;
};
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp
deleted file mode 100644
index aa733b68af..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cmakegeneratordialog.h"
-#include "../qmlprojectmanagertr.h"
-#include "cmakegeneratordialogtreemodel.h"
-
-#include <utils/utilsicons.h>
-#include <utils/detailswidget.h>
-
-#include <QDialogButtonBox>
-#include <QPushButton>
-#include <QLayout>
-#include <QLabel>
-
-#include <QSplitter>
-
-using namespace Utils;
-
-namespace QmlProjectManager {
-namespace GenerateCmake {
-
-CmakeGeneratorDialog::CmakeGeneratorDialog(const FilePath &rootDir,
- const FilePaths &files,
- const FilePaths invalidFiles)
- : QDialog(), m_rootDir(rootDir), m_files(files), m_invalidFiles(invalidFiles)
-{
- setWindowTitle(Tr::tr("Select Files to Generate"));
-
- QLabel *mainLabel = new QLabel(Tr::tr("Start CMakeFiles.txt generation"), this);
- mainLabel->setMargin(30);
-
- QVBoxLayout *dialogLayout = new QVBoxLayout(this);
- dialogLayout->addWidget(mainLabel);
- dialogLayout->addWidget(createDetailsWidget());
- dialogLayout->addWidget(createButtons());
- setLayout(dialogLayout);
-
- setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- setMaximumHeight(layout()->totalSizeHint().height());
-
- refreshNotificationText();
-}
-
-QTreeView* CmakeGeneratorDialog::createFileTree()
-{
- m_model = new CMakeGeneratorDialogTreeModel(m_rootDir, m_files, this);
-
- QTreeView *tree = new QTreeView(this);
- tree->setModel(m_model);
- tree->expandAll();
- tree->setHeaderHidden(true);
-
- return tree;
-}
-
-QWidget* CmakeGeneratorDialog::createDetailsWidget()
-{
- QTreeView* tree = createFileTree();
-
- m_notifications = new QTextEdit(this);
- m_warningIcon = Utils::Icons::WARNING.pixmap();
-
- QSplitter *advancedInnerWidget = new QSplitter(this);
- advancedInnerWidget->addWidget(tree);
- advancedInnerWidget->addWidget(m_notifications);
- advancedInnerWidget->setStretchFactor(0, 2);
- advancedInnerWidget->setStretchFactor(1, 1);
- advancedInnerWidget->setOrientation(Qt::Vertical);
- advancedInnerWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::MinimumExpanding);
-
- DetailsWidget *advancedWidget = new DetailsWidget(this);
- advancedWidget->setMinimumWidth(600);
- advancedWidget->setWidget(advancedInnerWidget);
- advancedWidget->setSummaryText(Tr::tr("Advanced Options"));
- connect(advancedWidget, &DetailsWidget::expanded, this, &CmakeGeneratorDialog::advancedVisibilityChanged);
-
- return advancedWidget;
-}
-
-QWidget* CmakeGeneratorDialog::createButtons()
-{
- QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
- auto *okButton = buttons->button(QDialogButtonBox::Ok);
- okButton->setDefault(true);
-
- connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
- connect(m_model, &CMakeGeneratorDialogTreeModel::checkedStateChanged, this, &CmakeGeneratorDialog::refreshNotificationText);
-
- return buttons;
-}
-
-FilePaths CmakeGeneratorDialog::getFilePaths()
-{
- FilePaths paths;
-
- QList<CheckableFileTreeItem*> items = m_model->checkedItems();
- for (CheckableFileTreeItem *item: items) {
- paths.append(FilePath::fromString(item->text()));
- }
-
- return paths;
-}
-
-const QString FILE_CREATE_NOTIFICATION = Tr::tr("File %1 will be created.\n");
-const QString FILE_OVERWRITE_NOTIFICATION = Tr::tr("File %1 will be overwritten.\n");
-const QString FILE_INVALID_NOTIFICATION = Tr::tr(
- "File %1 contains invalid characters and will be skipped.\n");
-
-void CmakeGeneratorDialog::refreshNotificationText()
-{
- QTextDocument *document = m_notifications->document();
- document->clear();
- document->addResource(QTextDocument::ImageResource, QUrl("cmakegendialog://warningicon"), m_warningIcon);
-
- QTextCursor cursor = m_notifications->textCursor();
- QTextImageFormat iformat;
- iformat.setName("cmakegendialog://warningicon");
-
- QList<CheckableFileTreeItem*> nodes = m_model->items();
-
- for (const auto &file : m_invalidFiles) {
- cursor.insertImage(iformat);
- cursor.insertText(QString(FILE_INVALID_NOTIFICATION).arg(file.displayName()));
- }
-
- for (CheckableFileTreeItem *node : nodes) {
- if (!m_files.contains(node->toFilePath()))
- continue;
-
- if (!node->toFilePath().exists() && node->isChecked()) {
- QString relativePath = QString(node->toFilePath().toString()).remove(m_rootDir.toString()+'/');
- cursor.insertText(QString(FILE_CREATE_NOTIFICATION).arg(relativePath));
- }
- }
-
- if (!document->toPlainText().isEmpty())
- cursor.insertBlock();
-
- for (CheckableFileTreeItem *node : nodes) {
- if (!m_files.contains(node->toFilePath()))
- continue;
-
- if (node->toFilePath().exists() && node->isChecked()) {
- QString relativePath = node->toFilePath().relativePathFrom(m_rootDir).toString();
- cursor.insertImage(iformat);
- cursor.insertText(QString(FILE_OVERWRITE_NOTIFICATION).arg(relativePath));
- }
- }
-}
-
-void CmakeGeneratorDialog::advancedVisibilityChanged(bool visible)
-{
- if (visible) {
- setMaximumHeight(QWIDGETSIZE_MAX);
- setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- }
- else {
- setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- int height = layout()->totalSizeHint().height();
- setMaximumHeight(height);
- resize(width(), height);
- }
-}
-
-} //GenerateCmake
-} //QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h
deleted file mode 100644
index fb15ec4de7..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-
-#ifndef CMAKEGENERATORDIALOG_H
-#define CMAKEGENERATORDIALOG_H
-
-#include "cmakegeneratordialogtreemodel.h"
-
-#include <utils/fileutils.h>
-
-#include <QDialog>
-#include <QTextEdit>
-#include <QTreeView>
-#include <QLabel>
-
-namespace QmlProjectManager {
-namespace GenerateCmake {
-
-class CmakeGeneratorDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- CmakeGeneratorDialog(const Utils::FilePath &rootDir,
- const Utils::FilePaths &files,
- const Utils::FilePaths invalidFiles);
- Utils::FilePaths getFilePaths();
-
-public slots:
- void refreshNotificationText();
- void advancedVisibilityChanged(bool visible);
-
-private:
- QTreeView* createFileTree();
- QWidget* createDetailsWidget();
- QWidget* createButtons();
-
-private:
- CMakeGeneratorDialogTreeModel *m_model;
- QTextEdit *m_notifications;
- QVariant m_warningIcon;
- Utils::FilePath m_rootDir;
- Utils::FilePaths m_files;
- Utils::FilePaths m_invalidFiles;
-};
-
-} //GenerateCmake
-} //QmlProjectManager
-
-#endif // CMAKEGENERATORDIALOG_H
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.cpp
deleted file mode 100644
index 525230bdfd..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cmakegeneratordialogtreemodel.h"
-#include "generatecmakelistsconstants.h"
-#include "checkablefiletreeitem.h"
-#include "../qmlprojectmanagertr.h"
-
-#include <utils/utilsicons.h>
-
-using namespace Utils;
-
-namespace QmlProjectManager {
-namespace GenerateCmake {
-
-CMakeGeneratorDialogTreeModel::CMakeGeneratorDialogTreeModel(const FilePath &rootDir,
- const FilePaths &files, QObject *parent)
- :QStandardItemModel(parent),
- rootDir(rootDir),
- m_icons(new QFileIconProvider())
-{
- createNodes(files, invisibleRootItem());
-}
-
-CMakeGeneratorDialogTreeModel::~CMakeGeneratorDialogTreeModel()
-{
- delete m_icons;
-}
-
-QVariant CMakeGeneratorDialogTreeModel::data(const QModelIndex &index, int role) const
-{
- if (index.isValid()) {
- const CheckableFileTreeItem *node = constNodeForIndex(index);
- if (role == Qt::CheckStateRole) {
- if (!node->isDir())
- return node->isChecked() ? Qt::Checked : Qt::Unchecked;
- return {};
- }
- else if (role == Qt::DisplayRole) {
- FilePath fullPath = node->toFilePath();
- return QVariant(fullPath.fileName());
- }
- else if (role == Qt::DecorationRole) {
- if (node->isFile())
- return Utils::Icons::WARNING.icon();
- if (node->isDir())
- return m_icons->icon(QFileIconProvider::Folder);
- else
- return Utils::Icons::NEWFILE.icon();
- }
- else if (role == Qt::ToolTipRole) {
- if (node->isFile())
- return Tr::tr("This file already exists and will be overwritten.");
- if (!node->toFilePath().exists())
- return Tr::tr("This file or folder will be created.");
- }
- }
-
- return QStandardItemModel::data(index, role);
-}
-
-bool CMakeGeneratorDialogTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- if (index.isValid()) {
- CheckableFileTreeItem *node = nodeForIndex(index);
- if (role == Qt::CheckStateRole) {
- node->setChecked(value.value<bool>());
- emit checkedStateChanged(node);
- return true;
- }
- }
-
- return QStandardItemModel::setData(index, value, role);;
-}
-
-const QList<CheckableFileTreeItem*> CMakeGeneratorDialogTreeModel::items() const
-{
- QList<QStandardItem*> standardItems = findItems(".*", Qt::MatchRegularExpression | Qt::MatchRecursive);
- QList<CheckableFileTreeItem*> checkableItems;
- for (QStandardItem *item : standardItems)
- checkableItems.append(static_cast<CheckableFileTreeItem*>(item));
-
- return checkableItems;
-}
-
-const QList<CheckableFileTreeItem*> CMakeGeneratorDialogTreeModel::checkedItems() const
-{
- QList<CheckableFileTreeItem*> allItems = items();
-
- QList<CheckableFileTreeItem*> checkedItems;
- for (CheckableFileTreeItem *item : allItems) {
- if (item->isChecked())
- checkedItems.append(item);
- }
-
- return checkedItems;
-}
-
-bool CMakeGeneratorDialogTreeModel::checkedByDefault(const Utils::FilePath &file) const
-{
- if (file.exists()) {
- QString relativePath = file.relativeChildPath(rootDir).toString();
- if (relativePath.compare(QmlProjectManager::GenerateCmake::Constants::FILENAME_CMAKELISTS) == 0)
- return false;
- if (relativePath.endsWith(QmlProjectManager::GenerateCmake::Constants::FILENAME_CMAKELISTS)
- && relativePath.length() > QString(QmlProjectManager::GenerateCmake::Constants::FILENAME_CMAKELISTS).length())
- return true;
- if (relativePath.compare(QmlProjectManager::GenerateCmake::Constants::FILENAME_MODULES) == 0)
- return true;
- if (relativePath.compare(
- FilePath::fromString(QmlProjectManager::GenerateCmake::Constants::DIRNAME_CPP)
- .pathAppended(QmlProjectManager::GenerateCmake::Constants::FILENAME_MAINCPP_HEADER)
- .toString())
- == 0)
- return true;
- }
-
- return !file.exists();
-}
-
-void CMakeGeneratorDialogTreeModel::createNodes(const FilePaths &candidates, QStandardItem *parent)
-{
- if (!parent)
- return;
-
- CheckableFileTreeItem *checkParent = dynamic_cast<CheckableFileTreeItem*>(parent);
- FilePath thisDir = (parent == invisibleRootItem()) ? rootDir : checkParent->toFilePath();
-
- for (const FilePath &file : candidates) {
- if (file.parentDir() == thisDir) {
- CheckableFileTreeItem *fileNode = new CheckableFileTreeItem(file);
- fileNode->setChecked(checkedByDefault(file));
- if (!file.exists())
- fileNode->setChecked(true);
- parent->appendRow(fileNode);
- }
- }
-
- FilePaths directSubDirs;
- for (const FilePath &file : candidates) {
- FilePath dir = file.parentDir();
- if (dir.parentDir() == thisDir && !directSubDirs.contains(dir))
- directSubDirs.append(dir);
- }
-
- for (const FilePath &subDir : directSubDirs) {
- CheckableFileTreeItem *dirNode = new CheckableFileTreeItem(subDir);
- parent->appendRow(dirNode);
-
- FilePaths subDirCandidates;
- for (const FilePath &file : candidates)
- if (file.isChildOf(subDir))
- subDirCandidates.append(file);
-
- createNodes(subDirCandidates, dirNode);
- }
-}
-
-const CheckableFileTreeItem* CMakeGeneratorDialogTreeModel::constNodeForIndex(const QModelIndex &index) const
-{
- const QStandardItem *parent = static_cast<const QStandardItem*>(index.internalPointer());
- const QStandardItem *item = parent->child(index.row(), index.column());
- return static_cast<const CheckableFileTreeItem*>(item);
-}
-
-CheckableFileTreeItem* CMakeGeneratorDialogTreeModel::nodeForIndex(const QModelIndex &index)
-{
- QStandardItem *parent = static_cast<QStandardItem*>(index.internalPointer());
- QStandardItem *item = parent->child(index.row(), index.column());
- return static_cast<CheckableFileTreeItem*>(item);
-}
-
-} //GenerateCmake
-} //QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.h
deleted file mode 100644
index 3fb79c2b96..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#ifndef CMAKEGENERATORDIALOGTREEMODEL_H
-#define CMAKEGENERATORDIALOGTREEMODEL_H
-
-#include "checkablefiletreeitem.h"
-
-#include <QFileIconProvider>
-#include <QStandardItemModel>
-
-#include <utils/fileutils.h>
-
-namespace QmlProjectManager {
-namespace GenerateCmake {
-
-class CMakeGeneratorDialogTreeModel : public QStandardItemModel
-{
- Q_OBJECT
-
-public:
- CMakeGeneratorDialogTreeModel(const Utils::FilePath &rootDir,
- const Utils::FilePaths &files, QObject *parent = nullptr);
- ~CMakeGeneratorDialogTreeModel();
-
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- bool setData(const QModelIndex &index, const QVariant &value, int role);
-
- const QList<CheckableFileTreeItem*> items() const;
- const QList<CheckableFileTreeItem*> checkedItems() const;
- const CheckableFileTreeItem* constNodeForIndex(const QModelIndex &index) const;
- CheckableFileTreeItem* nodeForIndex(const QModelIndex &index);
-
-signals:
- void checkedStateChanged(CheckableFileTreeItem *item);
-
-protected:
- bool checkedByDefault(const Utils::FilePath &file) const;
- Utils::FilePath rootDir;
-
-private:
- void createNodes(const Utils::FilePaths &candidates, QStandardItem *parent);
-
- QFileIconProvider* m_icons;
-};
-
-} //GenerateCmake
-} //QmlProjectManager
-
-
-#endif // CMAKEGENERATORDIALOGTREEMODEL_H
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
deleted file mode 100644
index f6bf1a4975..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cmakeprojectconverter.h"
-#include "cmakeprojectconverterdialog.h"
-#include "generatecmakelists.h"
-#include "generatecmakelistsconstants.h"
-#include "../qmlprojectmanagertr.h"
-
-#include <coreplugin/actionmanager/actionmanager.h>
-#include <coreplugin/actionmanager/actioncontainer.h>
-#include <coreplugin/icore.h>
-
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/projectmanager.h>
-#include <projectexplorer/target.h>
-
-#include <qmlprojectmanager/qmlprojectconstants.h>
-
-#include <QAction>
-#include <QMessageBox>
-#include <QRegularExpression>
-
-using namespace Utils;
-using namespace QmlProjectManager::GenerateCmake::Constants;
-
-namespace QmlProjectManager {
-namespace GenerateCmake {
-
-const QString MENU_ITEM_CONVERT = Tr::tr("Export as Latest Project Format...");
-const QString ERROR_TITLE = Tr::tr("Creating Project");
-const QString SUCCESS_TITLE = Tr::tr("Creating Project");
-const QString ERROR_TEXT = Tr::tr("Creating project failed.\n%1");
-const QString SUCCESS_TEXT = Tr::tr("Creating project succeeded.");
-
-void CmakeProjectConverter::generateMenuEntry(QObject *parent)
-{
- Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer(
- QmlProjectManager::Constants::EXPORT_MENU);
- auto action = new QAction(MENU_ITEM_CONVERT, parent);
- QObject::connect(action, &QAction::triggered, CmakeProjectConverter::onConvertProject);
- Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.ConvertToCmakeProject");
- exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_CONVERT);
-
- action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject()));
- QObject::connect(ProjectExplorer::ProjectManager::instance(),
- &ProjectExplorer::ProjectManager::startupProjectChanged,
- [action]() {
- auto currentBuildSystem = QmlBuildSystem::getStartupBuildSystem();
- bool isMCU = currentBuildSystem ? currentBuildSystem->qtForMCUs() : false;
-
- action->setEnabled(isMCU
- && isProjectConvertable(
- ProjectExplorer::ProjectManager::startupProject()));
- });
-}
-
-bool CmakeProjectConverter::isProjectConvertable(const ProjectExplorer::Project *project)
-{
- if (!project)
- return false;
-
- return !isProjectCurrentFormat(project);
-}
-
-const QStringList sanityCheckFiles({FILENAME_CMAKELISTS,
- FILENAME_MODULES,
- FILENAME_MAINQML,
- QString(DIRNAME_CONTENT)+'/'+FILENAME_CMAKELISTS,
- QString(DIRNAME_IMPORT)+'/'+FILENAME_CMAKELISTS,
- QString(DIRNAME_CPP)+'/'+FILENAME_MAINCPP,
- QString(DIRNAME_CPP)+'/'+FILENAME_ENV_HEADER,
- QString(DIRNAME_CPP)+'/'+FILENAME_MAINCPP_HEADER
- });
-
-bool CmakeProjectConverter::isProjectCurrentFormat(const ProjectExplorer::Project *project)
-{
- const QmlProjectManager::QmlProject *qmlprj = qobject_cast<const QmlProjectManager::QmlProject*>(project);
-
- if (!qmlprj)
- return false;
-
- FilePath rootDir = qmlprj->rootProjectDirectory();
- for (const QString &file : sanityCheckFiles)
- if (!rootDir.pathAppended(file).exists())
- return false;
-
- return true;
-}
-
-void CmakeProjectConverter::onConvertProject()
-{
- ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
- const QmlProjectManager::QmlProject *qmlProject =
- qobject_cast<const QmlProjectManager::QmlProject*>(project);
- if (qmlProject) {
- CmakeProjectConverterDialog dialog(qmlProject);
- if (dialog.exec()) {
- FilePath newProjectPath = dialog.newPath();
- CmakeProjectConverter converter;
- converter.convertProject(qmlProject, newProjectPath);
- }
- }
-}
-
-bool CmakeProjectConverter::convertProject(const QmlProjectManager::QmlProject *project,
- const FilePath &targetDir)
-{
- m_converterObjects.clear();
- m_projectDir = project->projectDirectory();
- m_newProjectDir = targetDir;
- m_project = project;
-
- m_rootDirFiles = QStringList(FILENAME_FILTER_QMLPROJECT);
- const QString confFile = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
- if (!confFile.isEmpty())
- m_rootDirFiles.append(confFile);
-
- bool retVal = prepareAndExecute();
-
- if (retVal) {
- QMessageBox::information(Core::ICore::dialogParent(), SUCCESS_TITLE, SUCCESS_TEXT);
- ProjectExplorer::OpenProjectResult result
- = ProjectExplorer::ProjectExplorerPlugin::openProject(newProjectFile());
- if (!result)
- ProjectExplorer::ProjectExplorerPlugin::showOpenProjectError(result);
- }
- else {
- QMessageBox::critical(Core::ICore::dialogParent(), ERROR_TITLE, ERROR_TEXT.arg(m_errorText));
- }
-
- return retVal;
-}
-
-bool CmakeProjectConverter::prepareAndExecute()
-{
- GenerateCmake::CmakeFileGenerator cmakeGenerator;
-
- if (!performSanityCheck())
- return false;
- if (!prepareBaseDirectoryStructure())
- return false;
- if (!prepareCopy())
- return false;
- if (!createPreparedProject())
- return false;
- if (!cmakeGenerator.prepare(m_newProjectDir, false))
- return false;
- if (!cmakeGenerator.execute())
- return false;
- if (!modifyNewFiles())
- return false;
-
- return true;
-}
-
-bool CmakeProjectConverter::isFileBlacklisted(const Utils::FilePath &file) const
-{
- if (!file.fileName().compare(FILENAME_CMAKELISTS))
- return true;
- if (!file.suffix().compare(FILENAME_SUFFIX_QMLPROJECT))
- return true;
- if (!file.suffix().compare(FILENAME_SUFFIX_USER))
- return true;
- if (m_rootDirFiles.contains(file.fileName()))
- return true;
-
- return false;
-}
-
-bool CmakeProjectConverter::isDirBlacklisted(const Utils::FilePath &dir) const
-{
- if (!dir.isDir())
- return true;
-
- return false;
-}
-
-const QString ERROR_CANNOT_WRITE_DIR = Tr::tr("Unable to write to directory\n%1.");
-
-bool CmakeProjectConverter::performSanityCheck()
-{
- if (!m_newProjectDir.parentDir().isWritableDir()) {
- m_errorText = ERROR_CANNOT_WRITE_DIR.arg(m_newProjectDir.parentDir().toString());
- return false;
- }
-
- return true;
-}
-
-bool CmakeProjectConverter::prepareBaseDirectoryStructure()
-{
- addDirectory(m_newProjectDir);
- addDirectory(contentDir());
- addDirectory(sourceDir());
- addDirectory(importDir());
- addDirectory(assetDir());
- addDirectory(assetImportDir());
- addFile(contentDir().pathAppended(FILENAME_APPMAINQML));
-
- return true;
-}
-
-bool CmakeProjectConverter::prepareCopy()
-{
- FilePaths rootFiles = m_projectDir.dirEntries({m_rootDirFiles, QDir::Files});
- for (const FilePath &file : rootFiles) {
- addFile(file, m_newProjectDir.pathAppended(file.fileName()));
- }
-
- prepareCopyDirFiles(m_projectDir, contentDir());
-
- FilePaths subDirs = m_projectDir.dirEntries(QDir::Dirs|QDir::NoDotAndDotDot);
- for (FilePath &subDir : subDirs) {
- if (subDir.fileName() == DIRNAME_IMPORT) {
- prepareCopyDirTree(subDir, importDir());
- }
- else if (subDir.fileName() == DIRNAME_CPP) {
- prepareCopyDirTree(subDir, sourceDir());
- }
- else if (subDir.fileName() == DIRNAME_ASSET) {
- prepareCopyDirTree(subDir, assetDir());
- }
- else if (subDir.fileName() == DIRNAME_ASSETIMPORT) {
- prepareCopyDirTree(subDir, assetImportDir());
- }
- else {
- prepareCopyDirTree(subDir, contentDir().pathAppended(subDir.fileName()));
- }
- }
-
- return true;
-}
-
-bool CmakeProjectConverter::prepareCopyDirFiles(const FilePath &dir, const FilePath &targetDir)
-{
- FilePaths dirFiles = dir.dirEntries(QDir::Files);
- for (FilePath file : dirFiles) {
- if (!isFileBlacklisted(file))
- addFile(file, targetDir.pathAppended(file.fileName()));
- }
-
- return true;
-}
-
-bool CmakeProjectConverter::prepareCopyDirTree(const FilePath &dir, const FilePath &targetDir)
-{
- prepareCopyDirFiles(dir, targetDir);
- FilePaths subDirs = dir.dirEntries(QDir::Dirs|QDir::NoDotAndDotDot);
- for (FilePath &subDir : subDirs) {
- if (isDirBlacklisted(subDir))
- continue;
- addDirectory(targetDir.pathAppended(subDir.fileName()));
- prepareCopyDirFiles(subDir, targetDir.pathAppended(subDir.fileName()));
- prepareCopyDirTree(subDir, targetDir.pathAppended(subDir.fileName()));
- }
-
- return true;
-}
-
-bool CmakeProjectConverter::addDirectory(const Utils::FilePath &target)
-{
- return addObject(ProjectConverterObjectType::Directory, FilePath(), target);
-}
-
-bool CmakeProjectConverter::addFile(const Utils::FilePath &target)
-{
- return addFile(FilePath(), target);
-}
-
-bool CmakeProjectConverter::addFile(const Utils::FilePath &original, const Utils::FilePath &target)
-{
- addDirectory(target.parentDir());
- return addObject(ProjectConverterObjectType::File, original, target);
-}
-
-bool CmakeProjectConverter::addObject(ProjectConverterObjectType type,
- const Utils::FilePath &original, const Utils::FilePath &target)
-{
- if (target.isChildOf(m_projectDir))
- return false;
-
- if (!target.isChildOf(m_newProjectDir) &&
- ((type == ProjectConverterObjectType::Directory) && (target != m_newProjectDir))) {
- return false;
- }
-
- for (ProjectConverterObject &o : m_converterObjects) {
- if (o.target == target)
- return false;
- }
-
- ProjectConverterObject object;
- object.type = type;
- object.target = target;
- object.original = original;
-
- m_converterObjects.append(object);
-
- return true;
-}
-
-bool CmakeProjectConverter::createPreparedProject()
-{
- for (ProjectConverterObject &pco : m_converterObjects) {
- if (pco.type == ProjectConverterObjectType::Directory) {
- pco.target.createDir();
- }
- else if (pco.type == ProjectConverterObjectType::File) {
- if (pco.original.isEmpty()) {
- QFile newFile(pco.target.toString());
- newFile.open(QIODevice::WriteOnly);
- newFile.close();
- }
- else {
- pco.original.copyFile(pco.target);
- }
- }
- }
-
- return true;
-}
-
-const FilePath CmakeProjectConverter::contentDir() const
-{
- return m_newProjectDir.pathAppended(DIRNAME_CONTENT);
-}
-
-const FilePath CmakeProjectConverter::sourceDir() const
-{
- return m_newProjectDir.pathAppended(DIRNAME_CPP);
-}
-
-const FilePath CmakeProjectConverter::importDir() const
-{
- return m_newProjectDir.pathAppended(DIRNAME_IMPORT);
-}
-
-const FilePath CmakeProjectConverter::assetDir() const
-{
- return contentDir().pathAppended(DIRNAME_ASSET);
-}
-
-const FilePath CmakeProjectConverter::assetImportDir() const
-{
- return m_newProjectDir.pathAppended(DIRNAME_ASSETIMPORT);
-}
-
-const FilePath CmakeProjectConverter::newProjectFile() const
-{
- return m_newProjectDir.pathAppended(m_project->projectFilePath().fileName());
-}
-
-const FilePath CmakeProjectConverter::projectMainFile() const
-{
- auto *target = m_project->activeTarget();
- if (target && target->buildSystem()) {
- auto buildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(target->buildSystem());
- if (buildSystem) {
- return buildSystem->mainFilePath();
- }
- }
- return {};
-}
-
-const QString CmakeProjectConverter::projectMainClass() const
-{
- return projectMainFile().baseName();
-}
-
-bool CmakeProjectConverter::modifyNewFiles()
-{
- return modifyAppMainQml() && modifyProjectFile();
-}
-
-const char APPMAIN_QMLFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectappmainqml.tpl";
-
-bool CmakeProjectConverter::modifyAppMainQml()
-{
- QString appMainQmlPath = contentDir().pathAppended(FILENAME_APPMAINQML).toString();
- QFile appMainQml(appMainQmlPath);
- appMainQml.open(QIODevice::ReadWrite);
- if (!appMainQml.isOpen())
- return false;
-
- QString templateContent = GenerateCmake::readTemplate(APPMAIN_QMLFILE_TEMPLATE_PATH);
- QString appMainQmlContent = templateContent.arg(projectMainClass());
-
- appMainQml.reset();
- appMainQml.write(appMainQmlContent.toUtf8());
- appMainQml.close();
-
- return true;
-}
-
-bool CmakeProjectConverter::modifyProjectFile()
-{
- QString projectFileName = m_project->projectFilePath().fileName();
- FilePath projectFilePath = m_newProjectDir.pathAppended(projectFileName);
- QFile projectFile(projectFilePath.toString());
- projectFile.open(QIODevice::ReadOnly);
- if (!projectFile.isOpen())
- return false;
- QString projectFileContent = QString::fromUtf8(projectFile.readAll());
- projectFile.close();
-
- const QRegularExpression mainFilePattern("^\\s*mainFile:\\s*\".*\"", QRegularExpression::MultilineOption);
- const QString mainFileString(" mainFile: \"content/App.qml\"");
-
- projectFileContent.replace(mainFilePattern, mainFileString);
-
- projectFile.open(QIODevice::WriteOnly|QIODevice::Truncate);
- if (!projectFile.isOpen())
- return false;
- projectFile.write(projectFileContent.toUtf8());
- projectFile.close();
-
- return true;
-}
-
-} //GenerateCmake
-} //QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.h b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.h
deleted file mode 100644
index 415123db70..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#ifndef CMAKEPROJECTCONVERTER_H
-#define CMAKEPROJECTCONVERTER_H
-
-#include <utils/fileutils.h>
-#include <qmlprojectmanager/qmlproject.h>
-
-namespace QmlProjectManager {
-
-namespace GenerateCmake {
-
-enum ProjectConverterObjectType {
- File,
- Directory
-};
-
-struct ProjectConverterObject {
- ProjectConverterObjectType type;
- Utils::FilePath target;
- Utils::FilePath original;
-};
-
-class CmakeProjectConverter {
-
-public:
- bool convertProject(const QmlProjectManager::QmlProject *project,
- const Utils::FilePath &targetDir);
- static void generateMenuEntry(QObject *parent);
- static void onConvertProject();
- static bool isProjectConvertable(const ProjectExplorer::Project *project);
- static bool isProjectCurrentFormat(const ProjectExplorer::Project *project);
-
-private:
- bool prepareAndExecute();
- bool isFileBlacklisted(const Utils::FilePath &file) const;
- bool isDirBlacklisted(const Utils::FilePath &dir) const;
- bool performSanityCheck();
- bool prepareBaseDirectoryStructure();
- bool prepareCopyDirFiles(const Utils::FilePath &dir, const Utils::FilePath &targetDir);
- bool prepareCopyDirTree(const Utils::FilePath &dir, const Utils::FilePath &targetDir);
- bool prepareCopy();
- bool addDirectory(const Utils::FilePath &target);
- bool addFile(const Utils::FilePath &target);
- bool addFile(const Utils::FilePath &original, const Utils::FilePath &target);
- bool addObject(ProjectConverterObjectType type,
- const Utils::FilePath &original, const Utils::FilePath &target);
- bool createPreparedProject();
-
- const Utils::FilePath contentDir() const;
- const Utils::FilePath sourceDir() const;
- const Utils::FilePath importDir() const;
- const Utils::FilePath assetDir() const;
- const Utils::FilePath assetImportDir() const;
- const Utils::FilePath newProjectFile() const;
-
- const QString environmentVariable(const QString &key) const;
- const Utils::FilePath projectMainFile() const;
- const QString projectMainClass() const;
- bool modifyNewFiles();
- bool modifyAppMainQml();
- bool modifyProjectFile();
-
-private:
- QList<ProjectConverterObject> m_converterObjects;
- QStringList m_rootDirFiles;
- Utils::FilePath m_projectDir;
- Utils::FilePath m_newProjectDir;
- const QmlProjectManager::QmlProject *m_project;
- QString m_errorText;
-};
-
-} //GenerateCmake
-} //QmlProjectManager
-
-#endif // CMAKEPROJECTCONVERTER_H
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.cpp
deleted file mode 100644
index 1d67a92b39..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cmakeprojectconverterdialog.h"
-#include "../qmlprojectmanagertr.h"
-
-#include <coreplugin/documentmanager.h>
-
-#include <QDialog>
-#include <QDialogButtonBox>
-#include <QFormLayout>
-#include <QGroupBox>
-#include <QLabel>
-#include <QPushButton>
-#include <QRegularExpression>
-
-using namespace Utils;
-
-namespace QmlProjectManager {
-namespace GenerateCmake {
-
-const QRegularExpression projectNameRegexp("^(?!(import))(?!(QtQml))(?!(QtQuick))(?:[A-Z][a-zA-Z0-9-_]*)$");
-
-static bool projectNameValidationFunction(FancyLineEdit *editor, QString *)
-{
- return editor->text().count(projectNameRegexp);
-}
-
-static bool dirValidationFunction(FancyLineEdit *editor, QString *)
-{
- return FilePath::fromString(editor->text()).isWritableDir();
-}
-
-const QString EXPLANATION_TEXT = Tr::tr("This process creates a copy of the existing project. The new project's folder structure is adjusted for CMake build process and necessary related new files are generated.\n\nThe new project can be opened in Qt Creator using the main CMakeLists.txt file.");
-const QString PROJECT_NAME_LABEL = Tr::tr("Name:");
-const QString PARENT_DIR_LABEL = Tr::tr("Create in:");
-
-CmakeProjectConverterDialog::CmakeProjectConverterDialog(const QmlProjectManager::QmlProject *oldProject)
- : QDialog()
-{
- const FilePath defaultDir = Core::DocumentManager::projectsDirectory();
- const QString defaultName = uniqueProjectName(defaultDir, oldProject->displayName());
-
- QLabel *mainLabel = new QLabel(EXPLANATION_TEXT, this);
- mainLabel->setWordWrap(true);
-
- mainLabel->setMargin(20);
- mainLabel->setMinimumWidth(600);
-
- m_errorLabel = new InfoLabel();
- m_errorLabel->setType(InfoLabel::InfoType::None);
-
- m_nameEditor = new FancyLineEdit();
- m_nameEditor->setValidationFunction(projectNameValidationFunction);
- m_nameEditor->setText(defaultName);
-
- m_dirSelector = new PathChooser();
- m_dirSelector->setExpectedKind(PathChooser::Directory);
- m_dirSelector->setValidationFunction(dirValidationFunction);
- m_dirSelector->setFilePath(defaultDir);
-
- QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
- m_okButton = buttons->button(QDialogButtonBox::Ok);
- m_okButton->setDefault(true);
-
- connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
- connect(m_nameEditor, &FancyLineEdit::textChanged, this, &CmakeProjectConverterDialog::pathValidChanged);
- connect(m_dirSelector->lineEdit(), &FancyLineEdit::textChanged, this, &CmakeProjectConverterDialog::pathValidChanged);
-
- QGroupBox *form = new QGroupBox();
- QFormLayout *formLayout = new QFormLayout(form);
- formLayout->addRow(PROJECT_NAME_LABEL, m_nameEditor);
- formLayout->addRow(PARENT_DIR_LABEL, m_dirSelector);
-
- QVBoxLayout *dialogLayout = new QVBoxLayout(this);
- dialogLayout->addWidget(mainLabel);
- dialogLayout->addWidget(form);
- dialogLayout->addWidget(m_errorLabel);
- dialogLayout->addWidget(buttons);
-
- pathValidChanged();
-}
-
-void CmakeProjectConverterDialog::pathValidChanged()
-{
- bool valid = isValid();
-
- if (valid)
- m_newProjectDir = m_dirSelector->filePath().pathAppended(m_nameEditor->text());
- else
- m_newProjectDir = FilePath();
-
- const QString error = errorText();
- m_errorLabel->setType(error.isEmpty() ? InfoLabel::None : InfoLabel::Warning);
- m_errorLabel->setText(error);
- m_okButton->setEnabled(valid);
-}
-
-const FilePath CmakeProjectConverterDialog::newPath() const
-{
- return m_newProjectDir;
-}
-
-const QStringList blackListedStarts = {"import","QtQml","QtQuick"};
-
-const QString CmakeProjectConverterDialog::startsWithBlacklisted(const QString &text) const
-{
- for (const QString &badWord : blackListedStarts) {
- if (text.startsWith(badWord))
- return badWord;
- }
-
- return {};
-}
-
-const QString ERROR_TEXT_NAME_EMPTY = Tr::tr("Name is empty.");
-const QString ERROR_TEXT_NAME_BAD_START = Tr::tr("Name must not start with \"%1\".");
-const QString ERROR_TEXT_NAME_LOWERCASE_START = Tr::tr("Name must begin with a capital letter");
-const QString ERROR_TEXT_NAME_BAD_CHARACTERS = Tr::tr("Name must contain only letters, numbers or characters - _.");
-
-const QString ERROR_DIR_NOT_DIR = Tr::tr("Target is not a directory.");
-const QString ERROR_DIR_NOT_WRITABLE = Tr::tr("Cannot write to target directory.");
-const QString ERROR_DIR_EXISTS = Tr::tr("Project directory already exists.");
-
-const QString CmakeProjectConverterDialog::errorText() const
-{
- QString text;
-
- if (!m_nameEditor->isValid()) {
- QString name = m_nameEditor->text();
-
- if (name.isEmpty())
- return ERROR_TEXT_NAME_EMPTY;
-
- const QString badStart = startsWithBlacklisted(text);
- if (!badStart.isEmpty())
- return ERROR_TEXT_NAME_BAD_START.arg(badStart);
-
- if (name[0].isLower())
- return ERROR_TEXT_NAME_LOWERCASE_START;
-
- return ERROR_TEXT_NAME_BAD_CHARACTERS;
-
- }
-
- if (!m_dirSelector->isValid()) {
- FilePath path = m_dirSelector->filePath();
- if (!path.isDir())
- return ERROR_DIR_NOT_DIR;
- if (!path.isWritableDir())
- return ERROR_DIR_NOT_WRITABLE;
- }
-
- if (m_dirSelector->filePath().pathAppended(m_nameEditor->text()).exists())
- return ERROR_DIR_EXISTS;
-
- return text;
-}
-
-const QString CmakeProjectConverterDialog::uniqueProjectName(const FilePath &dir, const QString &oldName) const
-{
- for (unsigned i = 0; ; ++i) {
- QString name = oldName;
- if (i)
- name += QString::number(i);
- if (!dir.pathAppended(name).exists())
- return name;
- }
- return oldName;
-}
-
-bool CmakeProjectConverterDialog::isValid()
-{
- FilePath newPath = m_dirSelector->filePath().pathAppended(m_nameEditor->text());
- return m_dirSelector->isValid() && m_nameEditor->isValid() && !newPath.exists();
-}
-
-} //GenerateCmake
-} //QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.h b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.h
deleted file mode 100644
index 071eec481a..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-
-#ifndef CMAKEPROJECTCONVERTERDIALOG_H
-#define CMAKEPROJECTCONVERTERDIALOG_H
-
-#include <qmlprojectmanager/qmlproject.h>
-#include <utils/fancylineedit.h>
-#include <utils/filepath.h>
-#include <utils/infolabel.h>
-#include <utils/pathchooser.h>
-
-#include <QDialog>
-
-namespace QmlProjectManager {
-namespace GenerateCmake {
-
-class CmakeProjectConverterDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- CmakeProjectConverterDialog(const QmlProjectManager::QmlProject *oldProject);
- const Utils::FilePath newPath() const;
-
-public slots:
- void pathValidChanged();
-
-private:
- const QString startsWithBlacklisted(const QString &text) const;
- const QString errorText() const;
- const QString uniqueProjectName(const Utils::FilePath &dir, const QString &oldName) const;
- bool isValid();
-
-private:
- Utils::FilePath m_newProjectDir;
- Utils::FancyLineEdit *m_nameEditor;
- Utils::PathChooser *m_dirSelector;
- Utils::InfoLabel *m_errorLabel;
- QPushButton *m_okButton;
-};
-
-} //GenerateCmake
-} //QmlProjectManager
-
-#endif // CMAKEPROJECTCONVERTERDIALOG_H
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp
new file mode 100644
index 0000000000..0b1d0c4c60
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp
@@ -0,0 +1,258 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "cmakewriter.h"
+#include "cmakegenerator.h"
+#include "cmakewriterv0.h"
+#include "cmakewriterv1.h"
+
+#include "qmlprojectmanager/qmlproject.h"
+#include "qmlprojectmanager/buildsystem/qmlbuildsystem.h"
+
+#include "utils/namevalueitem.h"
+
+#include <QFile>
+#include <QTextStream>
+
+namespace QmlProjectManager {
+
+namespace GenerateCmake {
+
+const char TEMPLATE_BIG_RESOURCES[] = R"(
+qt6_add_resources(%1 %2
+ BIG_RESOURCES
+ PREFIX "%3"
+ VERSION 1.0
+ FILES %4
+))";
+
+CMakeWriter::Ptr CMakeWriter::create(CMakeGenerator *parent)
+{
+ const QmlProject *project = parent->qmlProject();
+ QTC_ASSERT(project, return {});
+
+ const QmlBuildSystem *buildSystem = parent->buildSystem();
+ QTC_ASSERT(buildSystem, return {});
+
+ const QString versionString = buildSystem->versionDesignStudio();
+ bool ok = false;
+ if (float version = versionString.toFloat(&ok); ok && version > 4.4)
+ return std::make_unique<CMakeWriterV1>(parent);
+
+ return std::make_unique<CMakeWriterV0>(parent);
+}
+
+QString CMakeWriter::readTemplate(const QString &templatePath)
+{
+ QFile templatefile(templatePath);
+ templatefile.open(QIODevice::ReadOnly);
+ QTextStream stream(&templatefile);
+ QString content = stream.readAll();
+ templatefile.close();
+ return content;
+}
+
+CMakeWriter::CMakeWriter(CMakeGenerator *parent)
+ : m_parent(parent)
+{}
+
+const CMakeGenerator *CMakeWriter::parent() const
+{
+ return m_parent;
+}
+
+bool CMakeWriter::isPlugin(const NodePtr &node) const
+{
+ if (node->type == Node::Type::Module)
+ return true;
+ return false;
+}
+
+QString CMakeWriter::sourceDirName() const
+{
+ return "src";
+}
+
+void CMakeWriter::transformNode(NodePtr &) const
+{}
+
+std::vector<Utils::FilePath> CMakeWriter::files(const NodePtr &node, const FileGetter &getter) const
+{
+ std::vector<Utils::FilePath> out = getter(node);
+ for (const NodePtr &child : node->subdirs) {
+ if (child->type == Node::Type::Module)
+ continue;
+
+ auto childFiles = files(child, getter);
+ out.insert(out.end(), childFiles.begin(), childFiles.end());
+ }
+ return out;
+}
+
+std::vector<Utils::FilePath> CMakeWriter::qmlFiles(const NodePtr &node) const
+{
+ return files(node, [](const NodePtr &n) { return n->files; });
+}
+
+std::vector<Utils::FilePath> CMakeWriter::singletons(const NodePtr &node) const
+{
+ return files(node, [](const NodePtr &n) { return n->singletons; });
+}
+
+std::vector<Utils::FilePath> CMakeWriter::resources(const NodePtr &node) const
+{
+ return files(node, [](const NodePtr &n) { return n->resources; });
+}
+
+std::vector<Utils::FilePath> CMakeWriter::sources(const NodePtr &node) const
+{
+ return files(node, [](const NodePtr &n) { return n->sources; });
+}
+
+std::vector<QString> CMakeWriter::plugins(const NodePtr &node) const
+{
+ QTC_ASSERT(parent(), return {});
+ std::vector<QString> out;
+ collectPlugins(node, out);
+ return out;
+}
+
+QString CMakeWriter::getEnvironmentVariable(const QString &key) const
+{
+ QTC_ASSERT(parent(), return {});
+
+ QString value;
+ if (m_parent->buildSystem()) {
+ auto envItems = m_parent->buildSystem()->environment();
+ auto confEnv = std::find_if(
+ envItems.begin(), envItems.end(), [key](const Utils::EnvironmentItem &item) {
+ return item.name == key;
+ });
+ if (confEnv != envItems.end())
+ value = confEnv->value;
+ }
+ return value;
+}
+
+QString CMakeWriter::makeRelative(const NodePtr &node, const Utils::FilePath &path) const
+{
+ const QString dir = node->dir.toString();
+ return "\"" + Utils::FilePath::calcRelativePath(path.toString(), dir) + "\"";
+}
+
+QString CMakeWriter::makeQmlFilesBlock(const NodePtr &node) const
+{
+ QTC_ASSERT(parent(), return {});
+
+ QString qmlFileContent;
+ for (const Utils::FilePath &path : qmlFiles(node))
+ qmlFileContent.append(QString("\t\t%1\n").arg(makeRelative(node, path)));
+
+ QString str;
+ if (!qmlFileContent.isEmpty())
+ str.append(QString("\tQML_FILES\n%1").arg(qmlFileContent));
+
+ return str;
+}
+
+QString CMakeWriter::makeSingletonBlock(const NodePtr &node) const
+{
+ QString str;
+ const QString setProperties("set_source_files_properties(%1\n\tPROPERTIES\n\t\t%2 %3\n)\n\n");
+ for (const Utils::FilePath &path : node->singletons)
+ str.append(setProperties.arg(path.fileName()).arg("QT_QML_SINGLETON_TYPE").arg("true"));
+ return str;
+}
+
+QString CMakeWriter::makeSubdirectoriesBlock(const NodePtr &node) const
+{
+ QTC_ASSERT(parent(), return {});
+
+ QString str;
+ for (const NodePtr &n : node->subdirs) {
+ if (n->type == Node::Type::Module || n->type == Node::Type::Library
+ || n->type == Node::Type::App || parent()->hasChildModule(n))
+ str.append(QString("add_subdirectory(%1)\n").arg(n->dir.fileName()));
+ }
+ return str;
+}
+
+QString CMakeWriter::makeSetEnvironmentFn() const
+{
+ QTC_ASSERT(parent(), return {});
+ QTC_ASSERT(parent()->buildSystem(), return {});
+
+ const QmlBuildSystem *buildSystem = parent()->buildSystem();
+ const QString configFile = getEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
+
+ QString out("inline void set_qt_environment() {\n");
+ for (Utils::EnvironmentItem &envItem : buildSystem->environment()) {
+ QString key = envItem.name;
+ QString value = envItem.value;
+ if (value == configFile)
+ value.prepend(":/");
+ out.append(QString("\tqputenv(\"%1\", \"%2\");\n").arg(key).arg(value));
+ }
+ out.append("}");
+
+ return out;
+}
+
+std::tuple<QString, QString> CMakeWriter::makeResourcesBlocks(const NodePtr &node) const
+{
+ QString resourcesOut;
+ QString bigResourcesOut;
+
+ QString resourceFiles;
+ std::vector<QString> bigResources;
+ for (const Utils::FilePath &path : resources(node)) {
+ if (path.fileSize() > 5000000) {
+ bigResources.push_back(makeRelative(node, path));
+ continue;
+ }
+ resourceFiles.append(QString("\t\t%1\n").arg(makeRelative(node, path)));
+ }
+
+ if (!resourceFiles.isEmpty())
+ resourcesOut.append(QString("\tRESOURCES\n%1").arg(resourceFiles));
+
+ QString templatePostfix;
+ if (!bigResources.empty()) {
+ QString resourceContent;
+ for (const QString &res : bigResources)
+ resourceContent.append(QString("\n %1").arg(res));
+
+ const QString prefixPath = QString(node->uri).replace('.', '/');
+ const QString prefix = "/qt/qml/" + prefixPath;
+ const QString resourceName = node->name + "BigResource";
+
+ bigResourcesOut = QString::fromUtf8(TEMPLATE_BIG_RESOURCES, -1)
+ .arg(node->name, resourceName, prefix, resourceContent);
+ }
+
+ return {resourcesOut, bigResourcesOut};
+}
+
+void CMakeWriter::writeFile(const Utils::FilePath &path, const QString &content) const
+{
+ QFile fileHandle(path.toString());
+ if (fileHandle.open(QIODevice::WriteOnly)) {
+ QTextStream stream(&fileHandle);
+ stream << content;
+ } else {
+ QString text("Failed to write");
+ CMakeGenerator::logIssue(ProjectExplorer::Task::Error, text, path);
+ }
+ fileHandle.close();
+}
+
+void CMakeWriter::collectPlugins(const NodePtr &node, std::vector<QString> &out) const
+{
+ if (isPlugin(node))
+ out.push_back(node->name);
+ for (const auto &child : node->subdirs)
+ collectPlugins(child, out);
+}
+
+} // End namespace GenerateCmake.
+
+} // End namespace QmlProjectManager.
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h
new file mode 100644
index 0000000000..8766df0dcd
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include "utils/filepath.h"
+
+#include <QString>
+
+namespace QmlProjectManager {
+
+class QmlProject;
+class QmlBuildSystem;
+
+namespace GenerateCmake {
+
+struct Node
+{
+ enum class Type {
+ App,
+ Module,
+ Library,
+ Folder,
+ };
+
+ std::shared_ptr<Node> parent = nullptr;
+ Type type = Type::Folder;
+
+ QString uri;
+ QString name;
+ Utils::FilePath dir;
+
+ std::vector<std::shared_ptr<Node>> subdirs;
+ std::vector<Utils::FilePath> files;
+ std::vector<Utils::FilePath> singletons;
+ std::vector<Utils::FilePath> resources;
+ std::vector<Utils::FilePath> sources;
+};
+
+using NodePtr = std::shared_ptr<Node>;
+using FileGetter = std::function<std::vector<Utils::FilePath>(const NodePtr &)>;
+
+class CMakeGenerator;
+
+const char ENV_VARIABLE_CONTROLCONF[] =
+ "QT_QUICK_CONTROLS_CONF";
+
+const char DO_NOT_EDIT_FILE[] =
+ "### This file is automatically generated by Qt Design Studio.\n"
+ "### Do not change\n\n";
+
+const char TEMPLATE_LINK_LIBRARIES[] =
+ "target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE\n"
+ "%3"
+ ")";
+
+class CMakeWriter
+{
+public:
+ using Ptr = std::shared_ptr<CMakeWriter>;
+
+ static Ptr create(CMakeGenerator *parent);
+ static QString readTemplate(const QString &templatePath);
+
+ CMakeWriter(CMakeGenerator *parent);
+ const CMakeGenerator *parent() const;
+
+ virtual bool isPlugin(const NodePtr &node) const;
+ virtual QString sourceDirName() const;
+ virtual void transformNode(NodePtr &) const;
+
+ virtual void writeRootCMakeFile(const NodePtr &node) const = 0;
+ virtual void writeModuleCMakeFile(const NodePtr &node, const NodePtr &root) const = 0;
+ virtual void writeSourceFiles(const NodePtr &node, const NodePtr &root) const = 0;
+
+protected:
+ std::vector<Utils::FilePath> files(const NodePtr &node, const FileGetter &getter) const;
+ std::vector<Utils::FilePath> qmlFiles(const NodePtr &node) const;
+ std::vector<Utils::FilePath> singletons(const NodePtr &node) const;
+ std::vector<Utils::FilePath> resources(const NodePtr &node) const;
+ std::vector<Utils::FilePath> sources(const NodePtr &node) const;
+ std::vector<QString> plugins(const NodePtr &node) const;
+
+ QString getEnvironmentVariable(const QString &key) const;
+
+ QString makeRelative(const NodePtr &node, const Utils::FilePath &path) const;
+ QString makeQmlFilesBlock(const NodePtr &node) const;
+ QString makeSingletonBlock(const NodePtr &node) const;
+ QString makeSubdirectoriesBlock(const NodePtr &node) const;
+ QString makeSetEnvironmentFn() const;
+ std::tuple<QString, QString> makeResourcesBlocks(const NodePtr &node) const;
+
+ void writeFile(const Utils::FilePath &path, const QString &content) const;
+
+private:
+ void collectPlugins(const NodePtr &node, std::vector<QString> &out) const;
+ const CMakeGenerator *m_parent = nullptr;
+};
+
+} // End namespace GenerateCmake.
+
+} // End namespace QmlProjectManager.
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp
new file mode 100644
index 0000000000..5cb17d1e1b
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp
@@ -0,0 +1,180 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "cmakewriterv0.h"
+#include "cmakegenerator.h"
+
+namespace QmlProjectManager {
+
+namespace GenerateCmake {
+
+const char TEMPLATE_ADD_QML_MODULE[] = R"(
+qt6_add_qml_module(%1
+ URI "%2"
+ VERSION 1.0
+ RESOURCE_PREFIX "/qt/qml"
+%3))";
+
+CMakeWriterV0::CMakeWriterV0(CMakeGenerator *parent)
+ : CMakeWriter(parent)
+{}
+
+bool CMakeWriterV0::isPlugin(const NodePtr &node) const
+{
+ if (node->type == Node::Type::App)
+ return !node->files.empty() || !node->singletons.empty() || !node->resources.empty();
+
+ return CMakeWriter::isPlugin(node);
+}
+
+void CMakeWriterV0::transformNode(NodePtr &node) const
+{
+ QTC_ASSERT(parent(), return);
+
+ if (node->name == "src") {
+ node->type = Node::Type::Folder;
+ } else if (node->name == "content") {
+ node->type = Node::Type::Module;
+ } else if (node->type == Node::Type::App) {
+ Utils::FilePath path = node->dir.pathAppended("main.qml");
+ if (!path.exists()) {
+ QString text("Expected File not found.");
+ CMakeGenerator::logIssue(ProjectExplorer::Task::Error, text, path);
+ return;
+ }
+ if (!parent()->findFile(path))
+ node->files.push_back(path);
+ }
+}
+
+void CMakeWriterV0::writeRootCMakeFile(const NodePtr &node) const
+{
+ QTC_ASSERT(parent(), return);
+
+ const Utils::FilePath quickControlsPath = node->dir.pathAppended("qtquickcontrols2.conf");
+ if (!quickControlsPath.exists()) {
+ const QString quickControlsTemplate = readTemplate(":/templates/qtquickcontrols_conf");
+ writeFile(quickControlsPath, quickControlsTemplate);
+ }
+
+ const Utils::FilePath insightPath = node->dir.pathAppended("insight");
+ if (!insightPath.exists()) {
+ const QString insightTemplate = readTemplate(":/templates/insight");
+ writeFile(insightPath, insightTemplate);
+ }
+
+ const Utils::FilePath componentPath = node->dir.pathAppended("qmlcomponents");
+ if (!componentPath.exists()) {
+ const QString compTemplate = readTemplate(":/templates/qmlcomponents");
+ writeFile(componentPath, compTemplate);
+ }
+
+ const QString appName = parent()->projectName() + "App";
+ const QString qtcontrolsConfFile = getEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
+
+ QString fileSection = "";
+ if (!qtcontrolsConfFile.isEmpty())
+ fileSection = QString("\tFILES\n\t\t%1").arg(qtcontrolsConfFile);
+
+ QStringList srcs;
+ for (const Utils::FilePath &path : sources(node))
+ srcs.push_back(makeRelative(node, path));
+
+ const QString fileTemplate = readTemplate(":/templates/cmakeroot_v0");
+ const QString fileContent = fileTemplate.arg(appName, srcs.join(" "), fileSection);
+
+ const Utils::FilePath cmakeFile = node->dir.pathAppended("CMakeLists.txt");
+ writeFile(cmakeFile, fileContent);
+}
+
+void CMakeWriterV0::writeModuleCMakeFile(const NodePtr &node, const NodePtr &root) const
+{
+ QTC_ASSERT(parent(), return);
+
+ Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt");
+
+ QString content(DO_NOT_EDIT_FILE);
+ if (node->type == Node::Type::Folder && parent()->hasChildModule(node)) {
+ content.append(makeSubdirectoriesBlock(node));
+ writeFile(writeToFile, content);
+ return;
+ }
+
+ content.append(makeSubdirectoriesBlock(node));
+ content.append("\n");
+ content.append(makeSingletonBlock(node));
+
+ QString qmlModulesContent;
+ qmlModulesContent.append(makeQmlFilesBlock(node));
+
+ auto [resources, bigResources] = makeResourcesBlocks(node);
+ qmlModulesContent.append(resources);
+
+ if (!qmlModulesContent.isEmpty()) {
+ const QString addLibraryTemplate("qt_add_library(%1 STATIC)");
+ const QString addModuleTemplate(TEMPLATE_ADD_QML_MODULE);
+
+ content.append(addLibraryTemplate.arg(node->name));
+ content.append(addModuleTemplate.arg(node->name, node->uri, qmlModulesContent));
+ content.append("\n\n");
+ }
+
+ content.append(bigResources);
+
+ if (node->type == Node::Type::App) {
+ writeToFile = node->dir.pathAppended("qmlModules");
+ QString pluginNames;
+ for (const QString &moduleName : plugins(root))
+ pluginNames.append("\t" + moduleName + "plugin\n");
+
+ if (!pluginNames.isEmpty())
+ content += QString::fromUtf8(TEMPLATE_LINK_LIBRARIES, -1).arg(pluginNames);
+ }
+
+ writeFile(writeToFile, content);
+}
+
+void CMakeWriterV0::writeSourceFiles(const NodePtr &node, const NodePtr &root) const
+{
+ QTC_ASSERT(parent(), return);
+
+ const Utils::FilePath srcDir = node->dir;
+ if (!srcDir.exists()) {
+ srcDir.createDir();
+
+ const Utils::FilePath componentsHeaderPath = srcDir.pathAppended(
+ "import_qml_components_plugins.h");
+ const QString componentsHeaderContent = readTemplate(
+ ":/templates/import_qml_components_h");
+ writeFile(componentsHeaderPath, componentsHeaderContent);
+
+ const Utils::FilePath cppFilePath = srcDir.pathAppended("main.cpp");
+ const QString cppContent = readTemplate(":/templates/main_cpp_v0");
+ writeFile(cppFilePath, cppContent);
+ }
+
+ QString fileHeader(
+ "/*\n"
+ " * This file is automatically generated by Qt Design Studio.\n"
+ " * Do not change\n"
+ "*/\n\n");
+
+ const Utils::FilePath envHeaderPath = srcDir.pathAppended("app_environment.h");
+ QString envHeaderContent(fileHeader);
+ envHeaderContent.append("#include <QGuiApplication>\n\n");
+ envHeaderContent.append(makeSetEnvironmentFn());
+ writeFile(envHeaderPath, envHeaderContent);
+
+ QString importPluginsContent;
+ for (const QString &module : plugins(root))
+ importPluginsContent.append(QString("Q_IMPORT_QML_PLUGIN(%1)\n").arg(module + "Plugin"));
+
+ QString importPluginsHeader(fileHeader);
+ importPluginsHeader.append("#include <QtQml/qqmlextensionplugin.h>\n\n");
+ importPluginsHeader.append(importPluginsContent);
+
+ const Utils::FilePath headerFilePath = srcDir.pathAppended("import_qml_plugins.h");
+ writeFile(headerFilePath, importPluginsHeader);
+}
+
+} // namespace GenerateCmake
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.h b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.h
new file mode 100644
index 0000000000..a1cd16d0fc
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include "cmakewriter.h"
+
+namespace QmlProjectManager {
+
+namespace GenerateCmake {
+
+class CMakeWriterV0 final : public CMakeWriter
+{
+public:
+ CMakeWriterV0(CMakeGenerator *parent);
+
+ bool isPlugin(const NodePtr &node) const override;
+ void transformNode(NodePtr &node) const override;
+
+ void writeRootCMakeFile(const NodePtr &node) const override;
+ void writeModuleCMakeFile(const NodePtr &node, const NodePtr &root) const override;
+ void writeSourceFiles(const NodePtr &node, const NodePtr &root) const override;
+};
+
+} // namespace GenerateCmake
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp
new file mode 100644
index 0000000000..6d2d93a76b
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp
@@ -0,0 +1,185 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "cmakewriterv1.h"
+#include "cmakegenerator.h"
+
+#include "qmlprojectmanager/buildsystem/qmlbuildsystem.h"
+
+namespace QmlProjectManager {
+
+namespace GenerateCmake {
+
+const char TEMPLATE_SRC_CMAKELISTS[] = R"(
+target_sources(${CMAKE_PROJECT_NAME} PUBLIC
+%2)
+
+target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
+ Qt${QT_VERSION_MAJOR}::Core
+ Qt${QT_VERSION_MAJOR}::Gui
+ Qt${QT_VERSION_MAJOR}::Quick
+ Qt${QT_VERSION_MAJOR}::Qml))";
+
+CMakeWriterV1::CMakeWriterV1(CMakeGenerator *parent)
+ : CMakeWriter(parent)
+{}
+
+QString CMakeWriterV1::sourceDirName() const
+{
+ return "App";
+}
+
+void CMakeWriterV1::transformNode(NodePtr &node) const
+{
+ QTC_ASSERT(parent(), return);
+
+ QString contentDir = parent()->projectName() + "Content";
+ if (node->name == contentDir)
+ node->type = Node::Type::Module;
+}
+
+void CMakeWriterV1::writeRootCMakeFile(const NodePtr &node) const
+{
+ QTC_ASSERT(parent(), return);
+
+ const Utils::FilePath cmakeFolderPath = node->dir.pathAppended("cmake");
+ if (!cmakeFolderPath.exists())
+ cmakeFolderPath.createDir();
+
+ const Utils::FilePath insightPath = cmakeFolderPath.pathAppended("insight.cmake");
+ if (!insightPath.exists()) {
+ const QString insightTemplate = readTemplate(":/templates/insight");
+ writeFile(insightPath, insightTemplate);
+ }
+
+ const Utils::FilePath componentPath = cmakeFolderPath.pathAppended("qmlcomponents.cmake");
+ if (!componentPath.exists()) {
+ const QString compTemplate = readTemplate(":/templates/qmlcomponents");
+ writeFile(componentPath, compTemplate);
+ }
+
+ const Utils::FilePath sharedFile = node->dir.pathAppended("CMakeLists.txt.shared");
+ if (!sharedFile.exists()) {
+ const QString sharedTemplate = readTemplate(":/templates/cmake_shared");
+ writeFile(sharedFile, sharedTemplate);
+ }
+
+ const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt");
+ if (!file.exists()) {
+ const QString appName = parent()->projectName() + "App";
+
+ QString fileSection = "";
+ const QString configFile = getEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
+ if (!configFile.isEmpty())
+ fileSection = QString("\t\t%1").arg(configFile);
+
+ const QString fileTemplate = readTemplate(":/templates/cmakeroot_v1");
+ const QString fileContent = fileTemplate.arg(appName, fileSection);
+ writeFile(file, fileContent);
+ }
+}
+
+void CMakeWriterV1::writeModuleCMakeFile(const NodePtr &node, const NodePtr &) const
+{
+ QTC_ASSERT(parent(), return);
+
+ if (node->type == Node::Type::App) {
+ const Utils::FilePath userFile = node->dir.pathAppended("qds.cmake");
+ QString userFileContent(DO_NOT_EDIT_FILE);
+ userFileContent.append(makeSubdirectoriesBlock(node));
+ userFileContent.append("\n");
+
+ QString pluginNames;
+ std::vector<QString> plugs = plugins(node);
+ for (size_t i = 0; i < plugs.size(); ++i) {
+ pluginNames.append("\t" + plugs[i] + "plugin");
+ if (i != plugs.size() - 1)
+ pluginNames.append("\n");
+ }
+
+ QString linkLibrariesTemplate(
+ "target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE\n"
+ "%1)");
+
+ userFileContent.append(linkLibrariesTemplate.arg(pluginNames));
+ writeFile(userFile, userFileContent);
+ return;
+ }
+
+ Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt");
+ if (node->type == Node::Type::Folder && parent()->hasChildModule(node)) {
+ QString content(DO_NOT_EDIT_FILE);
+ content.append(makeSubdirectoriesBlock(node));
+ writeFile(writeToFile, content);
+ return;
+ }
+
+ QString prefix;
+ prefix.append(makeSubdirectoriesBlock(node));
+ prefix.append(makeSingletonBlock(node));
+
+ auto [resources, bigResources] = makeResourcesBlocks(node);
+ QString moduleContent;
+ moduleContent.append(makeQmlFilesBlock(node));
+ moduleContent.append(resources);
+
+ QString postfix;
+ postfix.append(bigResources);
+
+ const QString fileTemplate = readTemplate(":/templates/cmakemodule_v1");
+ const QString fileContent = fileTemplate
+ .arg(node->name, node->uri, prefix, moduleContent, postfix);
+ writeFile(writeToFile, fileContent);
+}
+
+void CMakeWriterV1::writeSourceFiles(const NodePtr &node, const NodePtr &root) const
+{
+ QTC_ASSERT(parent(), return);
+ QTC_ASSERT(parent()->buildSystem(), return);
+
+ const QmlBuildSystem *buildSystem = parent()->buildSystem();
+
+ const Utils::FilePath srcDir = node->dir;
+ if (!srcDir.exists())
+ srcDir.createDir();
+
+ const Utils::FilePath autogenDir = srcDir.pathAppended("autogen");
+ if (!autogenDir.exists())
+ autogenDir.createDir();
+
+ const Utils::FilePath mainCppPath = srcDir.pathAppended("main.cpp");
+ if (!mainCppPath.exists()) {
+ const QString cppContent = readTemplate(":/templates/main_cpp_v1");
+ writeFile(mainCppPath, cppContent);
+ }
+
+ const Utils::FilePath cmakePath = srcDir.pathAppended("CMakeLists.txt");
+ if (!cmakePath.exists()) {
+ std::vector<Utils::FilePath> sourcePaths = sources(node);
+ if (sourcePaths.empty())
+ sourcePaths.push_back(mainCppPath);
+
+ QString srcs = {};
+ for (const Utils::FilePath &src : sourcePaths)
+ srcs.append("\t" + makeRelative(node, src) + "\n");
+
+ QString fileTemplate = QString::fromUtf8(TEMPLATE_SRC_CMAKELISTS, -1).arg(srcs);
+ writeFile(cmakePath, fileTemplate);
+ }
+
+ const Utils::FilePath headerPath = autogenDir.pathAppended("environment.h");
+
+ QString environmentPrefix;
+ for (const QString &module : plugins(root))
+ environmentPrefix.append(QString("Q_IMPORT_QML_PLUGIN(%1)\n").arg(module + "Plugin"));
+
+ const QString mainFile("const char mainQmlFile[] = \"qrc:/qt/qml/%1\";");
+ environmentPrefix.append("\n");
+ environmentPrefix.append(mainFile.arg(buildSystem->mainFile()));
+
+ const QString environmentPostfix = makeSetEnvironmentFn();
+ const QString headerTemplate = readTemplate(":/templates/environment_h");
+ writeFile(headerPath, headerTemplate.arg(environmentPrefix, environmentPostfix));
+}
+
+} // namespace GenerateCmake
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.h b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.h
new file mode 100644
index 0000000000..2a6a05b07b
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include "cmakewriter.h"
+
+namespace QmlProjectManager {
+
+namespace GenerateCmake {
+
+class CMakeWriterV1 final : public CMakeWriter
+{
+public:
+ CMakeWriterV1(CMakeGenerator *parent);
+
+ QString sourceDirName() const override;
+ void transformNode(NodePtr &node) const override;
+
+ void writeRootCMakeFile(const NodePtr &node) const override;
+ void writeModuleCMakeFile(const NodePtr &node, const NodePtr &root) const override;
+ void writeSourceFiles(const NodePtr &node, const NodePtr &root) const override;
+};
+
+} // namespace GenerateCmake
+} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
deleted file mode 100644
index f46b508c8c..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
+++ /dev/null
@@ -1,668 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "generatecmakelists.h"
-
-#include "generatecmakelistsconstants.h"
-#include "cmakegeneratordialog.h"
-#include "../qmlprojectmanagertr.h"
-
-#include <coreplugin/actionmanager/actionmanager.h>
-#include <coreplugin/actionmanager/actioncontainer.h>
-
-#include <projectexplorer/buildsystem.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/projectmanager.h>
-#include <projectexplorer/target.h>
-
-#include <qmlprojectmanager/buildsystem/qmlbuildsystem.h>
-#include <qmlprojectmanager/qmlmainfileaspect.h>
-#include <qmlprojectmanager/qmlproject.h>
-#include <qmlprojectmanager/qmlprojectconstants.h>
-
-#include <extensionsystem/iplugin.h>
-#include <extensionsystem/pluginmanager.h>
-#include <extensionsystem/pluginspec.h>
-
-#include <utils/fileutils.h>
-
-#include <QAction>
-#include <QMenu>
-#include <QMessageBox>
-#include <QRegularExpression>
-#include <QStringList>
-#include <QTextStream>
-#include <QtConcurrent>
-
-using namespace Utils;
-using namespace QmlProjectManager::GenerateCmake::Constants;
-
-namespace QmlProjectManager {
-
-namespace GenerateCmake {
-
-static bool isQmlDesigner(const ExtensionSystem::PluginSpec *spec)
-{
- if (!spec)
- return false;
-
- return spec->name().contains("QmlDesigner");
-}
-
-static void trackUsage(const QString &id)
-{
- const auto plugins = ExtensionSystem::PluginManager::plugins();
- const auto it = std::find_if(plugins.begin(), plugins.end(), &isQmlDesigner);
- if (it != plugins.end()) {
- QObject *qmlDesignerPlugin = (*it)->plugin();
- QMetaObject::invokeMethod(qmlDesignerPlugin,
- "usageStatisticsNotifier",
- Qt::DirectConnection,
- Q_ARG(QString, id));
- }
-}
-
-bool operator==(const GeneratableFile &left, const GeneratableFile &right)
-{
- return (left.filePath == right.filePath && left.content == right.content);
-}
-
-enum ProjectDirectoryError {
- NoError = 0,
- MissingContentDir = 1<<1,
- MissingImportDir = 1<<2,
- MissingAssetDir = 1<<3,
- MissingAssetImportDir = 1<<4,
- MissingCppDir = 1<<5,
- MissingMainCMake = 1<<6,
- MissingMainQml = 1<<7,
- MissingAppMainQml = 1<<8,
- MissingQmlModules = 1<<9,
- MissingMainCpp = 1<<10,
- MissingMainCppHeader = 1<<11,
- MissingEnvHeader = 1<<12
-};
-
-const QString MENU_ITEM_GENERATE = Tr::tr("Generate CMake Build Files...");
-
-void generateMenuEntry(QObject *parent)
-{
- Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE);
-
- Core::ActionContainer *exportMenu = Core::ActionManager::createMenu(
- QmlProjectManager::Constants::EXPORT_MENU);
-
- exportMenu->menu()->setTitle(Tr::tr("Export Project"));
- menu->addMenu(exportMenu, Core::Constants::G_FILE_EXPORT);
-
- exportMenu->appendGroup(QmlProjectManager::Constants::G_EXPORT_GENERATE);
- exportMenu->appendGroup(QmlProjectManager::Constants::G_EXPORT_CONVERT);
- exportMenu->addSeparator(QmlProjectManager::Constants::G_EXPORT_CONVERT);
-
- auto action = new QAction(MENU_ITEM_GENERATE, parent);
- QObject::connect(action, &QAction::triggered, GenerateCmake::onGenerateCmakeLists);
- Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateCMakeLists");
- exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE);
-
- action->setEnabled(false);
- QObject::connect(ProjectExplorer::ProjectManager::instance(),
- &ProjectExplorer::ProjectManager::startupProjectChanged,
- [action]() {
- if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem())
- action->setEnabled(!buildSystem->qtForMCUs());
- });
-}
-
-void onGenerateCmakeLists()
-{
- trackUsage("generateCMakeProjectDialogOpened");
- FilePath rootDir = ProjectExplorer::ProjectManager::startupProject()->projectDirectory();
-
- int projectDirErrors = isProjectCorrectlyFormed(rootDir);
- if (projectDirErrors != NoError) {
- showProjectDirErrorDialog(projectDirErrors);
- if (isErrorFatal(projectDirErrors))
- return;
- }
-
- CmakeFileGenerator cmakeGen;
- cmakeGen.prepare(rootDir);
-
- FilePaths allFiles;
- for (const GeneratableFile &file: cmakeGen.fileQueue().queuedFiles())
- allFiles.append(file.filePath);
-
- CmakeGeneratorDialog dialog(rootDir, allFiles, cmakeGen.invalidFileNames());
- if (dialog.exec()) {
- FilePaths confirmedFiles = dialog.getFilePaths();
- cmakeGen.filterFileQueue(confirmedFiles);
- cmakeGen.execute();
- }
-
- trackUsage("generateCMakeProjectExecuted");
-}
-
-bool isErrorFatal(int error)
-{
- if (error & MissingContentDir ||
- error & MissingImportDir ||
- error & MissingCppDir ||
- error & MissingAppMainQml)
- return true;
-
- return false;
-}
-
-int isProjectCorrectlyFormed(const FilePath &rootDir)
-{
- int errors = NoError;
-
- if (!rootDir.pathAppended(DIRNAME_CONTENT).exists())
- errors |= MissingContentDir;
- if (!rootDir.pathAppended(DIRNAME_CONTENT).pathAppended(FILENAME_APPMAINQML).exists())
- errors |= MissingAppMainQml;
-
- if (!rootDir.pathAppended(DIRNAME_IMPORT).exists())
- errors |= MissingImportDir;
- if (!rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists())
- errors |= MissingAssetImportDir;
-
- if (!rootDir.pathAppended(DIRNAME_CPP).exists())
- errors |= MissingCppDir;
- if (!rootDir.pathAppended(DIRNAME_CPP).pathAppended(FILENAME_MAINCPP).exists())
- errors |= MissingMainCpp;
- if (!rootDir.pathAppended(DIRNAME_CPP).pathAppended(FILENAME_MAINCPP_HEADER).exists())
- errors |= MissingMainCppHeader;
- if (!rootDir.pathAppended(DIRNAME_CPP).pathAppended(FILENAME_ENV_HEADER).exists())
- errors |= MissingEnvHeader;
-
- if (!rootDir.pathAppended(FILENAME_CMAKELISTS).exists())
- errors |= MissingMainCMake;
- if (!rootDir.pathAppended(FILENAME_MODULES).exists())
- errors |= MissingQmlModules;
- if (!rootDir.pathAppended(FILENAME_MAINQML).exists())
- errors |= MissingMainQml;
-
- return errors;
-}
-
-const QString WARNING_MISSING_STRUCTURE_FATAL = Tr::tr("The project is not properly structured for automatically generating CMake files.\n\nAborting process.\n\nThe following files or directories are missing:\n\n%1");
-//const QString WARNING_MISSING_STRUCTURE_NONFATAL = Tr::tr("The project is not properly structured for automatically generating CMake files.\n\nThe following files or directories are missing and may be created:\n\n%1");
-const QString WARNING_TITLE_FATAL = Tr::tr("Cannot Generate CMake Files");
-//const QString WARNING_TITLE_NONFATAL = Tr::tr("Problems with Generating CMake Files");
-
-void showProjectDirErrorDialog(int error)
-{
- bool isFatal = isErrorFatal(error);
-
- if (isFatal) {
- QString fatalList;
-
- if (error & MissingContentDir)
- fatalList.append(QString(DIRNAME_CONTENT) + "\n");
- if (error & MissingAppMainQml)
- fatalList.append(QString(DIRNAME_CONTENT)
- + QDir::separator()
- + QString(FILENAME_APPMAINQML)
- + "\n");
- if (error & MissingCppDir)
- fatalList.append(QString(DIRNAME_CPP) + "\n");
- if (error & MissingImportDir)
- fatalList.append(QString(DIRNAME_IMPORT) + "\n");
-
- QMessageBox::critical(nullptr,
- WARNING_TITLE_FATAL,
- WARNING_MISSING_STRUCTURE_FATAL.arg(fatalList));
- }
-}
-
-bool FileQueue::queueFile(const FilePath &filePath, const QString &fileContent)
-{
- GeneratableFile file;
- file.filePath = filePath;
- file.content = fileContent;
- file.fileExists = filePath.exists();
- m_queuedFiles.append(file);
-
- return true;
-}
-
-const QVector<GeneratableFile> FileQueue::queuedFiles() const
-{
- return m_queuedFiles;
-}
-
-bool FileQueue::writeQueuedFiles()
-{
- for (GeneratableFile &file: m_queuedFiles)
- if (!writeFile(file))
- return false;
-
- return true;
-}
-
-bool FileQueue::writeFile(const GeneratableFile &file)
-{
- QFile fileHandle(file.filePath.toString());
- fileHandle.open(QIODevice::WriteOnly);
- QTextStream stream(&fileHandle);
- stream << file.content;
- fileHandle.close();
-
- return true;
-}
-
-void FileQueue::filterFiles(const Utils::FilePaths keepFiles)
-{
- QtConcurrent::blockingFilter(m_queuedFiles, [keepFiles](const GeneratableFile &qf) {
- return keepFiles.contains(qf.filePath);
- });
-}
-
-QString readTemplate(const QString &templatePath)
-{
- QFile templatefile(templatePath);
- templatefile.open(QIODevice::ReadOnly);
- QTextStream stream(&templatefile);
- QString content = stream.readAll();
- templatefile.close();
-
- return content;
-}
-
-const QString projectEnvironmentVariable(const QString &key)
-{
- QString value = {};
-
- if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) {
- auto envItems = buildSystem->environment();
- auto confEnv = std::find_if(envItems.begin(), envItems.end(), [key](EnvironmentItem &item) {
- return item.name == key;
- });
- if (confEnv != envItems.end())
- value = confEnv->value;
- }
-
- return value;
-}
-
-const QDir::Filters FILES_ONLY = QDir::Files;
-const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot;
-
-const char MAIN_CMAKEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincmakelists.tpl";
-const char QMLMODULES_FILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodules.tpl";
-
-bool CmakeFileGenerator::prepare(const FilePath &rootDir, bool checkFileBelongs)
-{
- m_checkFileIsInProject = checkFileBelongs;
-
- FilePath contentDir = rootDir.pathAppended(DIRNAME_CONTENT);
- FilePath importDir = rootDir.pathAppended(DIRNAME_IMPORT);
- FilePath assetImportDir = rootDir.pathAppended(DIRNAME_ASSETIMPORT);
-
- generateModuleCmake(contentDir);
- generateImportCmake(importDir);
- generateImportCmake(assetImportDir);
- generateMainCmake(rootDir);
- generateEntryPointFiles(rootDir);
-
- return true;
-}
-
-const FileQueue CmakeFileGenerator::fileQueue() const
-{
- return m_fileQueue;
-}
-
-void CmakeFileGenerator::filterFileQueue(const Utils::FilePaths &keepFiles)
-{
- m_fileQueue.filterFiles(keepFiles);
-}
-
-bool CmakeFileGenerator::execute()
-{
- return m_fileQueue.writeQueuedFiles();
-}
-
-FilePaths CmakeFileGenerator::invalidFileNames() const
-{
- return m_invalidFileNames;
-}
-
-const char DO_NOT_EDIT_FILE_COMMENT[] = "### This file is automatically generated by Qt Design Studio.\n### Do not change\n\n";
-const char ADD_SUBDIR[] = "add_subdirectory(%1)\n";
-
-void CmakeFileGenerator::generateMainCmake(const FilePath &rootDir)
-{
- //TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all.
- QString projectName = ProjectExplorer::ProjectManager::startupProject()->displayName();
- QString appName = projectName + "App";
-
- QString fileSection = "";
- const QString qtcontrolsConfFile = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
- if (!qtcontrolsConfFile.isEmpty())
- fileSection = QString(" FILES\n %1").arg(qtcontrolsConfFile);
-
- QString cmakeFileContent = GenerateCmake::readTemplate(MAIN_CMAKEFILE_TEMPLATE_PATH)
- .arg(appName)
- .arg(fileSection);
-
- queueCmakeFile(rootDir, cmakeFileContent);
-
- QString subdirIncludes;
- subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_CONTENT));
- subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_IMPORT));
- if (rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists())
- subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_ASSETIMPORT));
-
- QString modulesAsPlugins;
- for (const QString &moduleName : m_moduleNames)
- modulesAsPlugins.append(" " + moduleName + "plugin\n");
-
- QString moduleFileContent = GenerateCmake::readTemplate(QMLMODULES_FILE_TEMPLATE_PATH)
- .arg(appName)
- .arg(subdirIncludes)
- .arg(modulesAsPlugins);
-
- m_fileQueue.queueFile(rootDir.pathAppended(FILENAME_MODULES), moduleFileContent);
-}
-
-void CmakeFileGenerator::generateImportCmake(const FilePath &dir, const QString &modulePrefix)
-{
- if (!dir.exists())
- return;
-
- QString fileContent;
-
- fileContent.append(DO_NOT_EDIT_FILE_COMMENT);
- FilePaths subDirs = dir.dirEntries(DIRS_ONLY);
- for (FilePath &subDir : subDirs) {
- if (isDirBlacklisted(subDir))
- continue;
- if (getDirectoryTreeQmls(subDir).isEmpty() && getDirectoryTreeResources(subDir).isEmpty())
- continue;
- fileContent.append(QString(ADD_SUBDIR).arg(subDir.fileName()));
- QString prefix = modulePrefix.isEmpty() ?
- QString(modulePrefix % subDir.fileName()) :
- QString(QString(modulePrefix + '.') + subDir.fileName());
- if (getDirectoryQmls(subDir).isEmpty()) {
- generateImportCmake(subDir, prefix);
- } else {
- generateModuleCmake(subDir, prefix);
- }
- }
-
- queueCmakeFile(dir, fileContent);
-}
-
-const char MODULEFILE_PROPERTY_SINGLETON[] = "QT_QML_SINGLETON_TYPE";
-const char MODULEFILE_PROPERTY_SET[] = "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n";
-const char MODULEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodulecmakelists.tpl";
-
-void CmakeFileGenerator::generateModuleCmake(const FilePath &dir, const QString &uri)
-{
- QString fileTemplate = GenerateCmake::readTemplate(MODULEFILE_TEMPLATE_PATH);
-
- QString singletonContent;
- FilePaths qmldirFileList = dir.dirEntries({QStringList(FILENAME_QMLDIR), FILES_ONLY});
- if (!qmldirFileList.isEmpty()) {
- QStringList singletons = getSingletonsFromQmldirFile(qmldirFileList.first());
- for (QString &singleton : singletons) {
- singletonContent.append(QString(MODULEFILE_PROPERTY_SET).arg(singleton).arg(MODULEFILE_PROPERTY_SINGLETON).arg("true"));
- }
- }
-
- QStringList qmlFileList = getDirectoryTreeQmls(dir);
- QString qmlFiles;
- for (QString &qmlFile : qmlFileList)
- qmlFiles.append(QString(" %1\n").arg(qmlFile));
-
- QStringList resourceFileList = getDirectoryTreeResources(dir);
- QString resourceFiles;
- for (QString &resourceFile : resourceFileList)
- resourceFiles.append(QString(" %1\n").arg(resourceFile));
-
- QString moduleContent;
- if (!qmlFiles.isEmpty()) {
- moduleContent.append(QString(" QML_FILES\n%1").arg(qmlFiles));
- }
- if (!resourceFiles.isEmpty()) {
- moduleContent.append(QString(" RESOURCES\n%1").arg(resourceFiles));
- }
-
- QString moduleUri = uri.isEmpty() ?
- dir.fileName() :
- uri;
-
- QString moduleName = QString(moduleUri).replace('.', '_');
- m_moduleNames.append(moduleName);
-
- QString fileContent;
- fileContent.append(fileTemplate.arg(singletonContent, moduleName, moduleUri, moduleContent));
- queueCmakeFile(dir, fileContent);
-}
-
-QStringList CmakeFileGenerator::getSingletonsFromQmldirFile(const FilePath &filePath)
-{
- QStringList singletons;
- QFile f(filePath.toString());
- f.open(QIODevice::ReadOnly);
- QTextStream stream(&f);
-
- while (!stream.atEnd()) {
- QString line = stream.readLine();
- if (line.startsWith("singleton", Qt::CaseInsensitive)) {
- QStringList tokenizedLine = line.split(QRegularExpression("\\s+"));
- QString fileName = tokenizedLine.last();
- if (fileName.endsWith(".qml", Qt::CaseInsensitive)) {
- singletons.append(fileName);
- }
- }
- }
-
- f.close();
-
- return singletons;
-}
-
-QStringList CmakeFileGenerator::getDirectoryQmls(const FilePath &dir)
-{
- QStringList moduleFiles;
-
- const QStringList qmlFilesOnly(FILENAME_FILTER_QML);
- FilePaths allFiles = dir.dirEntries({qmlFilesOnly, FILES_ONLY});
- for (FilePath &file : allFiles) {
- if (includeFile(file)) {
- moduleFiles.append(file.fileName());
- }
- }
-
- return moduleFiles;
-}
-
-QStringList CmakeFileGenerator::getDirectoryResources(const FilePath &dir)
-{
- QStringList moduleFiles;
-
- FilePaths allFiles = dir.dirEntries(FILES_ONLY);
- for (FilePath &file : allFiles) {
- if (!file.fileName().endsWith(".qml", Qt::CaseInsensitive) && includeFile(file)) {
- moduleFiles.append(file.fileName());
- }
- }
-
- return moduleFiles;
-}
-
-QStringList CmakeFileGenerator::getDirectoryTreeQmls(const FilePath &dir)
-{
- QStringList qmlFileList;
-
- qmlFileList.append(getDirectoryQmls(dir));
-
- FilePaths subDirsList = dir.dirEntries(DIRS_ONLY);
- for (FilePath &subDir : subDirsList) {
- if (isDirBlacklisted(subDir))
- continue;
- QStringList subDirQmlFiles = getDirectoryTreeQmls(subDir);
- for (QString &qmlFile : subDirQmlFiles) {
- qmlFileList.append(subDir.fileName().append('/').append(qmlFile));
- }
- }
-
- return qmlFileList;
-}
-
-static void appendWidthQuotes(QStringList &list, const QString &string)
-{
- if (string.contains(' '))
- list.append("\"" + string + "\"");
- else
- list.append(string);
-}
-
-QStringList CmakeFileGenerator::getDirectoryTreeResources(const FilePath &dir)
-{
- QStringList resourceFileList;
-
- //for (const auto &string : getDirectoryResources(dir))
- // appendWidthQuotes(resourceFileList, string);
- resourceFileList.append(getDirectoryResources(dir));
-
- FilePaths subDirsList = dir.dirEntries(DIRS_ONLY);
- for (FilePath &subDir : subDirsList) {
- if (isDirBlacklisted(subDir))
- continue;
- QStringList subDirResources = getDirectoryTreeResources(subDir);
- for (QString &resource : subDirResources) {
- appendWidthQuotes(resourceFileList, subDir.fileName().append('/').append(resource));
- }
- }
-
- return resourceFileList;
-}
-
-void CmakeFileGenerator::queueCmakeFile(const FilePath &dir, const QString &content)
-{
- FilePath filePath = dir.pathAppended(FILENAME_CMAKELISTS);
- m_fileQueue.queueFile(filePath, content);
-}
-
-bool CmakeFileGenerator::isFileBlacklisted(const QString &fileName)
-{
- return (!fileName.compare(FILENAME_QMLDIR) ||
- !fileName.compare(FILENAME_CMAKELISTS));
-}
-
-bool CmakeFileGenerator::isDirBlacklisted(const FilePath &dir)
-{
- return (!dir.fileName().compare(DIRNAME_DESIGNER));
-}
-
-bool CmakeFileGenerator::validFileName(const Utils::FilePath &filePath)
-{
- QStringList invalidChars = {"!", "\"", "£", "$", "%", "!", "^", "&", "*", "(", ")", "=", "+",
- "'", ",", ";", ":", "#", "~", "{", "{", "[", "]", "<", ">", "?"};
- const QString baseName = filePath.baseName();
- for (const auto &c : invalidChars) {
- if (baseName.contains(c))
- return false;
- }
- return true;
-}
-
-bool CmakeFileGenerator::includeFile(const FilePath &filePath)
-{
- if (m_checkFileIsInProject) {
- ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
- if (!project->isKnownFile(filePath))
- return false;
- }
-
- if (validFileName(filePath))
- return !isFileBlacklisted(filePath.fileName());
- else
- m_invalidFileNames.append(filePath);
-
- return false;
-}
-
-bool CmakeFileGenerator::generateEntryPointFiles(const FilePath &dir)
-{
- const QString qtcontrolsConf = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
- if (!qtcontrolsConf.isEmpty())
- m_resourceFileLocations.append(qtcontrolsConf);
-
- bool cppOk = generateMainCpp(dir);
- bool qmlOk = generateMainQml(dir);
-
- return cppOk && qmlOk;
-}
-
-const char MAIN_CPPFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincpp.tpl";
-const char MAIN_CPPFILE_HEADER_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincppheader.tpl";
-const char MAIN_CPPFILE_HEADER_PLUGIN_LINE[] = "Q_IMPORT_QML_PLUGIN(%1)\n";
-const char ENV_HEADER_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectenvheader.tpl";
-const char ENV_HEADER_VARIABLE_LINE[] = " qputenv(\"%1\", \"%2\");\n";
-
-bool CmakeFileGenerator::generateMainCpp(const FilePath &dir)
-{
- FilePath srcDir = dir.pathAppended(DIRNAME_CPP);
-
- QString cppContent = GenerateCmake::readTemplate(MAIN_CPPFILE_TEMPLATE_PATH);
- FilePath cppFilePath = srcDir.pathAppended(FILENAME_MAINCPP);
- bool cppOk = m_fileQueue.queueFile(cppFilePath, cppContent);
-
- QString modulesAsPlugins;
- for (const QString &moduleName : m_moduleNames)
- modulesAsPlugins.append(
- QString(MAIN_CPPFILE_HEADER_PLUGIN_LINE).arg(moduleName + "Plugin"));
-
- QString headerContent = GenerateCmake::readTemplate(MAIN_CPPFILE_HEADER_TEMPLATE_PATH)
- .arg(modulesAsPlugins);
- FilePath headerFilePath = srcDir.pathAppended(FILENAME_MAINCPP_HEADER);
- bool pluginHeaderOk = m_fileQueue.queueFile(headerFilePath, headerContent);
-
- bool envHeaderOk = true;
- QString environment;
-
- if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) {
- for (EnvironmentItem &envItem : buildSystem->environment()) {
- QString key = envItem.name;
- QString value = envItem.value;
- if (isFileResource(value))
- value.prepend(":/");
- environment.append(QString(ENV_HEADER_VARIABLE_LINE).arg(key).arg(value));
- }
- QString envHeaderContent = GenerateCmake::readTemplate(ENV_HEADER_TEMPLATE_PATH)
- .arg(environment);
- FilePath envHeaderPath = srcDir.pathAppended(FILENAME_ENV_HEADER);
- envHeaderOk = m_fileQueue.queueFile(envHeaderPath, envHeaderContent);
- }
-
- return cppOk && pluginHeaderOk && envHeaderOk;
-}
-
-const char MAIN_QMLFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmainqml.tpl";
-
-bool CmakeFileGenerator::generateMainQml(const FilePath &dir)
-{
- QString content = GenerateCmake::readTemplate(MAIN_QMLFILE_TEMPLATE_PATH);
- FilePath filePath = dir.pathAppended(FILENAME_MAINQML);
- return m_fileQueue.queueFile(filePath, content);
-}
-
-bool CmakeFileGenerator::isFileResource(const QString &relativeFilePath)
-{
- if (m_resourceFileLocations.contains(relativeFilePath))
- return true;
-
- return false;
-}
-
-
-
-} //GenerateCmake
-} //QmlProjectManager
-
diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h
deleted file mode 100644
index db025daa27..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <projectexplorer/project.h>
-
-#include <utils/fileutils.h>
-
-namespace QmlProjectManager {
-namespace GenerateCmake {
-struct GeneratableFile {
- Utils::FilePath filePath;
- QString content;
- bool fileExists;
-};
-
-bool operator==(const GeneratableFile &left, const GeneratableFile &right);
-
-void generateMenuEntry(QObject *parent);
-void onGenerateCmakeLists();
-bool isErrorFatal(int error);
-int isProjectCorrectlyFormed(const Utils::FilePath &rootDir);
-void showProjectDirErrorDialog(int error);
-QString readTemplate(const QString &templatePath);
-const QString projectEnvironmentVariable(const QString &key);
-
-class FileQueue {
-public:
- bool queueFile(const Utils::FilePath &filePath, const QString &fileContent);
- const QVector<GeneratableFile> queuedFiles() const;
- bool writeQueuedFiles();
- void filterFiles(const Utils::FilePaths keepFiles);
-
-private:
- bool writeFile(const GeneratableFile &file);
-
-private:
- QVector<GeneratableFile> m_queuedFiles;
-};
-
-class CmakeFileGenerator {
-public:
- bool prepare(const Utils::FilePath &rootDir, bool check = true);
- const FileQueue fileQueue() const;
- void filterFileQueue(const Utils::FilePaths &keepFiles);
- bool execute();
- Utils::FilePaths invalidFileNames() const;
-
-private:
- void generateMainCmake(const Utils::FilePath &rootDir);
- void generateImportCmake(const Utils::FilePath &dir, const QString &modulePrefix = QString());
- void generateModuleCmake(const Utils::FilePath &dir, const QString &moduleUri = QString());
- bool generateEntryPointFiles(const Utils::FilePath &dir);
- bool generateMainCpp(const Utils::FilePath &dir);
- bool generateMainQml(const Utils::FilePath &dir);
- QStringList getDirectoryQmls(const Utils::FilePath &dir);
- QStringList getDirectoryResources(const Utils::FilePath &dir);
- QStringList getSingletonsFromQmldirFile(const Utils::FilePath &filePath);
- QStringList getDirectoryTreeQmls(const Utils::FilePath &dir);
- QStringList getDirectoryTreeResources(const Utils::FilePath &dir);
- void queueCmakeFile(const Utils::FilePath &filePath, const QString &content);
- bool isFileResource(const QString &relativeFilePath);
- bool isFileBlacklisted(const QString &fileName);
- bool isDirBlacklisted(const Utils::FilePath &dir);
- bool includeFile(const Utils::FilePath &filePath);
- bool validFileName(const Utils::FilePath &filePath);
-
-private:
- FileQueue m_fileQueue;
- QStringList m_resourceFileLocations;
- QStringList m_moduleNames;
- bool m_checkFileIsInProject;
-
- Utils::FilePaths m_invalidFileNames;
-};
-
-} //GenerateCmake
-
-} //QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelistsconstants.h b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelistsconstants.h
deleted file mode 100644
index b983ce7fec..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelistsconstants.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#ifndef GENERATECMAKELISTSCONSTANTS_H
-#define GENERATECMAKELISTSCONSTANTS_H
-
-#pragma once
-
-namespace QmlProjectManager {
-namespace GenerateCmake {
-namespace Constants {
-
-const char DIRNAME_CONTENT[] = "content";
-const char DIRNAME_IMPORT[] = "imports";
-const char DIRNAME_ASSET[] = "assets";
-const char DIRNAME_ASSETIMPORT[] = "asset_imports";
-const char DIRNAME_CPP[] = "src";
-const char DIRNAME_DESIGNER[] = "designer";
-
-const char FILENAME_CMAKELISTS[] = "CMakeLists.txt";
-const char FILENAME_APPMAINQML[] = "App.qml";
-const char FILENAME_MAINQML[] = "main.qml";
-const char FILENAME_MAINCPP[] = "main.cpp";
-const char FILENAME_MAINCPP_HEADER[] = "import_qml_plugins.h";
-const char FILENAME_MODULES[] = "qmlmodules";
-const char FILENAME_QMLDIR[] = "qmldir";
-const char FILENAME_ENV_HEADER[] = "app_environment.h";
-
-const char FILENAME_SUFFIX_QMLPROJECT[] = "qmlproject";
-const char FILENAME_SUFFIX_QML[] = "qml";
-const char FILENAME_SUFFIX_USER[] = "user";
-
-const char FILENAME_FILTER_QMLPROJECT[] = "*.qmlproject";
-const char FILENAME_FILTER_QML[] = "*.qml";
-
-const char ENV_VARIABLE_CONTROLCONF[] = "QT_QUICK_CONTROLS_CONF";
-
-} //Constants
-} //GenerateCmake
-} //QmlProjectManager
-
-#endif // GENERATECMAKELISTSCONSTANTS_H
diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectappmainqml.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectappmainqml.tpl
deleted file mode 100644
index 35f8218dc6..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectappmainqml.tpl
+++ /dev/null
@@ -1,14 +0,0 @@
-import QtQuick
-import QtQuick.Window
-
-Window {
- visible: true
- title: "%1"
- width: mainScreen.width
- height: mainScreen.height
-
- %1 {
- id: mainScreen
- }
-
-}
diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectenvheader.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectenvheader.tpl
deleted file mode 100644
index bf60adb4ac..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectenvheader.tpl
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * This file is automatically generated by Qt Design Studio.
- * Do not change.
-*/
-
-#include <QGuiApplication>
-
-void set_qt_environment()
-{
-%1
-}
diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincppheader.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincppheader.tpl
deleted file mode 100644
index 60cef09a82..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincppheader.tpl
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * This file is automatically generated by Qt Design Studio.
- * Do not change.
-*/
-
-#include <QtQml/qqmlextensionplugin.h>
-
-%1
diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmainqml.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmainqml.tpl
deleted file mode 100644
index fa8f6d1cc1..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmainqml.tpl
+++ /dev/null
@@ -1,6 +0,0 @@
-import QtQuick
-import content
-
-App {
-}
-
diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodulecmakelists.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodulecmakelists.tpl
deleted file mode 100644
index 93573a1ed7..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodulecmakelists.tpl
+++ /dev/null
@@ -1,12 +0,0 @@
-### This file is automatically generated by Qt Design Studio.
-### Do not change
-
-%1
-
-qt_add_library(%2 STATIC)
-qt6_add_qml_module(%2
- URI "%3"
- VERSION 1.0
- RESOURCE_PREFIX "/qt/qml"
-%4
-)
diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodules.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodules.tpl
deleted file mode 100644
index 2b74c49af3..0000000000
--- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodules.tpl
+++ /dev/null
@@ -1,16 +0,0 @@
-### This file is automatically generated by Qt Design Studio.
-### Do not change
-
-qt6_add_qml_module(%1
- URI "Main"
- VERSION 1.0
- RESOURCE_PREFIX "/qt/qml"
- NO_PLUGIN
- QML_FILES main.qml
-)
-
-%2
-
-target_link_libraries(%1 PRIVATE
-%3
-)
diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/cmakelists_txt_shared.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakelists_txt_shared.tpl
new file mode 100644
index 0000000000..3db7152bff
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakelists_txt_shared.tpl
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorProject>
+<qtcreator>
+ <data>
+ <variable>ProjectExplorer.Project.PluginSettings</variable>
+ <valuemap type="QVariantMap">
+ <valuelist type="QVariantList" key="ProjectExplorer.Project.Environment">
+ <value type="QString">QTC_DEFAULT_BUILD_DIRECTORY_TEMPLATE=../%{Asciify:%{Project:Name}-%{BuildConfig:Name}}</value>
+ </valuelist>
+ </valuemap>
+</data>
+<data>
+ <variable>Version</variable>
+ <value type="int">22</value>
+</data>
+</qtcreator>
diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakemodule_v1.tpl
index b81d253c90..25d894b347 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakemodule_v1.tpl
@@ -8,7 +8,6 @@ qt6_add_qml_module(%1
URI "%2"
VERSION 1.0
RESOURCE_PREFIX "/qt/qml"
-%4
-)
+%4)
%5
diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v0.tpl
index a23a311c1a..a23a311c1a 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v0.tpl
diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v1.tpl
index 9ca0ace63b..a5d5e2bd02 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v1.tpl
@@ -1,3 +1,4 @@
+
cmake_minimum_required(VERSION 3.21.1)
option(LINK_INSIGHT "Link Qt Insight Tracker library" ON)
@@ -5,6 +6,8 @@ option(BUILD_QDS_COMPONENTS "Build design studio components" ON)
project(%1 LANGUAGES CXX)
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
@@ -19,34 +22,25 @@ if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3)
qt_standard_project_setup()
endif()
-qt_add_executable(%1 src/main.cpp)
-
-qt_add_resources(%1 "configuration"
+qt_add_executable(${CMAKE_PROJECT_NAME})
+qt_add_resources(${CMAKE_PROJECT_NAME} "configuration"
PREFIX "/"
-%2
-)
+ FILES
+%2)
-target_link_libraries(%1 PRIVATE
- Qt${QT_VERSION_MAJOR}::Core
- Qt${QT_VERSION_MAJOR}::Gui
- Qt${QT_VERSION_MAJOR}::Quick
- Qt${QT_VERSION_MAJOR}::Qml
-)
+include(qds)
if (BUILD_QDS_COMPONENTS)
- include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents)
+ include(qmlcomponents OPTIONAL)
endif()
-include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules)
-
if (LINK_INSIGHT)
- include(${CMAKE_CURRENT_SOURCE_DIR}/insight)
+ include(insight OPTIONAL)
endif ()
include(GNUInstallDirs)
-install(TARGETS %1
+install(TARGETS ${CMAKE_PROJECT_NAME}
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
-
diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/environment_h.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/environment_h.tpl
new file mode 100644
index 0000000000..d835c942f6
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/environment_h.tpl
@@ -0,0 +1,24 @@
+/*
+ * This file is automatically generated by Qt Design Studio.
+ * Do not change.
+*/
+
+#include <QGuiApplication>
+#include "qqmlextensionplugin.h"
+
+%1
+
+#ifdef BUILD_QDS_COMPONENTS
+
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ComponentsPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EffectsPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ApplicationPlugin)
+Q_IMPORT_QML_PLUGIN(FlowViewPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_LogicHelperPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_MultiTextPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSimulatorPlugin)
+Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSystemPlugin)
+
+#endif
+
+%2
diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/import_qml_components_h.tpl
index 167481d7c7..84d3c1c777 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/import_qml_components_h.tpl
@@ -17,3 +17,4 @@ Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSimulatorPlugin)
Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSystemPlugin)
#endif
+
diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/insight.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/insight.tpl
new file mode 100644
index 0000000000..8245e31f0d
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/insight.tpl
@@ -0,0 +1,19 @@
+### This file is automatically generated by Qt Design Studio.
+### Do not change
+
+if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/qtinsight.conf)
+ if (QT_VERSION GREATER_EQUAL 6.5.0)
+ find_package(Qt6 REQUIRED COMPONENTS InsightTracker)
+
+ qt_add_resources(${CMAKE_PROJECT_NAME} "configuration"
+ PREFIX "/"
+ FILES
+ qtinsight.conf
+ )
+ target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
+ Qt6::InsightTracker
+ )
+ else()
+ message(WARNING "You need Qt 6.5.0 or newer to build the application.")
+ endif()
+endif()
diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincpp.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v0.tpl
index 0ff9201d91..0ff9201d91 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincpp.tpl
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v0.tpl
diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v1.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v1.tpl
new file mode 100644
index 0000000000..4d2a526d76
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v1.tpl
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+#include "autogen/environment.h"
+
+int main(int argc, char *argv[])
+{
+ set_qt_environment();
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ const QUrl url(mainQmlFile);
+ QObject::connect(
+ &engine, &QQmlApplicationEngine::objectCreated, &app,
+ [url](QObject *obj, const QUrl &objUrl) {
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ }, Qt::QueuedConnection);
+
+ engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
+ engine.addImportPath(":/");
+ engine.load(url);
+
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}
diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl
new file mode 100644
index 0000000000..a9f20243a6
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl
@@ -0,0 +1,34 @@
+### This file is automatically generated by Qt Design Studio.
+### Do not change
+
+message("Building designer components.")
+
+set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml")
+
+include(FetchContent)
+FetchContent_Declare(
+ ds
+ GIT_TAG qds-4.5
+ GIT_REPOSITORY https://code.qt.io/qt-labs/qtquickdesigner-components.git
+)
+
+FetchContent_GetProperties(ds)
+FetchContent_Populate(ds)
+
+target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
+ QuickStudioComponentsplugin
+ QuickStudioEffectsplugin
+ QuickStudioApplicationplugin
+ FlowViewplugin
+ QuickStudioLogicHelperplugin
+ QuickStudioMultiTextplugin
+ QuickStudioEventSimulatorplugin
+ QuickStudioEventSystemplugin
+ QuickStudioUtilsplugin
+)
+
+add_subdirectory(${ds_SOURCE_DIR} ${ds_BINARY_DIR})
+
+target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
+ BULD_QDS_COMPONENTS=true
+)
diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/qtquickcontrols2_conf.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/qtquickcontrols2_conf.tpl
new file mode 100644
index 0000000000..eb1f825a38
--- /dev/null
+++ b/src/plugins/qmlprojectmanager/cmakegen/templates/qtquickcontrols2_conf.tpl
@@ -0,0 +1,7 @@
+; This file can be edited to change the style of the application
+; Read "Qt Quick Controls 2 Configuration File" for details:
+; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html
+
+[Controls]
+Style=Basic
+
diff --git a/src/plugins/qmlprojectmanager/qmlprojectgen/qmlprojectgenerator.cpp b/src/plugins/qmlprojectmanager/qmlprojectgen/qmlprojectgenerator.cpp
index 32506183fa..0270a290fd 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectgen/qmlprojectgenerator.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectgen/qmlprojectgenerator.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlprojectgenerator.h"
-#include "../cmakegen/generatecmakelists.h"
+#include "../cmakegen/cmakewriter.h"
#include "../qmlprojectmanagertr.h"
#include <coreplugin/documentmanager.h>
@@ -61,7 +61,7 @@ bool QmlProjectFileGenerator::execute()
importDirs.removeAll("content");
const QString importPaths = createDirArrayEntry("importPaths", importDirs);
- const QString fileContent = GenerateCmake::readTemplate(QMLPROJECT_FILE_TEMPLATE_PATH)
+ const QString fileContent = GenerateCmake::CMakeWriter::readTemplate(QMLPROJECT_FILE_TEMPLATE_PATH)
.arg(contentEntry, imageEntry, jsEntry, assetEntry, importPaths);
QFile file(m_targetFile.toString());
diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
index 76fcd70157..0fa49b5102 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
+++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
@@ -47,14 +47,10 @@ QtcPlugin {
name: "CMake Generator"
prefix: "cmakegen/"
files: [
- "generatecmakelists.cpp", "generatecmakelists.h",
- "generatecmakelistsconstants.h",
- "checkablefiletreeitem.cpp", "checkablefiletreeitem.h",
"cmakegenerator.cpp", "cmakegenerator.h",
- "cmakegeneratordialogtreemodel.cpp", "cmakegeneratordialogtreemodel.h",
- "cmakegeneratordialog.cpp", "cmakegeneratordialog.h",
- "cmakeprojectconverter.cpp", "cmakeprojectconverter.h",
- "cmakeprojectconverterdialog.cpp", "cmakeprojectconverterdialog.h",
+ "cmakewriter.cpp", "cmakewriter.h",
+ "cmakewriterv0.cpp", "cmakewriterv0.h",
+ "cmakewriterv1.cpp", "cmakewriterv1.h"
]
}
diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
index 62abce5fe9..9cd1bae9c6 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
@@ -9,8 +9,7 @@
#include "qmlprojectmanagertr.h"
#include "qmlprojectrunconfiguration.h"
#include "projectfilecontenttools.h"
-#include "cmakegen/cmakeprojectconverter.h"
-#include "cmakegen/generatecmakelists.h"
+#include "cmakegen/cmakegenerator.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -386,11 +385,9 @@ void QmlProjectPlugin::initialize()
mainUifileAction->setEnabled(buildSystem->mainUiFilePath()
!= fileNode->filePath());
});
- }
- GenerateCmake::generateMenuEntry(this);
- if (ICore::isQtDesignStudio())
- GenerateCmake::CmakeProjectConverter::generateMenuEntry(this);
+ GenerateCmake::CMakeGenerator::createMenuAction(this);
+ }
}
void QmlProjectPlugin::displayQmlLandingPage()
diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp
index 4dea30c3d0..d6ab3a9745 100644
--- a/src/plugins/remotelinux/linuxdevicetester.cpp
+++ b/src/plugins/remotelinux/linuxdevicetester.cpp
@@ -5,6 +5,7 @@
#include "linuxdevice.h"
#include "remotelinuxtr.h"
+#include "utils/async.h"
#include <extensionsystem/pluginmanager.h>
diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
index 9e93384cfa..d526a9c436 100644
--- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp
+++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
@@ -333,6 +333,37 @@ public:
Core::EditorManager::openEditor(qmlFile);
}
+ Q_INVOKABLE bool exampleVersionOk(const QString &exampleVersion)
+ {
+ if (exampleVersion.isEmpty())
+ return true;
+
+ const QStringList exampleVersionParts = exampleVersion.split('.');
+ const QStringList qdsVersionParts = QCoreApplication::applicationVersion().split('.');
+
+ QList<int> exampleVerInts;
+ QList<int> qdsVerInts;
+ for (const QString &part : exampleVersionParts)
+ exampleVerInts.append(part.toInt());
+
+ for (const QString &part : qdsVersionParts)
+ qdsVerInts.append(part.toInt());
+
+ // pad zeros so both lists are same size
+ while (qdsVerInts.size() < exampleVerInts.size())
+ qdsVerInts.append(0);
+
+ while (exampleVerInts.size() < qdsVerInts.size())
+ exampleVerInts.append(0);
+
+ for (int i = 0; i < qdsVerInts.size(); ++i) {
+ if (exampleVerInts[i] < qdsVerInts[i])
+ return false;
+ }
+
+ return true;
+ }
+
public slots:
void resetProjects();
void delayedResetProjects();
diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp
index 749e7bf2f7..7a8fa1edbc 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -4975,7 +4975,7 @@ static QColor calcBlendColor(const QColor &baseColor, int level, int count)
if (level == count - 1)
return color90;
- const int blendFactor = level * (256 / (count - 2));
+ const int blendFactor = level * (256 / (count - 1));
return blendColors(color80, color90, blendFactor);
}
@@ -7471,8 +7471,7 @@ void TextEditorWidgetPrivate::handleBackspaceKey()
}
}
} else if (typingSettings.m_smartBackspaceBehavior == TypingSettings::BackspaceUnindents) {
- const QChar previousChar = q->document()->characterAt(pos - 1);
- if (!(previousChar == QLatin1Char(' ') || previousChar == QLatin1Char('\t'))) {
+ if (c.positionInBlock() > TabSettings::firstNonSpace(c.block().text())) {
if (cursorWithinSnippet)
c.beginEditBlock();
c.deletePreviousChar();
diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc
index 2913fbe15e..d76b1941b9 100644
--- a/src/tools/qml2puppet/editor3d_qt6.qrc
+++ b/src/tools/qml2puppet/editor3d_qt6.qrc
@@ -8,6 +8,8 @@
<file>mockfiles/images/editor_camera@2x.png</file>
<file>mockfiles/images/editor_particlesystem.png</file>
<file>mockfiles/images/editor_particlesystem@2x.png</file>
+ <file>mockfiles/images/crosshair.png</file>
+ <file>mockfiles/images/crosshair@2x.png</file>
<file>mockfiles/images/directional.png</file>
<file>mockfiles/images/directional@2x.png</file>
<file>mockfiles/images/point.png</file>
diff --git a/src/tools/qml2puppet/mockfiles/images/crosshair.png b/src/tools/qml2puppet/mockfiles/images/crosshair.png
new file mode 100644
index 0000000000..6c30263513
--- /dev/null
+++ b/src/tools/qml2puppet/mockfiles/images/crosshair.png
Binary files differ
diff --git a/src/tools/qml2puppet/mockfiles/images/crosshair@2x.png b/src/tools/qml2puppet/mockfiles/images/crosshair@2x.png
new file mode 100644
index 0000000000..5340bf0846
--- /dev/null
+++ b/src/tools/qml2puppet/mockfiles/images/crosshair@2x.png
Binary files differ
diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml
index 446d575683..255d93e529 100644
--- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml
+++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml
@@ -102,6 +102,23 @@ Item {
storeCameraState(0);
}
+ function approachObject()
+ {
+ if (!camera)
+ return;
+
+ var pickResult = _generalHelper.pickViewAt(view3d, width / 2, height / 2);
+ var resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
+
+ if (resolvedResult) {
+ var newLookAtAndZoom = _generalHelper.approachNode(camera, _defaultCameraLookAtDistance,
+ resolvedResult, view3D);
+ _lookAtPoint = newLookAtAndZoom.toVector3d();
+ _zoomFactor = newLookAtAndZoom.w;
+ storeCameraState(0);
+ }
+ }
+
function jumpToRotation(rotation)
{
let distance = camera.scenePosition.minus(_lookAtPoint).length()
@@ -138,7 +155,10 @@ Item {
else
nodes = targetNodes
- _lookAtPoint = _generalHelper.alignView(camera, nodes, _lookAtPoint);
+ var newLookAtAndZoom = _generalHelper.alignView(camera, nodes, _lookAtPoint,
+ _defaultCameraLookAtDistance);
+ _lookAtPoint = newLookAtAndZoom.toVector3d();
+ _zoomFactor = newLookAtAndZoom.w;
storeCameraState(0);
}
@@ -158,8 +178,7 @@ Item {
function moveCamera(moveVec)
{
- cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, _zoomFactor,
- moveVec);
+ cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, moveVec);
}
function getMoveVectorForKey(key) {
@@ -238,6 +257,13 @@ Item {
}
}
+ Image {
+ anchors.centerIn: parent
+ source: "qrc:///qtquickplugin/mockfiles/images/crosshair.png"
+ visible: cameraCtrl.flyMode && viewRoot.activeSplit === cameraCtrl.splitId
+ opacity: 0.7
+ }
+
MouseArea {
id: mouseHandler
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
@@ -302,7 +328,10 @@ Item {
Keys.onPressed: (event) => {
event.accepted = true;
- _generalHelper.startCameraMove(cameraCtrl.camera, cameraCtrl.getMoveVectorForKey(event.key));
+ if (cameraCtrl.flyMode && event.key === Qt.Key_Space)
+ approachObject();
+ else
+ _generalHelper.startCameraMove(cameraCtrl.camera, cameraCtrl.getMoveVectorForKey(event.key));
}
Keys.onReleased: (event) => {
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp
index 6886f7e41c..dadd817633 100644
--- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp
@@ -157,7 +157,7 @@ QVector3D GeneralHelper::panCamera(QQuick3DCamera *camera, const QMatrix4x4 star
// Moves camera in 3D space and returns new look-at point
QVector3D GeneralHelper::moveCamera(QQuick3DCamera *camera, const QVector3D &startLookAt,
- float zoomFactor, const QVector3D &moveVector)
+ const QVector3D &moveVector)
{
if (moveVector.length() < 0.001f)
@@ -171,7 +171,8 @@ QVector3D GeneralHelper::moveCamera(QQuick3DCamera *camera, const QVector3D &sta
const QVector3D xDelta = xAxis * moveVector.x();
const QVector3D yDelta = yAxis * moveVector.y();
const QVector3D zDelta = zAxis * moveVector.z();
- const QVector3D delta = (yDelta - xDelta - zDelta) * zoomFactor;
+ // Delta multiplier for nice default speed in default scene
+ const QVector3D delta = (yDelta - xDelta - zDelta) * .5f;
camera->setPosition(camera->position() + delta);
@@ -187,8 +188,14 @@ QVector3D GeneralHelper::rotateCamera(QQuick3DCamera *camera, const QPointF &ang
if (qAbs(angles.y()) > 0.001f)
camera->rotate(angles.y(), QVector3D(1.f, 0.f, 0.f), QQuick3DNode::LocalSpace);
// Rotation around Y-axis is done in scene space to keep horizon level
- if (qAbs(angles.x()) > 0.001f)
- camera->rotate(angles.x(), QVector3D(0.f, 1.f, 0.f), QQuick3DNode::SceneSpace);
+ if (qAbs(angles.x()) > 0.001f) {
+ // Since we are rotating in scene space, adjust direction according to camera up-vector
+ float angle = angles.x();
+ if (camera->up().y() <= 0)
+ angle = -angle;
+
+ camera->rotate(angle, QVector3D(0.f, 1.f, 0.f), QQuick3DNode::SceneSpace);
+ }
QMatrix4x4 m = camera->sceneTransform();
const float *dataPtr(m.data());
@@ -391,6 +398,55 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul
return QVector4D(lookAt, cameraZoomFactor);
}
+// Approaches the specified node without changing camera orientation
+QVector4D GeneralHelper::approachNode(
+ QQuick3DCamera *camera, float defaultLookAtDistance, QObject *node,
+ QQuick3DViewport *viewPort)
+{
+ auto node3d = qobject_cast<QQuick3DNode *>(node);
+ if (!camera || !node3d)
+ return QVector4D(0.f, 0.f, 0.f, 1.f);
+
+ QVector3D minBounds = maxVec;
+ QVector3D maxBounds = minVec;
+
+ getBounds(viewPort, node3d, minBounds, maxBounds); // Bounds are in node3d local coordinates
+
+ QVector3D extents = maxBounds - minBounds;
+ QVector3D focusLookAt = minBounds + (extents / 2.f);
+
+ if (node3d->parentNode()) {
+ QMatrix4x4 m = node3d->parentNode()->sceneTransform();
+ focusLookAt = m.map(focusLookAt);
+ }
+
+ float maxExtent = qSqrt(qreal(extents.x()) * qreal(extents.x())
+ + qreal(extents.y()) * qreal(extents.y())
+ + qreal(extents.z()) * qreal(extents.z()));
+
+ // Reset camera position to default zoom
+ QMatrix4x4 m = camera->sceneTransform();
+ const float *dataPtr(m.data());
+ QVector3D newLookVector(dataPtr[8], dataPtr[9], dataPtr[10]);
+ newLookVector.normalize();
+
+ // We don't want to change camera orientation, so calculate projection point on current
+ // camera look vector
+ QVector3D focusLookAtVector = focusLookAt - camera->position();
+ float dot = QVector3D::dotProduct(newLookVector, focusLookAtVector);
+ QVector3D newLookAt = camera->position() + dot * newLookVector;
+
+ newLookVector *= defaultLookAtDistance;
+ camera->setPosition(newLookAt + newLookVector);
+
+ float divisor = 1050.f;
+ float newZoomFactor = qBound(.01f, maxExtent / divisor, 100.f);
+ float cameraZoomFactor = zoomCamera(viewPort, camera, 0, defaultLookAtDistance, newLookAt,
+ newZoomFactor, false);
+
+ return QVector4D(newLookAt, cameraZoomFactor);
+}
+
// This function can be used to synchronously focus camera on a node, which doesn't have to be
// a selection box for bound calculations to work. This is used to focus the view for
// various preview image generations, where doing things asynchronously is not good
@@ -487,12 +543,10 @@ void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes)
// Aligns the camera to the first camera in nodes list.
// Aligning means taking the position and XY rotation from the source camera. Rest of the properties
// remain the same, as this is used to align edit cameras, which have fixed Z-rot, fov, and clips.
-// The new lookAt is set at same distance away as it was previously and scale isn't adjusted, so
-// the zoom factor of the edit camera stays the same.
-QVector3D GeneralHelper::alignView(QQuick3DCamera *camera, const QVariant &nodes,
- const QVector3D &lookAtPoint)
+// The camera zoom is reset to default.
+QVector4D GeneralHelper::alignView(QQuick3DCamera *camera, const QVariant &nodes,
+ const QVector3D &lookAtPoint, float defaultLookAtDistance)
{
- float lastDistance = (lookAtPoint - camera->position()).length();
const QVariantList varNodes = nodes.value<QVariantList>();
QQuick3DCamera *cameraNode = nullptr;
for (const auto &varNode : varNodes) {
@@ -502,15 +556,24 @@ QVector3D GeneralHelper::alignView(QQuick3DCamera *camera, const QVariant &nodes
}
if (cameraNode) {
+ if (auto orthoCamera = qobject_cast<QQuick3DOrthographicCamera *>(camera)) {
+ orthoCamera->setHorizontalMagnification(1.f);
+ orthoCamera->setVerticalMagnification(1.f);
+ // Force update on transform just in case position and rotation didn't change
+ float x = orthoCamera->x();
+ orthoCamera->setX(x + 1.f);
+ orthoCamera->setX(x);
+ }
camera->setPosition(cameraNode->scenePosition());
QVector3D newRotation = cameraNode->sceneRotation().toEulerAngles();
newRotation.setZ(0.f);
camera->setEulerRotation(newRotation);
+
}
- QVector3D lookAt = camera->position() + camera->forward() * lastDistance;
+ QVector3D lookAt = camera->position() + camera->forward() * defaultLookAtDistance;
- return lookAt;
+ return QVector4D(lookAt, 1.f);
}
bool GeneralHelper::fuzzyCompare(double a, double b)
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h
index 3ba6f8b1d9..62a95d86a6 100644
--- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h
@@ -55,7 +55,7 @@ public:
const QVector3D &pressPos, const QVector3D &currentPos,
float zoomFactor);
Q_INVOKABLE QVector3D moveCamera(QQuick3DCamera *camera,const QVector3D &startLookAt,
- float zoomFactor, const QVector3D &moveVector);
+ const QVector3D &moveVector);
Q_INVOKABLE QVector3D rotateCamera(QQuick3DCamera *camera, const QPointF &angles,
const QVector3D &lookAtPoint);
@@ -70,12 +70,14 @@ public:
const QVariant &nodes, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom = true,
bool closeUp = false);
+ Q_INVOKABLE QVector4D approachNode(QQuick3DCamera *camera, float defaultLookAtDistance,
+ QObject *node, QQuick3DViewport *viewPort);
Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node,
QQuick3DViewport *viewPort,
float defaultLookAtDistance, bool closeUp);
Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes);
- Q_INVOKABLE QVector3D alignView(QQuick3DCamera *camera, const QVariant &nodes,
- const QVector3D &lookAtPoint);
+ Q_INVOKABLE QVector4D alignView(QQuick3DCamera *camera, const QVariant &nodes,
+ const QVector3D &lookAtPoint, float defaultLookAtDistance);
Q_INVOKABLE bool fuzzyCompare(double a, double b);
Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property,
const QVariant& value);
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp
index fca884e94b..765b52321b 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp
@@ -436,10 +436,18 @@ QQuickItem *Qt5NodeInstanceServer::parentEffectItem(QQuickItem *item)
return nullptr;
}
-static bool isEffectItem(QQuickItem *item, QQuickShaderEffectSource *sourceItem)
+static bool isEffectItem(QQuickItem *item, QQuickShaderEffectSource *sourceItem, QQuickItem *target)
{
QQuickItemPrivate *pItem = QQuickItemPrivate::get(sourceItem);
+ if (item) {
+ QQmlProperty prop(item, "__effect");
+ if (prop.read().toBool()) {
+ prop = QQmlProperty(item, "source");
+ return prop.read().value<QQuickItem *>() == target;
+ }
+ }
+
if (!pItem || !pItem->layer())
return false;
@@ -477,7 +485,7 @@ QImage Qt5NodeInstanceServer::grabItem([[maybe_unused]] QQuickItem *item)
if (auto parent = item->parentItem()) {
const auto siblings = parent->childItems();
for (auto sibling : siblings) {
- if (isEffectItem(sibling, pItem->layer()->effectSource()))
+ if (isEffectItem(sibling, pItem->layer()->effectSource(), item))
return grabItem(sibling);
}
}
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp
index f9191950fd..53af00ab09 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp
@@ -146,7 +146,9 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands()
&& !changedPropertyList().isEmpty()
&& nodeInstanceClient()->bytesToWrite() < 10000) {
- Internal::QuickItemNodeInstance::updateDirtyNode(rootNodeInstance().contentItem());
+ QQuickItem *rootItem = rootNodeInstance().contentItem();
+ QQuickDesignerSupport::addDirty(rootItem, QQuickDesignerSupport::Content);
+ QQuickDesignerSupport::updateDirtyNode(rootItem);
nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()}));
}
@@ -239,6 +241,24 @@ void Qt5RenderNodeInstanceServer::changePropertyValues(const ChangeValuesCommand
if (instance.hasParent() && instance.propertyNames().contains("_isEffectItem"))
makeDirtyRecursive(instance.parent());
+ } else if (container.isDynamic() && hasInstanceForId(container.instanceId())) {
+ // Changes to dynamic properties are not always noticed by normal signal spy mechanism
+ addChangedProperty(InstancePropertyPair(instanceForId(container.instanceId()),
+ container.name()));
+ }
+ }
+}
+
+void Qt5RenderNodeInstanceServer::changePropertyBindings(const ChangeBindingsCommand &command)
+{
+ Qt5NodeInstanceServer::changePropertyBindings(command);
+
+ const QVector<PropertyBindingContainer> changes = command.bindingChanges;
+ for (const PropertyBindingContainer &container : changes) {
+ if (container.isDynamic() && hasInstanceForId(container.instanceId())) {
+ // Changes to dynamic properties are not always noticed by normal signal spy mechanism
+ addChangedProperty(InstancePropertyPair(instanceForId(container.instanceId()),
+ container.name()));
}
}
}
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h
index 738aa47b18..1e0f885da1 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h
+++ b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h
@@ -18,6 +18,7 @@ public:
void completeComponent(const CompleteComponentCommand &command) override;
void removeSharedMemory(const RemoveSharedMemoryCommand &command) override;
void changePropertyValues(const ChangeValuesCommand &command) override;
+ void changePropertyBindings(const ChangeBindingsCommand &command) override;
protected:
void collectItemChangesAndSendChangeCommands() override;
diff --git a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp
index 9d432fe047..a9c49b9661 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp
@@ -277,13 +277,27 @@ static bool layerEnabledAndEffect(QQuickItem *item)
return false;
}
+static QRectF getBoundingRectForEffect(QQuickItem *item)
+{
+ const auto siblings = item->parentItem()->childItems();
+ for (auto sibling : siblings) {
+ QQmlProperty prop(sibling, "__effect");
+ if (prop.read().toBool()) {
+ prop = QQmlProperty(sibling, "source");
+ if (prop.read().value<QQuickItem *>() == item)
+ return ServerNodeInstance::effectAdjustedBoundingRect(sibling);
+ }
+ }
+ return ServerNodeInstance::effectAdjustedBoundingRect(item);
+}
+
QRectF QuickItemNodeInstance::boundingRect() const
{
if (quickItem()) {
- if (quickItem()->clip()) {
+ if (layerEnabledAndEffect(quickItem()) && quickItem()->parentItem()) {
+ return getBoundingRectForEffect(quickItem());
+ } else if (quickItem()->clip()) {
return quickItem()->boundingRect();
- } else if (layerEnabledAndEffect(quickItem())) {
- return ServerNodeInstance::effectAdjustedBoundingRect(quickItem());
} else {
QSize maximumSize(4000, 4000);
auto isValidSize = [maximumSize] (const QRectF& rect) {
diff --git a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp
index 758e18df35..aa91b8ffaa 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp
@@ -26,6 +26,8 @@
#include <qmlprivategate.h>
+#include <QtQuick/private/qquickitem_p.h>
+
#include <QHash>
#include <QSet>
#include <QDebug>
@@ -116,8 +118,23 @@ bool ServerNodeInstance::isSubclassOf(QObject *object, const QByteArray &superTy
QRectF ServerNodeInstance::effectAdjustedBoundingRect(QQuickItem *item)
{
- if (item)
- return item->boundingRect().adjusted(-40, -40, 40, 40);
+ if (item) {
+ QQuickItemPrivate *pItem = QQuickItemPrivate::get(item);
+
+ QQmlProperty prop(item, "__effect");
+
+ if (pItem && pItem->layer() && pItem->layer()->sourceRect().isValid()) {
+ return pItem->layer()->sourceRect();
+ } else if (prop.read().toBool()) {
+ prop = QQmlProperty(item, "effectBoundingBox");
+ QRectF rect = prop.read().toRectF().adjusted(-40, -40, 40, 40);
+ if (rect.isValid())
+ return rect;
+ return item->boundingRect();
+ } else {
+ return item->boundingRect();
+ }
+ }
return {};
}
diff --git a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
index 123d4aee2d..096ac36244 100644
--- a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
+++ b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
@@ -10,6 +10,7 @@
#include <QQmlComponent>
#include <QFileInfo>
#include <QProcessEnvironment>
+#include <QJsonValue>
#include <private/qabstractfileengine_p.h>
#include <private/qfsfileengine_p.h>
@@ -35,16 +36,484 @@
#include <private/qquickanimation_p.h>
#include <private/qqmlmetatype_p.h>
#include <private/qqmltimer_p.h>
+#include <private/qqmlanybinding_p.h>
#ifdef QUICK3D_MODULE
#include <private/qquick3dobject_p.h>
#include <private/qquick3drepeater_p.h>
#endif
+
+
namespace QmlDesigner {
namespace Internal {
+static void addToPropertyNameListIfNotBlackListed(QQuickDesignerSupport::PropertyNameList *propertyNameList,
+ const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ if (!QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName))
+ propertyNameList->append(propertyName);
+}
+
+static QQuickDesignerSupport::PropertyNameList propertyNameListForWritablePropertiesInternal(QObject *object,
+ const QQuickDesignerSupport::PropertyName &baseName,
+ QObjectList *inspectedObjects,
+ int depth = 0)
+{
+ QQuickDesignerSupport::PropertyNameList propertyNameList;
+
+ if (depth > 2)
+ return propertyNameList;
+
+ if (!inspectedObjects->contains(object))
+ inspectedObjects->append(object);
+
+ const QMetaObject *metaObject = object->metaObject();
+ for (int index = 0; index < metaObject->propertyCount(); ++index) {
+ QMetaProperty metaProperty = metaObject->property(index);
+ QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name()));
+ if (declarativeProperty.isValid() && !declarativeProperty.isWritable() && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) {
+ if (declarativeProperty.name() != QLatin1String("parent")) {
+ QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read());
+ if (childObject)
+ propertyNameList.append(propertyNameListForWritablePropertiesInternal(childObject,
+ baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())
+ + '.', inspectedObjects,
+ depth + 1));
+ }
+ } else if (QQmlGadgetPtrWrapper *valueType
+ = QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.metaType())) {
+
+
+ const QVariant value = metaProperty.read(object);
+ QMetaType jsType = QMetaType::fromType<QJSValue>();
+ int userType = value.userType();
+
+ //qDebug() << jsType << jsType.id();
+ //qDebug() << "tp" << value.typeName();
+ //qDebug() << "ut" << userType;
+
+ if (userType == jsType.id()) {
+ qDebug() << "js value found";
+ //QJSValue jsValue = value.value<QJSValue>(); //crashes
+ //qDebug() << jsValue.isObject();
+ //qDebug() << jsValue.isQObject();
+ } else {
+
+
+
+ valueType->setValue(value);
+ propertyNameList.append(propertyNameListForWritablePropertiesInternal(valueType,
+ baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())
+ + '.', inspectedObjects,
+ depth + 1));
+ }
+
+ }
+
+ if (metaProperty.isReadable() && metaProperty.isWritable()) {
+ addToPropertyNameListIfNotBlackListed(&propertyNameList,
+ baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()));
+ }
+ }
+
+ return propertyNameList;
+}
+
+static QQuickDesignerSupport::PropertyNameList propertyNameListForWritablePropertiesFork(QObject *object)
+{
+ QObjectList localObjectList;
+ return propertyNameListForWritablePropertiesInternal(object, {}, &localObjectList);
+}
+
+static QQuickDesignerSupport::PropertyNameList allPropertyNamesFork(QObject *object,
+ const QQuickDesignerSupport::PropertyName &baseName = QQuickDesignerSupport::PropertyName(),
+ QObjectList *inspectedObjects = nullptr,
+ int depth = 0)
+{
+ QQuickDesignerSupport::PropertyNameList propertyNameList;
+
+ QObjectList localObjectList;
+
+ if (inspectedObjects == nullptr)
+ inspectedObjects = &localObjectList;
+
+ if (depth > 2)
+ return propertyNameList;
+
+ if (!inspectedObjects->contains(object))
+ inspectedObjects->append(object);
+
+ const QMetaObject *metaObject = object->metaObject();
+
+ QStringList deferredPropertyNames;
+ const int namesIndex = metaObject->indexOfClassInfo("DeferredPropertyNames");
+ if (namesIndex != -1) {
+ QMetaClassInfo classInfo = metaObject->classInfo(namesIndex);
+ deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
+ }
+
+ for (int index = 0; index < metaObject->propertyCount(); ++index) {
+ QMetaProperty metaProperty = metaObject->property(index);
+ QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name()));
+ if (declarativeProperty.isValid() && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) {
+ if (declarativeProperty.name() != QLatin1String("parent")
+ && !deferredPropertyNames.contains(declarativeProperty.name())) {
+ QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read());
+ if (childObject)
+ propertyNameList.append(allPropertyNamesFork(childObject,
+ baseName
+ + QQuickDesignerSupport::PropertyName(metaProperty.name())
+ + '.', inspectedObjects,
+ depth + 1));
+ }
+ } else if (QQmlGadgetPtrWrapper *valueType
+ = QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.metaType())) {
+
+ const QVariant value = metaProperty.read(object);
+ propertyNameList.append(baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()));
+ const QJsonValue jsonValue = value.toJsonValue();
+
+ if (!jsonValue.isNull()) {
+ qDebug() << "llokhere";
+ qDebug() << "name" << metaProperty.name();
+ qDebug() << "value" << value;
+ qDebug() << jsonValue;
+ }
+
+ if (value.isValid() && jsonValue.isNull()) {
+ //qDebug() << "llokhere crash";
+ //qDebug() << "name" << metaProperty.name();
+ //qDebug() << "value" << value;
+ //qDebug() << jsonValue;
+
+
+ QMetaType jsType = QMetaType::fromType<QJSValue>();
+
+ int userType = value.userType();
+
+ //qDebug() << jsType << jsType.id();
+ //qDebug() << "tp" << value.typeName();
+ //qDebug() << "ut" << userType;
+
+ if (userType == jsType.id()) {
+ qDebug() << "js value found";
+ //QJSValue jsValue = value.value<QJSValue>(); //crashes
+ //qDebug() << jsValue.isObject();
+ //qDebug() << jsValue.isQObject();
+ } else {
+
+
+ valueType->setValue(value);
+ propertyNameList.append(allPropertyNamesFork(valueType,
+ baseName
+ + QQuickDesignerSupport::PropertyName(metaProperty.name())
+ + '.', inspectedObjects,
+ depth + 1));
+ }
+
+
+ }
+ } else {
+ addToPropertyNameListIfNotBlackListed(&propertyNameList,
+ baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()));
+ }
+ }
+
+ return propertyNameList;
+}
+
+class DesignerCustomObjectDataFork
+{
+public:
+ static void registerData(QObject *object);
+ static DesignerCustomObjectDataFork *get(QObject *object);
+ static QVariant getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName);
+ static void doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName);
+ static bool hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName);
+ static bool hasBindingForProperty(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ bool *hasChanged);
+ static void setPropertyBinding(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ const QString &expression);
+ static void keepBindingFromGettingDeleted(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName);
+ void handleDestroyed();
+
+private:
+ DesignerCustomObjectDataFork(QObject *object);
+ void populateResetHashes();
+ QObject *object() const;
+ QVariant getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const;
+ void doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName);
+ bool hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const;
+ QQmlAnyBinding getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const;
+ bool hasBindingForProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName, bool *hasChanged) const;
+ void setPropertyBinding(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName, const QString &expression);
+ void keepBindingFromGettingDeleted(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName);
+
+ QObject *m_object;
+ QHash<QQuickDesignerSupport::PropertyName, QVariant> m_resetValueHash;
+ QHash<QQuickDesignerSupport::PropertyName, QQmlAnyBinding> m_resetBindingHash;
+ mutable QHash<QQuickDesignerSupport::PropertyName, bool> m_hasBindingHash;
+};
+
+typedef QHash<QObject*, DesignerCustomObjectDataFork*> CustomObjectDataHash;
+Q_GLOBAL_STATIC(CustomObjectDataHash, s_designerObjectToDataHash)
+
+struct HandleDestroyedFunctor {
+ DesignerCustomObjectDataFork *data;
+ void operator()() { data->handleDestroyed(); }
+};
+
+using namespace Qt::StringLiterals;
+
+DesignerCustomObjectDataFork::DesignerCustomObjectDataFork(QObject *object)
+ : m_object(object)
+{
+ if (object) {
+ populateResetHashes();
+ s_designerObjectToDataHash()->insert(object, this);
+
+ HandleDestroyedFunctor functor;
+ functor.data = this;
+ QObject::connect(object, &QObject::destroyed, functor);
+ }
+}
+
+void DesignerCustomObjectDataFork::registerData(QObject *object)
+{
+ new DesignerCustomObjectDataFork(object);
+}
+
+DesignerCustomObjectDataFork *DesignerCustomObjectDataFork::get(QObject *object)
+{
+ return s_designerObjectToDataHash()->value(object);
+}
+
+QVariant DesignerCustomObjectDataFork::getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ return data->getResetValue(propertyName);
+
+ return QVariant();
+}
+
+void DesignerCustomObjectDataFork::doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ data->doResetProperty(context, propertyName);
+}
+
+bool DesignerCustomObjectDataFork::hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ return data->hasValidResetBinding(propertyName);
+
+ return false;
+}
+
+bool DesignerCustomObjectDataFork::hasBindingForProperty(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ bool *hasChanged)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ return data->hasBindingForProperty(context, propertyName, hasChanged);
+
+ return false;
+}
+
+void DesignerCustomObjectDataFork::setPropertyBinding(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ const QString &expression)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ data->setPropertyBinding(context, propertyName, expression);
+}
+
+void DesignerCustomObjectDataFork::keepBindingFromGettingDeleted(QObject *object,
+ QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ DesignerCustomObjectDataFork* data = get(object);
+
+ if (data)
+ data->keepBindingFromGettingDeleted(context, propertyName);
+}
+
+void DesignerCustomObjectDataFork::populateResetHashes()
+{
+ const QQuickDesignerSupport::PropertyNameList propertyNameList =
+ propertyNameListForWritablePropertiesFork(object());
+
+ const QMetaObject *mo = object()->metaObject();
+ QByteArrayList deferredPropertyNames;
+ const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
+ if (namesIndex != -1) {
+ QMetaClassInfo classInfo = mo->classInfo(namesIndex);
+ deferredPropertyNames = QByteArray(classInfo.value()).split(',');
+ }
+
+ for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) {
+
+ if (deferredPropertyNames.contains(propertyName))
+ continue;
+
+ QQmlProperty property(object(), QString::fromUtf8(propertyName), QQmlEngine::contextForObject(object()));
+
+ auto binding = QQmlAnyBinding::ofProperty(property);
+
+ if (binding) {
+ m_resetBindingHash.insert(propertyName, binding);
+ } else if (property.isWritable()) {
+ m_resetValueHash.insert(propertyName, property.read());
+ }
+ }
+}
+
+QObject *DesignerCustomObjectDataFork::object() const
+{
+ return m_object;
+}
+
+QVariant DesignerCustomObjectDataFork::getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const
+{
+ return m_resetValueHash.value(propertyName);
+}
+
+void DesignerCustomObjectDataFork::doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
+
+ if (!property.isValid())
+ return;
+
+ // remove existing binding
+ QQmlAnyBinding::takeFrom(property);
+
+
+ if (hasValidResetBinding(propertyName)) {
+ QQmlAnyBinding binding = getResetBinding(propertyName);
+ binding.installOn(property);
+
+ if (binding.isAbstractPropertyBinding()) {
+ // for new style properties, we will evaluate during setBinding anyway
+ static_cast<QQmlBinding *>(binding.asAbstractBinding())->update();
+ }
+
+ } else if (property.isResettable()) {
+ property.reset();
+ } else if (property.propertyTypeCategory() == QQmlProperty::List) {
+ QQmlListReference list = qvariant_cast<QQmlListReference>(property.read());
+
+ if (!QQuickDesignerSupportProperties::hasFullImplementedListInterface(list)) {
+ qWarning() << "Property list interface not fully implemented for Class " << property.property().typeName() << " in property " << property.name() << "!";
+ return;
+ }
+
+ list.clear();
+ } else if (property.isWritable()) {
+ if (property.read() == getResetValue(propertyName))
+ return;
+
+ property.write(getResetValue(propertyName));
+ }
+}
+
+bool DesignerCustomObjectDataFork::hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
+{
+ return m_resetBindingHash.contains(propertyName) && m_resetBindingHash.value(propertyName);
+}
+
+QQmlAnyBinding DesignerCustomObjectDataFork::getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
+{
+ return m_resetBindingHash.value(propertyName);
+}
+
+bool DesignerCustomObjectDataFork::hasBindingForProperty(QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ bool *hasChanged) const
+{
+ if (QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName))
+ return false;
+
+ QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
+
+ bool hasBinding = QQmlAnyBinding::ofProperty(property);
+
+ if (hasChanged) {
+ *hasChanged = hasBinding != m_hasBindingHash.value(propertyName, false);
+ if (*hasChanged)
+ m_hasBindingHash.insert(propertyName, hasBinding);
+ }
+
+ return hasBinding;
+}
+
+void DesignerCustomObjectDataFork::setPropertyBinding(QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName,
+ const QString &expression)
+{
+ QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
+
+ if (!property.isValid())
+ return;
+
+ if (property.isProperty()) {
+ QString url = u"@designer"_s;
+ int lineNumber = 0;
+ QQmlAnyBinding binding = QQmlAnyBinding::createFromCodeString(property,
+ expression, object(), QQmlContextData::get(context), url, lineNumber);
+
+ binding.installOn(property);
+ if (binding.isAbstractPropertyBinding()) {
+ // for new style properties, we will evaluate during setBinding anyway
+ static_cast<QQmlBinding *>(binding.asAbstractBinding())->update();
+ }
+
+ if (binding.hasError()) {
+ if (property.property().userType() == QMetaType::QString)
+ property.write(QVariant(QLatin1Char('#') + expression + QLatin1Char('#')));
+ }
+
+ } else {
+ qWarning() << Q_FUNC_INFO << ": Cannot set binding for property" << propertyName << ": property is unknown for type";
+ }
+}
+
+void DesignerCustomObjectDataFork::keepBindingFromGettingDeleted(QQmlContext *context,
+ const QQuickDesignerSupport::PropertyName &propertyName)
+{
+ //Refcounting is taking care
+ Q_UNUSED(context);
+ Q_UNUSED(propertyName);
+}
+
+void DesignerCustomObjectDataFork::handleDestroyed()
+{
+ s_designerObjectToDataHash()->remove(m_object);
+ delete this;
+}
+
+
+
namespace QmlPrivateGate {
bool isPropertyBlackListed(const QmlDesigner::PropertyName &propertyName)
@@ -54,12 +523,14 @@ bool isPropertyBlackListed(const QmlDesigner::PropertyName &propertyName)
PropertyNameList allPropertyNames(QObject *object)
{
- return QQuickDesignerSupportProperties::allPropertyNames(object);
+ return allPropertyNamesFork(object);
+ //return QQuickDesignerSupportProperties::allPropertyNames(object);
}
PropertyNameList propertyNameListForWritableProperties(QObject *object)
{
- return QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object);
+ return propertyNameListForWritablePropertiesFork(object);
+ //return QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object);
}
void tweakObjects(QObject *object)
@@ -69,7 +540,9 @@ void tweakObjects(QObject *object)
void createNewDynamicProperty(QObject *object, QQmlEngine *engine, const QString &name)
{
- QQuickDesignerSupportProperties::createNewDynamicProperty(object, engine, name);
+ QQmlProperty qmlProp(object, name, engine->contextForObject(object));
+ if (!qmlProp.isValid())
+ QQuickDesignerSupportProperties::createNewDynamicProperty(object, engine, name);
}
void registerNodeInstanceMetaObject(QObject *object, QQmlEngine *engine)
@@ -187,7 +660,8 @@ bool hasFullImplementedListInterface(const QQmlListReference &list)
void registerCustomData(QObject *object)
{
- QQuickDesignerSupportProperties::registerCustomData(object);
+ DesignerCustomObjectDataFork::registerData(object);
+ //QQuickDesignerSupportProperties::registerCustomData(object);
}
QVariant getResetValue(QObject *object, const PropertyName &propertyName)
@@ -201,7 +675,8 @@ QVariant getResetValue(QObject *object, const PropertyName &propertyName)
else if (propertyName == "Layout.fillWidth")
return false;
else
- return QQuickDesignerSupportProperties::getResetValue(object, propertyName);
+ return DesignerCustomObjectDataFork::getResetValue(object, propertyName);
+ //return QQuickDesignerSupportProperties::getResetValue(object, propertyName);
}
static void setProperty(QObject *object, QQmlContext *context, const PropertyName &propertyName, const QVariant &value)
@@ -221,7 +696,9 @@ void doResetProperty(QObject *object, QQmlContext *context, const PropertyName &
else if (propertyName == "Layout.fillWidth")
setProperty(object, context, propertyName, getResetValue(object, propertyName));
else
- QQuickDesignerSupportProperties::doResetProperty(object, context, propertyName);
+ DesignerCustomObjectDataFork::doResetProperty(object, context, propertyName);
+
+ //QQuickDesignerSupportProperties::doResetProperty(object, context, propertyName);
}
bool hasValidResetBinding(QObject *object, const PropertyName &propertyName)
@@ -234,17 +711,22 @@ bool hasValidResetBinding(QObject *object, const PropertyName &propertyName)
return true;
else if (propertyName == "Layout.fillWidth")
return true;
- return QQuickDesignerSupportProperties::hasValidResetBinding(object, propertyName);
+ return
+ DesignerCustomObjectDataFork::hasValidResetBinding(object, propertyName);
+ //QQuickDesignerSupportProperties::hasValidResetBinding(object, propertyName);
}
bool hasBindingForProperty(QObject *object, QQmlContext *context, const PropertyName &propertyName, bool *hasChanged)
{
- return QQuickDesignerSupportProperties::hasBindingForProperty(object, context, propertyName, hasChanged);
+ return DesignerCustomObjectDataFork::hasBindingForProperty(object, context, propertyName, hasChanged);
+ //return QQuickDesignerSupportProperties::hasBindingForProperty(object, context, propertyName, hasChanged);
}
void setPropertyBinding(QObject *object, QQmlContext *context, const PropertyName &propertyName, const QString &expression)
{
- QQuickDesignerSupportProperties::setPropertyBinding(object, context, propertyName, expression);
+ DesignerCustomObjectDataFork::setPropertyBinding(object, context, propertyName, expression);
+
+ //QQuickDesignerSupportProperties::setPropertyBinding(object, context, propertyName, expression);
}
void emitComponentComplete(QObject *item)
@@ -317,7 +799,8 @@ void doComponentCompleteRecursive(QObject *object, NodeInstanceServer *nodeInsta
void keepBindingFromGettingDeleted(QObject *object, QQmlContext *context, const PropertyName &propertyName)
{
- QQuickDesignerSupportProperties::keepBindingFromGettingDeleted(object, context, propertyName);
+ DesignerCustomObjectDataFork::keepBindingFromGettingDeleted(object, context, propertyName);
+ //QQuickDesignerSupportProperties::keepBindingFromGettingDeleted(object, context, propertyName);
}
bool objectWasDeleted(QObject *object)